/// <summary> /// 将单精度浮点数据加入链表 /// </summary> /// <param name="list"></param> /// <param name="value"></param> public static void AddFloatToList(List <byte> list, float value) { byte[] arrtemp = ByteStruct.StructToBytes(value); list.Add(arrtemp[0]); list.Add(arrtemp[1]); list.Add(arrtemp[2]); list.Add(arrtemp[3]); }
public void TestSerializeSingletonStruct() { var byteStruct = new ByteStruct { a = 7 }; Expect.AreArraysEqual(new byte[] { 7 }, SerializableUtil.Serialize(byteStruct)); }
/// <summary> /// 添加时间到队列 /// </summary> /// <param name="list"></param> /// <param name="time"></param> public static void AddTimeToList(List <byte> list, long time) { byte[] _time = ByteStruct.StructToBytes(time); list.Add(_time[0]); list.Add(_time[1]); list.Add(_time[2]); list.Add(_time[3]); list.Add(_time[4]); list.Add(_time[5]); list.Add(_time[6]); list.Add(_time[7]); }
/// <summary> /// 添加时间到列表,重载datetime /// </summary> /// <param name="list"></param> /// <param name="time"></param> public static void AddTimeToList(List <byte> list, DateTime time) { long _time1 = TimerTick.TimeSpanToSecond(time); byte[] _time2 = ByteStruct.StructToBytes(_time1); list.Add(_time2[0]); list.Add(_time2[1]); list.Add(_time2[2]); list.Add(_time2[3]); list.Add(_time2[4]); list.Add(_time2[5]); list.Add(_time2[6]); list.Add(_time2[7]); }
/// <summary> /// 重写,内部数据处理单独封装为方法,一些重复处理在switch外完成 /// </summary> /// <param name="unit"> 操作单元数组</param> /// <param name="comtemp">发送信息缓存</param> /// <param name="b_recvframe">接受到的数据字节数组</param> /// <param name="errornum">请求操作单元的错误数</param> /// <param name="error">错误列表</param> /// <returns></returns> public static Dictionary <ushort, object> MsgHandle(ushort[] unit, PrepareData.Compare comtemp, byte[] b_recvframe, ref ushort errornum, Dictionary <ushort, ushort> error) { Dictionary <ushort, object> retinfo = new Dictionary <ushort, object>(); switch (HandleData.TypeofFrame(b_recvframe).frametype) { //接收的是长帧 case 1: { //接收数据的拆分 byte[] recvframeout = new byte[b_recvframe.Length - PrepareData.BUS_FRAME_MINLEN - 2]; Array.Copy(b_recvframe, PrepareData.BUS_FRAME_MINLEN, recvframeout, 0, b_recvframe.Length - PrepareData.BUS_FRAME_MINLEN - 2); //传入的字节数组只包括执行状态字和返回数据(除CRC外) TransInfo(recvframeout, unit, retinfo, ref errornum, error); #region 从右边去第一不方便,反相的对应取 //for (int i = 0; i < basebyte; i++) //{ // byte low = sendframeout[sendframeout.Length - 2 * (i + 2)]; // byte high = sendframeout[sendframeout.Length - 2 * (i + 2) + 1]; // ushort temp = (ushort)(((ushort)(high << 8)) | low); // //读最后一位执行状态字,看是否有错误,有错误则加到错误列表,并错误数加1 // //没有错误则读取倒数第一个值,与操作单元一起加入数据字典,取值时长度由数据字典截取 //} #endregion } break; //接收帧为短帧 case 0: { byte[] onlydata = ((PrepareData.Msg_Bus)ByteStruct.BytesToStruct(b_recvframe, typeof(PrepareData.Msg_Bus))).data; //只有执行状态字和返回数据 TransInfo(onlydata, unit, retinfo, ref errornum, error); } break; //返回的代码错误 default: { //MessageBox.Show("返回的代码错误"); } break; } return(retinfo); }
/// <summary> /// 接收到返回包的处理方法 /// </summary> /// <param name="error"></param> /// <param name="b_recvframe"></param> /// <returns></returns> public static Dictionary <long, ushort> MsgHandle(ref ushort error, byte[] b_recvframe) { Dictionary <long, ushort> retinfo = new Dictionary <long, ushort>(); //如果是短帧,一定没有查找到文件,相反如果是长帧一定查询到文件 switch (HandleData.TypeofFrame(b_recvframe).frametype) { //长帧(查询到文件) case 1: { //接收数据的拆分 byte[] recvframeout = new byte[b_recvframe.Length - PrepareData.BUS_FRAME_MINLEN - 2]; Array.Copy(b_recvframe, PrepareData.BUS_FRAME_MINLEN, recvframeout, 0, b_recvframe.Length - PrepareData.BUS_FRAME_MINLEN - 2); //传入的字节数组只包括执行状态字和返回数据(除CRC外) TransInfo(recvframeout, retinfo); } break; //短帧(未查询到文件) //不存在符合要求的文件、查询出错、配置文件应该会有,设备出厂默认配置、查询错误返回错误代码 case 0: { byte[] errortemp = new byte[2]; PrepareData.Msg_Bus temp = (PrepareData.Msg_Bus)ByteStruct.BytesToStruct( b_recvframe, typeof(PrepareData.Msg_Bus)); //从原始数据中截取两个首字节,错误代码只有两个字节 Array.Copy(temp.data, 0, errortemp, 0, 2); error = BitConverter.ToUInt16(errortemp, 0); error = (ushort)ByteStruct.BytesToStruct(errortemp, typeof(ushort)); retinfo = null; } break; //错误 default: { retinfo = null; //MessageBox.Show("获取文件信息的长短帧错误!"); } break; } return(retinfo); }
/// <summary> /// 正好占用八个字节的数据域,所以不采用list转array /// </summary> /// <param name="para"></param> /// <returns></returns> public static byte[] GetData(ReadFile.Parameter para) { byte[] tempdata = new byte[12]; byte[] temp = ByteStruct.StructToBytes(para.filename); Array.Copy(temp, tempdata, 8); //tempdata[0] = (byte)para.filename; //tempdata[1] = (byte)(para.filename >> 8); //tempdata[2] = (byte)(para.filename >> 16); //tempdata[3] = (byte)(para.filename >> 24); //tempdata[4] = (byte)(para.filename >> 32); //tempdata[5] = (byte)(para.filename >> 24); //tempdata[6] = (byte)(para.filename >> 24); //tempdata[7] = (byte)(para.filename >> 24); tempdata[8] = (byte)para.start; tempdata[9] = (byte)(para.start >> 8); tempdata[10] = (byte)para.readbytes; tempdata[11] = (byte)(para.readbytes >> 8); return(tempdata); }
/// <summary> /// 组织完整的数据帧 /// 注意:这里组织默认的都是小端方式 /// </summary> /// <param name="data">数据部分</param> /// <returns>完整的数据帧</returns> public static byte[] MergeMsg(ref PrepareData.Msg_Bus _frame, byte [] data) { byte[] sender; ushort CRC; _frame.data = new byte[PrepareData.BUS_FRAME_IN_DATALEN]; //数据域小于8,在帧内组织 if (data.Length <= 8) { //帧内拷贝 Array.Copy(data, _frame.data, data.Length); _frame.crc16 = 0x0000; sender = ByteStruct.StructToBytes(_frame); CRC = CRC16.CalculateCrc16(sender, 0, sender.Length - 2); sender[sender.Length - 1] = (byte)(CRC >> 8); sender[sender.Length - 2] = (byte)CRC; } //数据帧过长需要帧外组织 else { _frame.crc16 = 0x0000; //帧外的CRC校验 CRC = CRC16.CalculateCrc16(data); byte [] temp = ByteStruct.StructToBytes(_frame); //帧内数据的CRC校验码 ushort crctemp = CRC16.CalculateCrc16(temp, 0, temp.Length - 2); temp[temp.Length - 1] = (byte)(crctemp >> 8); temp[temp.Length - 2] = (byte)crctemp; //整体数据组织 sender = new byte[PrepareData.BUS_FRAME_MINLEN + data.Length + 2]; //帧内数据 temp.CopyTo(sender, 0); //帧外数据 Array.Copy(data, 0, sender, PrepareData.BUS_FRAME_MINLEN, data.Length); //帧外CRC sender[sender.Length - 1] = (byte)(CRC >> 8); sender[sender.Length - 2] = (byte)CRC; } return(sender); }
/// <summary> /// 获取数据域,依赖于上次读到的总和为文件开始读取位置,如果读取到数据则说明未读完,继续组文件发送包 /// 如果读到字节数为0,那么说明读取完毕,将本次读到的字节数ref返回,在外判断循环组包结束 /// </summary> /// <returns></returns> public static byte[] GetData(WriteFile.Parameter para, int type) { byte[] ret; //读请求个数的字节 byte[] filedata = new byte[para.writebytes]; //这里读完到之后指针已偏移,默认指定读到的数据能全部发送 para.filehandle.Seek(para.start, SeekOrigin.Begin); ushort writedown = (ushort)para.filehandle.Read(filedata, 0, para.writebytes); //((FileItem)filepool[para.packetnum]).FileIndex += writedown; if (0 != writedown) { if ((int)FileType.UpGradeFile == type) { ret = new byte[writedown + upgradelen]; Array.Copy(para.upgradefilename, ret, upgradelen); Array.Copy(filedata, 0, ret, upgradelen, writedown); } else { ret = new byte[writedown + datacfglen]; byte[] temp = ByteStruct.StructToBytes(para.filename); Array.Copy(temp, ret, datacfglen); Array.Copy(filedata, 0, ret, datacfglen, writedown); } } //当读到0个字节,即已经读到文件结尾的时候,直接返回文件名,为结束帧 else { if (1 == type) { ret = para.upgradefilename; } else { ret = ByteStruct.StructToBytes(para.filename); } } return(ret); }
/// <summary> /// 心跳检测,看和loopback组合 /// </summary> public static void HeartCheck() { //每一秒钟检查一次数据字典清楚超时请求 HeartTimer(); for (; ;) { byte[] buffer = new byte[Default_Len]; EndPoint remotecli = new IPEndPoint(IPAddress.Any, 0); int recv = IMServerUDP.shared.heartfd.ReceiveFrom(buffer, ref remotecli); if (Heart_Bus_Len == recv) { byte[] temp = new byte[recv]; Array.Copy(buffer, 0, temp, 0, recv); Heart_Bus hb = (Heart_Bus)ByteStruct.BytesToStruct(temp, typeof(Heart_Bus)); //是指定格式的心跳包 if (8 == temp.Length) { byte temp_id = hb.myid; //已经存在的设备 if (Define.heartcheck.Keys.Contains(temp_id)) { ushort nowtime = Define.heartcheck[temp_id]; Define.heartcheck[temp_id] = heartinterval; //在这里更新可能断线后重连的终端的ID和IP与PORT Define.id_ip_port[temp_id] = (IPEndPoint)remotecli; } //该设备不存在,暂时可以认为是新增设备,先添加进心跳检测队列 else { Define.heartcheck.Add(temp_id, heartinterval); //新增设备注册到设备号到IP和PORT的记录字典 Define.id_ip_port.Add(temp_id, (IPEndPoint)remotecli); } //接收到心跳包,原样返回,是在以上处理完成之后发送 IMServerUDP.shared.heartfd.SendTo(temp, remotecli); } } } }
/// <summary> /// 写操作单元响应处理 /// </summary> /// <param name="error">错误的数目</param> /// <param name="errornum">错误列表</param> /// <param name="b_recvframe">接受到的原始数据帧(接受的全部字节)</param> /// <returns></returns> public static int MsgHandle(Dictionary <ushort, ushort> error, ref ushort errornum, byte[] b_recvframe, ushort[] unit) { switch (HandleData.TypeofFrame(b_recvframe).frametype) { //接收到的是长帧 case 1: { byte[] onlydata = new byte[b_recvframe.Length - PrepareData.BUS_FRAME_MINLEN - 2]; Array.Copy(b_recvframe, PrepareData.BUS_FRAME_MINLEN, onlydata, 0, onlydata.Length); TransInfo(error, ref errornum, onlydata, unit); break; } //接收到的是短帧 case 0: { byte[] onlydata = ((PrepareData.Msg_Bus)ByteStruct.BytesToStruct(b_recvframe, typeof(PrepareData.Msg_Bus))).data; TransInfo(error, ref errornum, onlydata, unit); break; } //判断长短帧错误 default: { break; } } //写操作单元完成 if (0 == errornum) { return(1); } //写操作单元存在错误 else { return(0); } }
/// <summary> /// 获取错误信息的帧处理 /// </summary> /// <param name="b_recvframe">接收到的字节数组(完整的)</param> /// <returns></returns> public static string MsgHandle(byte[] b_recvframe) { string explain; switch (HandleData.TypeofFrame(b_recvframe).frametype) { //接收的是长帧 case 1: { //接收数据的拆分 byte[] recvframeout = new byte[b_recvframe.Length - PrepareData.BUS_FRAME_MINLEN - 2]; Array.Copy(b_recvframe, PrepareData.BUS_FRAME_MINLEN, recvframeout, 0, b_recvframe.Length - PrepareData.BUS_FRAME_MINLEN - 2); explain = Encoding.ASCII.GetString(recvframeout); } break; //接收帧为短帧 case 0: { byte[] onlydata = ((PrepareData.Msg_Bus)ByteStruct.BytesToStruct(b_recvframe, typeof(PrepareData.Msg_Bus))).data; explain = Encoding.ASCII.GetString(onlydata); } break; //返回的代码错误 default: { //如果返回的为null的话,那么请求错误代码失败(内容上的失败,毕竟已完成前序校验) explain = null; } break; } return(explain); }
/// <summary> /// 作为一个线程处理方法,当有连接时,为了不耽误执行完构造函数返回web端进行其他工作 /// 接受并分类处理数据 /// 长短帧 /// 心跳和非心跳 /// 标志位的操作 /// WEB端通过事件触发置位和轮训复位进行通信操控 /// 程序会阻塞到recievefrom()函数,所以需要开出两个线程来分别处理发送和接受 /// 发送:循环检测发送标志位 /// 接受:阻塞到recievefrom() /// </summary> public void InterAction() { int recvcounter = 0; //接受包计数 int testcounter = 0; //测试用误包计数 List <int> errorcode = new List <int>(); //测试用检测误包的误码 int retcode = 0; //帧检测返回代码 int flag = 0; //接收到数据包的数目 int recv; //接收到数据的字节数 byte[] bytes = new byte[PrepareData.BUS_FRAME_MINLEN]; //用来接收第一帧 //byte[] exbytes= null; //用来接收长帧的后续帧 PrepareData.Msg_Bus firststruct; //用来接收第一帧数据转化的数据结构 //history.AppendText(TimeHandle.getcurrenttime().ToString() + ": \r\n" + "waiting for a client ..." + "\r\n"); ///接收一个远端终结点并进行存储 IPEndPoint client = new IPEndPoint(IPAddress.Any, 0); ///这里不应该放锁,会在线程阻塞到receivefrom()导致程序崩 lock (shared) { shared.remotepoint = (EndPoint)client; ///作用:测试时过滤客户端发来的测试包 ///问题:放到下面开线程之前会导致sendmsg中消息发送不达 ///分析:考虑开出线程之后会不会继续执行,还是因为单方面上锁没有同步 recv = shared.listenfd.ReceiveFrom(bytes, ref shared.remotepoint); } ///报文解析,看是否是UDP发来的第一个心跳包 ///如果是,那么回送一个响应,同时返回已经通信 ///如果等待一段一段时间没有响应,那么在此发送短信 ///监听功能在短信发送后开启 ///接收到第一个心跳包,则跳出此函数进行下一个方法中receive for (; ;) { #region 最初目的:在一个循环中实现发送和接受的轮训检测 实际效果:会阻塞到接受函数,虽然会由于心跳接受跳出阻塞执行循环 改进方法:开出单独的线程处理 ///将发送检测放到前面,只要是不需要执行发送的情况下,都执行接收情况,因为有心跳包 ///在接受中再检测根据标志位区分接收心跳,或者接收心跳同时接收数据 //if (true == shared.udpsendflag) //{ // //线程锁操作,这里对值类型变量进行锁操作,考虑变量情况 // lock (this) // { // shared.udpsendflag = false; // ///打包发送将要发送的数据帧 // string what = "hello world!"; // byte []_what = Encoding.ASCII.GetBytes(what); // int sendcount = shared.listenfd.SendTo(_what, shared.remotepoint); // MessageBox.Show("发送了"+sendcount.ToString()+"!!!"); // //udprecvflag = true; // } //} //else //{ #endregion //都要接收,接收到之后在区分是心跳还是数据包,考虑这里会不会拥塞,讨论通信自身的拥塞等待机制 //接受客户端发过来的消息,并存储客户端的终结点信息 recv = shared.listenfd.ReceiveFrom(bytes, ref shared.remotepoint); shared.donerecvflag = true; //MessageBox.Show("发送完并已接收响应"); firststruct = (PrepareData.Msg_Bus)ByteStruct.BytesToStruct(bytes, typeof(PrepareData.Msg_Bus)); flag++; // retcode = HandleData.FrameInCheck(bytes); //如果不是检验合格的包 if (33 != retcode) { testcounter++; errorcode.Add(retcode); //帧错误处理 //MessageBox.Show("错误代码是:" + retcode.ToString()); } //检验合格的包的处理 #region 添加对心跳包的处理 /*判断心跳 * if(isheartbeat) 如果携带有效数据,则进行解析--心跳格式后续给出 * listenfd.sendto(remotepoint , heartbeat); * 如果是心跳帧,那么略过并回送响应 *这里设置一个定时器,如果连续三个心跳间隔没有收到心跳帧 *则认为设备掉线的一种情况 */ #endregion else { recvcounter++; //不是心跳包并且存在需要接收的数据 //---释放代码时考虑加锁 //if (true == udprecvflag && firststruct.msgType != 0x33) //{ // udprecvflag = false; //清零标志位 // //为短帧处理 // if (PrepareData.BUS_FRAME_IN_DATALEN == firststruct.dataLen) // { // if (0x33 == firststruct.msgType) // { // ///如果心跳包带信息,再过解析 // ///心跳包接受之后的响应,默认将接收到的包原样返回 // ///心跳包还是否需要校验和检测 // byte[] heart = new byte[1024]; //转接心跳包数据 // listenfd.SendTo(heart, remotepoint); // } // else // { // //短数据帧处理 // HandleData.ShortFrameHandle(); // ///一次数据接收完成,等待WEB端轮训标志来取数据,并在web端将标志清零 // ///至于数据通过哪里过渡,连接时考虑 // donerecvflag = true; // } // } // //为长帧处理 // else if (PrepareData.BUS_FRAME_IN_DATALEN < firststruct.dataLen) // { // //如果是长帧,那么在接收到第一帧之后,按数据长度循环接收数据 // int havenrecv = 0; // int nowrecv = 0; //多次发送时,每次接受到的数据的大小 // byte[] bytestemp = new byte[1024]; // exbytes = new byte[firststruct.dataLen + 2]; // while ((havenrecv += nowrecv) < firststruct.dataLen) // { // nowrecv = listenfd.ReceiveFrom(bytestemp, ref remotepoint); // Array.Copy(bytestemp, 0, exbytes, havenrecv, nowrecv); // } // //长数据帧处理 // HandleData.LongFrameHandle(); // ///一次数据接收并处理完成,等待WEB端轮训标志来取数据,并在web端将标志清零 // ///至于数据通过哪里过渡,连接时考虑 // donerecvflag = true; // } //} // } } } #region 忽略多线程的情况,暂时只考虑一个套接口负责一个连接,且不再处理其他设备的连接 //ParameterizedThreadStart _newoneclient = new ParameterizedThreadStart(HandleListen); //Thread newoneclient = new Thread(_newoneclient); //newoneclient.Start(remotepoint); //MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString()); #endregion #region 测试时未分线程时部分的注释 //string wel = "welcome to my test server!"; //bytes = Encoding.ASCII.GetBytes(wel); //listenfd.SendTo(bytes , remotepoint); //for (; ; ) //{ // bytes = new byte[1024]; // recv = listenfd.ReceiveFrom(bytes, ref remotepoint); // history.AppendText(TimeHandle.getcurrenttime().ToString() + ": \r\n" + Encoding.ASCII.GetString(bytes, 0, recv) + "\r\n"); // listenfd.SendTo(bytes, remotepoint); //} #endregion }
/// <summary> /// 未考虑数组的情况,用于解析帧 /// </summary> /// <param name="src">根据操作单元查字典把字节数组翻译为相应的数据类型</param> /// <param name="unit">操作单元</param> /// <returns></returns> public static object GetValue(byte[] src, ushort unit) { object temp; switch (MyDictionary.unittypedict[unit]) { //uchar case 0: { temp = src[0]; } break; //ushort case 1: { temp = ByteStruct.BytesToStruct(src, typeof(ushort)); } break; //float case 2: { temp = ByteStruct.BytesToStruct(src, typeof(float)); } break; //datetime case 3: { long _temp = (long)ByteStruct.BytesToStruct(src, typeof(long)); temp = TimerTick.TimeSpanToDate(_temp); } break; //char[] case 4: { char[] _temp = new char[src.Length]; for (int i = 0; i < src.Length; i++) { _temp[i] = (char)src[i]; } temp = _temp; break; } //ushort[2]==EraseRange case 5: { temp = (Define.EraseRange)ByteStruct.BytesToStruct(src, typeof(Define.EraseRange)); break; } //ushort[4]==GasFixPara_A case 6: { temp = (Define.GasFixPara_A)ByteStruct.BytesToStruct(src, typeof(Define.GasFixPara_A)); break; } //ushort[4]==GasFixPara_B case 7: { temp = (Define.GasFixPara_B)ByteStruct.BytesToStruct(src, typeof(Define.GasFixPara_B)); break; } //float[2]==EnvironmentSetting case 8: { temp = (Define.EnvironmentSetting)ByteStruct.BytesToStruct(src, typeof(Define.EnvironmentSetting)); break; } //float[3]==H2OAnlyParam_AW case 9: { temp = (Define.H2OAnlyParam_AW)ByteStruct.BytesToStruct(src, typeof(Define.H2OAnlyParam_AW)); break; } //float[3]==H2OAnlyParam_T case 10: { temp = (Define.H2OAnlyParam_T)ByteStruct.BytesToStruct(src, typeof(Define.H2OAnlyParam_T)); break; } //float[5]==GasFixParameters case 11: { temp = (Define.GasFixParameters)ByteStruct.BytesToStruct(src, typeof(Define.GasFixParameters)); break; } default: //MessageBox.Show("readunit-getdata:这里只做系统类型的处理,自定义类型暂未考虑"); temp = null; break; } return(temp); }
/// <summary> /// 翻译接受的数据字节数组 /// </summary> /// <param name="predata">传入字节数组(执行状态字和操作单元返回数据的组合)</param> /// <param name="unit"></param> /// <param name="remainder"></param> /// <param name="retinfo"></param> /// <param name="errornum"></param> /// <param name="error"></param> public static void TransInfo(byte[] predata, ushort[] unit, Dictionary <ushort, object> retinfo, ref ushort errornum, Dictionary <ushort, ushort> error) { int m = 0; //返回数据字节数组的下标 int index = 0; //操作单元数组的下标 //操作单元的个数,这里是从发送帧中间接得来 int basenum = unit.Length; int remainder = basenum % 8; //发来字节中的执行状态字所占字节数 int basebyte = (int)Math.Ceiling(basenum / 8.0); //存放执行状态字 byte[] b_basebyte = new byte[basebyte]; Array.Copy(predata, 0, b_basebyte, 0, basebyte); //返回值的数据信息(不包括执行状态字) byte[] onlydata = new byte[predata.Length - basebyte]; Array.Copy(predata, basebyte, onlydata, 0, predata.Length - basebyte); //只要传入需要解析的数据、操作单元数组、余数 for (int i = basebyte - 1; i >= 0; i--) { //处理如果存在少于8个执行状态字的首个字节 if (0 == i) { for (int j = 0; j <= remainder - 1; j++) { //对应操作单元的执行状态字是否成功 int result = b_basebyte[i] & (byte)(int)Math.Pow(2, j); //哪一个执行状态字 //int index = remainder - j - 1; //请求的操作单元返回错误 if (0 == result) { //获取错误代码 byte[] temp = new byte[2]; Array.Copy(onlydata, m % 8, temp, 0, 2); //推移移动的位数 m += 2; ushort errorcode = (ushort)ByteStruct.BytesToStruct(temp, typeof(ushort)); //errorcode处理 errornum++; error.Add(unit[index], errorcode); index++; //获取操作单元号(非结构体通信下读两个字节),获取错误代码存入 //获取操作单元和值存入 } else { //对应操作单元的值类型所占空间 ushort count = MyDictionary.unitlendict[unit[index]]; byte[] temp = new byte[count]; Array.Copy(onlydata, m, temp, 0, count); m += count; object value = GetValue(temp, unit[index]); retinfo.Add(unit[index], value); index++; } } //只处理一遍 //remainder = 0; } else { for (int j = 0; j <= 8 - 1; j++) { int result = b_basebyte[i] & (byte)(int)Math.Pow(2, j); //int index = i * 8 + 7 -j; //请求的操作单元返回错误 if (0 == result) { //获取错误代码 byte[] temp = new byte[2]; Array.Copy(onlydata, m, temp, 0, 2); //推移移动的位数 m += 2; ushort errorcode = (ushort)ByteStruct.BytesToStruct(temp, typeof(ushort)); //errorcode处理 errornum++; error.Add(unit[index], errorcode); index++; //获取操作单元号(非结构体通信下读两个字节),获取错误代码存入 //获取操作单元和值存入 } else { ushort count = MyDictionary.unitlendict[unit[index]]; byte[] temp = new byte[count]; Array.Copy(onlydata, m, temp, 0, count); m += count; object value = GetValue(temp, unit[index]); retinfo.Add(unit[index], value); index++; } } } } }
/// <summary> /// 数据域解析 /// </summary> /// <param name="error">错误列表</param> /// <param name="errornum">错误数</param> /// <param name="recvdata">执行状态字和错误代码(如果有错误代码)</param> public static void TransInfo(Dictionary <ushort, ushort> error, ref ushort errornum, byte[] recvdata, ushort[] unit) { int m = 0; //返回数据字节数组的下标 //操作单元的个数,这里是从发送帧中间接得来 int basenum = unit.Length; int remainder = basenum % 8; //操作单元数组的下标 int index = 0; //发来字节中的执行状态字所占字节数 int basebyte = (int)Math.Ceiling(basenum / 8.0); //存放执行状态字 byte[] b_basebyte = new byte[basebyte]; Array.Copy(recvdata, 0, b_basebyte, 0, basebyte); //返回的值数据信息(不包括执行状态字) byte[] onlydata = new byte[recvdata.Length - basebyte]; Array.Copy(recvdata, basebyte, onlydata, 0, recvdata.Length - basebyte); //全部正确的情况下,返回的执行状态字值 //这里如果需要提高效率,可以事先算出执行状态字全1的情况下值与理论个数的执行状态字的数值比较 //如果数值对应即可省去下面的扫描 //只要传入需要解析的数据、操作单元数组、余数 for (int i = basebyte - 1; i >= 0; i--) { //处理如果存在少于8个执行状态字的首个字节 if (0 == i) //如果I等于0,那么执行状态子当前在第一字节,即可能存在不满8个执行状态字情况 { for (int j = 0; j <= remainder - 1; j++) { //对应操作单元的执行状态字是否成功 int result = b_basebyte[i] & (byte)(int)Math.Pow(2, j); //请求的操作单元返回错误 if (0 == result) { //获取错误代码 byte[] temp = new byte[2]; Array.Copy(onlydata, m % 8, temp, 0, 2); //推移移动的位数 m += 2; ushort errorcode = (ushort)ByteStruct.BytesToStruct(temp, typeof(ushort)); //errorcode处理 errornum++; error.Add(unit[index], errorcode); //index++; } index++; } //只处理一遍 //remainder = 0; } else { for (int j = 0; j <= 8 - 1; j++) { int result = b_basebyte[i] & (byte)(int)Math.Pow(2, j); //请求的操作单元返回错误 if (0 == result) { //获取错误代码 byte[] temp = new byte[2]; Array.Copy(onlydata, m % 8, temp, 0, 2); //推移移动的位数 m += 2; ushort errorcode = (ushort)ByteStruct.BytesToStruct(temp, typeof(ushort)); //errorcode处理 errornum++; error.Add(unit[index], errorcode); //index++; } index++; } } } }
/// <summary> /// 接收到消息的处理,返回解析响应中下位机写入的字节数,用于外边判断是否继续传输(判断报错) /// </summary> /// <returns></returns> public static ushort MsgHandle(byte[] b_recvframe, ref ushort errorcode) { ushort thisdown = 0; //返回帧情况:如果为数据或配置文件只能是短暂;如果是升级文件,只能是长帧(文件名就16字节) //注意:这里统一数据配置和升级文件的交互方法,响应包应该全是短包 //FileItem fi = (FileItem)filepool[hdtr.packetnum]; PrepareData.Msg_Bus temp = (PrepareData.Msg_Bus)ByteStruct.BytesToStruct(b_recvframe, typeof(PrepareData.Msg_Bus)); //包含执行状态字 byte[] rawdata = temp.data; ushort datalen = (ushort)(temp.dataLen - 2); //终端没有接收到字节: 出错或者是文件传送完成握手响应 if (0 == datalen) { byte[] _errorcode = new byte[2]; Array.Copy(rawdata, _errorcode, 2); errorcode = BitConverter.ToUInt16(_errorcode, 0); thisdown = 0; } //终端接收到一定的数据 else { byte[] _errorcode = new byte[2]; byte[] _thisdonw = new byte[2]; Array.Copy(rawdata, _errorcode, 2); Array.Copy(rawdata, 2, _thisdonw, 0, 2); errorcode = BitConverter.ToUInt16(_errorcode, 0); thisdown = BitConverter.ToUInt16(_thisdonw, 0); } return(thisdown); #region 区别数据、配置文件和升级文件 //如果为数据文件或者配置文件 /*if (!fi.IsUpgradeFile) * { * PrepareData.Msg_Bus temp = (PrepareData.Msg_Bus)ByteStruct.BytesToStruct(b_recvframe, * typeof(PrepareData.Msg_Bus)); * //包含执行状态字 * byte[] rawdata = temp.data; * ushort datalen = (ushort)(temp.dataLen - 2); * //终端没有接收到字节: 出错或者是文件传送完成握手响应 * if (0 == datalen) * { * byte[] _errorcode = new byte[2]; * Array.Copy(rawdata, _errorcode, 2); * errorcode = BitConverter.ToUInt16(_errorcode, 0); * thisdown = 0; * } * //终端接收到一定的数据 * else * { * byte[] _errorcode = new byte[2]; * byte[] _thisdonw = new byte[2]; * Array.Copy(rawdata, _errorcode, 2); * Array.Copy(rawdata, 2, _thisdonw, 0, 2); * errorcode = BitConverter.ToUInt16(_errorcode, 0); * thisdown = BitConverter.ToUInt16(_thisdonw, 0); * } * } * //如果为升级文件 * else * { * byte 数据分割 * } * */ #endregion //错误代码 数据域 含义 // 0 0 服务器发送文件写结束帧后下位机的正常响应 // 0 != 0 正常响应下位机接收到服务器的文件数据并写入的正常响应 // != 0 null 有错误发生时的响应帧 }
/// <summary> /// 接受数据的处理,数据库中存储的为字节数组 /// h2o的为float数组 /// co2的为ushort数组 /// 六组分的为short数组 /// </summary> /// <param name="b_recvframe"></param> /// <param name="msgsubtype">发送数据帧的子类型</param> /// <returns></returns> public static object MsgHandle(byte[] b_recvframe, byte msgsubtype) { byte[] sampledata; int datalen; int point; List <H2OPoint> h2odata = new List <H2OPoint>(); List <ushort> co2data = new List <ushort>(); List <short> sixgasdata = new List <short>(); switch (HandleData.TypeofFrame(b_recvframe).frametype) { //长帧 case 1: { //去除了CRC校验2字节 datalen = b_recvframe.Length - PrepareData.BUS_FRAME_MINLEN - 2; sampledata = new byte[datalen]; Array.Copy(b_recvframe, PrepareData.BUS_FRAME_MINLEN, sampledata, 0, datalen); switch (msgsubtype) { //H2O case (byte)MSGEncoding.ReadBuffer.ReadH2OCache: { //AW、T、ppm一组 point = datalen / 12; for (int i = 0; i < point; i++) { //更新h20采样点为结构体(实际h2o采样点只有一个) H2OPoint temp = new H2OPoint(); byte[] _h2odata = new byte[4]; Array.Copy(sampledata, 4 * i, _h2odata, 0, 4); temp.h2o_AW = BitConverter.ToSingle(_h2odata, 0); Array.Copy(sampledata, 4 * i + 4, _h2odata, 0, 4); temp.h2o_T = BitConverter.ToSingle(_h2odata, 0); Array.Copy(sampledata, 4 * i + 8, _h2odata, 0, 4); temp.h2o_PPM = BitConverter.ToSingle(_h2odata, 0); ///原始顺序存储采样点数据 //for (int j = 0; j < 3; j++) //{ // byte[]_h2odata = new byte[4]; // //四个四个字节的取数据 // Array.Copy(sampledata , 4*(3*i+j) , _h2odata , 0 , 4); // h2odata.Add(BitConverter.ToSingle(_h2odata , 0)); //} } } break; //CO2 case (byte)MSGEncoding.ReadBuffer.ReadCO2Cache: { point = datalen / 2; for (int i = 0; i < point; i++) { byte[] _co2data = new byte[2]; Array.Copy(sampledata, 2 * i, _co2data, 0, 2); co2data.Add(BitConverter.ToUInt16(_co2data, 0)); } } break; //6组分 case (byte)MSGEncoding.ReadBuffer.ReadGasCache: { point = datalen / 2; for (int i = 0; i < point; i++) { byte[] _sixgasdata = new byte[2]; Array.Copy(sampledata, 2 * i, _sixgasdata, 0, 2); sixgasdata.Add(BitConverter.ToInt16(_sixgasdata, 0)); } } break; default: { //MessageBox.Show("读缓存过程错误!"); } break; } } break; //短帧 case 0: { sampledata = ((PrepareData.Msg_Bus)(ByteStruct.BytesToStruct(b_recvframe, typeof(PrepareData.Msg_Bus)))).data; datalen = sampledata.Length; switch (msgsubtype) { //H20必定为长帧,至少为三个浮点数位12字节 //case 0x00: // { // } // break; //CO2 case (byte)MSGEncoding.ReadBuffer.ReadCO2Cache: { point = datalen / 2; for (int i = 0; i < point; i++) { byte[] _co2data = new byte[2]; Array.Copy(sampledata, 2 * i, _co2data, 0, 2); co2data.Add(BitConverter.ToUInt16(_co2data, 0)); } } break; //6组分 case (byte)MSGEncoding.ReadBuffer.ReadGasCache: { point = datalen / 2; for (int i = 0; i < point; i++) { byte[] _sixgasdata = new byte[2]; Array.Copy(sampledata, 2 * i, _sixgasdata, 0, 2); sixgasdata.Add(BitConverter.ToInt16(_sixgasdata, 0)); } } break; default: { //MessageBox.Show("读缓存过程存在错误!"); } break; } } break; } if ((byte)MSGEncoding.ReadBuffer.ReadGasCache == msgsubtype) { return(sixgasdata.ToArray <short>()); } else if ((byte)MSGEncoding.ReadBuffer.ReadCO2Cache == msgsubtype) { return(co2data.ToArray <ushort>()); } else { return(h2odata.ToArray <H2OPoint>()); } }
/// <summary> /// 函数内完成交互,直到接收完成 /// 返回数据为空,且error为0x00,说明文件读取完成 /// 返回错误代码,且数据域为空为产生错误,例如:找不到文件 /// 返回数据域不为空,且error为0x00,说明正常读取文件 /// </summary> /// <param name="b_recvframe">接收到的字节数组,未拆包的</param> /// <param name="error">错误代码</param> /// <returns>已接收多少字节</returns> public static int MsgHandle(HandleData.TypeRet hdtr, byte[] b_recvframe, ref ushort error) { //执行状态字和文件数据 byte[] recvdata; //字节型执行状态字 byte[] _statecode = new byte[2]; //不包括执行状态字和CRC校验的文件数据 byte[] filedata; //转换为ushort类型的执行状态字 ushort statecode; //文件数据的长度(不包括执行状态字和CRC校验) int datalen = 0; int donerecvinmen = 0; byte packetnum = hdtr.packetnum; ReadFile.Parameter sender = (ReadFile.Parameter)Define.index_obj[packetnum]; switch (HandleData.TypeofFrame(b_recvframe).frametype) { //长帧 //statecode == 0表示无错误,且为长帧,表示肯定datalen肯定大于0 //如果datalen == 0则肯定为短帧情况,且表示文件接收完成 case 1: { //去除了CRC校验2字节(包括执行状态字和文件数据) recvdata = new byte[b_recvframe.Length - PrepareData.BUS_FRAME_MINLEN - 2]; Array.Copy(b_recvframe, PrepareData.BUS_FRAME_MINLEN, recvdata, 0, recvdata.Length); Array.Copy(recvdata, 0, _statecode, 0, 2); statecode = BitConverter.ToUInt16(_statecode, 0); //去除了返回状态字2字节 datalen = recvdata.Length - 2; //不能出现状态执行为0且数据长度为0的情况 //因为这是长帧,这种情况只能出现在短帧的情况下 //if (0 == statecode && datalen > 0) if (0 == statecode) { //不包括执行状态字和CRC校验码的文件数据 filedata = new byte[datalen]; Array.Copy(recvdata, 2, filedata, 0, datalen); //该帧属于以接收文件的部分(若讨论文件接收完毕,到短帧中处理) if (filepool.ContainsKey(packetnum)) { FileItem fitemp = (FileItem)filepool[packetnum]; byte[] lastfiledata = fitemp.filedata; byte[] datatemp = new byte[datalen + lastfiledata.Length]; Array.Copy(lastfiledata, 0, datatemp, 0, lastfiledata.Length); Array.Copy(recvdata, 0, datatemp, lastfiledata.Length, datalen); fitemp.Index += (ushort)datalen; fitemp.filedata = datatemp; filepool[packetnum] = fitemp; } //该帧是要传送文件的第一帧,要重新建立缓冲 else { FileItem fitemp = new FileItem(); fitemp.filename = sender.filename; fitemp.filedata = recvdata; fitemp.Index += (ushort)datalen; filepool.Add(packetnum, fitemp); } } donerecvinmen = datalen; error = statecode; } break; //短帧(分类处理,错误和不错误 文件传输结束) case 0: { recvdata = ((PrepareData.Msg_Bus)ByteStruct.BytesToStruct(b_recvframe, typeof(PrepareData.Msg_Bus))).data; int count = ((PrepareData.Msg_Bus)ByteStruct.BytesToStruct(b_recvframe, typeof(PrepareData.Msg_Bus))).dataLen; datalen = count - 2; Array.Copy(recvdata, 0, _statecode, 0, 2); statecode = BitConverter.ToUInt16(_statecode, 0); //传回的文件不存在错误 if (0 == statecode) { if (datalen > 0) { filedata = new byte[datalen]; Array.Copy(recvdata, 2, filedata, 0, datalen); //byte packetnum = hdtr.packetnum; //ReadFile.Parameter sender = (ReadFile.Parameter)Define.index_obj[packetnum]; //该帧属于以接收文件的部分(若讨论文件接收完毕,到短帧中处理) if (filepool.ContainsKey(packetnum)) { FileItem fitemp = (FileItem)filepool[packetnum]; byte[] lastfiledata = fitemp.filedata; byte[] datatemp = new byte[datalen + lastfiledata.Length]; Array.Copy(lastfiledata, 0, datatemp, 0, lastfiledata.Length); Array.Copy(recvdata, 0, datatemp, lastfiledata.Length, datalen); fitemp.filedata = datatemp; fitemp.Index += (ushort)datalen; filepool[packetnum] = fitemp; } //该帧是要传送文件的第一帧,要重新建立缓冲 else { FileItem fitemp = new FileItem(); fitemp.filename = sender.filename; fitemp.filedata = recvdata; fitemp.Index += (ushort)datalen; filepool.Add(packetnum, fitemp); } } //errorcode和datalen都为0,文件传输结束帧 else { //不论存在还是不存在都使用create模式,如果文件存在那么就直接覆盖 string filename = sender.filename.ToString(); string path = "d:\\ " + filename + ".txt"; //如果指定位置已经存在该文件 if (File.Exists(path)) { //MessageBox.Show("接收到的文件已存在,正在准备覆盖源文件!"); } FileItem fitemp = (FileItem)filepool[hdtr.packetnum]; byte[] lastfiledata = fitemp.filedata; FileStream tempfile = new FileStream(path, FileMode.Create, FileAccess.Write); tempfile.Write(lastfiledata, 0, lastfiledata.Length); tempfile.Flush(); tempfile.Close(); } } donerecvinmen = datalen; //传回的文件存在错误 error = statecode; } break; //错误情况 default: { //MessageBox.Show("读文件(msghandle):长短帧判断处理失败!返回值均为操作!"); } break; } return(donerecvinmen); }
public static byte[] GetData(Dictionary <ushort, object> unitvalue) { byte[] data; //方便动态组织数据 List <byte> datalist = new List <byte>(); foreach (KeyValuePair <ushort, object> kvp in unitvalue) { ushort unit = kvp.Key; object value = kvp.Value; ushort DS_type = MyDictionary.unittypedict[unit]; switch (DS_type) { //uchar case 0: { AddUshortToList(datalist, unit); datalist.Add((byte)value); break; } //ushort case 1: { AddUshortToList(datalist, unit); AddUshortToList(datalist, (ushort)value); break; } //float case 2: { AddUshortToList(datalist, unit); AddFloatToList(datalist, (float)value); break; } //datetime case 3: { AddUshortToList(datalist, unit); AddTimeToList(datalist, (DateTime)value); break; } //ushort[2] EraseRange case 4: //ushort[4] GasFixPara_A case 5: //ushort[4] GasFixPara_B case 6: //float[2] EnvironmentSetting case 7: //float[3] H2OAnlyParam_AW case 8: //float[3] H2OAnlyParam_T case 9: //float[5] GasFixParameters case 10: { AddUshortToList(datalist, unit); byte[] temp = ByteStruct.StructToBytes(value); AddBytesToList(datalist, temp); break; } default: { //MessageBox.Show("writeunit-getdata:数据类型出错!"); break; } } } #region //混合类型处理 //都是数据传输接口相关参数,暂时忽略 //else if ("x" == flag) //{ // if (157 == unit || 160 == unit) // { // AddUnitToList(datalist , unit); // AddIPToList(datalist , value); // AddUshortToList(datalist , Convert.ToUInt16(unitvalue.ElementAt(++i).Value)); // } // else // if (158 == unit || 161 == unit) // { // AddUnitToList(datalist, unit); // AddUshortToList(datalist, Convert.ToUInt16(value)); // datalist.Add((byte)Convert.ToUInt16(unitvalue.ElementAt(++i).Value)); // datalist.Add((byte)Convert.ToUInt16(unitvalue.ElementAt(++i).Value)); // datalist.Add((byte)Convert.ToUInt16(unitvalue.ElementAt(++i).Value)); // } // else // if (159 == unit || 162 == unit) // { // AddUnitToList(datalist, unit); // AddIPToList(datalist, value); // AddUshortToList(datalist, Convert.ToUInt16(unitvalue.ElementAt(++i).Value)); // AddIPToList(datalist, unitvalue.ElementAt(++i).Value); // AddIPToList(datalist, unitvalue.ElementAt(++i).Value); // } // else // { // MessageBox.Show("混合类型处理超出可行范围"); // } //} #endregion data = datalist.ToArray <byte>(); return(data); }