在数据采集系统中,通过ModbusTcp采集到数据之后,再通过MQTT转发到其他应用
MQTT操作
安装MQTT
mqtt介绍和环境安装
使用MQTT
在C#/Net中使用Mqtt
MQTT类封装
MQTT配置类
public class MqttConfig
{
public string Ip {
get; set; }
public int Port {
get; set; } = 1883;
public string Username {
get; set; } = "admin";
public string Password {
get; set; } = "12345";
public string ClientId {
get; set; }
}
MQTT控制类
public class MqttControllor
{
private MqttConfig _config;
private string _clientId;
MqttClientOptions _clientOptions;
private IMqttClient _mqttClient;
private readonly object _topicActionsLock = new object();
private Dictionary<string, Action<string, string>> _topicActions;
public MqttControllor(MqttConfig config, bool isAutoConnect = true)
{
_topicActions = new Dictionary<string, Action<string, string>>();
_config = config;
_clientId = config.ClientId == "" ? Guid.NewGuid().ToString() : config.ClientId;
MqttClientOptionsBuilder optionsBuilder = new MqttClientOptionsBuilder()
.WithTcpServer(_config.Ip, _config.Port)
.WithCredentials(_config.Username, _config.Password)
.WithClientId(_clientId);
_clientOptions = optionsBuilder.Build();
_mqttClient = new MqttFactory().CreateMqttClient();
// 客户端连接关闭事件
_mqttClient.DisconnectedAsync += MqttClient_DisconnectedAsync;
//客户端接收消息事件
_mqttClient.ApplicationMessageReceivedAsync +=
MqttClient_ApplicationMessageReceivedAsync;
if (isAutoConnect)
{
Task.Run(() =>
{
MqttConnect();
});
}
}
/// <summary>
/// 接收消息
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private async Task MqttClient_ApplicationMessageReceivedAsync(
MqttApplicationMessageReceivedEventArgs args
)
{
try
{
Console.WriteLine($"收到消息:{
args.ApplicationMessage.Topic}");
if (_topicActions.ContainsKey(args.ApplicationMessage.Topic))
{
_topicActions[args.ApplicationMessage.Topic]
.Invoke(
args.ApplicationMessage.Topic,
Encoding.UTF8.GetString(args.ApplicationMessage.Payload)
);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private Task MqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
{
Console.WriteLine($"客户端已断开与服务端的连接……");
//断开重连
_mqttClient = new MqttFactory().CreateMqttClient();
MqttConnect();
return Task.CompletedTask;
}
public void MqttConnect()
{
while (!_mqttClient.IsConnected)
{
try
{
Console.WriteLine($"正在连接……");
_mqttClient.ConnectAsync(_clientOptions).GetAwaiter().GetResult();
}
catch (Exception ex)
{
Task.Delay(1000).Wait();
Console.WriteLine("连接mqtt服务器失败");
}
}
Console.WriteLine($"客户端已连接到服务端……");
//连接成功,订阅主题
lock (_topicActionsLock)
{
foreach (var item in _topicActions)
{
_mqttClient.SubscribeAsync(item.Key);
}
}
}
/// <summary>
/// 订阅主题,添加到_topicActions,如果已经连接,则直接订阅
/// </summary>
/// <param name="topic"></param>
/// <param name="topicAction"></param>
public void SubscribeTopic(string topic, Action<string, string> topicAction)
{
lock (_topicActionsLock)
{
if (!_topicActions.ContainsKey(topic))
{
_topicActions.Add(topic, topicAction);
if (_mqttClient.IsConnected)
{
_mqttClient.SubscribeAsync(topic);
}
}
}
}
/// <summary>
/// 推送消息
/// </summary>
/// <param name="topic">主题</param>
/// <param name="data">消息内容</param>
/// <param name="qsLevel"></param>
/// <param name="retain"></param>
public void Publish(string topic, string data, int qsLevel = 0, bool retain = false)
{
qsLevel = Math.Clamp(qsLevel, 0, 2);
if (!_mqttClient.IsConnected)
{
throw new Exception("mqtt未连接");
}
var message = new MqttApplicationMessage
{
Topic = topic,
PayloadSegment = Encoding.UTF8.GetBytes(data),
QualityOfServiceLevel = (MqttQualityOfServiceLevel)qsLevel,
Retain = retain // 服务端是否保留消息。true为保留,如果有新的订阅者连接,就会立马收到该消息。
};
_mqttClient.PublishAsync(message);
}
}
Mqtt实现采集数据转发
DeviceLink中增加UID
属性作为设备ID
public class DeviceLink
{
/// <summary>
/// 设备ID
/// </summary>
public string UID {
get; set; }
public string Ip {
get; set; }
public int Port {
get; set; }
/// <summary>
/// 从站地址
/// </summary>
public int SlaveID {
get; set; } = 1;
}
创建一个消息格式的类
DeviceId为设备Id信息,Data键值对组为数据信息,key为点位ID名称,value为点位值
class DeviceMessage
{
public string DeviceId {
get; set; }
public Dictionary<string, object> Data {
get; set; }
public DeviceMessage()
{
Data = new Dictionary<string, object>();
}
}
使用
采集立刻推送
private static void ModbusTcp_ValueUpdated(RegisterPoint point, object value)
{
try
{
DeviceMessage device = new DeviceMessage {
DeviceId = deviceLink.UID };
device.Data.Add(point.UID, value);
var data = JsonSerializer.Serialize(device);
MqttControllor.Publish($"Device/{
deviceLink.UID}/Update", data); //采集立刻推送
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine($"Point:{
point.UID}-->Value:{
value}");
}
定时推送
while (true)
{
DeviceMessage device = new DeviceMessage {
DeviceId = deviceLink.UID };
foreach (RegisterPoint point in points)
{
Console.WriteLine($"Point:{
point.UID}-->Value:{
point.Value}");
device.Data.Add(point.UID, point.Value);
}
var data = JsonSerializer.Serialize(device);
MqttControllor.Publish($"Device/{
deviceLink.UID}/Time", data); //定时推送
Thread.Sleep(3000);
}
阶段
到此完成了一个最最基础的采集程序