Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eacb494794 | ||
|
|
bc619259b8 | ||
|
|
9a1ee565fc | ||
|
|
8437e2e511 |
79
Sharp7.Monitor/CustomHelpProvider.cs
Normal file
79
Sharp7.Monitor/CustomHelpProvider.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Cli;
|
||||
using Spectre.Console.Cli.Help;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Sharp7.Monitor;
|
||||
|
||||
internal class CustomHelpProvider(ICommandAppSettings settings) : HelpProvider(settings)
|
||||
{
|
||||
private readonly ICommandAppSettings settings = settings;
|
||||
|
||||
public override IEnumerable<IRenderable> GetFooter(ICommandModel model, ICommandInfo? command)
|
||||
{
|
||||
var helpStyles = settings.HelpProviderStyles?.Options;
|
||||
|
||||
var variableGrid = GetVariableExamples(helpStyles);
|
||||
|
||||
return
|
||||
[
|
||||
.. base.GetFooter(model, command),
|
||||
|
||||
new Text("VARIABLE EXAMPLES:", helpStyles?.Header ?? Style.Plain), Text.NewLine,
|
||||
new Text("For a detailed format description see https://github.com/evopro-ag/Sharp7Reactive."), Text.NewLine,
|
||||
Text.NewLine,
|
||||
variableGrid,
|
||||
Text.NewLine
|
||||
];
|
||||
}
|
||||
|
||||
public override IEnumerable<IRenderable> GetOptions(ICommandModel model, ICommandInfo? command) =>
|
||||
[
|
||||
..base.GetOptions(model, command),
|
||||
Text.NewLine,
|
||||
new Text("You can find details on rack and slot at https://github.com/fbarresi/Sharp7/wiki/Connection#rack-and-slot."), Text.NewLine,
|
||||
new Text("If you are using S7 1200 or 1500, you must explicitly allow RFC1006. See https://github.com/fbarresi/Sharp7#s7-12001500-notes."), Text.NewLine,
|
||||
Text.NewLine
|
||||
];
|
||||
|
||||
private static IRenderable GetVariableExamples(OptionStyle? helpStyles)
|
||||
{
|
||||
IReadOnlyList<VariableType> variableTypes =
|
||||
[
|
||||
new VariableType("bit", "single bit as boolean", "[grey]db3[/].[lightgoldenrod2_1]bit[/][grey]10[/].2"),
|
||||
new VariableType("byte", "single byte", "[grey]db3[/].[lightgoldenrod2_1]byte[/][grey]10[/]"),
|
||||
new VariableType("", "byte array", "[grey]db3[/].[lightgoldenrod2_1]byte[/][grey]10[/].5"),
|
||||
new VariableType("int", "16 bit signed integer", "[grey]db3[/].[lightgoldenrod2_1]int[/][grey]10[/]"),
|
||||
new VariableType("uint", "16 bit unsigned integer", "[grey]db3[/].[lightgoldenrod2_1]uint[/][grey]10[/]"),
|
||||
new VariableType("dint", "32 bit signed integer", "[grey]db3[/].[lightgoldenrod2_1]dint[/][grey]10[/]"),
|
||||
new VariableType("udint", "32 bit unsigned integer", "[grey]db3[/].[lightgoldenrod2_1]udint[/][grey]10[/]"),
|
||||
new VariableType("lint", "64 bit signed integer", "[grey]db3[/].[lightgoldenrod2_1]lint[/][grey]10[/]"),
|
||||
new VariableType("ulint", "64 bit unsigned integer", "[grey]db3[/].[lightgoldenrod2_1]ulint[/][grey]10[/]"),
|
||||
new VariableType("real", "32 bit float", "[grey]db3[/].[lightgoldenrod2_1]real[/][grey]10[/]"),
|
||||
new VariableType("lreal", "64 bit float", "[grey]db3[/].[lightgoldenrod2_1]lreal[/][grey]10[/]"),
|
||||
new VariableType("string", "ASCII text string", "[grey]db3[/].[lightgoldenrod2_1]string[/][grey]10[/].16"),
|
||||
new VariableType("wstring", "UTF-16 text string", "[grey]db3[/].[lightgoldenrod2_1]wstring[/][grey]10[/].16"),
|
||||
];
|
||||
|
||||
var grid = new Grid();
|
||||
grid.AddColumn(new GridColumn {Padding = new Padding(4, 4), NoWrap = true});
|
||||
grid.AddColumn(new GridColumn {Padding = new Padding(0, 0, 4, 0)});
|
||||
grid.AddColumn(new GridColumn {Padding = new Padding(0)});
|
||||
|
||||
grid.AddRow(
|
||||
new Text("Identifier", helpStyles?.DefaultValueHeader ?? Style.Plain),
|
||||
new Text("Description", helpStyles?.DefaultValueHeader ?? Style.Plain),
|
||||
new Text("Example", helpStyles?.DefaultValueHeader ?? Style.Plain)
|
||||
);
|
||||
|
||||
foreach (var variableType in variableTypes)
|
||||
grid.AddRow(
|
||||
new Text(variableType.Identifier),
|
||||
new Text(variableType.Description),
|
||||
new Markup(variableType.Example)
|
||||
);
|
||||
return grid;
|
||||
}
|
||||
|
||||
internal record VariableType(string Identifier, string Description, string Example);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ public static class CustomStyles
|
||||
public static Style Default { get; } = new(background:Color.Black);
|
||||
|
||||
public static Style Error { get; } = Default.Foreground(Color.Red);
|
||||
public static Style Hex { get; } = Default.Foreground(Color.Blue);
|
||||
public static Style Hex { get; } = Default.Foreground(Color.LightGoldenrod2_1);
|
||||
public static Style Note { get; } = Default.Foreground(Color.DarkSlateGray1);
|
||||
public static Style TableBorder { get; } = Default.Foreground(Color.DarkGreen);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Resources;
|
||||
using System.Text;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
@@ -25,9 +26,18 @@ internal class Program
|
||||
var app =
|
||||
new CommandApp<ReadPlcCommand>()
|
||||
.WithData(cts.Token)
|
||||
.WithDescription("This program connects to a PLC and reads the variables specified as command line arguments.");
|
||||
.WithDescription("This program connects to a Siemens S7 PLC using RFC1006 and reads the variables specified.");
|
||||
|
||||
app.Configure(config => { config.SetApplicationName("s7mon.exe"); });
|
||||
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.AddExample("192.0.0.10 db100.int12");
|
||||
config.AddExample("192.0.0.10 --cpu 2 --rack 1 db100.int12");
|
||||
|
||||
config.SetHelpProvider(new CustomHelpProvider(config.Settings));
|
||||
|
||||
config.SetApplicationName(OperatingSystem.IsWindows() ? "s7mon.exe" : "s7mon");
|
||||
});
|
||||
|
||||
return await app.RunAsync(args);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
{
|
||||
{
|
||||
"profiles": {
|
||||
"Sharp7.Monitor": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "10.30.110.62 DB2050.Bit0.1 DB2050.Byte1 DB2050.Byte2.4 DB2050.Int6 DB2050.UInt8 DB2050.DInt10 DB2050.UDInt14 DB2050.LInt18 DB2050.ULInt26 DB2050.Real34 DB2050.LReal38 DB2050.String50.20 DB2050.WString80.20 DB2050.Byte130.20 "
|
||||
},
|
||||
|
||||
"no args": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ using Sharp7.Rx;
|
||||
using Sharp7.Rx.Enums;
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Cli;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Sharp7.Monitor;
|
||||
|
||||
@@ -31,27 +30,6 @@ internal sealed class ReadPlcCommand : AsyncCommand<ReadPlcCommand.Settings>
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static IRenderable FormatCellData(object value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
IRenderable renderable => renderable,
|
||||
Exception ex => new Text(ex.Message, CustomStyles.Error),
|
||||
byte[] byteArray => new Text(string.Join(" ", byteArray.Select(b => $"0x{b:X2}")), CustomStyles.Hex),
|
||||
byte => FormatNo(),
|
||||
short => FormatNo(),
|
||||
ushort => FormatNo(),
|
||||
int => FormatNo(),
|
||||
uint => FormatNo(),
|
||||
long => FormatNo(),
|
||||
ulong => FormatNo(),
|
||||
|
||||
_ => new Text(value.ToString() ?? "", CustomStyles.Default)
|
||||
};
|
||||
|
||||
Markup FormatNo() => new($"[blue]0x{value:X2}[/] {value}", CustomStyles.Default);
|
||||
}
|
||||
|
||||
private static async Task RunProgram(Settings settings, CancellationToken token)
|
||||
{
|
||||
AnsiConsole.MarkupLine($"Connecting to plc [green]{settings.PlcIp}[/], CPU [green]{settings.CpuMpiAddress}[/], rack [green]{settings.RackNumber}[/]. ");
|
||||
@@ -105,8 +83,7 @@ internal sealed class ReadPlcCommand : AsyncCommand<ReadPlcCommand.Settings>
|
||||
foreach (var record in variableContainer.VariableRecords)
|
||||
if (record.HasUpdate(out var value))
|
||||
table.Rows.Update(
|
||||
record.RowIdx, 1,
|
||||
FormatCellData(value)
|
||||
record.RowIdx, 1, RenderUtil.FormatCellData(value)
|
||||
);
|
||||
|
||||
ctx.Refresh();
|
||||
@@ -124,16 +101,16 @@ internal sealed class ReadPlcCommand : AsyncCommand<ReadPlcCommand.Settings>
|
||||
public required string PlcIp { get; init; }
|
||||
|
||||
[CommandArgument(1, "[variables]")]
|
||||
[Description("Variables to read from S7, like Db200.Int4.\r\nFor format description see https://github.com/evopro-ag/Sharp7Reactive.")]
|
||||
[Description("Variables to read from S7, like Db200.Int4")]
|
||||
public required string[] Variables { get; init; }
|
||||
|
||||
[CommandOption("-c|--cpu")]
|
||||
[Description("CPU MPI address of S7 instance.\r\nSee https://github.com/fbarresi/Sharp7/wiki/Connection#rack-and-slot.\r\n")]
|
||||
[Description("CPU MPI address of S7 instance")]
|
||||
[DefaultValue(0)]
|
||||
public int CpuMpiAddress { get; init; }
|
||||
|
||||
[CommandOption("-r|--rack")]
|
||||
[Description("Rack number of S7 instance.\r\nSee https://github.com/fbarresi/Sharp7/wiki/Connection#rack-and-slot.\r\n")]
|
||||
[Description("Rack number of S7 instance")]
|
||||
[DefaultValue(0)]
|
||||
public int RackNumber { get; init; }
|
||||
|
||||
|
||||
44
Sharp7.Monitor/RenderUtil.cs
Normal file
44
Sharp7.Monitor/RenderUtil.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Sharp7.Monitor;
|
||||
|
||||
internal static class RenderUtil
|
||||
{
|
||||
public static IRenderable FormatCellData(object value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
IRenderable renderable => renderable,
|
||||
Exception ex => new Text(ex.Message, CustomStyles.Error),
|
||||
byte[] byteArray => FormatByteArray(byteArray),
|
||||
byte => FormatNo(),
|
||||
short => FormatNo(),
|
||||
ushort => FormatNo(),
|
||||
int => FormatNo(),
|
||||
uint => FormatNo(),
|
||||
long => FormatNo(),
|
||||
ulong => FormatNo(),
|
||||
|
||||
_ => new Text(value.ToString() ?? "", CustomStyles.Default)
|
||||
};
|
||||
|
||||
IRenderable FormatNo() => new Paragraph()
|
||||
.Append($"0x{value:X2}", CustomStyles.Hex)
|
||||
.Append(" ")
|
||||
.Append($"{value}", CustomStyles.Default)
|
||||
;
|
||||
|
||||
IRenderable FormatByteArray(byte[] byteArray) =>
|
||||
new Paragraph()
|
||||
.Append("0x " + string.Join(" ", byteArray.Select(b => $"{b:X2}")), CustomStyles.Hex)
|
||||
.Append(Environment.NewLine)
|
||||
.Append(new string(
|
||||
byteArray
|
||||
.Select(b => (char) b)
|
||||
.Select(c => char.IsControl(c) ? '·' : c)
|
||||
.ToArray()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ namespace Sharp7.Monitor;
|
||||
public class VariableRecord
|
||||
{
|
||||
private object value = new();
|
||||
private int valueUpdated;
|
||||
private volatile int valueUpdated;
|
||||
public required string Address { get; init; }
|
||||
public required int RowIdx { get; init; }
|
||||
|
||||
@@ -52,4 +52,4 @@ public class VariableRecord
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
dotnet publish .\Sharp7.Monitor\Sharp7.Monitor.csproj -c Release --output publish\non-sc --no-self-contained
|
||||
|
||||
dotnet publish .\Sharp7.Monitor\Sharp7.Monitor.csproj -c Release --output publish\sc -p:PublishTrimmed=true -p:EnableCompressionInSingleFile=true --self-contained
|
||||
dotnet publish .\Sharp7.Monitor\Sharp7.Monitor.csproj -c Release -r win-x64 --self-contained -p:PublishTrimmed=true -p:EnableCompressionInSingleFile=true --output publish\sc\win-x64
|
||||
dotnet publish .\Sharp7.Monitor\Sharp7.Monitor.csproj -c Release -r linux-x64 --self-contained -p:PublishTrimmed=true -p:EnableCompressionInSingleFile=true --output publish\sc\linux-x64
|
||||
|
||||
|
||||
17
readme.md
17
readme.md
@@ -9,9 +9,18 @@ It displays the variable values in a table format directly in your console. The
|
||||
|
||||
## Usage
|
||||
|
||||
- Download `s7mon.exe` binary from [releases](https://github.com/Peter-B-/Sharp7.Monitor/releases/latest).
|
||||
- Download the zip file for your plattform from [releases](https://github.com/Peter-B-/Sharp7.Monitor/releases/latest):
|
||||
|
||||
- [Windows x64](https://github.com/Peter-B-/Sharp7.Monitor/releases/latest/download/s7mon.win-x64.zip) - If you are not sure what to download, this is most likely what you need.
|
||||
- [Windows Arm 64](https://github.com/Peter-B-/Sharp7.Monitor/releases/latest/download/s7mon.win-arm64.zip)
|
||||
- [Linux x64](https://github.com/Peter-B-/Sharp7.Monitor/releases/latest/download/s7mon.linux-x64.zip)
|
||||
- [Linux Arm 64](https://github.com/Peter-B-/Sharp7.Monitor/releases/latest/download/s7mon.linux-arm64.zip)
|
||||
|
||||
This binary is self contained. No installation of .Net Runtime is required.
|
||||
- Extract the `s7mon.exe`, resp. `s7mon` binary.
|
||||
|
||||
The binaries are self contained - no installation of .Net Runtime is required.
|
||||
|
||||
|
||||
|
||||
- Run the following command:
|
||||
```powershell
|
||||
@@ -19,6 +28,10 @@ It displays the variable values in a table format directly in your console. The
|
||||
```
|
||||
Replace `<IP_Address>` with the IP address of your Siemens S7 PLC, and list the desired variables (e.g., `DB2050.Byte1`, `DB2050.Int6`, etc.).
|
||||
|
||||
You can find a description of the variable format on the [Sharp7.Rx readme](https://github.com/evopro-ag/Sharp7Reactive).
|
||||
|
||||
If the connection cannot be established, try setting [CPU and Rack](https://github.com/fbarresi/Sharp7/wiki/Connection#rack-and-slot) with the `--cpu` and `--rack` parameters.
|
||||
|
||||
- The program will establish a connection to the PLC and continuously display the values of the specified variables in a table format.
|
||||
|
||||
Press `Ctrl + C` to exit.
|
||||
|
||||
Reference in New Issue
Block a user