终端一体化运控平台
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

366 rader
14 KiB

  1. using BPASmartClient.Helper;
  2. using BPASmartClient.IoT;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Security.Cryptography;
  8. using System.Text;
  9. using System.Threading;
  10. using uPLibrary.Networking.M2Mqtt;
  11. using uPLibrary.Networking.M2Mqtt.Messages;
  12. namespace BPASmartDatavDeviceClient.IoT
  13. {
  14. /// <summary>
  15. /// DataV客户端
  16. /// </summary>
  17. public class DataVReport
  18. {
  19. #region 外部调用
  20. /// <summary>
  21. /// 初始化IOT连接
  22. /// </summary>
  23. public bool Initialize(string url,string clientId,string deviceId,ref string message)
  24. {
  25. if(string.IsNullOrEmpty(url))return false;
  26. DeviceTable device;
  27. if (!CreateLinks(url,clientId,deviceId, out device))
  28. {
  29. message += $"客户端{clientId}设备{deviceId}阿里云上没有该设备。";
  30. return false;
  31. }
  32. IOT_Subscribe(BroadcastTopic);//订阅广播主题
  33. if (DatavDeviceClient.IsConnected) message += $"设备{device.devicename} {device.remark}阿里云连接成功.";
  34. else message += $"设备{device.devicename} {device.remark}阿里云连接失败.不能上报业务信息";
  35. return DatavDeviceClient.IsConnected;
  36. }
  37. /// <summary>
  38. /// 获取连接状态
  39. /// </summary>
  40. public bool GetIsConnected()
  41. {
  42. try
  43. {
  44. if (DatavDeviceClient == null || !DatavDeviceClient.IsConnected)
  45. return false;
  46. else return true;
  47. }
  48. catch (Exception ex)
  49. {
  50. return false;
  51. throw;
  52. }
  53. }
  54. /// <summary>
  55. /// 断开连接
  56. /// </summary>
  57. public void Disconnect()
  58. {
  59. if (DatavDeviceClient != null)
  60. {
  61. DatavDeviceClient.Disconnect();
  62. }
  63. }
  64. /// <summary>
  65. /// 发布消息
  66. /// </summary>
  67. /// <param name="topic"></param>
  68. /// <param name="message"></param>
  69. public void IOT_Publish(string topic, string message)
  70. {
  71. var id = DatavDeviceClient.Publish(topic, Encoding.UTF8.GetBytes(message));
  72. }
  73. /// <summary>
  74. /// 订阅主题
  75. /// </summary>
  76. /// <param name="topic"></param>
  77. public void IOT_Subscribe(string topic)
  78. {
  79. if (SubTopicList.Contains(topic))
  80. {
  81. SubTopicList.Add(topic);
  82. }
  83. DatavDeviceClient.Subscribe(new string[] { topic }, new byte[] { 0 });
  84. }
  85. #endregion
  86. #region 私有函数
  87. /// <summary>
  88. /// 设置变量
  89. /// </summary>
  90. /// <param name="_ProductKey"></param>
  91. /// <param name="_DeviceName"></param>
  92. /// <param name="_DeviceSecret"></param>
  93. /// <param name="_RegionId"></param>
  94. private void SetValue(string _ProductKey, string _DeviceName, string _DeviceSecret, string _RegionId = "cn-shanghai")
  95. {
  96. ProductKey = _ProductKey;
  97. DeviceName = _DeviceName;
  98. DeviceSecret = _DeviceSecret;
  99. RegionId = _RegionId;
  100. PubTopic = "/sys/" + ProductKey + "/" + DeviceName + "/thing/event/property/post";
  101. SubTopic = "/sys/" + ProductKey + "/" + DeviceName + "/thing/event/property/set";
  102. UserPubTopic = "/" + ProductKey + "/" + DeviceName + "/user/update";
  103. UserSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/get";
  104. BroadcastTopic = "/broadcast/" + ProductKey + "/" + DeviceName + "_SetDevice";
  105. AlarmSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/AlarmMessage";
  106. LogsSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/ExceptionLogs";
  107. HeartbeatSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/HeartbeatAndState";
  108. TargetStatusSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/TargetStatus";
  109. ScreenShowPubTopic = "/" + ProductKey + "/" + DeviceName + "/user/ScreenShow";
  110. }
  111. /// <summary>
  112. /// 创建连接
  113. /// </summary>
  114. private bool CreateLinks(string url, string clientId, string deviceId, out DeviceTable device)
  115. {
  116. try
  117. {
  118. string json = HttpRequestHelper.HttpGetRequest($"{url}/api/Device/Query?clientId={clientId}&deviceId={deviceId}");
  119. JsonMsg<List<DeviceTable>> jsonMsg = Tools.JsonToObjectTools<JsonMsg<List<DeviceTable>>>(json);
  120. if (jsonMsg.obj != null && jsonMsg.obj.data != null)
  121. {
  122. device = jsonMsg.obj.data.FirstOrDefault();
  123. if (device == null) return false;
  124. SetValue(device.productkey, device.devicename, device.devicesecret);
  125. IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
  126. string _clientId = host.AddressList.FirstOrDefault(
  127. ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).ToString();
  128. string t = Convert.ToString(DateTimeOffset.Now.ToUnixTimeMilliseconds());
  129. string signmethod = "hmacmd5";
  130. Dictionary<string, string> dict = new Dictionary<string, string>();
  131. dict.Add("productKey", ProductKey);
  132. dict.Add("deviceName", DeviceName);
  133. dict.Add("clientId", _clientId);
  134. dict.Add("timestamp", t);
  135. mqttUserName = DeviceName + "&" + ProductKey;
  136. mqttPassword = IotSignUtils.sign(dict, DeviceSecret, signmethod);
  137. mqttClientId = clientId + "|securemode=3,signmethod=" + signmethod + ",timestamp=" + t + "|";
  138. targetServer = ProductKey + ".iot-as-mqtt." + RegionId + ".aliyuncs.com";
  139. ConnectMqtt(targetServer, mqttClientId, mqttUserName, mqttPassword);
  140. return true;
  141. }
  142. else
  143. {
  144. device = null;
  145. return false;
  146. }
  147. }
  148. catch (Exception ex)
  149. {
  150. device = null;
  151. return false;
  152. }
  153. }
  154. /// <summary>
  155. /// MQTT创建连接
  156. /// </summary>
  157. /// <param name="targetServer"></param>
  158. /// <param name="mqttClientId"></param>
  159. /// <param name="mqttUserName"></param>
  160. /// <param name="mqttPassword"></param>
  161. private void ConnectMqtt(string targetServer, string mqttClientId, string mqttUserName, string mqttPassword)
  162. {
  163. DatavDeviceClient = new MqttClient(targetServer);
  164. DatavDeviceClient.ProtocolVersion = MqttProtocolVersion.Version_3_1_1;
  165. DatavDeviceClient.Connect(mqttClientId, mqttUserName, mqttPassword, false, 60);
  166. DatavDeviceClient.MqttMsgPublishReceived += Client_MqttMsgPublishReceived;
  167. DatavDeviceClient.ConnectionClosed += Client_ConnectionClosed;
  168. }
  169. /// <summary>
  170. /// MQTT 断开事件
  171. /// </summary>
  172. /// <param name="sender"></param>
  173. /// <param name="e"></param>
  174. private static void Client_ConnectionClosed(object sender, EventArgs e)
  175. {
  176. // 尝试重连
  177. _TryContinueConnect();
  178. }
  179. /// <summary>
  180. /// 订阅数据接收
  181. /// </summary>
  182. /// <param name="sender"></param>
  183. /// <param name="e"></param>
  184. private static void Client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
  185. {
  186. string topic = e.Topic;
  187. string message = Encoding.UTF8.GetString(e.Message);
  188. if (DataVMessageAction != null)
  189. {
  190. DataVMessageAction.Invoke(topic, message);
  191. }
  192. }
  193. /// <summary>
  194. /// 自动重连主体
  195. /// </summary>
  196. private static void _TryContinueConnect()
  197. {
  198. Thread retryThread = new Thread(new ThreadStart(delegate
  199. {
  200. while (DatavDeviceClient == null || !DatavDeviceClient.IsConnected)
  201. {
  202. if (DatavDeviceClient.IsConnected) break;
  203. if (DatavDeviceClient == null)
  204. {
  205. DatavDeviceClient = new MqttClient(targetServer);
  206. DatavDeviceClient.ProtocolVersion = MqttProtocolVersion.Version_3_1_1;
  207. DatavDeviceClient.Connect(mqttClientId, mqttUserName, mqttPassword, false, 60);
  208. DatavDeviceClient.MqttMsgPublishReceived += Client_MqttMsgPublishReceived;
  209. DatavDeviceClient.ConnectionClosed += Client_ConnectionClosed;
  210. if (DatavDeviceClient.IsConnected)
  211. {
  212. SubTopicList?.ForEach(par =>{DatavDeviceClient.Subscribe(new string[] { par }, new byte[] { 0 }); });
  213. }
  214. Thread.Sleep(3000);
  215. continue;
  216. }
  217. try
  218. {
  219. DatavDeviceClient.Connect(mqttClientId, mqttUserName, mqttPassword, false, 60);
  220. if (DatavDeviceClient.IsConnected)
  221. {
  222. SubTopicList?.ForEach(par => { DatavDeviceClient.Subscribe(new string[] { par }, new byte[] { 0 }); });
  223. UnConnectMqtt?.Invoke("重新连接阿里云MQTT成功!");
  224. }
  225. }
  226. catch (Exception ce)
  227. {
  228. UnConnectMqtt?.Invoke("重新连接阿里云MQTT失败!");
  229. }
  230. // 如果还没连接不符合结束条件则睡2秒
  231. if (!DatavDeviceClient.IsConnected)
  232. {
  233. Thread.Sleep(2000);
  234. }
  235. }
  236. }));
  237. retryThread.Start();
  238. }
  239. #endregion
  240. #region 私有IOT连接变量
  241. private static string ProductKey = "grgpECHSL7q";
  242. private static string DeviceName = "hbldev";
  243. private static string DeviceSecret = "4ec120de0c866199183b22e2e3135aeb";
  244. private static string RegionId = "cn-shanghai";
  245. private static string mqttUserName = string.Empty;
  246. private static string mqttPassword = string.Empty;
  247. private static string mqttClientId = string.Empty;
  248. private static string targetServer = string.Empty;
  249. #endregion
  250. #region 公有变量
  251. /// <summary>
  252. /// 设备消息数据回调
  253. /// </summary>
  254. public static Action<string, string> DataVMessageAction { get; set; }
  255. /// <summary>
  256. /// 重连事件
  257. /// </summary>
  258. public static Action<string> UnConnectMqtt { get; set; }
  259. /// <summary>
  260. /// 客户端
  261. /// </summary>
  262. public static MqttClient DatavDeviceClient { get; set; }
  263. /// <summary>
  264. /// 当前设备
  265. /// </summary>
  266. public DeviceTable deviceTable { get; set; }
  267. #endregion
  268. #region 发布或订阅主题或URL地址
  269. /// <summary>
  270. /// 属性发布消息主题
  271. /// </summary>
  272. public static string PubTopic = "/" + ProductKey + "/" + DeviceName + "/user/update";
  273. /// <summary>
  274. /// 属性接收消息主题
  275. /// </summary>
  276. public static string SubTopic = "/" + ProductKey + "/" + DeviceName + "/user/get";
  277. /// <summary>
  278. /// 自定义发布消息主题
  279. /// </summary>
  280. public static string UserPubTopic = "/" + ProductKey + "/" + DeviceName + "/user/update";
  281. /// <summary>
  282. /// 自定义接收消息主题
  283. /// </summary>
  284. public static string UserSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/get";
  285. /// <summary>
  286. /// 告警订阅主题
  287. /// </summary>
  288. public static string AlarmSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/AlarmMessage";
  289. /// <summary>
  290. /// 日志订阅主题
  291. /// </summary>
  292. public static string LogsSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/ExceptionLogs";
  293. /// <summary>
  294. /// 上下线订阅主题
  295. /// </summary>
  296. public static string HeartbeatSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/HeartbeatAndState";
  297. /// <summary>
  298. /// 属性状态主题
  299. /// </summary>
  300. public static string TargetStatusSubTopic = "/" + ProductKey + "/" + DeviceName + "/user/TargetStatus";
  301. /// <summary>
  302. /// 大屏展示发布主题
  303. /// </summary>
  304. public static string ScreenShowPubTopic = "/" + ProductKey + "/" + DeviceName + "/user/ScreenShow";
  305. /// <summary>
  306. /// 广播主题
  307. /// </summary>
  308. public static string BroadcastTopic = "/broadcast/" + "grgpECHSL7q" + "/" + DeviceName + "_SetDevice";
  309. /// <summary>
  310. /// 订阅主题集合
  311. /// </summary>
  312. public static List<string> SubTopicList = new List<string>();
  313. #endregion
  314. }
  315. /// <summary>
  316. /// Iot 设备上报
  317. /// </summary>
  318. public class IotSignUtils
  319. {
  320. public static string sign(Dictionary<string, string> param,
  321. string deviceSecret, string signMethod)
  322. {
  323. string[] sortedKey = param.Keys.ToArray();
  324. Array.Sort(sortedKey);
  325. StringBuilder builder = new StringBuilder();
  326. foreach (var i in sortedKey)
  327. {
  328. builder.Append(i).Append(param[i]);
  329. }
  330. byte[] key = Encoding.UTF8.GetBytes(deviceSecret);
  331. byte[] signContent = Encoding.UTF8.GetBytes(builder.ToString());
  332. //这里根据signMethod动态调整,本例子硬编码了: 'hmacmd5'
  333. var hmac = new HMACMD5(key);
  334. byte[] hashBytes = hmac.ComputeHash(signContent);
  335. StringBuilder signBuilder = new StringBuilder();
  336. foreach (byte b in hashBytes)
  337. signBuilder.AppendFormat("{0:x2}", b);
  338. return signBuilder.ToString();
  339. }
  340. }
  341. }