终端一体化运控平台
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

468 строки
17 KiB

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