Register.cs 13.3 KB
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DL.Com;
using DL.Utils;

namespace DL.Com.Protocol.Modbus
{
    /// <summary>
    /// 寄存器
    /// </summary>
    public abstract class Register
    {
        public delegate void StatusChangedEventHandler(StatusChangedEventArgs statusChangedEventArgs);
        public event StatusChangedEventHandler StatusChanged;
        protected static log4net.ILog log;
        protected static ushort _serialnum = 0;
        public Register(string logname = "logModbus")
        {
            log = log4net.LogManager.GetLogger(logname);
            FunctionCodes = new Dictionary<RegisterFunction, byte>();
            FrameStruct = new DataFrame();
        }
        /// <summary>
        /// 功能码
        /// </summary>
        public Dictionary<RegisterFunction, byte> FunctionCodes { get; set; }
        /// <summary>
        /// 帧结构
        /// </summary>
        public DataFrame FrameStruct { get; protected set; }
        /// <summary>
        /// 数据帧--字节数据
        /// </summary>
        public byte[] DataFrame { get { return FrameStruct.ToBytes(); } }
        /// <summary>
        /// PLC地址
        /// </summary>
        public string PLCAddress { get; }
        /// <summary>
        /// 根据命令设置PDU参数
        /// </summary>
        /// <param name="cmdByte"></param>
        /// <returns></returns>
        protected abstract void SetParamPDU(CmdByte cmdByte);
        /// <summary>
        /// 设置MBAP报文头
        /// </summary>
        /// <param name="serialnum">事务处理标识,即报文序列号</param>
        /// <param name="equipAddr">单元标识符,即设备地址</param>
        protected void SetMBAP(ushort serialnum, byte equipAddr)
        {
            FrameStruct.SetMBAP(serialnum, equipAddr);
            _serialnum++;
            if (_serialnum >= ushort.MaxValue)
            {
                _serialnum = 0;
            }
        }
        /// <summary>
        /// 设置帧结构
        /// </summary>
        /// <param name="funcStr">功能</param>
        /// <param name="addr">开始地址</param>
        /// <param name="value">数量/值</param>
        /// <param name="bytelenght">字节长度,写多个线圈/寄存器使用</param>
        /// <param name="valueofmulti">写入的值,写多个线圈/寄存器使用</param>
        protected void SetPDU(RegisterFunction func, Byte2 addr, Byte2 value, byte bytelenght = 0x00, byte[] valueofmulti = null)
        {
            FrameStruct.SetPDU(FunctionCodes[func], addr, value, bytelenght, valueofmulti);
        }
        /// <summary>
        /// 检查响应的MBAP+功能码是否与请求一致
        /// </summary>
        /// <param name="data">响应的数据</param>
        /// <returns>true:请求与返回对应</returns>
        private bool CheckResponse(byte[] data, bool ignoreSerialNum = false)
        {
            if (data != null && data.Length > 8)
            {
                if (FrameStruct.MBAP.SerialNum.ByteH.Equals(data[0]) &&
                    FrameStruct.MBAP.SerialNum.ByteL.Equals(data[1]) &&
                    FrameStruct.MBAP.ProtocolId.ByteH.Equals(data[2]) &&
                    FrameStruct.MBAP.ProtocolId.ByteL.Equals(data[3]) &&
                    FrameStruct.MBAP.EquipAddr.Equals(data[6]) &&
                    FrameStruct.PDU.FuncCode.Equals(data[7])
                    )
                {
                    return true;
                }
                if (ignoreSerialNum)
                {
                    if (
                    FrameStruct.MBAP.ProtocolId.ByteH.Equals(data[2]) &&
                    FrameStruct.MBAP.ProtocolId.ByteL.Equals(data[3]) &&
                    FrameStruct.MBAP.EquipAddr.Equals(data[6]) &&
                    FrameStruct.PDU.FuncCode.Equals(data[7])
    )
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// 获取响应数据的结果,并返回响应的
        /// </summary>
        /// <param name="data">响应的数据</param>
        /// <returns>true:响应成功</returns>
        protected bool GetResponseResult(byte[] response, out byte[] data, bool ignoreSerial = false)
        {
            if (CheckResponse(response, ignoreSerial))
            {
                data = new byte[response.Length - 8];
                Array.Copy(response, 8, data, 0, response.Length - 8);
                return true;
            }
            if (response != null && response.Length >= 8)
            {
                data = new byte[] { response[7] };
                if ((data[0] & 0x80) == 0x80)
                {
                    LogUtil.Error($"从站异常响应[{StringHelper.ToHexString(data[0])}],响应数据为[{StringHelper.ToHexString(response)}]", log);
                }
                return false;
            }


            data = new byte[] { 0x00 };
            return false;
        }
        /// <summary>
        /// 解析读操作的响应数据
        /// </summary>
        /// <param name="response">响应的字节数据</param>
        /// <param name="data"></param>
        /// <param name="ignoreserial">忽略请求编号</param>
        /// <returns>true:读取成功</returns>
        protected virtual bool ParseRead(byte[] response, out byte[] data, bool ignoreserial = false)
        {
            data = null;
            if (GetResponseResult(response, out byte[] data1, ignoreserial))
            {
                int len = data1[0];
                data = new byte[len];
                Array.Copy(data1, 1, data, 0, len);
                return true;
            }
            return false;
        }
        /// <summary>
        /// 解析写单个操作的响应数据
        /// </summary>
        /// <param name="response">响应的字节数据</param>
        /// <returns>true:写入单个操作成功</returns>
        protected abstract bool ParseWriteSingle(byte[] response);
        /// <summary>
        /// 解析写多个操作的响应数据
        /// </summary>
        /// <param name="response">响应的字节数据</param>
        /// <returns>true:写多单个操作成功</returns>
        protected abstract bool ParseWriteMultiple(byte[] response);
        /// <summary>
        /// 获取响应的结果
        /// </summary>
        /// <param name="function"></param>
        /// <param name="response"></param>
        /// <param name="data"></param>
        /// <returns>解析</returns>
        public bool GetResult(RegisterFunction function, byte[] response, out byte[] data)
        {
            bool rtn = false;
            data = null;
            switch (function)
            {
                case RegisterFunction.Read:
                    rtn = ParseRead(response, out data);
                    break;
                case RegisterFunction.WriteSingle:
                    rtn = ParseWriteSingle(response);
                    break;
                case RegisterFunction.WriteMultiple:
                    rtn = ParseWriteMultiple(response);
                    break;
                default:
                    rtn = ParseRead(response, out data);
                    break;
            }
            return rtn;
        }
        public bool GetReadReault(byte[] response, out byte[] data)
        {
            bool rtn = ParseRead(response, out data, true);
            return rtn;
        }
        /// <summary>
        /// 分离多个反馈字节
        /// </summary>
        /// <param name="data"></param>
        /// <returns>《功能码,响应的数据》</returns>
        public static List<byte[]> SplitResponse(byte[] data)
        {
            List<byte[]> rtn = new List<byte[]>();
            List<byte> tmpdata = new List<byte>();
            tmpdata.AddRange(data);
            while (tmpdata.Count >= 8)
            {
                if (ParseHeader(tmpdata.ToArray(), out byte func, out int len))
                {
                    byte[] tmp = new byte[len + 6];
                    Array.Copy(tmpdata.ToArray(), 0, tmp, 0, len + 6);
                    rtn.Add(tmp);
                    tmpdata.RemoveRange(0, tmp.Length);
                }
            }
            return rtn;
        }
        /// <summary>
        /// 解析头
        /// </summary>
        /// <param name="header"></param>
        /// <param name="func">功能码</param>
        /// <param name="len">长度</param>
        /// <returns>解析成功</returns>
        public static bool ParseHeader(byte[] header, out byte func, out int len)
        {
            func = 0;
            len = 0;
            if (header != null && header.Length >= 8)
            {
                func = header[7];
                len = Byte2.GetInt32(header[4], header[5]);
                return true;
            }
            return false;
        }
        /// <summary>
        /// 写入多个值时,将写入的值转换为发送的数组
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static int[] GetValuesWithMultiMode(int[] data)
        {
            int mod = data.Length % 8;
            List<int> tmp = new List<int>();
            int t = 0;
            for (int i = 0; i < data.Length; i++)
            {
                if ((i + 1) % 8 == 0)
                {
                    t += data[i] * (int)Math.Pow(2, data[i]);
                    tmp.Add(t);
                    t = 0;
                }
                else
                {
                    t += data[i] * (int)Math.Pow(2, data[i]);
                }

            }
            if (mod != 0)
                tmp.Add(t);
            return tmp.ToArray();
        }
        /// <summary>
        /// 设置并获取请求字节数组
        /// </summary>
        /// <param name="func"></param>
        /// <param name="startAddr"></param>
        /// <param name="countOrValue"></param>
        /// <param name="ValuesOfMultiple"></param>
        /// <returns></returns>
        public virtual byte[] GetRequestBytes(RegisterFunction func, ushort startAddr, ushort countOrValue, byte equipAddr = 1, ushort[] ValuesOfMultiple = null)
        {
            CmdByte cmdByte = new CmdByte();
            cmdByte.StartAddr = Byte2.GetStru2Byte(startAddr);
            cmdByte.Function = func;
            cmdByte.Count = Byte2.GetStru2Byte(countOrValue);
            if (func.Equals(RegisterFunction.WriteMultiple))
            {
                if (ValuesOfMultiple == null)
                    return null;
                List<byte> values = new List<byte>();
                foreach (var item in ValuesOfMultiple)
                {
                    values.Add((byte)item);
                }
                cmdByte.ByteLength = (byte)(countOrValue % 8 == 0 ? countOrValue / 8 : countOrValue / 8 + 1);
                cmdByte.ValuesOfMultiple = values.ToArray();
            }
            SetMBAP(_serialnum, equipAddr);
            SetParamPDU(cmdByte);
            return DataFrame;
        }
    }

    public class StatusChangedEventArgs : EventArgs
    {
        public Dictionary<int, byte> ChangedIdxAndValue { get; set; }
        public byte[] Data;
        public bool Init { get; set; }
        public StatusChangedEventArgs()
        {
            ChangedIdxAndValue = new Dictionary<int, byte>();
        }
    }
    public class CmdByte
    {
        public RegisterFunction Function { get; set; }
        public Byte2 StartAddr { get; set; }
        public Byte2 Count { get; set; }
        public byte ByteLength { get; set; }
        public byte[] ValuesOfMultiple { get; set; }
        string ToHexString(byte[] data)
        {
            if (data == null)
                return "";
            StringBuilder stringBuilder = new StringBuilder();
            foreach (var item in data)
            {
                stringBuilder.Append(item.ToString("x2"));
                stringBuilder.Append(" ");
            }
            return stringBuilder.ToString().Trim();
        }

        public override string ToString()
        {
            return $"[Function={Function.ToString()},StartAddr={StartAddr},Count/Value={Count},ByteLength={ByteLength},ValuesOfMultiple={ToHexString(ValuesOfMultiple)}]";
        }
    }
    /// <summary>
    /// 寄存器功能
    /// </summary>
    public enum RegisterFunction
    {
        /// <summary>
        /// 读
        /// </summary>
        Read,
        /// <summary>
        /// 写单个
        /// </summary>
        WriteSingle,
        /// <summary>
        /// 写多个
        /// </summary>
        WriteMultiple,
    }
    /// <summary>
    /// 寄存器类型
    /// </summary>
    public enum RegisterType
    {
        /// <summary>
        /// 线圈,支持的功能码为 0x01,0x05,0x0f
        /// </summary>
        CoilSatus,
        /// <summary>
        /// 离散输入,支持的功能码为 0x02
        /// </summary>
        InputStatus,
        /// <summary>
        /// 输入寄存器,支持的功能码为 0x04
        /// </summary>
        InputRegister,
        /// <summary>
        /// 保持寄存器,支持的功能码为 0x03,0x06,0x10
        /// </summary>
        HoldingRegister,
    }
}