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)
{
}
}
}