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

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