/// <summary> /// Initializes a new instance of the <see cref="TcpReadCommand"/> class. /// </summary> /// <param name="client"></param> /// <param name="logger"></param> public TcpReadCommand(ITcpModbusClient client, ILogger <TcpReadCommand> logger) : base("read", "Supporting Modbus TCP read operations.") { // Setup command options. AddOption(new Option <bool> (new string[] { "-?", "--help" }, "Show help and usage information")); AddOption(new Option <bool> (new string[] { "-c", "--coil" }, "Reads coil(s)")); AddOption(new Option <bool> (new string[] { "-d", "--discrete" }, "Reads discrete input(s)")); AddOption(new Option <bool> (new string[] { "-h", "--holding" }, "Reads holding register(s)")); AddOption(new Option <bool> (new string[] { "-i", "--input" }, "Reads input register(s)")); AddOption(new Option <bool> (new string[] { "-x", "--hex" }, "Displays the values in HEX")); AddOption(new Option <ushort>(new string[] { "-n", "--number" }, "The number of items to read").Name("Number").Default((ushort)1)); AddOption(new Option <ushort>(new string[] { "-o", "--offset" }, "The offset of the first item").Name("Offset").Default((ushort)0)); AddOption(new Option <string>(new string[] { "-t", "--type" }, "Reads the specified data type").Name("Type") .FromAmong("bits", "string", "byte", "short", "ushort", "int", "uint", "float", "double", "long", "ulong")); // Add custom validation. AddValidator(r => { var optionHelp = r.Children.Contains("?"); var optionC = r.Children.Contains("c"); var optionD = r.Children.Contains("d"); var optionH = r.Children.Contains("h"); var optionI = r.Children.Contains("i"); var optionX = r.Children.Contains("x"); var optionN = r.Children.Contains("n"); var optionO = r.Children.Contains("o"); var optionT = r.Children.Contains("t"); if ((!optionC && !optionD && !optionH && !optionI) || ((optionC && (optionD || optionH || optionI)) || (optionD && (optionC || optionH || optionI)) || (optionH && (optionD || optionC || optionI)) || (optionI && (optionD || optionH || optionC))) || optionHelp) { return("Specify a single read option (coils, discrete inputs, holding registers, input registers)."); } return(null); }); // Setup execution handler. Handler = CommandHandler.Create <IConsole, bool, TcpReadCommandOptions>((console, verbose, options) => { logger.LogInformation("Handler()"); // Run additional checks on options. options.CheckOptions(console); // Using TCP client options. client.TcpSlave.Address = options.Address; client.TcpSlave.Port = options.Port; client.TcpSlave.ID = options.SlaveID; client.TcpMaster.ReceiveTimeout = options.ReceiveTimeout; client.TcpMaster.SendTimeout = options.SendTimeout; if (verbose) { console.Out.WriteLine($"Modbus Commandline Application: {RootCommand.ExecutableName}"); console.Out.WriteLine(); console.Out.Write("TcpMasterData: "); console.Out.WriteLine(JsonSerializer.Serialize <TcpMasterData>(client.TcpMaster, _jsonoptions)); console.Out.Write("TcpSlaveData: "); console.Out.WriteLine(JsonSerializer.Serialize <TcpSlaveData>(client.TcpSlave, _jsonoptions)); console.Out.WriteLine(); } try { if (client.Connect()) { // Reading coils. if (options.Coil) { CommandHelper.ReadingCoils(console, client, options.SlaveID, options.Number, options.Offset); } // Reading discrete inputs. if (options.Discrete) { CommandHelper.ReadingDiscreteInputs(console, client, options.SlaveID, options.Number, options.Offset); } // Reading holding registers. if (options.Holding) { CommandHelper.ReadingHoldingRegisters(console, client, options.SlaveID, options.Number, options.Offset, options.Type, options.Hex); } // Reading input registers. if (options.Input) { CommandHelper.ReadingInputRegisters(console, client, options.SlaveID, options.Number, options.Offset, options.Type, options.Hex); } } else { console.Out.WriteLine($"Modbus TCP slave not found at {options.Address}:{options.Port}."); return(ExitCodes.NotSuccessfullyCompleted); } } catch (Exception ex) { console.Out.WriteLine($"Exception: {ex.Message}"); return(ExitCodes.UnhandledException); } finally { if (client.Connected) { client.Disconnect(); } } return(ExitCodes.SuccessfullyCompleted); }); }
/// <summary> /// Initializes a new instance of the <see cref="TcpMonitorCommand"/> class. /// </summary> /// <param name="client"></param> /// <param name="logger"></param> public TcpMonitorCommand(ITcpModbusClient client, ILogger <TcpMonitorCommand> logger) : base("monitor", "Supporting Modbus TCP monitor operations.") { // Setup command options. AddOption(new Option <bool> (new string[] { "-?", "--help" }, "Show help and usage information")); AddOption(new Option <bool> (new string[] { "-c", "--coil" }, "Reads coil(s)")); AddOption(new Option <bool> (new string[] { "-d", "--discrete" }, "Reads discrete input(s)")); AddOption(new Option <bool> (new string[] { "-h", "--holding" }, "Reads holding register(s)")); AddOption(new Option <bool> (new string[] { "-i", "--input" }, "Reads input register(s)")); AddOption(new Option <bool> (new string[] { "-x", "--hex" }, "Displays the values in HEX")); AddOption(new Option <ushort>(new string[] { "-n", "--number" }, "The number of items to read").Name("Number").Default((ushort)1)); AddOption(new Option <ushort>(new string[] { "-o", "--offset" }, "The offset of the first item").Name("Offset").Default((ushort)0)); AddOption(new Option <string>(new string[] { "-t", "--type" }, "Reads the specified data type").Name("Type") .FromAmong("bits", "string", "byte", "short", "ushort", "int", "uint", "float", "double", "long", "ulong")); AddOption(new Option <uint> (new string[] { "-r", "--repeat" }, "The number of times to read").Name("Repeat").Default((uint)10)); AddOption(new Option <uint> (new string[] { "-s", "--seconds" }, "The seconds between read times").Name("Seconds").Default((uint)1)); // Add custom validation. AddValidator(r => { var optionHelp = r.Children.Contains("?"); var optionC = r.Children.Contains("c"); var optionD = r.Children.Contains("d"); var optionH = r.Children.Contains("h"); var optionI = r.Children.Contains("i"); var optionX = r.Children.Contains("x"); var optionN = r.Children.Contains("n"); var optionO = r.Children.Contains("o"); var optionT = r.Children.Contains("t"); if ((!optionC && !optionD && !optionH && !optionI) || ((optionC && (optionD || optionH || optionI)) || (optionD && (optionC || optionH || optionI)) || (optionH && (optionD || optionC || optionI)) || (optionI && (optionD || optionH || optionC))) || optionHelp) { return("Specify a single read option (coils, discrete inputs, holding registers, input registers)."); } return(null); }); // Setup execution handler. Handler = CommandHandler.Create <IConsole, CancellationToken, bool, TcpMonitorCommandOptions>(async(console, token, verbose, options) => { logger.LogInformation("Handler()"); // Run additional checks on options. options.CheckOptions(console); // Using TCP client options. client.TcpSlave.Address = options.Address; client.TcpSlave.Port = options.Port; client.TcpSlave.ID = options.SlaveID; client.TcpMaster.ReceiveTimeout = options.ReceiveTimeout; client.TcpMaster.SendTimeout = options.SendTimeout; if (verbose) { console.Out.WriteLine($"Modbus Commandline Application: {RootCommand.ExecutableName}"); console.Out.WriteLine(); console.Out.Write("TcpMasterData: "); console.Out.WriteLine(JsonSerializer.Serialize <TcpMasterData>(client.TcpMaster, _jsonoptions)); console.Out.Write("TcpSlaveData: "); console.Out.WriteLine(JsonSerializer.Serialize <TcpSlaveData>(client.TcpSlave, _jsonoptions)); console.Out.WriteLine(); } try { if (client.Connect()) { try { bool forever = (options.Repeat == 0); bool header = true; var time = DateTime.UtcNow; while (!token.IsCancellationRequested) { var start = DateTime.UtcNow; #pragma warning disable CS8604 // Possible null reference argument (logger). if (verbose && !header) { console.Out.WriteLine($"Time elapsed {start - time:d'.'hh':'mm':'ss'.'fff}"); } ReadingData(client, console, logger, options, header); #pragma warning restore CS8604 // Possible null reference argument (logger). // Only first call is printing the header. header = false; var end = DateTime.UtcNow; double delay = options.Seconds - (end - start).TotalSeconds; if (delay < 0) { logger?.LogWarning($"Monitoring: no time between reads (min. {delay + options.Seconds})."); } if ((--options.Repeat > 0) && delay > 0) { await Task.Delay(TimeSpan.FromSeconds(delay), token); } if (!forever && (options.Repeat == 0)) { break; } } } catch (AggregateException aex) when(aex.InnerExceptions.All(e => e is OperationCanceledException)) { console.Out.WriteLine("Monitoring cancelled."); } catch (OperationCanceledException) { console.Out.WriteLine("Monitoring cancelled."); } catch (Exception ex) { console.Out.WriteLine($"Exception: {ex.Message}"); return(ExitCodes.UnhandledException); } } else { console.Out.WriteLine($"Modbus TCP slave not found at {options.Address}:{options.Port}."); return(ExitCodes.NotSuccessfullyCompleted); } } catch (Exception ex) { console.Out.WriteLine($"Exception: {ex.Message}"); return(ExitCodes.NotSuccessfullyCompleted); } finally { if (client.Connected) { client.Disconnect(); } } return(ExitCodes.SuccessfullyCompleted); }); }
/// <summary> /// Initializes a new instance of the <see cref="TcpCommand"/> class. /// </summary> /// <param name="client"></param> /// <param name="tcpReadCommand"></param> /// <param name="tcpWriteCommand"></param> /// <param name="tcpMonitorCommand"></param> /// <param name="settings"></param> /// <param name="logger"></param> public TcpCommand(ITcpModbusClient client, TcpReadCommand tcpReadCommand, TcpWriteCommand tcpWriteCommand, TcpMonitorCommand tcpMonitorCommand, AppSettings settings, ILogger <TcpCommand> logger) : base("tcp", "Subcommand supporting standard Modbus TCP operations.") { // Setup command options. AddGlobalOption(new Option <string>("--address", "Sets the Modbus slave IP address").Name("Address").Default(settings.TcpSlave.Address).IPAddress()); AddGlobalOption(new Option <int>("--port", "Sets the Modbus slave IP port").Name("Port").Default(settings.TcpSlave.Port).Range(0, 65535)); AddGlobalOption(new Option <byte>("--slaveid", "Sets the Modbus slave ID").Name("SlaveID").Default(settings.TcpSlave.ID)); AddGlobalOption(new Option <int>("--receive-timeout", "Sets the receive timeout").Name("ReceiveTimeout").Default(settings.TcpMaster.ReceiveTimeout).Range(0, Int32.MaxValue).Hide()); AddGlobalOption(new Option <int>("--send-timeout", "Sets the send timeout").Name("SendTimeout").Default(settings.TcpMaster.SendTimeout).Range(0, Int32.MaxValue).Hide()); // Add sub commands. AddCommand(tcpReadCommand); AddCommand(tcpWriteCommand); AddCommand(tcpMonitorCommand); // Setup execution handler. Handler = CommandHandler.Create <IConsole, bool, TcpCommandOptions>((console, verbose, options) => { logger.LogInformation("Handler()"); // Using TCP client options. client.TcpSlave.Address = options.Address; client.TcpSlave.Port = options.Port; client.TcpSlave.ID = options.SlaveID; client.TcpMaster.ReceiveTimeout = options.ReceiveTimeout; client.TcpMaster.SendTimeout = options.SendTimeout; if (verbose) { console.Out.WriteLine($"Modbus Commandline Application: {RootCommand.ExecutableName}"); console.Out.WriteLine(); console.Out.Write("TcpMasterData: "); console.Out.WriteLine(JsonSerializer.Serialize <TcpMasterData>(client.TcpMaster, _jsonoptions)); console.Out.Write("TcpSlaveData: "); console.Out.WriteLine(JsonSerializer.Serialize <TcpSlaveData>(client.TcpSlave, _jsonoptions)); console.Out.WriteLine(); } try { if (client.Connect()) { console.Out.WriteLine($"Modbus TCP slave found at {options.Address}:{options.Port}."); return(ExitCodes.SuccessfullyCompleted); } else { console.Out.WriteLine($"Modbus TCP slave not found at {options.Address}:{options.Port}."); return(ExitCodes.NotSuccessfullyCompleted); } } catch (Exception ex) { console.Out.WriteLine($"Exception: {ex.Message}"); return(ExitCodes.UnhandledException); } finally { if (client.Connected) { client.Disconnect(); } } }); }
/// <summary> /// Initializes a new instance of the <see cref="TcpWriteCommand"/> class. /// </summary> /// <param name="client"></param> /// <param name="logger"></param> public TcpWriteCommand(ITcpModbusClient client, ILogger <TcpWriteCommand> logger) : base("write", "Supporting Modbus TCP write operations.") { // Setup command arguments and options. AddOption(new Option <bool> (new string[] { "-?", "--help" }, "Show help and usage information")); AddOption(new Option <string>(new string[] { "-c", "--coil" }, "Write coil(s).").Name("Json")); AddOption(new Option <string>(new string[] { "-h", "--holding" }, "Writes holding register(s).").Name("Json")); AddOption(new Option <bool> (new string[] { "-x", "--hex" }, "Writes the HEX values (string)")); AddOption(new Option <ushort>(new string[] { "-o", "--offset" }, "The offset of the first item.").Name("Offset").Default((ushort)0)); AddOption(new Option <string>(new string[] { "-t", "--type" }, "Reads the specified data type").Name("Type") .FromAmong("bits", "string", "byte", "short", "ushort", "int", "uint", "float", "double", "long", "ulong")); // Add custom validation. AddValidator(r => { var optionHelp = r.Children.Contains("?"); var optionC = r.Children.Contains("c"); var optionH = r.Children.Contains("h"); var optionX = r.Children.Contains("x"); var optionN = r.Children.Contains("n"); var optionO = r.Children.Contains("o"); var optionT = r.Children.Contains("t"); if ((!optionC && !optionH) || (optionC && optionH) || optionHelp) { return("Specify a single write option (coils or holding registers)."); } return(null); }); // Setup execution handler. Handler = CommandHandler.Create <IConsole, bool, TcpWriteCommandOptions>((console, verbose, options) => { logger.LogInformation("Handler()"); // Run additional checks on options. options.CheckOptions(console); // Using TCP client options. client.TcpSlave.Address = options.Address; client.TcpSlave.Port = options.Port; client.TcpSlave.ID = options.SlaveID; client.TcpMaster.ReceiveTimeout = options.ReceiveTimeout; client.TcpMaster.SendTimeout = options.SendTimeout; if (verbose) { console.Out.WriteLine($"Modbus Commandline Application: {RootCommand.ExecutableName}"); console.Out.WriteLine(); console.Out.Write("TcpMasterData: "); console.Out.WriteLine(JsonSerializer.Serialize <TcpMasterData>(client.TcpMaster, _jsonoptions)); console.Out.Write("TcpSlaveData: "); console.Out.WriteLine(JsonSerializer.Serialize <TcpSlaveData>(client.TcpSlave, _jsonoptions)); console.Out.WriteLine(); } try { if (client.Connect()) { // Writing coils. CommandHelper.WritingCoils(console, client, options.SlaveID, options.Offset, options.Coil); // Writing holding registers. CommandHelper.WritingHoldingRegisters(console, client, options.SlaveID, options.Offset, options.Holding, options.Type, options.Hex); } else { console.Out.WriteLine($"Modbus TCP slave not found at {options.Address}:{options.Port}."); return(ExitCodes.NotSuccessfullyCompleted); } } catch (JsonException jex) { logger.LogError(jex, $"Exception parsing JSON data values."); return(ExitCodes.NotSuccessfullyCompleted); } catch (Exception ex) { console.Out.WriteLine($"Exception: {ex.Message}"); return(ExitCodes.UnhandledException); } finally { if (client.Connected) { client.Disconnect(); } } return(ExitCodes.SuccessfullyCompleted); }); }