提交全部资料
This commit is contained in:
328
4.测试上位机/VS工程/MqttClientWin/FmMqttClient.cs
Normal file
328
4.测试上位机/VS工程/MqttClientWin/FmMqttClient.cs
Normal file
@@ -0,0 +1,328 @@
|
||||
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);
|
||||
}
|
||||
|
||||
// <summary>
|
||||
// 服务器连接成功
|
||||
// </summary>
|
||||
// <param name="sender"></param>
|
||||
// <param name="e"></param>
|
||||
private void MqttClient_Connected(object sender, EventArgs e)
|
||||
{
|
||||
Invoke((new Action(() =>
|
||||
{
|
||||
txtReceiveMessage.AppendText("已连接到MQTT服务器!" + Environment.NewLine);
|
||||
|
||||
})));
|
||||
}
|
||||
|
||||
// <summary>
|
||||
// 断开服务器连接
|
||||
// </summary>
|
||||
// <param name="sender"></param>
|
||||
// <param name="e"></param>
|
||||
private void MqttClient_Disconnected(object sender, EventArgs e)
|
||||
{
|
||||
Invoke((new Action(() =>
|
||||
{
|
||||
txtReceiveMessage.AppendText("已断开MQTT连接!" + Environment.NewLine);
|
||||
})));
|
||||
}
|
||||
|
||||
// <summary>
|
||||
// 接收到消息
|
||||
// </summary>
|
||||
// <param name="sender"></param>
|
||||
// <param name="e"></param>
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// <summary>
|
||||
// 订阅消息
|
||||
// </summary>
|
||||
// <param name="sender"></param>
|
||||
// <param name="e"></param>
|
||||
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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user