终端一体化运控平台
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

463 lines
19 KiB

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