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

495 lines
18 KiB

  1. using BPA.Helper;
  2. using BPASmartClient.IoT;
  3. using Newtonsoft.Json;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.IO;
  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 = APIHelper.GetInstance.GetRequest($"{APIurl}/api/Device/Query?clientId={ClientId}");
  149. string json = APIHelper.GetInstance.GetRequest($"{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. }