一组RS485设备操作命令

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZNJM2.Win
{
    public class CommandBase
    {
        public byte Addr { get; set; }

        public List<byte> CmdBytes { get; protected set; }
        public string Name { get; set; }
        public Object Result { get; set; }
        public DateTime? BTime { get; set; }
        public DateTime? ETime { get; set; }
        public int delayMillis { get; set; }
        protected Func<byte[], int, byte[]> CRC { get; set; }
        /// <summary>
        /// 执行次数
        /// </summary>
        public long? RunCount { get; set; }
        /// <summary>
        /// 错误次数
        /// 每次成功执行后设置成0
        /// </summary>
        public long? ErrCount { get; set; }
        public Exception Err { get; set; }

        public List<byte> ReturnBytes { get; set; }


        /// <summary>
        ///  如 01 02,
        ///     01-02,
        ///     0102,
        ///   等形式
        /// </summary>
        public string CmdText { get; set; }
        public string CmdHexString
        {
            get
            {
                return BitConverter.ToString(CmdBytes.ToArray());
            }
        }
        public CommandBase()
        {
            CmdBytes=new List<byte>();
            ReturnBytes = new List<byte>();
            Addr = 0x01;
            ErrCount = 0;
            RunCount = 0;
            CRC = CRCUtil.CRC16;

        }

        #region 方法

        /// <summary>
        /// 生成命令的字节数组
        ///  如 01 02
        ///     01-02
        ///     0102
        ///   等形式
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        public virtual List<byte> ParserCmd()
        {
            var cmdBytes = ParserCmdWithoutCRC();
            var crc2 = CRC(cmdBytes, cmdBytes.Length);


            CmdBytes = new List<byte>(cmdBytes.Concat(crc2));
            return CmdBytes;

        }

        protected virtual byte[] ParserCmdWithoutCRC()
        {
            string cmdText = CmdText;
            var cmd = cmdText.Replace(" ", "").Replace("-", "").Trim();
            if (string.IsNullOrWhiteSpace(cmd))
            {
                throw new Exception("空字符串!");
            }
            if ((cmd.Length % 2) != 0)
            {
                throw new Exception("命令字符窜长度必须是2的倍数!");
            }
            cmd = BitConverter.ToString(new byte[] { Addr }) + cmd;
            var cmdBytes = new byte[cmd.Length / 2];
            for (int i = 0; i < cmd.Length; i = i + 2)
            {
                cmdBytes[i / 2] = byte.Parse(cmd.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
            }
            return cmdBytes;
        }

        protected virtual bool VerifyResult()
        {

            if (ReturnBytes.Count <= 0)
            {
                Err=new Exception("零长度返回!");
                ErrCount++;
                return false;

            }
            if (ReturnBytes.Count <= 2)
            {
                Err = new Exception("返回字节少于2!");
                ErrCount++;
                return false;
            }

            //CRC Check\
            var len = ReturnBytes.Count;
            var crc = CRC(ReturnBytes.ToArray(), len-2);
            if (crc[0] != ReturnBytes[len - 2] || crc[1] != ReturnBytes[len - 1])
            {
                Err = new Exception("CRC校验错误!");
                ErrCount++;
                return false;
            }

            if (ReturnBytes[0] != Addr)
            {
                Err = new Exception("非期望的设备地址!");
                ErrCount++;
                return false;
            }
            if (ReturnBytes[1] != CmdBytes[1])
            {
                Err = new Exception("非期望的命令!");
                ErrCount++;
                return false;
            }




            return true;
        }

        public virtual bool ParserReturn()
        {
            return VerifyResult();
           
        }
        #endregion
    }
    public class RFIDCmdBase : CommandBase
    {
        public RFIDCmdBase()
        {

            CRC = CRCUtil.CRC16ForRFID;

        }


        /// <summary>
        /// 生成命令的字节数组
        ///  如 01 02
        ///     01-02
        ///     0102
        ///   等形式
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        public override List<byte> ParserCmd()
        {
            var cmdBytes = ParserCmdWithoutCRC();
            var crc2 = CRC(cmdBytes, cmdBytes.Length);


            CmdBytes = new List<byte>(cmdBytes.Concat(crc2));
            return CmdBytes;

        }

        protected override byte[] ParserCmdWithoutCRC()
        {
            string cmdText = CmdText;
            var cmd = cmdText.Replace(" ", "").Replace("-", "").Trim();
            if (string.IsNullOrWhiteSpace(cmd))
            {
                throw new Exception("空字符串!");
            }
            if ((cmd.Length % 2) != 0)
            {
                throw new Exception("命令字符窜长度必须是2的倍数!");
            }
            cmd = BitConverter.ToString(new byte[] { Addr }) + cmd;
            var cmdBytes = new byte[cmd.Length / 2];
            for (int i = 0; i < cmd.Length; i = i + 2)
            {
                cmdBytes[i / 2] = byte.Parse(cmd.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
            }


            var lenV = (byte)(cmdBytes.Length + 2); //添加命令长度
            return new byte[] { lenV }.Concat(cmdBytes).ToArray();

          
        }

        protected override bool VerifyResult()
        {

            if (ReturnBytes.Count <= 0)
            {
                Err = new Exception("零长度返回!");
                ErrCount++;
                return false;

            }
            if (ReturnBytes.Count <= 5)
            {
                Err = new Exception("返回字节少于5!");
                ErrCount++;
                return false;
            }

            //CRC Check\
            var len = ReturnBytes.Count;
            var crc = CRC(ReturnBytes.ToArray(), len - 2);
            if (crc[0] != ReturnBytes[len - 2] || crc[1] != ReturnBytes[len - 1])
            {
                Err = new Exception("CRC校验错误!");
                ErrCount++;
                return false;
            }

            if (ReturnBytes[1] != Addr)
            {
                Err = new Exception("非期望的设备地址!");
                ErrCount++;
                return false;
            }
            if (ReturnBytes[2] != CmdBytes[2])
            {
                Err = new Exception("非期望的命令!");
                ErrCount++;
                return false;
            }




            return true;
        }

    }

}
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace ZNJM2.Win
{
    #region 开关命令
    public class PowerSwitchQuery : CommandBase
    {
        public PowerSwitchQuery(byte addr)
        {
            Addr = addr;
            CmdText = "03";
            CmdBytes = ParserCmd();
            Name = "开关-状态";
        }

        public override bool ParserReturn()
        {
            bool flag = false;
            flag = base.VerifyResult();
            if (flag && ReturnBytes.Count >= 5)
            {
                flag = true;
                Result = ReturnBytes[2] == 0 ? "Off" : "On";
                ErrCount = 0;
                Err = null;
             

            }
            
            return flag;


        }
    }

    public class PowerSwitchOn : CommandBase
    {
        public PowerSwitchOn(byte addr)
        {
            Addr = addr;
            CmdText = "0601";
            CmdBytes = ParserCmd();
            Name = "开关-打开";
        }
    }

    public class PowerSwitchOff : CommandBase
    {
        public PowerSwitchOff(byte addr)
        {
            Addr = addr;
            CmdText = "0600";
            CmdBytes = ParserCmd();
            Name = "开关-关闭";
        }
    }
    #endregion

    #region 计米器
    public class LengthCounterQuery : CommandBase
    {
        public LengthCounterQuery(byte addr)
        {
            Addr = addr;
            CmdText = "03 00 01";
            ParserCmd();
            Name = "计米器-读数";
        }

        public override bool ParserReturn()
        {
            var flag=base.ParserReturn();

            if (ReturnBytes.Count > 6 && flag)
            {

                var vhex = BitConverter.ToString(new byte[] { ReturnBytes[5], ReturnBytes[6], ReturnBytes[3], ReturnBytes[4] });
                var value = int.Parse(vhex.Replace("-", ""), NumberStyles.HexNumber);
                Result = value;
                ErrCount = 0;
                Err = null;
                

            }
            return flag;
                 
        }

    }

    //清空读数
    public class LengthCounterCls : CommandBase
    {
        public LengthCounterCls(byte addr)
        {
            Addr = addr;
            CmdText = "06 00 0C 01";
            ParserCmd();
            Name = "计米器-清零";
        }

    }
    #endregion

    #region 电表

        //public class VoltageQuery : CommandBase
        //{
        //    public VoltageQuery(byte addr)
        //    {
        //        Addr = addr;
        //        CmdText = "03-00-0C-00-02";
        //        CmdBytes = ParserCmd();
        //        Name = "电表-电压";
        //    }
        //    public override bool ParserReturn()
        //    {
        //        bool flag = false;
        //        flag = base.VerifyResult();
        //        if (flag && ReturnBytes.Count >= 7)
        //        {
        //            flag = true;
        //            Result = BitConverter.ToString(ReturnBytes.ToArray(), 0);
        //            Console.WriteLine(Result.ToString());

        //            var tempArr = new byte[4];
        //            ReturnBytes.CopyTo(3, tempArr, 0, 4);

        //            Result = BitConverter.ToSingle(tempArr.Reverse().ToArray(), 0);


        //            ErrCount = 0;
        //            Err = null;


        //        }

        //        return flag;


        //    }
        //}
        //public class ElectricCurrent : CommandBase
        //{
        //    public ElectricCurrent(byte addr)
        //    {
        //        Addr = addr;
        //        CmdText = "03-00-0A-00-02";
        //        CmdBytes = ParserCmd();
        //        Name = "电表-电流";
        //    }
        //    public override bool ParserReturn()
        //    {
        //        bool flag = false;
        //        flag = base.VerifyResult();
        //        if (flag && ReturnBytes.Count >= 7)
        //        {
        //            flag = true;
        //            Result = BitConverter.ToString(ReturnBytes.ToArray(), 0);
        //            Console.WriteLine(Result.ToString());
        //            ===========
        //            var tempArr = new byte[4];
        //            ReturnBytes.CopyTo(3, tempArr, 0, 4);

        //            Result = BitConverter.ToSingle(tempArr.Reverse().ToArray(), 0);

        //            ErrCount = 0;
        //            Err = null;


        //        }

        //        return flag;


        //    }
        //}
        //public class ElectricEnergy : CommandBase
        //{
        //    public ElectricEnergy(byte addr)
        //    {
        //        Addr = addr;
        //        CmdText = "03-00-1A-00-02";
        //        CmdBytes = ParserCmd();
        //        Name = "电表-电能";
        //    }
        //    public override bool ParserReturn()
        //    {
        //        bool flag = false;
        //        flag = base.VerifyResult();
        //        if (flag && ReturnBytes.Count >= 7)
        //        {
        //            flag = true;
        //            Result = BitConverter.ToString(ReturnBytes.ToArray(), 0);
        //            Console.WriteLine(Result.ToString());
        //           // ===========
        //            var tempArr = new byte[4];
        //            ReturnBytes.CopyTo(3, tempArr, 0, 4);

        //            Result = BitConverter.ToSingle(tempArr.Reverse().ToArray(), 0);

        //            ErrCount = 0;
        //            Err = null;


        //        }

        //        return flag;


        //    }
        //}
    #endregion


    #region 电表DDS238-1Z系列
        public class VoltageQuery : CommandBase
        {
            public VoltageQuery(byte addr)
            {
                Addr = addr;
                CmdText = "03-00-0C-00-01";
                CmdBytes = ParserCmd();
                Name = "电表-电压";
            }
            public override bool ParserReturn()
            {
                bool flag = false;
                flag = base.VerifyResult();
                if (flag && ReturnBytes.Count >= 7)
                {
                    flag = true;
                    Result = BitConverter.ToString(ReturnBytes.ToArray(), 0);
                    Console.WriteLine(Result.ToString());

                    var tempArr = new byte[2];
                    ReturnBytes.CopyTo(3, tempArr, 0, tempArr.Length);

                    Result = BitConverter.ToUInt16(tempArr.Reverse().ToArray(), 0) * 0.1;


                    ErrCount = 0;
                    Err = null;


                }

                return flag;


            }
        }
        public class ElectricCurrent : CommandBase
        {
            public ElectricCurrent(byte addr)
            {
                Addr = addr;
                CmdText = "03-00-0d-00-01";
                CmdBytes = ParserCmd();
                Name = "电表-电流";
            }
            public override bool ParserReturn()
            {
                bool flag = false;
                flag = base.VerifyResult();
                if (flag && ReturnBytes.Count >= 5)
                {
                    flag = true;
                    Result = BitConverter.ToString(ReturnBytes.ToArray(), 0);
                    Console.WriteLine(Result.ToString());
                   // ===========
                    var tempArr = new byte[2];
                    ReturnBytes.CopyTo(3, tempArr, 0, tempArr.Length);

                    Result = BitConverter.ToUInt16(tempArr.Reverse().ToArray(), 0) * 0.01;

                    ErrCount = 0;
                    Err = null;


                }

                return flag;


            }
        }
        public class ElectricEnergy : CommandBase
        {
            public ElectricEnergy(byte addr)
            {
                Addr = addr;
                CmdText = "03-00-00-00-02";
                CmdBytes = ParserCmd();
                Name = "电表-电能";
            }
            public override bool ParserReturn()
            {
                bool flag = false;
                flag = base.VerifyResult();
                if (flag && ReturnBytes.Count >= 7)
                {
                    flag = true;
                    Result = BitConverter.ToString(ReturnBytes.ToArray(), 0);
                    Console.WriteLine(Result.ToString());
                    // ===========
                    var tempArr = new byte[4];
                    ReturnBytes.CopyTo(3, tempArr, 0, tempArr.Length);

                    Result = BitConverter.ToUInt32(tempArr.Reverse().ToArray(), 0) * 0.01;

                    ErrCount = 0;
                    Err = null;


                }

                return flag;


            }
        }

        public class ElectricFrequency : CommandBase
        {
            public ElectricFrequency(byte addr)
            {
                Addr = addr;
                CmdText = "03-00-11-00-01";
                CmdBytes = ParserCmd();
                Name = "电表-频率";
            }
            public override bool ParserReturn()
            {
                bool flag = false;
                flag = base.VerifyResult();
                if (flag && ReturnBytes.Count >= 7)
                {
                    flag = true;
                    Result = BitConverter.ToString(ReturnBytes.ToArray(), 0);
                    Console.WriteLine(Result.ToString());
                    // ===========
                    var tempArr = new byte[2];
                    ReturnBytes.CopyTo(3, tempArr, 0, tempArr.Length);

                    Result = BitConverter.ToUInt16(tempArr.Reverse().ToArray(), 0) * 0.01;

                    ErrCount = 0;
                    Err = null;


                }

                return flag;


            }
        }
    #endregion

#region RFID指令

     public class RFIDGetReaderInfo : RFIDCmdBase
        {
            /// <summary>
            /// 功率
            /// </summary>
            public byte Power { get; set; }
            /// <summary>
            /// 命令最大响应时间
            /// 单位100ms
            /// </summary>
            public byte CmdDelay { get; set; }
            public RFIDGetReaderInfo(byte addr)
        {
            Addr = addr;
            CmdText = "21";
            CmdBytes = ParserCmd();
            Name = "标签-获取读卡器信息";
           
        }
       
             public override bool ParserReturn()
        {
     

            bool flag = true;
            flag = base.VerifyResult();
            var RetArr=ReturnBytes.ToArray();
            var status = BitConverter.ToString(RetArr,3,1);
            if (ReturnBytes[3] != 0x00)
            {
                Err = new Exception(string.Format("返回的命令状态是{0},非期望的{1}",status,0x00));
                ErrCount++;
                return false;
            }
            if (flag && ReturnBytes.Count >= 7)
            {
                flag = true;
                Result = BitConverter.ToString(RetArr, 0);
                //Console.WriteLine(Result.ToString());


                Power = RetArr[0x0A];
                CmdDelay = RetArr[0x0B];

                ErrCount = 0;
                Err = null;


            }

            return flag;


        }
        }
     public class RFIDQueryTID : RFIDCmdBase
    {

         public int? LblNum { get; set; }
         public List<String> LblList { get; set; }
        public RFIDQueryTID(byte addr)
        {
            Addr = addr;
            CmdText = "01 02 04";
            LblList = new List<string>();
            CmdBytes = ParserCmd();
            Name = "标签-查询TID";
           
        }
       
        public override bool ParserReturn()
        {
            LblNum = 0;
            LblList.Clear();

            bool flag = true;
            flag = base.VerifyResult();
            var RetArr=ReturnBytes.ToArray();
            var status = BitConverter.ToString(RetArr,3,1);
            if (ReturnBytes[3] != 0x01)
            {
                Err = new Exception(string.Format("返回的命令状态是{0},非期望的{1}",status,0x01));
                ErrCount++;
                return false;
            }
            if (flag && ReturnBytes.Count >= 7)
            {
                flag = true;
                Result = BitConverter.ToString(RetArr, 0);
                //Console.WriteLine(Result.ToString());


                LblNum = ReturnBytes[4];
                int offset=5;
                for (int i = 0; i < LblNum; i++)
                {
                    var lblLen=ReturnBytes[offset];
                    var tmpArr=new byte[lblLen];
                    var startIndex=offset +1;
                    Array.Copy(RetArr, startIndex, tmpArr, 0, lblLen);
                    LblList.Add(BitConverter.ToString(tmpArr).Replace("-", ""));
                    offset += lblLen + 1;

                }
                


                ErrCount = 0;
                Err = null;


            }

            return flag;


        }
    }
     public class RFIDQueryEPC : RFIDCmdBase
     {

         public int? LblNum { get; set; }
         public List<String> LblList { get; set; }
         public List<List<byte>> LblBytesList { get; set; }
         public RFIDQueryEPC(byte addr)
         {
             Addr = addr;
             CmdText = "01";
             LblList = new List<string>();
             LblBytesList = new List<List<byte>>();
             CmdBytes = ParserCmd();
             Name = "标签-查询EPC";

         }

         public override bool ParserReturn()
         {
             LblNum = 0;
             LblList.Clear();

             bool flag = true;
             flag = base.VerifyResult();
             var RetArr = ReturnBytes.ToArray();
             var status = BitConverter.ToString(RetArr, 3, 1);
             if (ReturnBytes[3] != 0x01)
             {
                 Err = new Exception(string.Format("返回的命令状态是{0},非期望的{1}", status, 0x01));
                 ErrCount++;
                 return false;
             }
             if (flag && ReturnBytes.Count >= 7)
             {
                 flag = true;
                 Result = BitConverter.ToString(RetArr, 0);
                 //Console.WriteLine(Result.ToString());


                 LblNum = ReturnBytes[4];
                 int offset = 5;
                 for (int i = 0; i < LblNum; i++)
                 {
                     var lblLen = ReturnBytes[offset];
                     var tmpArr = new byte[lblLen];
                     var startIndex = offset + 1;
                     Array.Copy(RetArr, startIndex, tmpArr, 0, lblLen);
                     LblList.Add(BitConverter.ToString(tmpArr).Replace("-", ""));
                     LblBytesList.Add(tmpArr.ToList());
                     offset += lblLen + 1;

                 }



                 ErrCount = 0;
                 Err = null;


             }

             return flag;


         }
     }
     public class RFIDQueryTopOne : RFIDCmdBase
     {
         public int? LblNum { get; set; }
         public List<String> LblList { get; set; }
         public RFIDQueryTopOne(byte addr)
        {
            Addr = addr;
            CmdText = "0F";
            LblList = new List<string>();
            CmdBytes = ParserCmd();
            Name = "标签-查询单张";
           
        }
       
        public override bool ParserReturn()
        {
            LblNum = 0;
            LblList.Clear();

            bool flag = true;
            flag = base.VerifyResult();
            var RetArr=ReturnBytes.ToArray();
            var status = BitConverter.ToString(RetArr,3,1);
            if (ReturnBytes[3] != 0x01)
            {
                Err = new Exception(string.Format("返回的命令状态是{0},非期望的{1}",status,0x01));
                ErrCount++;
                return false;
            }
            if (flag && ReturnBytes.Count >= 7)
            {
                flag = true;
                Result = BitConverter.ToString(RetArr, 0);
                //Console.WriteLine(Result.ToString());


                LblNum = ReturnBytes[4];
                int offset=5;
                for (int i = 0; i < LblNum; i++)
                {
                    var lblLen=ReturnBytes[offset];
                    var tmpArr=new byte[lblLen];
                    var startIndex=offset +1;
                    Array.Copy(RetArr, startIndex, tmpArr, 0, lblLen);
                    LblList.Add(BitConverter.ToString(tmpArr).Replace("-", ""));
                    offset += lblLen + 1;

                }
                


                ErrCount = 0;
                Err = null;


            }

            return flag;


        }
     }
     public class RFIDSetPower : RFIDCmdBase
     {
         /// <summary>
         /// 
         /// </summary>
         /// <param name="addr"></param>
         /// <param name="power">功率 1-30</param>
         public RFIDSetPower(byte addr,byte power)
        {
            Addr = addr;
            CmdText = "2f" + BitConverter.ToString(new byte[]{power});
          
            CmdBytes = ParserCmd();
            Name = "标签-设置功率";
           
        }
         public override bool ParserReturn()
         {
        

             bool flag = true;
             flag = base.VerifyResult();
             var RetArr = ReturnBytes.ToArray();
             var status = BitConverter.ToString(RetArr, 3, 1);
             if (ReturnBytes[3] != 0x00)
             {
                 Err = new Exception(string.Format("返回的命令状态是{0},非期望的{1}", status, 0x01));
                 ErrCount++;
                 return false;
             }
             if (flag && ReturnBytes.Count >= 7)
             {
                 flag = true;
                 Result = "成功";

                 ErrCount = 0;
                 Err = null;


             }

             return flag;


         }
     }
     public class RFIDReadUserArea : RFIDCmdBase
     {


         public List<byte> ResultBytes { get; private set; }
         public List<byte> EPC { get; set; }
         private byte WordPtr { get; set; }
         private byte Num { get; set; }

         private RFIDReadUserArea()
         {
             CmdText = "02";
             ResultBytes = new List<byte>();
             Name = "标签-查询用户区域数据";
         }
         /// <summary>
         /// 
         /// </summary>
         /// <param name="addr"></param>
         /// <param name="startAdd">开始位置(单位:字)</param>
         /// <param name="wordLen">字数,一个字二字节</param>
         public RFIDReadUserArea(byte addr,byte[] EPCArr,byte startAdd,byte wordLen):this()
         {
             Addr = addr;
             WordPtr = startAdd;
             Num = wordLen;
             

             if(EPCArr!=null)EPC = new List<byte>(EPCArr);
             CmdBytes = ParserCmd();
             
             
            

         }
         /// <summary>
         /// 
         /// </summary>
         /// <param name="addr"></param>
         /// <param name="startAdd">开始位置(单位:字)</param>
         /// <param name="wordLen">字数,一个字二字节</param>
         public RFIDReadUserArea(byte addr, string EPCStr, byte startAdd, byte wordLen):this()
         {

             Addr = addr;
             WordPtr = startAdd;
             Num = wordLen;
             
             #region EPC
             var epc = EPCStr.Replace("-", "").Replace(" ", "");
             var epcdBytes = new byte[epc.Length / 2];
             for (int i = 0; i < epc.Length; i = i + 2)
             {
                 epcdBytes[i / 2] = byte.Parse(epc.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
             }
              EPC = new List<byte>(epcdBytes);
             #endregion

              CmdBytes = ParserCmd();
             

            



         }
         protected override byte[] ParserCmdWithoutCRC()
         {
             string cmdText = CmdText;
             var cmd = cmdText.Replace(" ", "").Replace("-", "").Trim();
             if (string.IsNullOrWhiteSpace(cmd))
             {
                 throw new Exception("空字符串!");
             }
             if ((cmd.Length % 2) != 0)
             {
                 throw new Exception("命令字符窜长度必须是2的倍数!");
             }
             cmd = BitConverter.ToString(new byte[] { Addr }) + cmd;
             var cmdBytes = new byte[cmd.Length / 2];
             for (int i = 0; i < cmd.Length; i = i + 2)
             {
                 cmdBytes[i / 2] = byte.Parse(cmd.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
             }
             //构建命令的data部分
             var data=new List<byte>();
             data.Add((byte)(EPC.Count / 2));//ENum
             data.AddRange(EPC);//EPC
             data.Add(0x03);//Mem
             data.Add(WordPtr);
             data.Add(Num);
             data.AddRange(new byte[] { 00, 00, 00, 00 });//Pwd
             #region 掩模
             //一字节为单位
             //不设置表示对整个EPC序列进行匹配
             //设置后,只对设置指定的开始位置到设定的字节数个字节进行匹配
             //如设置0开始,3字节,则进行的匹配是 xx-xx-xx-_________________
             //即只匹配前三个字节
             //data.AddRange(new byte[] { 0x0B, 0x01 });
             #endregion


             var lenV = (byte)(cmdBytes.Length + data.Count+ 2 ); //添加命令长度
             return new byte[] { lenV }.Concat(cmdBytes).Concat(data).ToArray();
         }
         public override bool ParserReturn()
         {

             ResultBytes.Clear();

             bool flag = true;
             flag = base.VerifyResult();
             var RetArr = ReturnBytes.ToArray();
             var status = BitConverter.ToString(RetArr, 3, 1);
             if (ReturnBytes[3] != 0x00)
             {
                 Err = new Exception(string.Format("返回的命令状态是{0},非期望的{1}", status, 0x00));
                 ErrCount++;
                 return false;
             }
             if (flag && ReturnBytes.Count >= (4 + (Num * 2) +2))
             {
                 flag = true;
              

                 var tmpArr = new byte[Num * 2];
                 Array.Copy(RetArr, 4, tmpArr, 0, tmpArr.Length);
                 ResultBytes.AddRange(tmpArr);
                 Result = BitConverter.ToString(tmpArr);



                 ErrCount = 0;
                 Err = null;


             }

             return flag;


         }
     }

     public class RFIDWriteUserArea : RFIDCmdBase
     {


         public List<byte> ResultBytes { get; set; }
         public List<byte> EPC { get; set; }
         private byte WordPtr { get; set; }
         private byte Num { get; set; }
         private byte[] Data { get; set; }
         private RFIDWriteUserArea()
         {
             CmdText = "03";
             ResultBytes = new List<byte>();
             Name = "标签-写用户区域数据";
         }
         /// <summary>
         /// 
         /// </summary>
         /// <param name="addr"></param>
         /// <param name="startAdd">开始位置(单位:字)</param>
         /// <param name="datas">要写入的字,一个字二字节,datas长度必须是2的整数倍</param>
         public RFIDWriteUserArea(byte addr, byte[] EPCArr, byte startAdd, byte[] datas)
             : this()
         {
             Addr = addr;
             WordPtr = startAdd;
             Data = datas;
             if ((datas.Length % 2) != 0)
             {
                 throw new Exception("数据长度必须是2的整数倍!");
             }

             if (EPCArr != null) EPC = new List<byte>(EPCArr);
             CmdBytes = ParserCmd();




         }
         /// <summary>
         /// 
         /// </summary>
         /// <param name="addr"></param>
         /// <param name="startAdd">开始位置(单位:字)</param>
         /// <param name="data">Unicode</param>
         public RFIDWriteUserArea(byte addr, string EPCStr, byte startAdd, string data)
             : this()
         {

             Addr = addr;
             WordPtr = startAdd;

             #region Data
             Data = Encoding.Unicode.GetBytes(data);
             #endregion
             #region EPC
             var epc = EPCStr.Replace("-", "").Replace(" ", "");
             var epcdBytes = new byte[epc.Length / 2];
             for (int i = 0; i < epc.Length; i = i + 2)
             {
                 epcdBytes[i / 2] = byte.Parse(epc.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
             }
             EPC = new List<byte>(epcdBytes);
             #endregion

             CmdBytes = ParserCmd();






         }
         protected override byte[] ParserCmdWithoutCRC()
         {
             string cmdText = CmdText;
             var cmd = cmdText.Replace(" ", "").Replace("-", "").Trim();
             if (string.IsNullOrWhiteSpace(cmd))
             {
                 throw new Exception("空字符串!");
             }
             if ((cmd.Length % 2) != 0)
             {
                 throw new Exception("命令字符窜长度必须是2的倍数!");
             }
             cmd = BitConverter.ToString(new byte[] { Addr }) + cmd;
             var cmdBytes = new byte[cmd.Length / 2];
             for (int i = 0; i < cmd.Length; i = i + 2)
             {
                 cmdBytes[i / 2] = byte.Parse(cmd.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
             }
             //构建命令的data部分
             var data = new List<byte>();
             var dataWords =(Data.Length / 2) ;
             data.Add((byte)dataWords);//WNUM
             data.Add((byte)(EPC.Count / 2));//ENum
             data.AddRange(EPC);//EPC
             data.Add(0x03);//Mem,0x03是用户区
             data.Add(WordPtr);
             data.AddRange(Data);
             data.AddRange(new byte[] { 00, 00, 00, 00 });//Pwd
             #region 掩模
             //一字节为单位
             //不设置表示对整个EPC序列进行匹配
             //设置后,只对设置指定的开始位置到设定的字节数个字节进行匹配
             //如设置0开始,3字节,则进行的匹配是 xx-xx-xx-_________________
             //即只匹配前三个字节
             //data.AddRange(new byte[] { 0x0B, 0x01 });
             #endregion


             var lenV = (byte)(cmdBytes.Length + data.Count + 2); //添加命令长度
             

             return new byte[] { lenV }.Concat(cmdBytes).Concat(data).ToArray();
         }
         public override bool ParserReturn()
         {

             ResultBytes.Clear();

             bool flag = true;
             flag = base.VerifyResult();
             var RetArr = ReturnBytes.ToArray();
             var status = BitConverter.ToString(RetArr, 3, 1);
             if (ReturnBytes[3] != 0x00)
             {
                 Err = new Exception(string.Format("返回的命令状态是{0},非期望的{1}", status, "0x00"));
                 ErrCount++;
                 return false;
             }
             if (flag && ReturnBytes.Count >= 6)
             {
                 flag = true;


                 Result = "写入成功!";



                 ErrCount = 0;
                 Err = null;


             }

             return flag;


         }
     }
#endregion
}
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZNJM2.Win
{
    #region 校验
    public static class CRCUtil
    {
        private static readonly byte[] aucCRCHi = {
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
            0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40
        };
        private static readonly byte[] aucCRCLo = {
            0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
            0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
            0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
            0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
            0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
            0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
            0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
            0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 
            0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
            0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
            0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
            0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
            0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 
            0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
            0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
            0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
            0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
            0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
            0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
            0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
            0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
            0x41, 0x81, 0x80, 0x40
        };
        /// <summary>
        /// CRC效验
        /// </summary>
        /// <param name="pucFrame">效验数据</param>
        /// <param name="usLen">数据长度</param>
        /// <returns>效验结果</returns>
        public static byte[] CRC16(byte[] pucFrame, int usLen)
        {
            int i = 0;
            byte ucCRCHi = 0xFF;
            byte ucCRCLo = 0xFF;
            UInt16 iIndex = 0x0000;

            while (usLen-- > 0)
            {
                iIndex = (UInt16)(ucCRCLo ^ pucFrame[i++]);
                ucCRCLo = (byte)(ucCRCHi ^ aucCRCHi[iIndex]);
                ucCRCHi = aucCRCLo[iIndex];
            }

            var tmpArr = BitConverter.GetBytes((ucCRCHi << 8 | ucCRCLo));
            var bytes = new byte[] { tmpArr[0], tmpArr[1] };
            return bytes;

        }

        public static byte[] CRC16ForRFID(byte[] datas, int len)
        {
            uint i, j;
            uint crc16 = 0xffff;
            for (i = 0; i < len; i++)
            {
                crc16 ^= datas[i];
                for (j = 0; j < 8; j++)
                {
                    if ((crc16 & 0x01) == 1)
                    {
                        //标准crc16使用0xA001,RFID里使用0x8408
                        //crc16 = (crc16 >> 1) ^ 0xA001;
                        crc16 = (crc16 >> 1) ^ 0x8408;
                    }
                    else
                    {
                        crc16 = crc16 >> 1;
                    }
                }
            }
            var bs = BitConverter.GetBytes(crc16);
            return new byte[] { bs[0], bs[1] };
        }

        public static byte[] Crc16(byte[] datas, int length)
        {
            uint i, j;
            uint crc16 = 0xffff;
            for (i = 0; i < length; i++)
            {
                crc16 ^= datas[i];
                for (j = 0; j < 8; j++)
                {
                    if ((crc16 & 0x01) == 1)
                    {
                        
                        crc16 = (crc16 >> 1) ^ 0xA001;
   
                    }
                    else
                    {
                        crc16 = crc16 >> 1;
                    }
                }
            }
            var bs = BitConverter.GetBytes(crc16);
            return new byte[] { bs[0], bs[1] };
        }

        public static byte[] ParserHexStr(string str)
        {
            string cmdText = str;
            var cmd = cmdText.Replace(" ", "").Replace("-", "").Trim();
            if (string.IsNullOrWhiteSpace(cmd))
            {
                throw new Exception("空字符串!");
            }
            if ((cmd.Length % 2) != 0)
            {
                throw new Exception("命令字符窜长度必须是2的倍数!");
            }
        
            var cmdBytes = new byte[cmd.Length / 2];
            for (int i = 0; i < cmd.Length; i = i + 2)
            {
                cmdBytes[i / 2] = byte.Parse(cmd.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
            }
            return cmdBytes;
        }
    }
    #endregion
}
View Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.Net;

namespace ZNJM2.Win
{
    public partial class Form3 : Form
    {


        private bool Enable = false;
        public Form3()
        {
            InitializeComponent();
        }
        public SerialPort COMPort { get; private set; }

        private void EnsureCOMPortOpen()
        {
            if (COMPort == null)
            {
                //配置COMPort
                COMPort = new SerialPort("COM2", 57600, Parity.None, 8, StopBits.One);
                COMPort.Encoding = Encoding.ASCII;
                COMPort.ReadTimeout = 1000;
                COMPort.WriteTimeout = 500;
                COMPort.ReadBufferSize = 512;
                COMPort.WriteBufferSize = 512;
             
                //COMPort.DtrEnable = true;
            }
            if (!COMPort.IsOpen)
            {
                COMPort.Open();
            }
            Thread.Sleep(100);
        }
        private void button1_Click(object sender, EventArgs e)
        {
            Enable = true;
          
            ThreadPool.QueueUserWorkItem(o =>
            {
                Do();
            }, null);
            button1.Enabled = false;
            button2.Enabled = true;
        }
        public void Do()
        {
            try
            {
               
                EnsureCOMPortOpen();
                Console.WriteLine("串口打开!");
                byte[] buffer = new byte[COMPort.ReadBufferSize];
                List<byte> myBuff=new List<byte>();

                //D7-A3-88-40 4.27
                //00 00 01 ab

                var cmds = new List<CommandBase>();
                //cmds.Add(new VoltageQuery(01));
                //cmds.Add(new ElectricCurrent(01));
                //cmds.Add(new ElectricEnergy(01));
                //cmds.Add(new ElectricFrequency(01));
                //cmds.Add(new RFIDQueryTID(00));
                //cmds.Add(new RFIDQueryEPC(00));
                //cmds.Add(new RFIDSetPower(00, 5));
                //cmds.Add(new RFIDQueryTopOne(00));
               // cmds.Add(new RFIDGetReaderInfo(00));


                string strV = "张三李四网无123456789abc";
                var writeBytes = new List<byte>();
                writeBytes.AddRange(BitConverter.GetBytes(DateTime.Now.Ticks));
                writeBytes.AddRange(Encoding.UTF8.GetBytes(strV));
                var readWords = 0;
                if ((writeBytes.Count % 2) == 0)
                {
                    readWords = writeBytes.Count / 2;
                }
                else
                {
                    readWords = (writeBytes.Count / 2) + 1;
                    writeBytes.Add(0x00);
                }
                Console.WriteLine(BitConverter.ToString(writeBytes.ToArray()));
               

                cmds.Add(new RFIDWriteUserArea(00, CRCUtil.ParserHexStr("E20040807212025312409A77"), 00, writeBytes.ToArray()));
                cmds.Add(new RFIDReadUserArea(00, "E20040807212025312409A77", 00, (byte)readWords));
                //cmds.Add(new RFIDReadUserArea(00, "E20040807212023812309C8B", 00, 1));
                while (Enable)
                {
                    foreach (var cmd in cmds)
                    {
                        Console.WriteLine("==========================");
                        Console.WriteLine(cmd.Name);
                   
                        COMPort.Write(cmd.CmdBytes.ToArray(), 0, cmd.CmdBytes.Count);
                        Thread.Sleep(100 * 3);

                        int len = COMPort.Read(buffer, 0, buffer.Length);
               

                        byte[] tempArr = new byte[len];
                        Array.Copy(buffer, tempArr, len);
                        cmd.ReturnBytes = new List<byte>(tempArr);

                        Console.WriteLine("RAW:" +BitConverter.ToString(tempArr));

                        if (cmd.ParserReturn())
                        {
                            #region
                            if (cmd is RFIDReadUserArea)
                            {
                                var t = (RFIDReadUserArea)cmd;
                                Console.WriteLine("ReadData:" +t.Result);
                         

                                var dt=new DateTime( BitConverter.ToInt64(t.ResultBytes.ToArray(), 0));
                                Console.WriteLine(dt.ToString());
                                Console.WriteLine("UTF-8:" + Encoding.UTF8.GetString(t.ResultBytes.ToArray(), 8,t.ResultBytes.Count -8 ));
                            }
                            if (cmd is RFIDWriteUserArea)
                            {
                                Console.WriteLine("ReadData:" + cmd.Result);
                            }
                            #endregion
                            #region QueryTID
                            if (cmd is RFIDQueryTID)
                            {

                                Console.WriteLine("LblNum:" + ((RFIDQueryTID)cmd).LblNum);
                                foreach (var lbl in ((RFIDQueryTID)cmd).LblList)
                                {
                                    Console.WriteLine(lbl);
                                }
                            }
                            #endregion
                            #region RFIDQueryEPC
                            if (cmd is RFIDQueryEPC)
                            {

                                Console.WriteLine("LblNum:" + ((RFIDQueryEPC)cmd).LblNum);

                                foreach (var lbl in ((RFIDQueryEPC)cmd).LblList)
                                {
                                    
                                    Console.WriteLine(lbl);
                                }
                            }
                            #endregion
                            #region RFIDGetReaderInfo
                            if (cmd is RFIDGetReaderInfo)
                            {

                                Console.WriteLine("Power:" + ((RFIDGetReaderInfo)cmd).Power);
                                Console.WriteLine("CmdDelay:" + ((RFIDGetReaderInfo)cmd).CmdDelay);
                            }
                            #endregion
                            if (cmd is RFIDQueryTopOne)
                            {
                                Console.WriteLine("LblNum:" + ((RFIDQueryTopOne)cmd).LblNum);
                                foreach (var lbl in ((RFIDQueryTopOne)cmd).LblList)
                                {
                                    Console.WriteLine(lbl);
                                }
                            }
                        }
                        else
                        {
                            Console.WriteLine(cmd.Err.ToString());
                           // COMPort.Read(buffer, 0, buffer.Length);
                        }
              
                    }
                    //using (WebClient wc = new WebClient())
                    //{
                    //    var url = string.Format(@"http://192.168.9.6:7896/Default.aspx?rnd={0}&m=U&V={1}&I={2}&E={3}",
                    //        DateTime.Now.Ticks,
                    //        cmds.OfType<VoltageQuery>().FirstOrDefault().Result,
                    //        cmds.OfType<ElectricCurrent>().FirstOrDefault().Result,
                    //        cmds.OfType<ElectricEnergy>().FirstOrDefault().Result
                    //        );
                    //    Console.WriteLine(url);
                    //    wc.DownloadString(url);
                    //    wc.Dispose();
                    //}
                    Thread.Sleep(1000 * 2);


                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);

            }
            finally
            {
                if (COMPort != null && COMPort.IsOpen)
                {
                    COMPort.Close();
                }
                if (Enable)
                {
                    ThreadPool.QueueUserWorkItem(o =>
                    {
                        Do();
                    }, null);
                }
                Console.WriteLine("结束!");
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Enable = false;
            button1.Enabled = true;
            button2.Enabled = false;
        }
    }
}
View Code

 

转载于:https://www.cnblogs.com/wdfrog/p/4606976.html

1. RS485通讯协议………………………………………………………………… 1 2. 仪表分类说明…………………………………………………………………… 8 2.1. XMA5000 ………………………………………………………………………… 9 2.2. XMB5000 …………………………………………………………………………11 2.3. XMD5000 …………………………………………………………………………12 2.4. XMDT5000 ………………………………………………………………………14 2.5. XME5000 …………………………………………………………………………14 2.6. XMG5000 …………………………………………………………………………14 2.7. XMG6000 …………………………………………………………………………15 2.8. XMG7000 …………………………………………………………………………15 2.9. XMGA5000…………………………………………………………………………16 2.10. XMH5000 …………………………………………………………………………18 2.11. XMHG5000…………………………………………………………………………18 2.12. XMI5000 …………………………………………………………………………18 2.13. XMJ5000 …………………………………………………………………………19 2.14. XML5000 …………………………………………………………………………20 2.15. XMLH5000…………………………………………………………………………21 2.16. XMPHB5000 ………………………………………………………………………21 2.17. XMPHG5000 ………………………………………………………………………21 2.18. XMPHGA5000………………………………………………………………………21 2.19. XMR5000 …………………………………………………………………………21 2.20. XMS5000 …………………………………………………………………………21 2.21. XMT6000 …………………………………………………………………………22 2.22. XMZ5000 …………………………………………………………………………23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值