电总协议计算长度和校验

电总协议相关文档

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

请求bean与length、checkSUM相关代码

public class RequestBean
    {
        public byte SOI { get; set; } = 0x7E;
        public byte[] VER { get; set; } = new byte[] { 0x32, 0x30 };
        public byte[] ADR { get; set; } = new byte[] { 0x30, 0x31 };
        public byte[] CID1 { get; set; } = new byte[] { 0x32, 0x41 };
        public byte[] CID2 { get; set; } = new byte[2];
        public List<byte> LENGTH { get; set; } = new List<byte>();
        public List<byte> INFO { get; set; } = new List<byte>();
        public List<byte> CHKSUM { get; set; } = new List<byte>();
        public byte EOI { get; set; } = 0X0D;
    }
    public static class CommonUtils
    {
        public static byte[] GetBytes(this RequestBean bean)
        {
            bean.LENGTH = GetLength(bean.INFO.Count);
            bean.CHKSUM = GetCheckSum(bean);

            var list = new List<byte>();
            list.Add(bean.SOI);
            list.AddRange(bean.VER);
            list.AddRange(bean.ADR);
            list.AddRange(bean.CID1);
            list.AddRange(bean.CID2);
            list.AddRange(bean.LENGTH);
            list.AddRange(bean.INFO);
            list.AddRange(bean.CHKSUM);
            list.Add(bean.EOI);

            return list.ToArray();
        }

        private static List<byte> GetLength(int input)
        {
            if (input <= 0)
            {
                return new List<byte> { 0X30, 0X30, 0X30, 0X30 };
            }

            var _byte = new List<byte>();

            var m = Convert.ToString(input, 2).PadLeft(12, '0'); //输入转字符串
            int i = 0;
            var sum = m.GroupBy(v => i++ % 3).Select(c => Convert.ToInt32(string.Join("", c), 2)).Sum(); //求和
            var check = (~sum + 1) & 15; //取反+1
            var value = ((check << 12) + input).ToString("X2"); //串连

            var array = value.ToCharArray();

            foreach (var b in array)
            {
                _byte.Add((byte)b);
            }
            return _byte;
        }

        private static List<byte> GetCheckSum(RequestBean bean)
        {
            var _byte = new List<byte>();

            var sum = bean.VER[0] + bean.VER[1] + bean.ADR[0] + bean.ADR[1] +
                      bean.CID1[0] + bean.CID1[1] + bean.CID2[0] + bean.CID2[1] +
                      bean.LENGTH[0] + bean.LENGTH[1] + bean.LENGTH[2] + bean.LENGTH[3];

            if (bean.INFO.Any())
            {
                foreach (var b in bean.INFO)
                {
                    sum += b;
                }
            }
            //sum = 0x038E;

            var value = (~sum + 1) & 65535; //取反+1

            string _value = value.ToString("X2");
            var array = _value.ToCharArray();

            foreach (var b in array)
            {
                _byte.Add((byte)b);
            }

            return _byte;
        }        
    }

测试代码部分:

1、如果你是串口模式(232或485),设定好协议里的波特率 、数据位 、停止位 、奇偶校验位 直接通过串口写入_serialPort.Write(buf, offSet, buf.Length),

   public class ScanProvider
    {
        #region 初始化串口扫描枪


        public SerialPort _serialPort = new SerialPort();

        /// <summary>
        /// </summary>
        /// <param name="portName">串口名</param>
        /// <param name="baudRate">波特率</param>
        public ScanProvider(SerialPort _serialPort)
        {
            this._serialPort = _serialPort;
            
            this._serialPort.DataReceived += _serialPort_DataReceived;
            
        }

        #endregion


        #region Public   


        /// <summary>  
        /// 是否处于打开状态  
        /// </summary>  
        public bool IsOpen
        {
            get { return _serialPort != null && _serialPort.IsOpen; }
        }

        public bool WritePort(byte[] send, int offset,int count)
        {
            try
            {
                this._serialPort.Write(send, offset, count);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>  
        /// 打开串口  
        /// </summary>  
        /// <returns></returns>  
        public bool Open()
        {
            try
            {

                if (_serialPort == null)
                    return this.IsOpen;



                if (_serialPort.IsOpen)
                    this.Close();



                _serialPort.Open();


            }
            catch (Exception e)
            {


                _serialPort.Close();
            }



            return this.IsOpen;
        }


        /// <summary>  
        /// 关闭串口  
        /// </summary>  
        public void Close()
        {
            if (this.IsOpen)
                _serialPort.Close();
        }


        /// <summary>  
        /// 向串口内写入  
        /// </summary>  
        /// <param name="send">写入数据</param>  
        /// <param name="offSet">偏移量</param>  
        /// <param name="count">写入数量</param>  
        public void Write(byte[] send, int offSet, int count)
        {
            if (this.IsOpen)
            {
                _serialPort.Write(send, offSet, count);
            }
        }


        public void Dispose()
        {
            if (this._serialPort == null)
                return;
            if (this._serialPort.IsOpen)
                this.Close();
            this._serialPort.Dispose();
            this._serialPort = null;
        }


        #endregion

        void _serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            // 等待100ms,防止读取不全的情况  
            Thread.Sleep(100);


            ReceiveDate();

        }


        public void ReceiveDate()
        {
            byte[] m_recvBytes = new byte[_serialPort.BytesToRead]; //定义缓冲区大小  
            int result = _serialPort.Read(m_recvBytes, 0, m_recvBytes.Length); //从串口读取数据  
            if (result <= 0)
                return;
            string strResult = Encoding.UTF8.GetString(m_recvBytes, 0, m_recvBytes.Length); //对数据进行转换  
            StringBuilder str = new StringBuilder();

            foreach (var b in m_recvBytes)
            {
                str.Append($"{b} ");
            }
            //for (int i = 7; i < m_recvBytes.Length; i++)
            //{
            //    if (i > m_recvBytes.Length - 3) break;
            //    str.Append($"{m_recvBytes[i]} ");
            //}

            Console.WriteLine($"接收: {str}");
            Console.WriteLine(strResult);
            Console.WriteLine();


            _serialPort.DiscardInBuffer();


            //if (this.DataReceived != null)
            //    this.DataReceived(this, new SerialSortEventArgs() { Code = strResult });
        }


        //public event EventHandler<SerialSortEventArgs> DataReceived;



    }
 static void Main(string[] args)
        {
                var _serialPort = new SerialPort("COM2");
            // 波特率  
            _serialPort.BaudRate = 9600;
            // 数据位  
            _serialPort.DataBits = 8;
            // 停止位  
            _serialPort.StopBits = StopBits.One;
            // 无奇偶校验位  
            _serialPort.Parity = Parity.None;

            var a = new ScanProvider(_serialPort);
            a.Open();

            Task.Run(() =>
            {
                while (true)
                {
                    Thread.Sleep(5000);
                    
                    var bean = new RequestBean();
                    //此处0x35 的ASC2码为5  0x31的ASC2码为1  组合起为就是51, 
                    bean.CID2 = new byte[] { 0x35, 0x31 };
                    //bean.INFO = new byte[2];

                    var buf = bean.GetBytes();

                    var String16 = byteToHexStr(buf);

                    Console.WriteLine($"发送: {String16}");
                    a.Write(buf, 0, buf.Length);

                }
            });
         }

2、如果是通过串口(232或485)转TCP协议,此设备一般都可以在设置里设置串口参数(我这里使用USR-232),设置好TCP模式后,用TCP发送ClientUPS.Send(buf); 我这里代码是tcpClient模式,设备为tcpServer模式。

	using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace L.Servers.OutsideSystem.VertivUPS
{
    #region ClientUPS
    public class ClientUPS
    {
        //定义,最好定义成静态的, 因为我们只需要一个就好  
        static Client smanager = null;
        //定义事件与委托  
        public delegate void ReceiveData(byte[] message);
        public delegate void ServerClosed();
        public static event ReceiveData OnReceiveData;
        public static event ServerClosed OnServerClosed;

        static byte[] heartBeat = new byte[0];

        /// <summary>  
        /// 心跳定时器  
        /// </summary>  
        static System.Timers.Timer heartTimer = null;

        /// <summary>  
        /// 判断是否已连接  
        /// </summary>  
        public static bool Connected
        {
            get { return smanager != null && smanager.Connected; }
        }

        #region 基本方法

        /// <summary>  
        /// 连接到服务器  
        /// </summary>  
        /// <returns></returns>  
        public static SocketError Connect(string ip, int port)
        {
            if (Connected) return SocketError.Success;
            if (string.IsNullOrWhiteSpace(ip) || port <= 0) return SocketError.Fault;
            //创建连接对象, 连接到服务器  
            smanager = new Client(ip, port);

            SocketError socketError = SocketError.ConnectionRefused;
            socketError = smanager.Connect();

            if (socketError == SocketError.Success)
            {
                //连接成功后,就注册事件. 最好在成功后再注册.  
                smanager.ServerDataHandler += OnReceivedServerData;
                smanager.ServerStopEvent += OnServerStopEvent;
            }
            return socketError;
        }

        /// <summary>  
        /// 断开连接  
        /// </summary>  
        public static void Disconnect()
        {
            try
            {
                smanager?.Disconnect();
                if (heartTimer != null)
                    heartTimer = null;
            }
            catch (Exception)
            {
                Console.WriteLine("未能关闭socket连接");
            }
        }
        /// <summary>  
        /// 发送消息  
        /// </summary>  
        /// <param name="message">消息实体</param>  
        /// <returns>True.已发送; False.未发送</returns>  
        public static bool Send(string message)
        {
            if (!Connected) return false;

            byte[] buff = Encoding.UTF8.GetBytes(message);
            //加密,根据自己的需要可以考虑把消息加密  
            //buff = AESEncrypt.Encrypt(buff, m_aesKey);  
            smanager.Send(buff);
            return true;
        }


        /// <summary>  
        /// 发送字节流  
        /// </summary>  
        /// <param name="buff"></param>  
        /// <returns></returns>  
        public static bool Send(byte[] buff)
        {
            if (!Connected) return false;
            smanager.Send(buff);
            return true;
        }

        /// <summary>  
        /// 接收消息  
        /// </summary>  
        /// <param name="buff"></param>  
        private static void OnReceivedServerData(byte[] buff)
        {
            //To do something  
            //你要处理的代码,可以实现把buff转化成你具体的对象, 再传给前台  
            if (OnReceiveData != null)
                OnReceiveData(buff);
        }

        /// <summary>  
        /// 服务器已断开  
        /// </summary>  
        private static void OnServerStopEvent()
        {
            if (OnServerClosed != null)
                OnServerClosed();
        }

        #endregion

        public static void StartHeartbeat(byte[] ht)
        {
            if (heartTimer == null)
            {
                heartBeat = ht;
                heartTimer = new System.Timers.Timer();
                heartTimer.Elapsed += TimeElapsed;
            }
            heartTimer.AutoReset = true;     //循环执行  
            heartTimer.Interval = 30 * 1000; //每30秒执行一次  
            heartTimer.Enabled = true;
            heartTimer.Start();

            //初始化心跳包消息  
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void TimeElapsed(object sender, System.Timers.ElapsedEventArgs e) => Send(heartBeat);
    }
    #endregion

    #region Client 连接到server,收发消息逻辑
    class Client : IDisposable
    {
        private const int BuffSize = 1024;

        private Socket clientSocket;

        private bool connected = false;

        private IPEndPoint hostEndPoint;

        private static AutoResetEvent autoConnectEvent = new AutoResetEvent(false);

        BufferManager m_bufferManager;
        //定义接收数据的对象  
        List<byte> m_buffer;
        //发送与接收的MySocketEventArgs变量定义.  
        private List<MySocketEventArgs> listArgs = new List<MySocketEventArgs>();

        private MySocketEventArgs receiveEventArgs = new MySocketEventArgs();
        int tagCount = 0;

        /// <summary>  
        /// 当前连接状态  
        /// </summary>  
        public bool Connected { get { return clientSocket != null && clientSocket.Connected; } }

        //服务器主动发出数据受理委托及事件  
        public delegate void OnServerDataReceived(byte[] receiveBuff);
        public event OnServerDataReceived ServerDataHandler;

        //服务器主动关闭连接委托及事件  
        public delegate void OnServerStop();
        public event OnServerStop ServerStopEvent;


        // Create an uninitialized client instance.  
        // To start the send/receive processing call the  
        // Connect method followed by SendReceive method.  
        internal Client(String ip, Int32 port)
        {
            // Instantiates the endpoint and socket.  
            hostEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
            clientSocket = new Socket(hostEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            m_bufferManager = new BufferManager(BuffSize * 2, BuffSize);
            m_buffer = new List<byte>();
        }

        /// <summary>  
        /// 连接到主机  
        /// </summary>  
        /// <returns>0.连接成功, 其他值失败,参考SocketError的值列表</returns>  
        internal SocketError Connect()
        {
            SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs();
            connectArgs.UserToken = clientSocket;
            connectArgs.RemoteEndPoint = hostEndPoint;
            connectArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnect);

            clientSocket.ConnectAsync(connectArgs);
            autoConnectEvent.WaitOne(); //阻塞. 让程序在这里等待,直到连接响应后再返回连接结果  

            return connectArgs.SocketError; ;
        }

        /// Disconnect from the host.  
        internal void Disconnect()
        {
            clientSocket.Disconnect(true);//true--这个socket会重用,false--这个socket不再重用。
        }

        // Calback for connect operation  
        private void OnConnect(object sender, SocketAsyncEventArgs e)
        {
            // Signals the end of connection.  
            autoConnectEvent.Set(); //释放阻塞.  
            // Set the flag for socket connected.  
            connected = (e.SocketError == SocketError.Success);
            //如果连接成功,则初始化socketAsyncEventArgs  
            if (connected)
                initArgs(e);
        }


        #region args

        /// <summary>  
        /// 初始化收发参数  
        /// </summary>  
        /// <param name="e"></param>  
        private void initArgs(SocketAsyncEventArgs e)
        {
            m_bufferManager.InitBuffer();
            //发送参数  
            initSendArgs();
            //接收参数  
            receiveEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
            receiveEventArgs.UserToken = e.UserToken;
            receiveEventArgs.ArgsTag = 0;
            m_bufferManager.SetBuffer(receiveEventArgs);

            //启动接收,不管有没有,一定得启动.否则有数据来了也不知道.  
            if (!e.ConnectSocket.ReceiveAsync(receiveEventArgs))
                ProcessReceive(receiveEventArgs);
        }

        /// <summary>  
        /// 初始化发送参数MySocketEventArgs  
        /// </summary>  
        /// <returns></returns>  
        MySocketEventArgs initSendArgs()
        {
            MySocketEventArgs sendArg = new MySocketEventArgs();
            sendArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
            sendArg.UserToken = clientSocket;
            sendArg.RemoteEndPoint = hostEndPoint;
            sendArg.IsUsing = false;
            Interlocked.Increment(ref tagCount);
            sendArg.ArgsTag = tagCount;
            lock (listArgs)
            {
                listArgs.Add(sendArg);
            }
            return sendArg;
        }



        void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            MySocketEventArgs mys = (MySocketEventArgs)e;
            // determine which type of operation just completed and call the associated handler  
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    mys.IsUsing = false; //数据发送已完成.状态设为False  
                    ProcessSend(e);
                    break;
                default:
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }
        }

        // This method is invoked when an asynchronous receive operation completes.   
        // If the remote host closed the connection, then the socket is closed.    
        // If data was received then the data is echoed back to the client.  
        //  
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            try
            {
                // check if the remote host closed the connection  
                Socket token = (Socket)e.UserToken;
                if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                {
                    //读取数据  
                    byte[] data = new byte[e.BytesTransferred];
                    Array.Copy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);
                    lock (m_buffer)
                    {
                        m_buffer.AddRange(data);
                    }

                    DoReceiveEvent(data);

                    if (!token.ReceiveAsync(e))
                        this.ProcessReceive(e);
                }
                else
                {
                    ProcessError(e); ///服务器断开
                }
            }
            catch (Exception xe)
            {
                Console.WriteLine(xe.Message);
            }
        }

        // This method is invoked when an asynchronous send operation completes.    
        // The method issues another receive on the socket to read any additional   
        // data sent from the client  
        //  
        // <param name="e"></param>  
        private void ProcessSend(SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
            {
                ProcessError(e);
            }
        }

        #endregion

        #region read write

        // Close socket in case of failure and throws  
        // a SockeException according to the SocketError.  
        private void ProcessError(SocketAsyncEventArgs e)
        {
            Socket s = (Socket)e.UserToken;
            if (s.Connected)
            {
                // close the socket associated with the client  
                try
                {
                    s.Shutdown(SocketShutdown.Both);
                }
                catch (Exception ex)
                {
                    // throws if client process has already closed  
                    Console.WriteLine(ex.Message);
                }
                finally
                {
                    if (s.Connected)
                    {
                        s.Close();
                    }
                    connected = false;
                }
            }
            //这里一定要记得把事件移走.  
            foreach (MySocketEventArgs arg in listArgs)
                arg.Completed -= IO_Completed;
            receiveEventArgs.Completed -= IO_Completed;

            if (ServerStopEvent != null)
                ServerStopEvent(); //服务器断开事件
        }

        // Exchange a message with the host.  
        internal void Send(byte[] buff)
        {
            if (connected)
            {
                MySocketEventArgs sendArgs = listArgs.Find(a => a.IsUsing == false);
                if (sendArgs == null)
                {
                    sendArgs = initSendArgs();
                }
                lock (sendArgs) //要锁定,
                {
                    sendArgs.IsUsing = true;
                    sendArgs.SetBuffer(buff, 0, buff.Length);
                }
                clientSocket.SendAsync(sendArgs);
            }
            else
            {
                throw new SocketException((Int32)SocketError.NotConnected);
            }
        }

        /// <summary>  
        /// 使用新进程通知事件回调  
        /// </summary>  
        /// <param name="buff"></param>  
        private void DoReceiveEvent(byte[] buff)
        {
            if (ServerDataHandler == null) return;
            //ServerDataHandler(buff); //可直接调用.  
            //但我更喜欢用新的线程,这样不拖延接收新数据.  
            Thread thread = new Thread(new ParameterizedThreadStart((obj) =>
            {
                ServerDataHandler((byte[])obj);
            }));
            thread.IsBackground = true;
            thread.Start(buff);
        }

        #endregion

        #region IDisposable Members

        // Disposes the instance of SocketClient.

        public void Dispose()
        {
            autoConnectEvent.Close();
            if (clientSocket.Connected)
            {
                clientSocket.Close();
            }
        }
        #endregion
    }
    #endregion
    #region BufferManager
    class BufferManager
    {
        int m_numBytes;                 // the total number of bytes controlled by the buffer pool  
        byte[] m_buffer;                // the underlying byte array maintained by the Buffer Manager  
        Stack<int> m_freeIndexPool;     //   
        int m_currentIndex;
        int m_bufferSize;

        public BufferManager(int totalBytes, int bufferSize)
        {
            m_numBytes = totalBytes;
            m_currentIndex = 0;
            m_bufferSize = bufferSize;
            m_freeIndexPool = new Stack<int>();
        }

        // Allocates buffer space used by the buffer pool  
        public void InitBuffer()
        {
            // create one big large buffer and divide that   
            // out to each SocketAsyncEventArg object  
            m_buffer = new byte[m_numBytes];
        }

        // Assigns a buffer from the buffer pool to the   
        // specified SocketAsyncEventArgs object  
        //  
        // <returns>true if the buffer was successfully set, else false</returns>  
        public bool SetBuffer(SocketAsyncEventArgs args)
        {

            if (m_freeIndexPool.Count > 0)
            {
                args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
            }
            else
            {
                if ((m_numBytes - m_bufferSize) < m_currentIndex)
                {
                    return false;
                }
                args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
                m_currentIndex += m_bufferSize;
            }
            return true;
        }

        // Removes the buffer from a SocketAsyncEventArg object.    
        // This frees the buffer back to the buffer pool  
        public void FreeBuffer(SocketAsyncEventArgs args)
        {
            m_freeIndexPool.Push(args.Offset);
            args.SetBuffer(null, 0, 0);
        }
    }
    #endregion

    #region MySocketEventArgs 获取SocketAsyncEventArgs 的使用状态
    class MySocketEventArgs : SocketAsyncEventArgs
    {

        /// <summary>  
        /// 标识,只是一个编号而已  
        /// </summary>  
        public int ArgsTag { get; set; }

        /// <summary>  
        /// 设置/获取使用状态  
        /// </summary>  
        public bool IsUsing { get; set; }
    }
    #endregion
}

  static void Main(string[] args)
        {

            SocketError socketError = ClientUPS.Connect("192.168.188.221", 39221);
            if (socketError == SocketError.Success)
            {

                ClientUPS.OnReceiveData += Request_OnReceiveData;
                ClientUPS.OnServerClosed += () => { ClientUPS.Disconnect(); };
            }


            Task.Run(() =>
            {
                while (true)
                {
                    Thread.Sleep(5000);
                    var bean = new RequestBean();
                    bean.CID2 = new byte[] { 0x35, 0x31 };
                    //bean.INFO = new byte[2];

                    var buf = bean.GetBytes();

                    var String16 = byteToHexStr(buf);

                    Console.WriteLine($"发送: {String16}");
                    ClientUPS.Send(buf);

                }
            });


            Console.WriteLine("服务已启动.按Q退出.");

            var result = Console.ReadKey();
            while (result.Key != ConsoleKey.Q)
            {
                result = Console.ReadKey();
            }
            Console.WriteLine("服务已退出.");

        }

3、如果是通过串口(232或485)转TCP协议,而设备无法设置串口参数,此时你需要用虚拟串口软件新建一个虚拟串口,创建虚拟串口后,按照上面方法1连接。

完整代码

电总协议完整代码

以上纯属个人见解,高手勿喷…

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值