终端一体化运控平台
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.
 
 
 

468 lines
19 KiB

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