RS485.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. using System.IO.Ports;
  2. namespace AmrControl.Common
  3. {
  4. public delegate void DltReceiveCarStateCmd(CarStateCmd carState);
  5. public class RS485
  6. {
  7. SerialPort serial;
  8. string portName = "COM4";
  9. int baudRate = 38400;
  10. int dataBits = 8;
  11. StopBits stopBits = StopBits.One;
  12. Parity parity = Parity.None;
  13. byte[] buf;
  14. int len;
  15. int start = 0, end = 0;
  16. bool IsStart = false;
  17. ushort head; //两字节的帧头、帧尾
  18. //车的定时更新状态事件
  19. public event DltReceiveCarStateCmd ReceiveCarStateCmdEvent;
  20. //车的到达目标地址的事件
  21. public event DltReceiveCarStateCmd CarArrivedEvent;
  22. //车上线的事件
  23. public event DltReceiveCarStateCmd CarConnectedEvent;
  24. //车离线的事件
  25. public event DltReceiveCarStateCmd CarDisconnectedEvent;
  26. public int CarCount
  27. {
  28. get
  29. {
  30. if(cars == null)
  31. {
  32. return 0;
  33. }
  34. else
  35. {
  36. return cars.Count;
  37. }
  38. }
  39. }
  40. private object lockobj = new object();
  41. //所有的车
  42. Dictionary<ushort, CarStateCmd> cars;
  43. public RS485()
  44. {
  45. buf = new byte[10240];
  46. len = 0;
  47. start = -1;
  48. end = 0;
  49. cars = new Dictionary<ushort, CarStateCmd>();
  50. portName = AppHelper.ReadAppSettings("zigbee", "Com");
  51. baudRate = Int32.Parse(AppHelper.ReadAppSettings("zigbee", "baudRate"));
  52. }
  53. private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
  54. {
  55. //报文的首尾都是0x7e,判断思路是当发现有两个7e时候,结合长度位判断是否是一条报文
  56. //如果是则提取报文并把剩余数据前移到索引0,如果不是则把第2个7e之前的数据抛弃掉
  57. int size = serial.BytesToRead;
  58. //正常情况下,不应该出现超出缓冲的报文,如果出现则可能是出错了,直接把索引置0
  59. if (len + size > buf.Length)
  60. {
  61. //以缓存大小为主
  62. size = buf.Length - len;
  63. }
  64. serial.Read(buf, len, size);
  65. int olen = len;
  66. len += size;
  67. for (int i = olen; i < len; i++)
  68. {
  69. head = (ushort)(((head << 8) & 0xff00) + buf[i]);
  70. //这里要求不能连续出现三个0x7e
  71. if(head == 0x7e7e)
  72. {
  73. start = i - 1;
  74. }
  75. else if (head == 0x0d0a)
  76. {
  77. end = i;
  78. if((start >=0) &&((end - start + 1 )== buf[start + 2]))
  79. {
  80. //帧长度正确
  81. byte[] datas = new byte[end - start + 1];
  82. Array.Copy(buf, start, datas, 0, datas.Length);
  83. HandleReceive(datas);
  84. //移动剩余数据到0;
  85. for (int j = end + 1; j < len; j++)
  86. {
  87. buf[j - end - 1] = buf[j];
  88. }
  89. len = len - end - 1; //重新调整长度
  90. i = -1;//下一个循环从0开始
  91. start = -1;//第2个7前移动到0
  92. end = 0;
  93. }
  94. else
  95. {
  96. start = -1;
  97. end = 0;
  98. }
  99. }
  100. }
  101. //如果处理完了缓存依然是满的,则抛弃数据,重新归零
  102. if (len >= buf.Length)
  103. {
  104. //相当于初始化缓冲
  105. len = 0;
  106. start = -1;
  107. end = 0;
  108. }
  109. }
  110. //处理接收到的报文,先转义再校验
  111. bool HandleReceive(byte[] datas)
  112. {
  113. //长度不是正常报文的长度
  114. if (datas == null || datas.Length < 8 || IsStart == false || AmrManager.ProcessExit)
  115. return false;
  116. ushort carid = (ushort)(datas[4] + ((datas[5] << 8) & 0xff00));
  117. //命令码
  118. switch (datas[3])
  119. {
  120. case 0x01:
  121. //不存在则创建
  122. CarStateCmd cmd = null;
  123. bool ok = false;
  124. //保留最新的
  125. lock (lockobj)
  126. {
  127. if (cars.ContainsKey((ushort)carid))
  128. {
  129. cmd = (CarStateCmd)cars[carid];
  130. cmd.sendTime = DateTime.Now;
  131. ok = true;
  132. }
  133. else
  134. {
  135. cmd = new CarStateCmd();
  136. cmd.iCarID = carid;
  137. cmd.carId = "0x" + carid.ToString("X2");
  138. cmd.sendTime = DateTime.Now;
  139. cars.Add(carid, cmd);
  140. }
  141. }
  142. //这里的写和心跳里面的读不能同时执行
  143. HandleCarStateMsg(cmd, datas);
  144. if (ok == false)
  145. {
  146. //车辆上线是一个事件,需要对外抛出事件
  147. if (CarConnectedEvent != null)
  148. {
  149. Task.Run(() => {
  150. cmd.msgType = MessageType.MSG_TYPE_CAR_Connected;
  151. CarConnectedEvent(cmd);
  152. });
  153. }
  154. }
  155. break;
  156. case 0x04:
  157. CarStateCmd cmd2 = new CarStateCmd();
  158. cmd2.carId = "0x" + carid.ToString("X2");
  159. cmd2.iCarID = carid;
  160. HandleCarArrivedMsg(cmd2, datas);
  161. //车辆到位是一个事件,需要对外抛出事件
  162. if(CarArrivedEvent != null)
  163. {
  164. Task.Run(() => {
  165. CarArrivedEvent(cmd2);
  166. });
  167. }
  168. break;
  169. default:
  170. break;
  171. }
  172. //获取锁,移除旧信息
  173. //如果长度值匹配得上,就是正常的包
  174. return true;
  175. }
  176. //处理车的心跳消息,更新到车的对象中
  177. void HandleCarStateMsg(CarStateCmd cmd , byte[] datas)
  178. {
  179. cmd.msgType = MessageType.MSG_TYPE_CAR_REPORT;
  180. cmd.powerLevel = datas[8].ToString();
  181. cmd.speed = (datas[21] * 0.1).ToString("0.00");
  182. cmd.state = (datas[22] > 0) ? CarState.Moving : CarState.Stoped; //根据要求,未完整,还有是否故障,是否中断,是否到达等
  183. cmd.loading = datas[23] > 0 ? true : false;
  184. cmd.isEnable = datas[24] > 0? true : false;
  185. int x, y, z, yaw;
  186. x = (int)(datas[9] + ((datas[10] << 8) & 0xff00));
  187. y = (int)(datas[11] + ((datas[12] << 8) & 0xff00));
  188. z = (int)(datas[13] + ((datas[14] << 8) & 0xff00));
  189. cmd.curPos = new Position(x,y,z, (z * 0.01).ToString("0.00"));
  190. x = (int)(datas[15] + ((datas[16] << 8) & 0xff00));
  191. y = (int)(datas[17] + ((datas[18] << 8) & 0xff00));
  192. z = (int)(datas[19] + ((datas[20] << 8) & 0xff00));
  193. cmd.destPos = new Position(x,y,z, (z * 0.01).ToString("0.00"));
  194. if(datas[27] == 0)
  195. {
  196. cmd.type = CarType.AMR;
  197. }
  198. else
  199. {
  200. cmd.type = CarType.AGV;
  201. }
  202. //几下更新数据的时间,更新数据与发送数据到mqtt总线上的时间不一样
  203. cmd.sendTime = DateTime.Now;
  204. //此次z和yaw赋值一样
  205. //需要z和yaw分开,要和老蔡商量
  206. }
  207. //心跳和车到达消息都存在车的属性缓存消息结构中,只是中间产生了车到达的事件
  208. void HandleCarArrivedMsg(CarStateCmd cmd , byte[] datas)
  209. {
  210. cmd.state = CarState.Arrived; //小车到达的消息,需要和老马确认
  211. cmd.msgType = MessageType.MSG_TYPE_CAR_Arrived;
  212. int x, y, z, yaw;
  213. x = (int)(datas[8] + ((datas[9] << 8) & 0xff00));
  214. y = (int)(datas[10] + ((datas[11] << 8) & 0xff00));
  215. z = (int)(datas[12] + ((datas[13] << 8) & 0xff00));
  216. cmd.curPos = new Position(x, y, z, (z*0.01).ToString("0.00"));
  217. x = (int)(datas[14] + ((datas[15] << 8) & 0xff00));
  218. y = (int)(datas[16] + ((datas[17] << 8) & 0xff00));
  219. z = (int)(datas[18] + ((datas[19] << 8) & 0xff00));
  220. cmd.destPos = new Position(x, y, z, (z * 0.01).ToString("0.00"));
  221. //此次z和yaw赋值一样
  222. if (datas[27] == 0)
  223. {
  224. cmd.type = CarType.AMR;
  225. }
  226. else
  227. {
  228. cmd.type = CarType.AGV;
  229. }
  230. //几下更新数据的时间,更新数据与发送数据到mqtt总线上的时间不一样
  231. cmd.sendTime = DateTime.Now;
  232. }
  233. //串口对外发送
  234. public bool Send(byte[] buffer)
  235. {
  236. if (buffer == null)
  237. return false;
  238. if (serial == null || serial.IsOpen == false)
  239. Start();
  240. if (serial != null && serial.IsOpen)
  241. {
  242. lock (this)
  243. {
  244. serial.Write(buffer, 0, buffer.Length);
  245. }
  246. }
  247. return true;
  248. }
  249. //串口启动后的定期执行事件,如果有内容,则对外发送定时消息
  250. private void CreateHeartBeat()
  251. {
  252. // return;
  253. List<CarStateCmd> offlinecars = new List<CarStateCmd>();
  254. List<CarStateCmd> onlinecars = new List<CarStateCmd>();
  255. Task.Run(() => {
  256. while (true)
  257. {
  258. if (IsStart == false || AmrManager.ProcessExit)
  259. break;
  260. Thread.Sleep(350);
  261. //保留最新的,lock内的内容要尽快结束,并且不要出错,另一个线程会把最新的车状态替换到cars里面。
  262. lock (lockobj)
  263. {
  264. offlinecars.Clear();
  265. onlinecars.Clear();
  266. List<CarStateCmd> carstates = cars.Values.ToList();
  267. foreach (var item in carstates)
  268. {
  269. //首先移除超时的消息,正常是每辆车100ms都有数据上来
  270. if ((DateTime.Now - item.sendTime).TotalMilliseconds > 2000)
  271. {
  272. //超时,认为掉线
  273. offlinecars.Add(item);
  274. //移除
  275. cars.Remove((ushort)item.iCarID);
  276. }
  277. else
  278. {
  279. onlinecars.Add(item);
  280. }
  281. }
  282. }
  283. //对offlinecars,产生离线消息
  284. //车辆上线是一个事件,需要对外抛出事件
  285. if (CarDisconnectedEvent != null)
  286. {
  287. foreach (var item in offlinecars)
  288. {
  289. item.msgType = MessageType.MSG_TYPE_CAR_Disconnected;
  290. CarDisconnectedEvent(item);
  291. }
  292. }
  293. //对onlinecars,产生状态变更消息
  294. if (ReceiveCarStateCmdEvent != null)
  295. {
  296. foreach (var item in onlinecars)
  297. {
  298. ReceiveCarStateCmdEvent(item);
  299. }
  300. }
  301. }
  302. });
  303. }
  304. public bool Start()
  305. {
  306. if (serial != null)
  307. serial.Close();
  308. serial = new SerialPort();
  309. serial.PortName = portName;
  310. serial.BaudRate = baudRate;
  311. serial.DataBits = dataBits;
  312. serial.StopBits = stopBits;
  313. serial.Parity = parity;
  314. serial.ReceivedBytesThreshold = 1;
  315. len = 0;
  316. start = 0;
  317. end = 0;
  318. serial.DataReceived += Serial_DataReceived;
  319. try
  320. {
  321. serial.Open();
  322. IsStart = true;
  323. //创建定时程序
  324. CreateHeartBeat();
  325. }
  326. catch (Exception)
  327. {
  328. //串口线被拔掉时会有异常
  329. }
  330. return serial.IsOpen;
  331. }
  332. public bool Stop()
  333. {
  334. if (serial != null)
  335. {
  336. serial.Close();
  337. serial = null;
  338. IsStart = false;
  339. len = 0;
  340. start = -1;
  341. end = 0;
  342. }
  343. return true;
  344. }
  345. public void Dispose()
  346. {
  347. try
  348. {
  349. Stop();
  350. }
  351. catch (Exception)
  352. {
  353. }
  354. }
  355. //对车发送命令
  356. public bool SendCarCmd(CallCarCmd cmd)
  357. {
  358. byte[] datas = new byte[36];
  359. datas[0] = 0xfd; //zigbee帧头
  360. datas[1] = 32; //数据长度
  361. ushort id = Convert.ToUInt16(cmd.carId, 16);
  362. datas[2] = (byte)(id & 0xff);
  363. datas[3] = (byte)((id >> 8) & 0xff);
  364. datas[4] = 0x7e;
  365. datas[4 + 1] = 0x7e;
  366. datas[4 + 2] = 32;
  367. datas[4 + 3] = 0x02; //命令
  368. datas[4 + 6] = (byte)(id & 0xff);
  369. datas[4 + 7] = (byte)((id >> 8) & 0xff);
  370. datas[4 + 8] = (byte)(cmd.destPos.x & 0xff);
  371. datas[4 + 9] = (byte)((cmd.destPos.x >> 8) & 0xff);
  372. datas[4 + 10] = (byte)(cmd.destPos.y & 0xff);
  373. datas[4 + 11] = (byte)((cmd.destPos.y >> 8) & 0xff);
  374. datas[4 + 12] = (byte)(cmd.destPos.z & 0xff);
  375. datas[4 + 13] = (byte)((cmd.destPos.z >> 8) & 0xff);
  376. if(id != 0xfe04)
  377. {
  378. datas[4 + 27] = (byte)CarType.AMR;
  379. }
  380. else
  381. {
  382. datas[4 + 27] = (byte)CarType.AGV;
  383. }
  384. datas[4 + 30] = 0x0d;
  385. datas[4 + 31] = 0x0a;
  386. return Send(datas);
  387. }
  388. //让车停
  389. public bool SendCarStop(StopCarCmd cmd)
  390. {
  391. byte[] datas = new byte[36];
  392. datas[0] = 0xfd; //zigbee帧头
  393. datas[1] = 32; //数据长度
  394. ushort id = Convert.ToUInt16(cmd.carId, 16);
  395. datas[2] = (byte)(id & 0xff);
  396. datas[3] = (byte)((id >> 8) & 0xff);
  397. datas[4] = 0x7e;
  398. datas[4 + 1] = 0x7e;
  399. datas[4 + 2] = 32;
  400. datas[4 + 3] = 0x03; //命令
  401. datas[4 + 6] = (byte)(id & 0xff); //目的ID
  402. datas[4 + 7] = (byte)((id >> 8) & 0xff);
  403. if (id != 0xfe04)
  404. {
  405. datas[4 + 27] = (byte)CarType.AMR;
  406. }
  407. else
  408. {
  409. datas[4 + 27] = (byte)CarType.AGV;
  410. }
  411. datas[4 + 30] = 0x0d;
  412. datas[4 + 31] = 0x0a;
  413. return Send(datas);
  414. }
  415. //对车使能
  416. public bool SendCarEnable(EnableCarCmd cmd)
  417. {
  418. byte[] datas = new byte[36];
  419. datas[0] = 0xfd; //zigbee帧头
  420. datas[1] = 32; //数据长度
  421. ushort id = Convert.ToUInt16(cmd.carId, 16);
  422. datas[2] = (byte)(id & 0xff);
  423. datas[3] = (byte)((id >> 8) & 0xff);
  424. datas[4] = 0x7e;
  425. datas[4 + 1] = 0x7e;
  426. datas[4 + 2] = 32;
  427. datas[4 + 3] = 0x05; //命令
  428. datas[4 + 6] = (byte)(id & 0xff); //目的ID
  429. datas[4 + 7] = (byte)((id >> 8) & 0xff);
  430. if (id != 0xfe04)
  431. {
  432. datas[4 + 27] = (byte)CarType.AMR;
  433. }
  434. else
  435. {
  436. datas[4 + 27] = (byte)CarType.AGV;
  437. }
  438. datas[4 + 30] = 0x0d;
  439. datas[4 + 31] = 0x0a;
  440. return Send(datas);
  441. }
  442. //停止车使能
  443. public bool SendCarDisable(DisableCarCmd cmd)
  444. {
  445. byte[] datas = new byte[36];
  446. datas[0] = 0xfd; //zigbee帧头
  447. datas[1] = 32; //数据长度
  448. ushort id = Convert.ToUInt16(cmd.carId, 16);
  449. datas[2] = (byte)(id & 0xff);
  450. datas[3] = (byte)((id >> 8) & 0xff);
  451. datas[4] = 0x7e;
  452. datas[4 + 1] = 0x7e;
  453. datas[4 + 2] = 32;
  454. datas[4 + 3] = 0x06; //命令
  455. datas[4 + 6] = (byte)(id & 0xff); //目的ID
  456. datas[4 + 7] = (byte)((id >> 8) & 0xff);
  457. if (id != 0xfe04)
  458. {
  459. datas[4 + 27] = (byte)CarType.AMR;
  460. }
  461. else
  462. {
  463. datas[4 + 27] = (byte)CarType.AGV;
  464. }
  465. datas[4 + 30] = 0x0d;
  466. datas[4 + 31] = 0x0a;
  467. return Send(datas);
  468. }
  469. public bool SendCarShowImage(CarShowCmd cmd)
  470. {
  471. byte[] datas = new byte[36];
  472. datas[0] = 0xfd; //zigbee帧头
  473. datas[1] = 32; //数据长度
  474. ushort id = Convert.ToUInt16(cmd.carId, 16);
  475. datas[2] = (byte)(id & 0xff);
  476. datas[3] = (byte)((id >> 8) & 0xff);
  477. datas[4] = 0x7e;
  478. datas[4 + 1] = 0x7e;
  479. datas[4 + 2] = 32;
  480. datas[4 + 3] = 0x07; //命令
  481. datas[4 + 6] = (byte)(id & 0xff); //目的ID
  482. datas[4 + 7] = (byte)((id >> 8) & 0xff);
  483. //为了解析简单,数组必须是2
  484. if(cmd.channelIds != null && cmd.picCodes != null && cmd.picCodes.Length > 1 && cmd.channelIds.Length > 1)
  485. {
  486. datas[8] = cmd.picCodes[0];
  487. datas[9] = cmd.picCodes[1];
  488. }
  489. //else
  490. //{
  491. // //否则索引号为0,默认不显示
  492. //}
  493. if (id != 0xfe04)
  494. {
  495. datas[4 + 27] = (byte)CarType.AMR;
  496. }
  497. else
  498. {
  499. datas[4 + 27] = (byte)CarType.AGV;
  500. }
  501. datas[4 + 30] = 0x0d;
  502. datas[4 + 31] = 0x0a;
  503. return Send(datas);
  504. }
  505. }
  506. }