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

IOTDevServer.cs 17 KiB

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