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

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