5 Commits

Author SHA1 Message Date
Peter-B-
eacb494794 Improve output formating 2024-05-14 20:49:47 +02:00
Peter Butzhammer
bc619259b8 Extend in-game docu 2024-05-10 22:05:54 +02:00
Peter Butzhammer
9a1ee565fc Update local build script 2024-05-10 22:05:09 +02:00
Peter Butzhammer
8437e2e511 Add download links to readme 2024-05-07 21:54:29 +02:00
Peter Butzhammer
937c55a72a Update to release version of Sharp7.Rx 2024-05-07 21:27:38 +02:00
10 changed files with 168 additions and 38 deletions

View 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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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": ""
}
}
}
}

View File

@@ -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; }

View 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()
)
);
}
}

View File

@@ -16,7 +16,7 @@
<ItemGroup>
<PackageReference Include="Spectre.Console" Version="0.49.0" />
<PackageReference Include="Spectre.Console.Cli" Version="0.49.0" />
<PackageReference Include="Sharp7.Rx" Version="2.0.9-prerelease" />
<PackageReference Include="Sharp7.Rx" Version="2.0.16" />
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" PrivateAssets="All" />
</ItemGroup>

View File

@@ -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;
}
}
}
}

View File

@@ -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

View File

@@ -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.