123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- using System.IO.Ports;
- namespace AmrControl.Common
- {
- public delegate void DltReceiveCarStateCmd(CarStateCmd carState);
- public class RS485
- {
- SerialPort serial;
- string portName = "COM4";
- int baudRate = 38400;
- int dataBits = 8;
- StopBits stopBits = StopBits.One;
- Parity parity = Parity.None;
- byte[] buf;
- int len;
- int start = 0, end = 0;
- bool IsStart = false;
- ushort head; //两字节的帧头、帧尾
- //车的定时更新状态事件
- public event DltReceiveCarStateCmd ReceiveCarStateCmdEvent;
- //车的到达目标地址的事件
- public event DltReceiveCarStateCmd CarArrivedEvent;
- //车上线的事件
- public event DltReceiveCarStateCmd CarConnectedEvent;
- //车离线的事件
- public event DltReceiveCarStateCmd CarDisconnectedEvent;
- public int CarCount
- {
- get
- {
- if(cars == null)
- {
- return 0;
- }
- else
- {
- return cars.Count;
- }
- }
- }
- private object lockobj = new object();
- //所有的车
- Dictionary<ushort, CarStateCmd> cars;
- public RS485()
- {
- buf = new byte[10240];
- len = 0;
- start = -1;
- end = 0;
- cars = new Dictionary<ushort, CarStateCmd>();
- portName = AppHelper.ReadAppSettings("zigbee", "Com");
- baudRate = Int32.Parse(AppHelper.ReadAppSettings("zigbee", "baudRate"));
- }
- private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
- {
- //报文的首尾都是0x7e,判断思路是当发现有两个7e时候,结合长度位判断是否是一条报文
- //如果是则提取报文并把剩余数据前移到索引0,如果不是则把第2个7e之前的数据抛弃掉
- int size = serial.BytesToRead;
- //正常情况下,不应该出现超出缓冲的报文,如果出现则可能是出错了,直接把索引置0
- if (len + size > buf.Length)
- {
- //以缓存大小为主
- size = buf.Length - len;
- }
- serial.Read(buf, len, size);
- int olen = len;
- len += size;
- for (int i = olen; i < len; i++)
- {
- head = (ushort)(((head << 8) & 0xff00) + buf[i]);
- //这里要求不能连续出现三个0x7e
- if(head == 0x7e7e)
- {
- start = i - 1;
- }
- else if (head == 0x0d0a)
- {
- end = i;
- if((start >=0) &&((end - start + 1 )== buf[start + 2]))
- {
- //帧长度正确
- byte[] datas = new byte[end - start + 1];
- Array.Copy(buf, start, datas, 0, datas.Length);
- HandleReceive(datas);
- //移动剩余数据到0;
- for (int j = end + 1; j < len; j++)
- {
- buf[j - end - 1] = buf[j];
- }
- len = len - end - 1; //重新调整长度
- i = -1;//下一个循环从0开始
- start = -1;//第2个7前移动到0
- end = 0;
- }
- else
- {
- start = -1;
- end = 0;
- }
-
- }
- }
- //如果处理完了缓存依然是满的,则抛弃数据,重新归零
- if (len >= buf.Length)
- {
- //相当于初始化缓冲
- len = 0;
- start = -1;
- end = 0;
- }
- }
- //处理接收到的报文,先转义再校验
- bool HandleReceive(byte[] datas)
- {
- //长度不是正常报文的长度
- if (datas == null || datas.Length < 8 || IsStart == false || AmrManager.ProcessExit)
- return false;
- ushort carid = (ushort)(datas[4] + ((datas[5] << 8) & 0xff00));
- //命令码
- switch (datas[3])
- {
- case 0x01:
- //不存在则创建
- CarStateCmd cmd = null;
- bool ok = false;
- //保留最新的
- lock (lockobj)
- {
- if (cars.ContainsKey((ushort)carid))
- {
- cmd = (CarStateCmd)cars[carid];
- cmd.sendTime = DateTime.Now;
- ok = true;
- }
- else
- {
- cmd = new CarStateCmd();
- cmd.iCarID = carid;
- cmd.carId = "0x" + carid.ToString("X2");
- cmd.sendTime = DateTime.Now;
- cars.Add(carid, cmd);
- }
- }
- //这里的写和心跳里面的读不能同时执行
- HandleCarStateMsg(cmd, datas);
- if (ok == false)
- {
- //车辆上线是一个事件,需要对外抛出事件
- if (CarConnectedEvent != null)
- {
- Task.Run(() => {
- cmd.msgType = MessageType.MSG_TYPE_CAR_Connected;
- CarConnectedEvent(cmd);
- });
- }
- }
- break;
- case 0x04:
- CarStateCmd cmd2 = new CarStateCmd();
- cmd2.carId = "0x" + carid.ToString("X2");
- cmd2.iCarID = carid;
- HandleCarArrivedMsg(cmd2, datas);
- //车辆到位是一个事件,需要对外抛出事件
- if(CarArrivedEvent != null)
- {
- Task.Run(() => {
- CarArrivedEvent(cmd2);
- });
- }
- break;
- default:
- break;
- }
-
- //获取锁,移除旧信息
- //如果长度值匹配得上,就是正常的包
- return true;
- }
- //处理车的心跳消息,更新到车的对象中
- void HandleCarStateMsg(CarStateCmd cmd , byte[] datas)
- {
- cmd.msgType = MessageType.MSG_TYPE_CAR_REPORT;
- cmd.powerLevel = datas[8].ToString();
- cmd.speed = (datas[21] * 0.1).ToString("0.00");
- cmd.state = (datas[22] > 0) ? CarState.Moving : CarState.Stoped; //根据要求,未完整,还有是否故障,是否中断,是否到达等
- cmd.loading = datas[23] > 0 ? true : false;
- cmd.isEnable = datas[24] > 0? true : false;
- int x, y, z, yaw;
- x = (int)(datas[9] + ((datas[10] << 8) & 0xff00));
- y = (int)(datas[11] + ((datas[12] << 8) & 0xff00));
- z = (int)(datas[13] + ((datas[14] << 8) & 0xff00));
- cmd.curPos = new Position(x,y,z, (z * 0.01).ToString("0.00"));
- x = (int)(datas[15] + ((datas[16] << 8) & 0xff00));
- y = (int)(datas[17] + ((datas[18] << 8) & 0xff00));
- z = (int)(datas[19] + ((datas[20] << 8) & 0xff00));
- cmd.destPos = new Position(x,y,z, (z * 0.01).ToString("0.00"));
- if(datas[27] == 0)
- {
- cmd.type = CarType.AMR;
- }
- else
- {
- cmd.type = CarType.AGV;
- }
- //几下更新数据的时间,更新数据与发送数据到mqtt总线上的时间不一样
- cmd.sendTime = DateTime.Now;
- //此次z和yaw赋值一样
- //需要z和yaw分开,要和老蔡商量
- }
- //心跳和车到达消息都存在车的属性缓存消息结构中,只是中间产生了车到达的事件
- void HandleCarArrivedMsg(CarStateCmd cmd , byte[] datas)
- {
- cmd.state = CarState.Arrived; //小车到达的消息,需要和老马确认
- cmd.msgType = MessageType.MSG_TYPE_CAR_Arrived;
- int x, y, z, yaw;
- x = (int)(datas[8] + ((datas[9] << 8) & 0xff00));
- y = (int)(datas[10] + ((datas[11] << 8) & 0xff00));
- z = (int)(datas[12] + ((datas[13] << 8) & 0xff00));
- cmd.curPos = new Position(x, y, z, (z*0.01).ToString("0.00"));
- x = (int)(datas[14] + ((datas[15] << 8) & 0xff00));
- y = (int)(datas[16] + ((datas[17] << 8) & 0xff00));
- z = (int)(datas[18] + ((datas[19] << 8) & 0xff00));
- cmd.destPos = new Position(x, y, z, (z * 0.01).ToString("0.00"));
- //此次z和yaw赋值一样
- if (datas[27] == 0)
- {
- cmd.type = CarType.AMR;
- }
- else
- {
- cmd.type = CarType.AGV;
- }
- //几下更新数据的时间,更新数据与发送数据到mqtt总线上的时间不一样
- cmd.sendTime = DateTime.Now;
- }
- //串口对外发送
- public bool Send(byte[] buffer)
- {
- if (buffer == null)
- return false;
- if (serial == null || serial.IsOpen == false)
- Start();
- if (serial != null && serial.IsOpen)
- {
- lock (this)
- {
- serial.Write(buffer, 0, buffer.Length);
- }
- }
- return true;
- }
- //串口启动后的定期执行事件,如果有内容,则对外发送定时消息
- private void CreateHeartBeat()
- {
- // return;
- List<CarStateCmd> offlinecars = new List<CarStateCmd>();
- List<CarStateCmd> onlinecars = new List<CarStateCmd>();
- Task.Run(() => {
- while (true)
- {
- if (IsStart == false || AmrManager.ProcessExit)
- break;
- Thread.Sleep(350);
- //保留最新的,lock内的内容要尽快结束,并且不要出错,另一个线程会把最新的车状态替换到cars里面。
- lock (lockobj)
- {
- offlinecars.Clear();
- onlinecars.Clear();
- List<CarStateCmd> carstates = cars.Values.ToList();
- foreach (var item in carstates)
- {
- //首先移除超时的消息,正常是每辆车100ms都有数据上来
- if ((DateTime.Now - item.sendTime).TotalMilliseconds > 2000)
- {
- //超时,认为掉线
- offlinecars.Add(item);
- //移除
- cars.Remove((ushort)item.iCarID);
- }
- else
- {
- onlinecars.Add(item);
- }
- }
- }
- //对offlinecars,产生离线消息
- //车辆上线是一个事件,需要对外抛出事件
- if (CarDisconnectedEvent != null)
- {
- foreach (var item in offlinecars)
- {
- item.msgType = MessageType.MSG_TYPE_CAR_Disconnected;
- CarDisconnectedEvent(item);
- }
- }
- //对onlinecars,产生状态变更消息
- if (ReceiveCarStateCmdEvent != null)
- {
- foreach (var item in onlinecars)
- {
- ReceiveCarStateCmdEvent(item);
- }
- }
- }
- });
- }
- public bool Start()
- {
- if (serial != null)
- serial.Close();
- serial = new SerialPort();
- serial.PortName = portName;
- serial.BaudRate = baudRate;
- serial.DataBits = dataBits;
- serial.StopBits = stopBits;
- serial.Parity = parity;
- serial.ReceivedBytesThreshold = 1;
- len = 0;
- start = 0;
- end = 0;
- serial.DataReceived += Serial_DataReceived;
- try
- {
- serial.Open();
- IsStart = true;
- //创建定时程序
- CreateHeartBeat();
- }
- catch (Exception)
- {
- //串口线被拔掉时会有异常
- }
- return serial.IsOpen;
- }
- public bool Stop()
- {
- if (serial != null)
- {
- serial.Close();
- serial = null;
- IsStart = false;
- len = 0;
- start = -1;
- end = 0;
- }
- return true;
- }
- public void Dispose()
- {
- try
- {
- Stop();
- }
- catch (Exception)
- {
- }
- }
- //对车发送命令
- public bool SendCarCmd(CallCarCmd cmd)
- {
- byte[] datas = new byte[36];
- datas[0] = 0xfd; //zigbee帧头
- datas[1] = 32; //数据长度
- ushort id = Convert.ToUInt16(cmd.carId, 16);
- datas[2] = (byte)(id & 0xff);
- datas[3] = (byte)((id >> 8) & 0xff);
- datas[4] = 0x7e;
- datas[4 + 1] = 0x7e;
- datas[4 + 2] = 32;
- datas[4 + 3] = 0x02; //命令
- datas[4 + 6] = (byte)(id & 0xff);
- datas[4 + 7] = (byte)((id >> 8) & 0xff);
- datas[4 + 8] = (byte)(cmd.destPos.x & 0xff);
- datas[4 + 9] = (byte)((cmd.destPos.x >> 8) & 0xff);
- datas[4 + 10] = (byte)(cmd.destPos.y & 0xff);
- datas[4 + 11] = (byte)((cmd.destPos.y >> 8) & 0xff);
- datas[4 + 12] = (byte)(cmd.destPos.z & 0xff);
- datas[4 + 13] = (byte)((cmd.destPos.z >> 8) & 0xff);
- if(id != 0xfe04)
- {
- datas[4 + 27] = (byte)CarType.AMR;
- }
- else
- {
- datas[4 + 27] = (byte)CarType.AGV;
- }
- datas[4 + 30] = 0x0d;
- datas[4 + 31] = 0x0a;
- return Send(datas);
- }
- //让车停
- public bool SendCarStop(StopCarCmd cmd)
- {
- byte[] datas = new byte[36];
- datas[0] = 0xfd; //zigbee帧头
- datas[1] = 32; //数据长度
- ushort id = Convert.ToUInt16(cmd.carId, 16);
- datas[2] = (byte)(id & 0xff);
- datas[3] = (byte)((id >> 8) & 0xff);
- datas[4] = 0x7e;
- datas[4 + 1] = 0x7e;
- datas[4 + 2] = 32;
- datas[4 + 3] = 0x03; //命令
- datas[4 + 6] = (byte)(id & 0xff); //目的ID
- datas[4 + 7] = (byte)((id >> 8) & 0xff);
- if (id != 0xfe04)
- {
- datas[4 + 27] = (byte)CarType.AMR;
- }
- else
- {
- datas[4 + 27] = (byte)CarType.AGV;
- }
- datas[4 + 30] = 0x0d;
- datas[4 + 31] = 0x0a;
- return Send(datas);
- }
- //对车使能
- public bool SendCarEnable(EnableCarCmd cmd)
- {
- byte[] datas = new byte[36];
- datas[0] = 0xfd; //zigbee帧头
- datas[1] = 32; //数据长度
- ushort id = Convert.ToUInt16(cmd.carId, 16);
- datas[2] = (byte)(id & 0xff);
- datas[3] = (byte)((id >> 8) & 0xff);
- datas[4] = 0x7e;
- datas[4 + 1] = 0x7e;
- datas[4 + 2] = 32;
- datas[4 + 3] = 0x05; //命令
- datas[4 + 6] = (byte)(id & 0xff); //目的ID
- datas[4 + 7] = (byte)((id >> 8) & 0xff);
- if (id != 0xfe04)
- {
- datas[4 + 27] = (byte)CarType.AMR;
- }
- else
- {
- datas[4 + 27] = (byte)CarType.AGV;
- }
- datas[4 + 30] = 0x0d;
- datas[4 + 31] = 0x0a;
- return Send(datas);
- }
- //停止车使能
- public bool SendCarDisable(DisableCarCmd cmd)
- {
- byte[] datas = new byte[36];
- datas[0] = 0xfd; //zigbee帧头
- datas[1] = 32; //数据长度
- ushort id = Convert.ToUInt16(cmd.carId, 16);
- datas[2] = (byte)(id & 0xff);
- datas[3] = (byte)((id >> 8) & 0xff);
- datas[4] = 0x7e;
- datas[4 + 1] = 0x7e;
- datas[4 + 2] = 32;
- datas[4 + 3] = 0x06; //命令
- datas[4 + 6] = (byte)(id & 0xff); //目的ID
- datas[4 + 7] = (byte)((id >> 8) & 0xff);
- if (id != 0xfe04)
- {
- datas[4 + 27] = (byte)CarType.AMR;
- }
- else
- {
- datas[4 + 27] = (byte)CarType.AGV;
- }
- datas[4 + 30] = 0x0d;
- datas[4 + 31] = 0x0a;
- return Send(datas);
- }
- public bool SendCarShowImage(CarShowCmd cmd)
- {
- byte[] datas = new byte[36];
- datas[0] = 0xfd; //zigbee帧头
- datas[1] = 32; //数据长度
- ushort id = Convert.ToUInt16(cmd.carId, 16);
- datas[2] = (byte)(id & 0xff);
- datas[3] = (byte)((id >> 8) & 0xff);
- datas[4] = 0x7e;
- datas[4 + 1] = 0x7e;
- datas[4 + 2] = 32;
- datas[4 + 3] = 0x07; //命令
- datas[4 + 6] = (byte)(id & 0xff); //目的ID
- datas[4 + 7] = (byte)((id >> 8) & 0xff);
- //为了解析简单,数组必须是2
- if(cmd.channelIds != null && cmd.picCodes != null && cmd.picCodes.Length > 1 && cmd.channelIds.Length > 1)
- {
- datas[8] = cmd.picCodes[0];
- datas[9] = cmd.picCodes[1];
- }
- //else
- //{
- // //否则索引号为0,默认不显示
- //}
- if (id != 0xfe04)
- {
- datas[4 + 27] = (byte)CarType.AMR;
- }
- else
- {
- datas[4 + 27] = (byte)CarType.AGV;
- }
- datas[4 + 30] = 0x0d;
- datas[4 + 31] = 0x0a;
- return Send(datas);
- }
- }
- }
|