using BorgCh; using BorgCh.Comm; using BorgCh.Comm.Modbus; using System; using System.Globalization; using System.Threading; public class modbusmaster { [STAThread] static public void Main( string[] args ) { modbusmaster c = new modbusmaster(); c.run( args ); } private Master mb; private ModbusError last_modbus_error; public modbusmaster() { } public void usage() { Console.WriteLine( "\n" + "usage:\n" + " modbusmaster.exe [options] connspec command slave_id addr count\n" + " modbusmaster.exe [options] connspec command slave_id args...\n" + "\n" + " connspec: portname-baudrate-databits-parity-stopbits\n" + " or: tcp-hostname-port\n" + " or: tcpadu-hostname-port\n" + " parity: 'N' 'E' 'O'\n" + " stopbits '1' '1.5' '2'\n" + "\n" + " command:\n" + " holding addr count\n" + " Read holding registers.\n" + " input addr count\n" + " Read input registers.\n" + " write addr data...\n" + " Write single/multiple registers.\n" + " write-multiple addr data...\n" + " Write multiple registers.\n" + "\n" + " options:\n" + "\n" + " --help\n" + " -h Show this help text.\n" + "\n" + " --format fmt\n" + " -f fmt Specify the format of the returned data.\n" + " The format argument is a series of letters, each letter\n" + " defines the format of one field to output. The following\n" + " format letters are defined:\n" + " f 32-bit floating point.\n" + " i signed 16-bit integer, decimal.\n" + " I signed 32-bit integer, decimal.\n" + " u unsigned 16-bit integer, decimal.\n" + " U unsigned 32-bit integer, decimal.\n" + " x unsigned 16-bit integer, hexadecimal.\n" + " X unsigned 32-bit integer, hexadecimal.\n" + "\n" + " --interval num\n" + " -i num Sets the interval between requests in missliseconds.\n" + "\n" + " --number num\n" + " -n num Specify the number of queries to be done.\n" + "\n" + " --log\n" + " -l Log to standard output.\n" + "\n" + " --timeout num\n" + " -t num Set the timeout in milliseconds. The default is 1000ms.\n" + "\n" + "example:\n" + " modbusmaster.exe COM1:-115200-8-N-1 holding 1 100 8\n" + "" ); } public void run( string[] args ) { bool do_log = false; String format = null; int argc = 0; uint number = 1; ushort interval = 1000; ushort timeout = 1000; int error_count = 0; for( int i=0; i= args.Length ) { Console.WriteLine( "missing argument for format option." ); return; } format = args[i]; } else if( args[i] == "-i" || args[i] == "--interval" ) { i++; if( i >= args.Length ) { Console.WriteLine( "missing argument for interval option." ); return; } if( !parse_u16( args[i], out interval ) ) { Console.WriteLine( "invalid argument for interval option." ); return; } } else if( args[i] == "-l" || args[i] == "--log" ) { do_log = true; } else if( args[i] == "-n" || args[i] == "--number" ) { i++; if( i >= args.Length ) { Console.WriteLine( "missing argument for number option." ); return; } if( !parse_u32( args[i], out number ) ) { Console.WriteLine( "invalid argument for number option." ); return; } } else if( args[i] == "-t" || args[i] == "--timeout" ) { i++; if( i >= args.Length ) { Console.WriteLine( "missing argument for timeout option." ); return; } if( !parse_u16( args[i], out timeout ) ) { Console.WriteLine( "invalid argument for timeout option." ); return; } } else { Console.WriteLine( "Unknown option '{0}'.", args[i] ); return; } } if( argc < 5 ) { usage(); return; } Logger logger = null; if( do_log ) { // Logger.set_default_level( LogLevel.ALL ); // Logger.set_level( "a.b.c", LogLevel.ALL, false ); // logger = Logger.get_logger( "modbusmaster" ); logger = Logger.get_logger( "modbusmaster", LogLevel.ALL ); } try { mb = Master.create( args[0], logger ); mb.set_timeout_ms( timeout ); String func = args[1]; bool multiple = number > 1; while( number > 0 ) { if( func == "holding" || func == "input" ) { if( !do_read_regs( args, func, format ) ) { break; } } else if( func == "write" || func == "write-multiple" ) { if( !do_write_regs( args, func ) ) { break; } } else { Console.WriteLine( String.Format( "invalid command '{0}'", args[1] ) ); break; } if( last_modbus_error != ModbusError.OK ) { error_count++; } if( --number > 0 ) { Thread.Sleep( interval ); Console.WriteLine( "{0}...", number ); } } if( multiple && error_count > 0 ) { Console.WriteLine( "{0} error{1}.", error_count, error_count > 1 ? "s" : "" ); } } catch( Exception ex ) { Console.WriteLine( String.Format( "invalid port spec '{0}':\n {1}", args[0], ex.Message ) ); } } bool do_write_regs( string[] args, String func ) { uint slave_id; uint addr; if( !parse_u32( args[2], out slave_id ) ) { Console.WriteLine( String.Format( "invalid addr '{0}'", args[2] ) ); return false; } if( !parse_u32( args[3], out addr ) ) { Console.WriteLine( String.Format( "invalid addr '{0}'", args[2] ) ); return false; } int count = args.Length - 4; if( count > 1 ) { func = "write-multiple"; } ushort[] data = new ushort[count]; for( int i=0; i 0 ) { call_read_regs( func, slave_id, addr, count > 125 ? 125 : count, format ); addr += 125; count -= 125; } return true; } void call_read_regs( String func, int slave_id, int addr, int count, String format ) { ModbusError err; ushort[] data; data = new ushort[count]; if( func == "holding" ) { err = mb.read_holding_registers( (byte)slave_id, (ushort)addr, data ); } else if( func == "input" ) { err = mb.read_input_registers( (byte)slave_id, (ushort)addr, data ); } else { err = ModbusError.INTERNAL_ERROR; } last_modbus_error = err; if( err == ModbusError.OK ) { if( format == null ) { String line = ""; int i; for( i=0; i count ) { Console.WriteLine( "Missing data for format letters." ); return; } uint uvalue = data[j]; int svalue = (int)uvalue; if( size > 1 ) { uvalue <<= 16; uvalue |= data[j+1]; svalue = (int)uvalue; } else if( svalue >= 0x8000 ) { svalue -= 0x00010000; } String value; switch( format[i] ) { case 'i': case 'I': value = String.Format( "{0}", svalue ); break; case 'u': case 'U': value = String.Format( "{0}", uvalue ); break; case 'x': value = String.Format( "{0:X4}", uvalue ); break; case 'X': value = String.Format( "{0:X08}", uvalue ); break; case 'f': bytes[3] = (byte)(uvalue >> 24); bytes[2] = (byte)(uvalue >> 16); bytes[1] = (byte)(uvalue >> 8); bytes[0] = (byte)(uvalue >> 0); float v = System.BitConverter.ToSingle( bytes, 0 ); value = String.Format( "{0}", v ); break; default: Console.WriteLine( "Unknown format letter '{0]'.", format.Substring( i, 1 ) ); return; } Console.WriteLine( "{0:D5} 0x{0:X04}: {1}", addr+j, value ); } } bool parse_u32( String s, out uint n ) { n = 0; if( s.StartsWith( "0x" ) ) { // if( !uint.TryParse( s, NumberStyles.HexNumber, if( !uint.TryParse( s.Substring( 2 ), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out n ) ) { return false; } } else { if( !uint.TryParse( s, out n ) ) { return false; } } return true; } bool parse_u16( String s, out ushort n ) { uint i; n = 0; if( !parse_u32( s, out i ) ) { return false; } if( i > 0xFFFF ) { return false; } n = (ushort)i; return true; } }