终端一体化运控平台
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

408 linhas
16 KiB

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