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

namespace TCPCLIENT
{
 public   class Siemens200_modbusRTU
    {
        static String HEXES = "0123456789ABCDEF";
        byte uchCRCHi = (byte)0xFF;
        byte uchCRCLo = (byte)0xFF;
         private String address;
       private CRC16 _crc = new CRC16();
      #region "ModbusRTU协议说明"

        /**Modbus 地址
        通常 Modbus 地址由 5 位数字组成,包括起始的数据类型代号,以及后面的偏移地址。Modbus Master 协议库把标
        准的 Modbus 地址映射为所谓 Modbus 功能号,读写从站的数据。Modbus Master 协议库支持如下地址:
        · 00001 - 09999:数字量输出(线圈)
        · 10001 - 19999:数字量输入(触点)
        · 30001 - 39999:输入数据寄存器(通常为模拟量输入)
        · 40001 - 49999:数据保持寄存器

       * Modbus 地址 S7-200 数据区
         00001 ~ 00128 Q0.0 ~ Q15.7
         10001 ~ 10128 I0.0 ~ I15.7
         30001 ~ 30032 AIW0 ~ AIW62
         40001 ~ 4xxxx T ~ T + 2 * (xxxx -1)
         其中T 为S7-200 中的缓冲区起始地址,即 HoldStart。
         如果已知S7-200 中的V 存储区地址,推算Modbus 地址的公式如下:
         Modbus 地址 = 40000 + (T/2+1) ; T 为偶数
         * 
         *功能码
            主站使用相应功能码作用于此从站的效用
            1 读取单个/多个线圈(离散量输出点)状态。 功能 1 返回任意个数输出点(Q)的 ON/OFF 状态。
            2 读取单个/多个触点(离散量输入点)状态。 功能 2 返回任意个数输入点(I)的 ON/OFF 状态。
            3 读取单个/多个保持寄存器。功能 3 返回 V 存储区的内容。在 Modbus 协议下保持寄存器都是“字”值,在一次请求中可以读取最
              多 120 个字的数据。
            4 读取单个/多个输入寄存器。
              功能 4 返回 S7-200 的模拟量数据值。
            5 写单个线圈(离散量输出点)。
              功能 5 用于将离散量输出点设置为指定的值。这个点不是被强制的,用户程序可以覆盖 Modbus 通
                    信请求写入的值。
            6 写单个保持寄存器。
           功能 6 写一个值到 S7-200 的 V 存储区的保持寄存器中。
           15 写多个线圈(离散量输出点)。功能 15 把多个离散量输出点的值写到 S7-200 的输出映像寄存器(Q 区)。输出点的地址必须
                以字节边界起始(如 Q0.0 或 Q2.0),并且输出点的数目必须是 8 的整数倍。这是此 Modbus RTU 从站指令库的限制。些点不
                是被强制的,用户程序可以覆盖 Modbus 通信请求写入的值。
           16 些多个保持寄存器。
         *    功能 16 写多个值到 S7-200 的 V 存储区的保持寄存器中。在一次请求中可以写最多 120 个字的数据。
       */
      #endregion

      /// <summary>
      /// 地址码
      /// </summary>
      /// <param name="_address"></param>

      public void  SetAddress(String _address)
          {
          this.address = _address;
          }


      /// <summary>
      /// 功能码
      /// </summary>
      public enum modbusRTU_Func
      { 
          FUNC_READ_I=0x2, //读数字输入I
          FUNC_READ_V=0X3,  //读V寄存器
          FUNC_READ_AI=0X4, //读模拟AI寄存器
          FUNC_WRITE_1Q=0X5, //写单个数字输出寄存器Q
          FUNC_WRITE_1V=0X6, //写单个数据保存寄存器V
          FUNC_WRITE_NQ=0XF, //写多个数字输出寄存器Q
          FUNC_WRITE_NV=0X10 //写多个数据保存寄存器V
      }
      public struct strType
      {
          public byte HighType;
          public byte LowType;    
      }

      private static  byte byteLength = 8;   // 字节位长 
      /*
            整型32bit转换成字节型8bit数组
            @param number 待求数值
            @param length 数值所占字节数
         */
      private static byte[] intToByteArray(int number, int length)
          {
          byte[] byteArray = new byte[length];

          int shiftNum = 0;	// 移位数
          for (int i = 0; i < length; i++)
              {
              shiftNum = (length - i - 1) * byteLength;
              byteArray[i] = (byte)((number >> shiftNum) & 0xFF);
              }
          return byteArray;
          }

      /// <summary>
      /// 获取读命令字节数组
      /// </summary>
      /// <param name="Address">设备地址</param>
      /// <param name="Func">功能码</param>
      /// <param name="Type">寄存器类型</param>
      /// <param name="BeginAddr">起始地址</param>
      /// <param name="Count">读取点数</param>
      /// <returns>返回读取命令字节数组</returns>
      public byte[] GetReadCmd(byte Address,byte Func ,String Type, String BeginAddr,int Count)
      {
          List<strType> HL_Type = new List<strType>();
          byte[] buf = new byte[8];
          buf[0] = Address;
          buf[1] = Func;
          HL_Type=getRealAddrArray(Type, new String[] {BeginAddr});
          buf[2] = HL_Type[0].LowType;
          buf[3] = HL_Type[0].HighType;

          byte[] Len_Type = intToByteArray(Count, 2);
          buf[4] = Len_Type[0];
          buf[5] = Len_Type[1];

          buf = CRC16.GetCRC16Full(new byte[] { buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]}, true);
          return buf;
      }

     /// <summary>
      ///获取写命令字节数组
     /// </summary>
     /// <param name="Address"></param>
     /// <param name="Func"></param>
     /// <param name="Type"></param>
     /// <param name="Addr"></param>
     /// <param name="value"></param>
     /// <returns></returns>
      public byte[] GetWriteCmd(byte Address, byte Func, String Type, String Addr, byte HighType, byte Lowtype)
      {
          List<strType> HL_Type = new List<strType>();
          byte[] buf = new byte[8];
          buf[0] = Address;
          buf[1] = Func;
          HL_Type = getRealAddrArray(Type, new String[] { Addr });
          buf[2] = HL_Type[0].LowType;
          buf[3] = HL_Type[0].HighType;


          buf[4] = HighType;
          buf[5] = Lowtype;

          buf = CRC16.GetCRC16Full(new byte[] { buf[0], buf[1], buf[2], buf[3], buf[4], buf[5] }, true);
          return buf;
      }


      public  List<strType> getRealAddrArray(String Type, String[] loc)
      {
      List<strType> typeList = new System.Collections.Generic.List<strType>();
      switch (Type)
          {
          case "Q":
              Dictionary<String, Int32> Q_Map = new System.Collections.Generic.Dictionary<String, Int32>();
              //输出寄存器数量:00001 ~ 00128  共128个
              int Index = 0;
              Int32 Val = 0;
              for (int j = 0; j < 128; j++)
                  {
                  //0  7   8  17 25  
                  Index=j/8;
                  if (Val == 8 )
                      {
                       Val = 0;
                      }
                  Q_Map.Add("Q"+Index+"."+Val, j);
                  Val+=1;
                  }
              for (int i = 0; i < loc.Length; i++)
                  {
                  if (Q_Map.ContainsKey(loc[i])==true)
                      {

                      int buf = Q_Map[loc[i]];
                      byte[] bytes = BitConverter.GetBytes(buf);
                      strType type = new strType();
                      if (bytes.Length == 4)
                          {
                          type.HighType = bytes[1];
                          type.LowType =bytes[0];
                          }
                      typeList.Add(type);
                      }
                  }
              break;

          case "I":
               Dictionary<String, Int32> I_Map = new System.Collections.Generic.Dictionary<String, Int32>();
              //输入寄存器数量:00001 ~ 00128  共128个
              Index = 0;
               Val = 0;
              for (int j = 0; j < 128; j++)
                  {
                  //0  7   8  17 25  
                  Index=j/8;
                  if (Val == 8 )
                      {
                       Val = 0;
                      }
                  I_Map.Add("I"+Index+"."+Val, j);
                  Val+=1;
                  }
              for (int i = 0; i < loc.Length; i++)
                  {
                  if (I_Map.ContainsKey(loc[i])==true)
                      {

                      int buf = I_Map[loc[i]];
                      byte[] bytes = BitConverter.GetBytes(buf);
                      strType type = new strType();
                      if (bytes.Length == 4)
                          {
                          type.HighType = bytes[1];
                          type.LowType =bytes[0];
                          }
                      typeList.Add(type);
                      }
                  }
              break;
          case "AI":
              break;

          case "VW":
                Dictionary<String, Int32> V_Map = new System.Collections.Generic.Dictionary<String, Int32>();
                //数据保持寄存器数量:40001 ~ 4XXXX   40000 + (T/2+1) ; T 为偶数
              Index = 0;
               Val = 0;
              for (int j = 0; j < 9999; j++)
                  {
                  if (j % 2==0)
                      {
                       V_Map.Add("VW"+j, j/2);
                      }
                  }
              for (int i = 0; i < loc.Length; i++)
                  {
                  if (V_Map.ContainsKey(loc[i])==true)
                      {
                      int buf = V_Map[loc[i]];
                      byte[] bytes = BitConverter.GetBytes(buf);
                      strType type = new strType();
                      if (bytes.Length == 4)
                          {
                          type.HighType = bytes[0];
                          type.LowType =bytes[1];
                          }
                      typeList.Add(type);
                      }
                  }
              break;
          default:
              break;
          }
      return typeList;      
     }


      public static byte[] ConvertStringToByteArray(string stringToConvert)
      {
          return (new UnicodeEncoding()).GetBytes(stringToConvert);
      }


        /// <summary> 
        /// 字节数组转16进制字符串 
        /// </summary> 
        /// <param name="bytes"></param> 
        /// <returns></returns> 
        public  string byteToHexStr(byte[] bytes) 
        { 
        string returnStr = ""; 
        if (bytes != null) 
        { 
        for (int i = 0; i < bytes.Length; i++) 
        { 
        returnStr += bytes[i].ToString("X2"); 
        } 
        } 
        return returnStr; 
        } 


      /// <summary> 
      /// 字符串转16进制字节数组 
      /// </summary> 
      /// <param name="hexString"></param> 
      /// <returns></returns> 
      private static byte[] strToToHexByte(string hexString)
      {
          hexString = hexString.Replace(" ", "");
          if ((hexString.Length % 2) != 0)
              hexString += " ";
          byte[] returnBytes = new byte[hexString.Length / 2];
          for (int i = 0; i < returnBytes.Length; i++)
              returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
          return returnBytes;
      }

        }

    }