Facebook
From haziux, 3 Years ago, written in C#.
Embed
Download Paste or View Raw
Hits: 49
  1. class ModbusTCPClient
  2.     {
  3.         private NetworkStream           _stream                     = default( NetworkStream );
  4.         private TcpClient               _tcpClient                  = default( TcpClient );
  5.  
  6.         #region Request / Response messages charasteristic fields
  7.  
  8.         #region Function codes
  9.  
  10.         /// <summary>
  11.         /// Function code specifying reading registers values.
  12.         /// </summary>
  13.         private const byte READ_INPUT_REGISTERS_FUNCTION_CODE       = 4;
  14.         /// <summary>
  15.         /// Function code specifying reading device informations.
  16.         /// </summary>
  17.         private const byte READ_DEVICE_IDENTIFICATION_FUNCTION_CODE = 43;
  18.  
  19.         #endregion
  20.  
  21.         /// <summary>
  22.         /// Identification of a MODBUS Request / Response transaction.
  23.         /// </summary>
  24.         private ushort              transactionIdentifierInternal      = 0;
  25.         /// <summary>
  26.         /// 0 = MODBUS protocol.
  27.         /// </summary>
  28.         private readonly byte[]     protocolIdentifier                 = new byte[ 2 ] { 0 , 0 };
  29.         /// <summary>
  30.         /// Identification of a remote server. Should be neutral (=255) in MODBUS TCP
  31.         /// </summary>
  32.         private readonly byte       unitIdentifier                     = 255;
  33.         /// <summary>
  34.         /// CRC-32 error check code.
  35.         /// </summary>
  36.         private byte[]              crc                               = new byte[ 2 ] { 132 , 10 };
  37.  
  38.         #region Reading registers messages characteristic fields
  39.  
  40.         /// <summary>
  41.         /// Number of following bytes.
  42.         /// </summary>
  43.         private byte[]  length                                      = new byte[ 2 ] { 6 , 0 };
  44.  
  45.         /// <summary>
  46.         /// The address of the firs register in sequential registers read
  47.         /// </summary>
  48.         private byte[]  startingAddress                             = new byte[ 2 ] { 0 , 0 };
  49.         /// <summary>
  50.         /// Number of registers to read
  51.         /// </summary>
  52.         private byte[]  quantity                                    = new byte[ 2 ] { 255 , 0 };
  53.  
  54.         #endregion
  55.  
  56.         #region Reading device information characteristic fields
  57.  
  58.  
  59.         #endregion
  60.  
  61.  
  62.         #endregion
  63.  
  64.         #region Delegates and Events
  65.  
  66.         public delegate void ConnectedChanged( string message );
  67.         public delegate void ErrorHandler( string message );
  68.  
  69.         public event ConnectedChanged   _connectedChangedEvent      = null;
  70.         public event ErrorHandler       _connectionErrorEvent       = null;
  71.         public event ErrorHandler       _readingDataErrorEvent      = null;
  72.  
  73.         #endregion
  74.  
  75.         #region Construction and connection maintenance
  76.  
  77.         public ModbusTCPClient()
  78.         {
  79.         }
  80.  
  81.         public void Connect( int timeout )
  82.         {
  83.             try
  84.             {
  85.                 this._tcpClient             = new TcpClient( "192.168.0.152" , 502 );
  86.                 this._stream                = this._tcpClient.GetStream();
  87.                 this._stream.WriteTimeout   = timeout;
  88.                 this._stream.ReadTimeout    = timeout;
  89.             }
  90.             catch ( Exception e )
  91.             {
  92.                 this._connectionErrorEvent( e.Message );
  93.                 return;
  94.             }
  95.             this._connectedChangedEvent( "Connected to the server." );
  96.         }
  97.  
  98.         public void CloseConnection()
  99.         {
  100.             if ( this._stream != null )
  101.             {
  102.                 this._stream.Dispose();
  103.                 this._stream = null;
  104.             }
  105.             if ( this._tcpClient != null )
  106.             {
  107.                 this._tcpClient.Close();
  108.                 this._tcpClient = null;
  109.             }
  110.             this._connectedChangedEvent( "Disconnected from the server." );
  111.         }
  112.  
  113.         #endregion
  114.  
  115.         public void ReadDeviceIdentification()
  116.         {
  117.             if ( this._stream != null && this._stream.CanRead )
  118.             {
  119.                 byte[] transactionIdentifier = this.GetTransactionIdentifier();
  120.  
  121.                 byte MEIType        = 14;
  122.                 byte deviceIDCode   = 1;
  123.  
  124.  
  125.                 byte[] requestArray = new byte[]
  126.                     {
  127.                   transactionIdentifier[1] , transactionIdentifier[0] , protocolIdentifier[1] , protocolIdentifier[0] , length[1] , length[0] ,
  128.                   unitIdentifier , READ_DEVICE_IDENTIFICATION_FUNCTION_CODE , MEIType , 0 , deviceIDCode , startingAddress[1] , startingAddress[0] , crc[0] , crc[1]
  129.                     };
  130.                 byte[] responseArray = new byte[ 2100 ]; //TODO: get reasonable value, MODBUS ADU = 260 bytes (253 - PDU and 7 MBAP)
  131.                                                          //The register data in the response message are packed as two bytes per register, with the
  132.                                                          //binary contents right justified within each byte.For each register, the first byte contains the
  133.                                                          //high order bits and the second contains the low order bits.
  134.  
  135.                 if ( ( this._tcpClient == null ) || ( !this._tcpClient.Connected ) )
  136.                 {
  137.                     return;
  138.                 }
  139.                 try
  140.                 {
  141.                     this._stream.Write( requestArray , 0 , checked(( int )requestArray.Length - 2) );
  142.                     this._stream.Read( responseArray , 0 , responseArray.Length );
  143.                 }
  144.                 catch ( Exception e )
  145.                 {
  146.                     this._connectionErrorEvent( e.Message );
  147.                     return;
  148.                 }
  149.             }
  150.             else
  151.             {
  152.                 this._readingDataErrorEvent( "TCP stream is not ready to be read." );
  153.             }
  154.  
  155.         }
  156.  
  157.         public short[] ReadInputRegisters( int registersQuantity )   //TODO int[] - check what valueas are returned from server
  158.         {
  159.             if ( this._stream != null && this._stream.CanRead )
  160.             {
  161.                 byte[] transactionIdentifier = new byte[ 2 ];
  162.                 transactionIdentifier = this.GetTransactionIdentifier();
  163.  
  164.                 byte[] requestArray = new byte[]
  165.                     {
  166.                   transactionIdentifier[1] , transactionIdentifier[0] , protocolIdentifier[1] , protocolIdentifier[0] , length[1] , length[0] ,
  167.                   unitIdentifier , READ_INPUT_REGISTERS_FUNCTION_CODE , startingAddress[1] , startingAddress[0] , quantity[1] , quantity[0] , crc[0] , crc[1]
  168.                     };
  169.                 byte[] responseArray = new byte[ 2100 ]; //TODO: get reasonable value
  170.  
  171.                 if ( ( this._tcpClient == null ) || ( !this._tcpClient.Connected ) )
  172.                 {
  173.                     return null;
  174.  
  175.                 }
  176.                 try
  177.                 {
  178.                     this._stream.Write( requestArray , 0 , checked(( int )requestArray.Length - 2) );
  179.                     this._stream.Read( responseArray , 0 , responseArray.Length );
  180.                 }
  181.                 catch ( Exception e )
  182.                 {
  183.                     this._connectionErrorEvent( e.Message );
  184.                     return null;
  185.                 }
  186.  
  187.                 if ( this.CheckResponseForExceptions( READ_INPUT_REGISTERS_FUNCTION_CODE , responseArray[ 7 ] , responseArray[ 8 ] ) )
  188.                 {
  189.                     short[] valuesFromServer = new short[ registersQuantity ];
  190.                     for ( int i = 0 ; i < registersQuantity ; i++ )
  191.                     {
  192.                         byte num3 = responseArray[ checked(9 + checked(i * 2)) ];
  193.                         byte num4 = responseArray[ checked(checked(9 + checked(i * 2)) + 1) ];
  194.                         responseArray[ checked(9 + checked(i * 2)) ] = num4;
  195.                         responseArray[ checked(checked(9 + checked(i * 2)) + 1) ] = num3;
  196.                         valuesFromServer[ i ] = BitConverter.ToInt16( responseArray , checked(9 + checked(i * 2)) );
  197.                     }
  198.                     return valuesFromServer;
  199.                 }
  200.             }
  201.             else
  202.             {
  203.                 this._readingDataErrorEvent( "TCP stream is not ready to be read." );
  204.             }
  205.  
  206.             return null;
  207.         }
  208.  
  209.         #region Helper methods
  210.  
  211.         private bool CheckResponseForExceptions( byte functionCode , byte response7 , byte response8 )
  212.         {
  213.             if ( response7 != functionCode )
  214.             {
  215.                 switch ( response8 )
  216.                 {
  217.                     case 1:
  218.                     {
  219.                         this._readingDataErrorEvent( "Function code not supported by master" );
  220.                         return false;
  221.                     }
  222.                     case 2:
  223.                     {
  224.                         this._readingDataErrorEvent( "Starting address invalid or starting address + quantity invalid" );
  225.                         return false;
  226.                     }
  227.                     case 3:
  228.                     {
  229.                         this._readingDataErrorEvent( "Quantity of registers invalid" );
  230.                         return false;
  231.                     }
  232.                     case 4:
  233.                     {
  234.                         this._readingDataErrorEvent( "Error reading" );
  235.                         return false;
  236.                     }
  237.                     default:
  238.                     {
  239.                         this._readingDataErrorEvent( "Unspecified error" );
  240.                         return false;
  241.                     }
  242.                 }
  243.             }
  244.             return true;
  245.         }
  246.  
  247.         private byte[] GetTransactionIdentifier()
  248.         {
  249.             this.transactionIdentifierInternal++;
  250.             return BitConverter.GetBytes( this.transactionIdentifierInternal );
  251.         }
  252.  
  253.         #endregion
  254.     }
captcha