using MetroFramework.Forms; using MQTTnet; using MQTTnet.Core; using MQTTnet.Core.Client; using MQTTnet.Core.Packets; using MQTTnet.Core.Protocol; using System; using System.Collections.Generic; using System.Drawing; using System.Text; using System.Text.Json; using System.Threading.Tasks; using System.Windows.Forms; namespace MqttClientWin { public partial class FmMqttClient : MetroForm { private MqttClient mqttClient = null; public FmMqttClient() { InitializeComponent(); txtSubTopic.Text = "/Uplink"; txtSubService.Text = "mqtt.usot.top"; txtReceiveMessage.AppendText("Copyright © 2025 路怀帅. All rights reserved." + Environment.NewLine); } // // 服务器连接成功 // // // private void MqttClient_Connected(object sender, EventArgs e) { Invoke((new Action(() => { txtReceiveMessage.AppendText("已连接到MQTT服务器!" + Environment.NewLine); }))); } // // 断开服务器连接 // // // private void MqttClient_Disconnected(object sender, EventArgs e) { Invoke((new Action(() => { txtReceiveMessage.AppendText("已断开MQTT连接!" + Environment.NewLine); }))); } // // 接收到消息 // // // private void MqttClient_ApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e) { Invoke((Action)(() => { // 原始消息输出到 txtReceiveMessage(保持不变) string payloadStr = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); txtReceiveMessage.AppendText($">> {payloadStr}{Environment.NewLine}"); txtMessage.Clear(); // 结构化解析结果输出到 txtMessage txtMessage.AppendText($"📩 来自主题 {e.ApplicationMessage.Topic} 的消息 [{DateTime.Now:HH:mm:ss}]{Environment.NewLine}"); try { using (var doc = JsonDocument.Parse(payloadStr)) { var root = doc.RootElement; // 提取和显示其他信息... string deviceName = root.TryGetProperty("deviceName", out var deviceNameProp) ? deviceNameProp.GetString() : "N/A"; string devEUI = root.TryGetProperty("devEUI", out var devEUIProp) ? devEUIProp.GetString() : "N/A"; string gatewayTimeStr = root.TryGetProperty("gatewayTime", out var gatewayTimeProp) ? gatewayTimeProp.GetString() : "N/A"; string localTime = "N/A"; if (!string.IsNullOrEmpty(gatewayTimeStr) && gatewayTimeStr != "N/A") { if (DateTimeOffset.TryParse(gatewayTimeStr, out var dto)) { localTime = dto.LocalDateTime.ToString("yyyy-MM-dd HH:mm:ss"); } } // 提取 rxInfo[0] 中的 rssi 和 loRaSNR string rssi = "N/A"; string loRaSNR = "N/A"; if (root.TryGetProperty("rxInfo", out var rxInfoArray) && rxInfoArray.ValueKind == JsonValueKind.Array && rxInfoArray.GetArrayLength() > 0) { var gatewayInfo = rxInfoArray[0]; rssi = gatewayInfo.TryGetProperty("rssi", out var rssiProp) ? rssiProp.GetInt32().ToString() : "N/A"; loRaSNR = gatewayInfo.TryGetProperty("loRaSNR", out var snrProp) ? snrProp.GetDouble().ToString("F1") : "N/A"; } // 显示提取的字段 txtMessage.AppendText($"🔧 设备名称: {deviceName}{Environment.NewLine}"); txtMessage.AppendText($"📡 设备EUI: {devEUI}{Environment.NewLine}"); txtMessage.AppendText($"🕒 网关时间: {localTime}{Environment.NewLine}"); txtMessage.AppendText($"📶 信号强度 (RSSI): {rssi} dBm{Environment.NewLine}"); txtMessage.AppendText($"⚡ 信噪比 (LoRaSNR): {loRaSNR} dB{Environment.NewLine}"); // 处理解码的 data 字段 if (root.TryGetProperty("data", out var dataElement)) { string base64Data = dataElement.GetString(); if (!string.IsNullOrEmpty(base64Data)) { try { byte[] decodedBytes = Convert.FromBase64String(base64Data); string decodedJson = Encoding.UTF8.GetString(decodedBytes); using (var innerDoc = JsonDocument.Parse(decodedJson)) { var innerRoot = innerDoc.RootElement; // 更新按钮状态 UpdateButtonState(innerRoot, "T", btn_t); UpdateButtonState(innerRoot, "R", btn_r); UpdateButtonState(innerRoot, "C", btn_c); // 对于E字段,由于其对应多个按钮且条件不同,单独处理 if (innerRoot.TryGetProperty("E", out var eProp)) { string eStr = eProp.GetString(); if (int.TryParse(eStr, out int eValue)) { btn_s.BackColor = (eValue == 3) ? Color.Lime : Color.Gainsboro; btn_q.BackColor = (eValue == 4) ? Color.Lime : Color.Gainsboro; btn_e.BackColor = (eValue == 5) ? Color.Lime : Color.Gainsboro; btn_m.BackColor = (eValue == 6) ? Color.Lime : Color.Gainsboro; btn_p.BackColor = (eValue == 7) ? Color.Lime : Color.Gainsboro; btn_n.BackColor = (eValue == 8) ? Color.Lime : Color.Gainsboro; } else { btn_s.BackColor = Color.Gainsboro; btn_q.BackColor = Color.Gainsboro; btn_e.BackColor = Color.Gainsboro; btn_m.BackColor = Color.Gainsboro; btn_p.BackColor = Color.Gainsboro; btn_n.BackColor = Color.Gainsboro; } } string prettyJson = innerRoot.ToString(); // 自动格式化 txtMessage.AppendText($"📦 Data解码:{prettyJson}{Environment.NewLine}"); } } catch (FormatException) { txtMessage.AppendText($"❌ Base64 解码失败: 数据格式错误{Environment.NewLine}"); } catch (Exception ex) { txtMessage.AppendText($"❌ 解码异常: {ex.Message}{Environment.NewLine}"); } } else { txtMessage.AppendText($"📦 Base64 data: (空){Environment.NewLine}"); } } else { txtMessage.AppendText($"📦 Base64 data: (未找到字段){Environment.NewLine}"); } } } catch (Exception ex) { txtMessage.AppendText($"❌ JSON 解析失败: {ex.Message}{Environment.NewLine}{Environment.NewLine}"); } })); } // 通用按钮状态更新方法 private void UpdateButtonState(JsonElement root, string fieldName, Button button) { if (root.TryGetProperty(fieldName, out var fieldProp)) { string valueStr = fieldProp.GetString(); // 先作为字符串获取 if (int.TryParse(valueStr, out int value)) { bool isOn = (value == 1); button.BackColor = isOn ? Color.Lime : Color.Gainsboro; } } } // // 订阅消息 // // // private async void BtnSubscribe_ClickAsync(object sender, EventArgs e) { string broker = txtSubService.Text.Trim(); string topic = txtSubTopic.Text.Trim(); if (string.IsNullOrEmpty(broker)) { MessageBox.Show("MQTT 服务器地址不能为空!"); return; } if (string.IsNullOrEmpty(topic)) { MessageBox.Show("订阅主题不能为空!"); return; } // 断开旧连接(如果已连接) if (mqttClient != null && mqttClient.IsConnected) { await mqttClient.DisconnectAsync(); } // 初始化客户端(如果尚未创建) if (mqttClient == null) { mqttClient = new MqttClientFactory().CreateMqttClient() as MqttClient; mqttClient.ApplicationMessageReceived += MqttClient_ApplicationMessageReceived; mqttClient.Connected += MqttClient_Connected; mqttClient.Disconnected += MqttClient_Disconnected; } try { var options = new MqttClientTcpOptions { Server = broker, ClientId = Guid.NewGuid().ToString().Substring(0, 5), UserName = "", Password = "", // 注意:某些版本需要 char[] CleanSession = true, // 可选:设置端口(默认1883) // Port = 1883 }; // 连接服务器 await mqttClient.ConnectAsync(options); // 连接成功后订阅主题 var topicFilter = new TopicFilter(topic, MqttQualityOfServiceLevel.AtMostOnce); await mqttClient.SubscribeAsync(topicFilter); Invoke(new Action(() => { txtReceiveMessage.AppendText($"[订阅] 已连接到 {broker} 并订阅主题: [{topic}]{Environment.NewLine}"); btnSubscribe.Enabled = false; // 防止重复点击 })); } catch (Exception ex) { Invoke(new Action(() => { txtReceiveMessage.AppendText($"[错误] 连接或订阅失败: {ex.Message}{Environment.NewLine}"); MessageBox.Show($"连接失败: {ex.Message}"); })); } } private void FmMqttClient_Load(object sender, EventArgs e) { } private void txtPubTopic_TextChanged(object sender, EventArgs e) { } private void txtSubService_TextChanged(object sender, EventArgs e) { } private void label4_Click(object sender, EventArgs e) { } private void label5_Click(object sender, EventArgs e) { } private void txtReceiveMessage_TextChanged(object sender, EventArgs e) { } private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e) { } private void button1_Click(object sender, EventArgs e) { } private void button2_Click(object sender, EventArgs e) { } private void button2_Click_1(object sender, EventArgs e) { } } }