Guest User

ModbusTCP controller

a guest
Jun 26th, 2014
545
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 15.44 KB | None | 0 0
  1. /* Basic implementation of a ModbusTCP interface in C#. This is known to work with .Net 3.5. Will probably work with
  2.  * other versions just fine.
  3.  * Calls to Logger.Log reference a simple log file writer; implement your own error reporting there.
  4.  * The MODBUS_TCP_PORT definition will vary according to your system.
  5.  */
  6.  
  7. using System;
  8. using System.Threading;
  9. using System.Net.Sockets;
  10.  
  11. namespace ModbusTCP {
  12.     public class Controller
  13.     {
  14.         #region Constants
  15.  
  16.         private const int MAX_ERRORS = 5;           // pulled out of a hat
  17.  
  18.         // MODBUS command codes
  19.         private const int CMD_READBIT =    0x01;
  20.         private const int CMD_READREG =    0x03;
  21.         private const int CMD_WRITEBIT =   0x05;
  22.         private const int CMD_WRITEREG =   0x06;
  23.         private const int CMD_WRMULTIREG = 0x10;
  24.  
  25.         // other MODBUS settings
  26.         private const UInt16 POLY16 =       0xA001;
  27.         private const Int32 MODBUS_TCP_PORT = 502;
  28.  
  29.         #endregion
  30.  
  31.         private int modbusMsgNum;
  32.         private Socket sck;
  33.         private System.Net.IPEndPoint ctlAddr;
  34.  
  35.         private bool msgPending = false;
  36.  
  37.         private int errorCount = 0;
  38.         private uint totalTXbytes = 0;
  39.         private uint totalRXbytes = 0;
  40.  
  41.         public Controller()
  42.         {
  43.         }
  44.  
  45.         public void Init( int RemoteAddr )
  46.         {
  47.             int retry = 1;
  48.             do {
  49.                 // try to connect to the controller
  50.                 try {
  51.                     // for some reason we need to reverse the address,
  52.                     //   e.g. "192.168.100.10" becomes 0x0a64a8c0
  53.                     Int64 addr = (RemoteAddr & 0xFF);
  54.                     addr = (addr << 8) | ((RemoteAddr >> 8) & 0xFF);
  55.                     addr = (addr << 8) | ((RemoteAddr >> 16) & 0xFF);
  56.                     addr = (addr << 8) | ((RemoteAddr >> 24) & 0xFF);
  57.                     ctlAddr = new System.Net.IPEndPoint( addr & 0xFFFFFFFF, MODBUS_TCP_PORT );
  58.  
  59.                     sck = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
  60.                     sck.NoDelay = true;
  61.                     sck.ReceiveTimeout = 100;
  62.                     sck.Connect( ctlAddr );
  63.  
  64.                     installed = true;
  65.                     break;
  66.                 }
  67.                 catch( Exception ex ) {
  68.                     // if it fails, just be graceful and report an error
  69.                     if( sck != null && sck.Connected ) {
  70.                         sck.Close();
  71.                         sck = null;
  72.                     }
  73.  
  74.                     Logger.Log( EventLevel.Error, "Error connecting to ModbusTCP controller: "
  75.                                                     + ex.Message );
  76.                 }
  77.  
  78.                 Thread.Sleep( 1000 );
  79.             } while( retry++ <= 3 );
  80.  
  81.             modbusMsgNum = 1;
  82.         }
  83.  
  84.         /// <summary>
  85.         /// Sends the command and waits for the response
  86.         /// </summary>
  87.         /// <param name="cmd">Command to send</param>
  88.         /// <param name="rsp">Response received</param>
  89.         private int SendRcvCmd( byte[] cmd, ref byte[] rsp )
  90.         {
  91.             if( cmd == null ||cmd.Length == 0 ) {
  92.                 Logger.Log( EventLevel.Error, "Null command passed to Controller.SendRcvCmd" );
  93.                 return 0;
  94.             }
  95.  
  96.             if( rsp == null || rsp.Length == 0 ) {
  97.                 Logger.Log( EventLevel.Error, "Null receive buffer passed to Controller.SendRcvCmd" );
  98.                 return 0;
  99.             }
  100.  
  101.             if( sck == null || !sck.Connected ){
  102.                 try {
  103.                     sck = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
  104.                     sck.NoDelay = true;
  105.                     sck.ReceiveTimeout = 250;
  106.                     sck.Connect( ctlAddr );
  107.                 }
  108.                 catch( Exception ex ) {
  109.                     errorCount++;
  110.                     Logger.Log( EventLevel.Warning,
  111.                                 "Error reconnecting with controller: " + ex.Message ) );
  112.                     return 0;
  113.                 }
  114.             }
  115.  
  116.             int timeout = 500;
  117.             while( msgPending ) {
  118.                 Thread.Sleep( 25 );
  119.                 timeout -= 25;
  120.                 if( timeout < 0 ) {
  121.                     errorCount++;
  122.                     Logger.Log( EventLevel.Warning, "Timeout waiting for command to complete" );
  123.                     return 0;
  124.                 }
  125.             }
  126.  
  127.             msgPending = true;
  128.  
  129.             cmd[0] = (byte)((modbusMsgNum >> 8) & 0xFF);
  130.             cmd[1] = (byte)(modbusMsgNum & 0xff);
  131.             modbusMsgNum++;
  132.  
  133.             bool reconnectRequired = false;
  134.             int byteCount = 0;
  135.             try {
  136. /*
  137.                 // log raw transmit data
  138.                 StringBuilder txStr = new StringBuilder();
  139.                 for( int idx = 0; idx < cmd.Length; idx++ ) {
  140.                     txStr.AppendFormat( "{0:x02} ", cmd[idx] );
  141.                 }
  142.                 Logger.Log( EventLevel.Debug, "tx> " + txStr.ToString() );
  143. */
  144.  
  145.                 int retry;
  146.                 for( retry = 0; retry < 4; retry++ ) {
  147.                     sck.Send( cmd );
  148.  
  149.                     totalTXbytes += (uint)cmd.Length;
  150.  
  151.                     Thread.Sleep( 25 );     // !!! better way to delay until data comes back?
  152.  
  153.                     byteCount = sck.Receive( rsp, rsp.Length, SocketFlags.None );
  154.  
  155.                     if( byteCount > 0 ) {
  156.                         // wictory!
  157. /*
  158.                         // log raw rx data
  159.                         StringBuilder rxStr = new StringBuilder();
  160.                         for( int idx = 0; idx < byteCount; idx++ ) {
  161.                             rxStr.AppendFormat( "{0:x02} ", rsp[idx] );
  162.                         }
  163.                         Logger.Log( EventLevel.Debug, "rx< " + rxStr.ToString() );
  164. */
  165.  
  166.                         totalRXbytes += (uint)byteCount;
  167.                         break;
  168.                     } else {
  169.                         // else, try again
  170.                         if( !sck.Connected || retry >= 3 ) {
  171.                             errorCount++;
  172.  
  173.                             Logger.Log( EventLevel.Warning,
  174.                                 "Communication with controller lost, resynchronizing" ) );
  175.  
  176.                             sck.Close();
  177.                             reconnectRequired = true;
  178.                             break;
  179.                         }
  180.                     }
  181.                 }
  182.             }
  183.             catch( Exception ex ) {
  184.                 // something got borked bad enough to lose our connection
  185.                 msgPending = false;
  186.                 byteCount = 0;
  187.                 Logger.Log( EventLevel.Warning, ex.Message + " in Controller.SendRecvCmd at "
  188.                                                 + ex.StackTrace );
  189.  
  190.                 // flag ourselves as needing help
  191.                 sck.Close();
  192.                 reconnectRequired = true;
  193.  
  194.                 errorCount++;
  195.             }
  196.  
  197.             if( reconnectRequired ) {
  198.                 try {
  199.                     // reconnect
  200.                     sck = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
  201.                     sck.NoDelay = true;
  202.                     sck.ReceiveTimeout = 250;
  203.                     sck.Connect( ctlAddr );
  204.  
  205.                     // and try once more
  206.                     sck.Send( cmd );
  207.  
  208.                     totalTXbytes += (uint)cmd.Length;
  209.  
  210.                     Thread.Sleep( 25 );
  211.                     byteCount = sck.Receive( rsp, rsp.Length, SocketFlags.None );
  212.  
  213.                     totalRXbytes += (uint)byteCount;
  214.                 }
  215.                 catch( Exception ex ) {
  216.                     Logger.Log( EventLevel.Error, "Lost communicatoin with controller: " + ex.Message );
  217.                 }
  218.             }
  219.  
  220.             msgPending = false;
  221.  
  222.             return byteCount;
  223.         }
  224.  
  225.         /// <summary>
  226.         /// Formats a register write command
  227.         /// </summary>
  228.         /// <param name="regAddr">MODBUS register address</param>
  229.         /// <param name="val">16-bit value to write</param>
  230.         public bool WriteReg( int regAddr, int val )
  231.         {
  232.             if( disabled || sck == null || !sck.Connected ) {
  233.                 return false;
  234.             }
  235.  
  236.             bool successFlag = true;
  237.  
  238.             byte[] txbuf = new byte[12];
  239.             byte[] rxbuf = new byte[16];        // !!! check this
  240.  
  241.             //txbuf[0] and [1] hold a message ID
  242.             txbuf[2] = 0;       // protocol ID
  243.             txbuf[3] = 0;       //   MODBUS ID = 0
  244.  
  245.             txbuf[4] = 0;       // hi byte of data length
  246.             txbuf[5] = 6;       // lo byte of data length
  247.  
  248.             txbuf[6] = modbusAddr;
  249.             txbuf[7] = CMD_WRITEREG;
  250.  
  251.             txbuf[8] = (byte)((regAddr >> 8) & 0xff);   // addr hi byte
  252.             txbuf[9] = (byte)(regAddr & 0xff);          // addr lo byte
  253.             txbuf[10] = (byte)((val >> 8) & 0xff);
  254.             txbuf[11] = (byte)(val & 0xff);
  255.  
  256.             int rcvdbytes = SendRcvCmd( txbuf, ref rxbuf );
  257.             if( rcvdbytes < 8 ) {
  258.                 // minimum response is 7-byte header + command code
  259.                 Logger.Log( EventLevel.Warning, String.Format( "Short MODBUS response to WRITEREG. reg addr = {0}",
  260.                                         regAddr ) );
  261.  
  262.                 successFlag = false;
  263.             }
  264.  
  265.             if( (rxbuf[7] & 0x80) != 0 ) {
  266.                 Logger.Log( EventLevel.Warning, String.Format( "MODBUS error 0x{0:X} writing register {1}",
  267.                                                                     rxbuf[8], regAddr ) );
  268.                 successFlag = false;
  269.             }
  270.  
  271.             return successFlag;
  272.         }
  273.  
  274.         public bool ReadReg( int regAddr, ref int result )
  275.         {
  276.             if( disabled || sck == null || !sck.Connected ) {
  277.                 return false;
  278.             }
  279.  
  280.             bool successFlag = true;
  281.  
  282.             byte[] txbuf = new byte[12];
  283.             byte[] rxbuf = new byte[16];
  284.  
  285.             //txbuf[0] and [1] hold a message ID
  286.             txbuf[2] = 0;       // protocol ID
  287.             txbuf[3] = 0;       //   MODBUS ID = 0
  288.  
  289.             txbuf[4] = 0;       // hi byte of data length
  290.             txbuf[5] = 6;       // lo byte of data length
  291.  
  292.             txbuf[6] = modbusAddr;
  293.             txbuf[7] = CMD_READREG;
  294.  
  295.             txbuf[8] = (byte)((regAddr >> 8) & 0xff);   // addr hi byte
  296.             txbuf[9] = (byte)(regAddr & 0xff);          // addr lo byte
  297.             txbuf[10] = 0;
  298.             txbuf[11] = 1;                              // read a single 16-bit register
  299.  
  300.             int rxCount = SendRcvCmd( txbuf, ref rxbuf );
  301.             if( rxCount < 8 ) {
  302.                 // minimum response is 7-byte header + command code
  303.                 Logger.Log( EventLevel.Warning, String.Format( "Short MODBUS response to READREG. reg addr = {0}",
  304.                                         regAddr ) );
  305.  
  306.                 successFlag = false;
  307.             }
  308.  
  309.             if( (rxbuf[7] & 0x80) != 0 ) {
  310.                 Logger.Log( EventLevel.Warning, String.Format( "MODBUS error 0x{0:X} reading register {1}",
  311.                                                                     rxbuf[8], regAddr ) );
  312.                 successFlag = false;
  313.             }
  314.  
  315.             if( rxCount >= 11 ) {
  316.                 result = rxbuf[9];
  317.                 result = (result << 8) + rxbuf[10];
  318.             } else {
  319.                 // if we don't get a value back, then it's a failure
  320.                 result = 0;
  321.                 successFlag = false;
  322.             }
  323.  
  324.             return successFlag;
  325.         }
  326.  
  327.         public bool SetFlagReg( int regAddr, int val )
  328.         {
  329.             if( disabled || sck == null || !sck.Connected ) {
  330.                 return false;
  331.             }
  332.  
  333.             bool successFlag = true;
  334.  
  335.             byte[] txbuf = new byte[12];
  336.             byte[] rxbuf = new byte[16];
  337.  
  338.             //txbuf[0] and [1] hold a message ID
  339.             txbuf[2] = 0;       // protocol ID
  340.             txbuf[3] = 0;       //   MODBUS ID = 0
  341.  
  342.             txbuf[4] = 0;       // hi byte of data length
  343.             txbuf[5] = 6;       // lo byte of data length
  344.  
  345.             txbuf[6] = modbusAddr;
  346.             txbuf[7] = CMD_WRITEBIT;
  347.  
  348.             txbuf[8] = (byte)((regAddr >> 8) & 0xff);   // addr hi byte
  349.             txbuf[9] = (byte)(regAddr & 0xff);          // addr lo byte
  350.             txbuf[10] = (byte)(val & 0xff);
  351.             txbuf[11] = 0;
  352.  
  353.             int rxcnt = SendRcvCmd( txbuf, ref rxbuf );
  354.             if( rxcnt < 8 ) {
  355.                 // minimum response is 7-byte header + command code
  356.                 Logger.Log( EventLevel.Warning, String.Format( "Short MODBUS response to WRITEBIT. reg addr = {0}, {1} bytes rcvd.",
  357.                                         regAddr, rxcnt ) );
  358.  
  359.                 successFlag = false;
  360.             }
  361.  
  362.             if( (rxbuf[7] & 0x80) != 0 ) {
  363.                 Logger.Log( EventLevel.Warning, String.Format( "MODBUS error 0x{0:X} setting flag {1}",
  364.                                                                     rxbuf[8], regAddr ) );
  365.  
  366.                 successFlag = false;
  367.             }
  368.  
  369.             return successFlag;
  370.         }
  371.  
  372.         public bool GetFlagReg( int regAddr, ref int result )
  373.         {
  374.             if( disabled || sck == null || !sck.Connected ) {
  375.                 return false;
  376.             }
  377.  
  378.             bool successFlag = true;
  379.             byte[] txbuf = new byte[12];
  380.             byte[] rxbuf = new byte[16];
  381.  
  382.             //txbuf[0] and [1] hold a message ID
  383.             txbuf[2] = 0;       // protocol ID
  384.             txbuf[3] = 0;       //   MODBUS ID = 0
  385.  
  386.             txbuf[4] = 0;       // hi byte of data length
  387.             txbuf[5] = 6;       // lo byte of data length
  388.  
  389.             txbuf[6] = modbusAddr;
  390.             txbuf[7] = CMD_READBIT;
  391.  
  392.             txbuf[8] = (byte)((regAddr >> 8) & 0xff);   // addr hi byte
  393.             txbuf[9] = (byte)(regAddr & 0xff);          // addr lo byte
  394.             txbuf[10] = 0;
  395.             txbuf[11] = 1;                               // read a single 16-bit register
  396.  
  397.             int rxCount = SendRcvCmd( txbuf, ref rxbuf );
  398.             if( rxCount < 8 ) {
  399.                 // minimum response is 7-byte header + command code
  400.                 Logger.Log( EventLevel.Warning, String.Format( "Short MODBUS response to READREG. reg addr = {0}",
  401.                                     regAddr ) );
  402.                 successFlag = false;
  403.             }
  404.  
  405.             if( (rxbuf[7] & 0x80) != 0 ) {
  406.                 Logger.Log( EventLevel.Warning, String.Format( "MODBUS error 0x{0:X} reading register {1}",
  407.                                                                 rxbuf[8], regAddr ) );
  408.                 successFlag = false;
  409.             }
  410.  
  411.             if( rxCount >= 10 ) {
  412.                 // single byte response
  413.                 result = rxbuf[9];
  414.             } else {
  415.                 // if we don't get a value back, then it's a failure
  416.                 result = 0;
  417.                 successFlag = false;
  418.             }
  419.  
  420.             return successFlag;
  421.         }
  422.  
  423.         public void Shutdown()
  424.         {
  425.             Logger.Log( EventLevel.Info, "ModbusTCP.Shutdown" );
  426.  
  427.             try {
  428.                 sck.Close();
  429.             }
  430.             catch {}
  431.             finally {
  432.                 sck = null;
  433.             }
  434.         }
  435.     }
  436. }
Advertisement
Add Comment
Please, Sign In to add comment