Files
wwtools/graccesscli/docs/clifx_reference.md
Joseph Doherty 32f26272ae Initial commit: Wonderware / System Platform tools and reference
Five tools under one repo, all docs organized per DOCS-GUIDE.md:

- aalogcli: .NET 4.8 / x86 CliFx CLI for reading System Platform binary
  logs (*.aaLGX) for LLM debugging, built on aaOpenSource/aaLog. Commands:
  last, tail, range, unread, fields. Stable JSON envelope under --llm-json.
  Build template under lib/build/ for rebuilding aaLogReader.dll.

- aot: ArchestrA Object Toolkit 2014 v4.0 reference material. Dev guide
  (Markdown converted from CHM), API reference for the ArchestrA.Toolkit
  namespace, and the Monitor / Watchdog VS sample solutions.

- graccesscli: .NET 4.8 / x86 CliFx CLI that automates Galaxy
  configuration via the ArchestrA GRAccess COM interop. Includes session
  daemon, IPC protocol, and llm-json envelope contract.

- grdb: SQL/DDL exploration of the Galaxy Repository database. DDL
  captures, reusable queries, hierarchy / contained-name <-> tag-name
  translation notes.

- histdb: LLM-oriented reference for AVEVA Historian retrieval. INSQL
  linked-server, extension tables, every wwXxx time-domain extension,
  every retrieval mode, alarm/event SQL recipes, REST API. Distilled
  from the 243-page Historian Retrieval Guide.

Root contains:
- CLAUDE.md: thin index pointing into each tool's README.
- DOCS-GUIDE.md: doctrine for organizing docs for LLM consumption.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:22:20 -04:00

246 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CliFx Reference (v2.3.6)
Local reference for the CliFx CLI framework. Source: https://github.com/Tyrrrz/CliFx
## Setup
```csharp
using CliFx;
public static class Program
{
public static async Task<int> Main() =>
await new CliApplicationBuilder()
.AddCommandsFromThisAssembly()
.Build()
.RunAsync();
}
```
**Important:** `Main()` must return the integer exit code from `RunAsync()`.
`RunAsync()` resolves args from `Environment.GetCommandLineArgs()` and env vars from `Environment.GetEnvironmentVariables()`. You can also provide them manually via overloads.
## Defining Commands
Commands implement `ICommand` and are decorated with `[Command]`:
```csharp
using CliFx;
using CliFx.Attributes;
[Command(Description = "Calculates the logarithm of a value.")]
public class LogCommand : ICommand
{
[CommandParameter(0, Description = "Value whose logarithm is to be found.")]
public required double Value { get; init; }
[CommandOption("base", 'b', Description = "Logarithm base.")]
public double Base { get; init; } = 10;
public ValueTask ExecuteAsync(IConsole console)
{
var result = Math.Log(Value, Base);
console.Output.WriteLine(result);
return default;
}
}
```
Use `IConsole` (not `System.Console`) for all console I/O — this enables testability.
## Parameters vs Options
| Aspect | `[CommandParameter]` | `[CommandOption]` |
|---|---|---|
| Binding | By position order | By name (`--foo`) or short name (`-f`) |
| Required by default | Yes | No |
| Make optional | Omit `required` (last param only) | Omit `required` |
| Make required | Use `required` keyword | Use `required` keyword |
| Non-scalar (collections) | Only last parameter | Any option |
| Env var fallback | No | Yes (`EnvironmentVariable = "ENV_FOO"`) |
```csharp
// Required option
[CommandOption("foo")]
public required string Foo { get; init; }
// Optional parameter (must be last)
[CommandParameter(0)]
public string? OptionalParam { get; init; }
// Option with env var fallback
[CommandOption("foo", EnvironmentVariable = "ENV_FOO")]
public required string FooWithFallback { get; init; }
// Non-scalar option
[CommandOption("items")]
public required IReadOnlyList<string> Items { get; init; }
```
## Argument Syntax (POSIX-style)
```
myapp [...directives] [command] [...parameters] [...options]
```
- `--foo bar` → option "foo" = "bar"
- `-f bar` → option 'f' = "bar"
- `--switch` → option "switch" (no value / boolean)
- `-abc` → options 'a', 'b', 'c' (no value)
- `-i file1.txt file2.txt` → option 'i' = ["file1.txt", "file2.txt"]
- `-i file1.txt -i file2.txt` → same as above
## Value Conversion
Supports out of the box:
- **Primitives**: `int`, `bool`, `double`, `ulong`, `char`, etc.
- **Date/time**: `DateTime`, `DateTimeOffset`, `TimeSpan`
- **Enums**: by name or numeric value
- **String-initializable**: types with `ctor(string)` or static `Parse(string)` — e.g. `FileInfo`, `Guid`, `BigInteger`
- **Nullable** versions of all above
- **Collections**: `T[]`, `IReadOnlyList<T>`, `List<T>`, `HashSet<T>`, etc.
### Custom Converter
```csharp
public class VectorConverter : BindingConverter<Vector2>
{
public override Vector2 Convert(string? rawValue)
{
if (string.IsNullOrWhiteSpace(rawValue))
return default;
var components = rawValue.Split('x', 'X', ';');
var x = int.Parse(components[0], CultureInfo.InvariantCulture);
var y = int.Parse(components[1], CultureInfo.InvariantCulture);
return new Vector2(x, y);
}
}
[CommandParameter(0, Converter = typeof(VectorConverter))]
public required Vector2 Point { get; init; }
```
## Multiple / Nested Commands
Give each command a unique name. Common name segments create hierarchy:
```csharp
[Command] // Default (unnamed) command
public class DefaultCommand : ICommand { ... }
[Command("cmd1")] // Child of default
public class FirstCommand : ICommand { ... }
[Command("cmd1 sub")] // Child of cmd1
public class SubCommand : ICommand { ... }
```
Usage: `myapp cmd1 sub arg1 --opt value`
Default command is optional. If absent, running without a command shows root help text.
## Error Reporting
Use `CommandException` to report errors with specific exit codes:
```csharp
throw new CommandException("Division by zero is not supported.", 133);
```
Exit codes should be 1255 (8-bit unsigned) to avoid overflow on Unix.
## Graceful Cancellation
```csharp
public async ValueTask ExecuteAsync(IConsole console)
{
var cancellation = console.RegisterCancellationHandler();
await DoSomethingAsync(cancellation);
}
```
First interrupt signal triggers the token. Second interrupt force-kills.
## Dependency Injection
Use `UseTypeActivator(...)` with `Microsoft.Extensions.DependencyInjection`:
```csharp
await new CliApplicationBuilder()
.AddCommandsFromThisAssembly()
.UseTypeActivator(commandTypes =>
{
var services = new ServiceCollection();
services.AddSingleton<MyService>();
foreach (var commandType in commandTypes)
services.AddTransient(commandType);
return services.BuildServiceProvider();
})
.Build()
.RunAsync();
```
## Testing
Use `FakeInMemoryConsole` to test commands in isolation:
### Command-level test
```csharp
using var console = new FakeInMemoryConsole();
var command = new ConcatCommand { Left = "foo", Right = "bar" };
await command.ExecuteAsync(console);
var stdOut = console.ReadOutputString();
Assert.Equal("foo bar", stdOut);
```
### Application-level test
```csharp
using var console = new FakeInMemoryConsole();
var app = new CliApplicationBuilder()
.AddCommand<ConcatCommand>()
.UseConsole(console)
.Build();
var args = new[] { "--left", "foo", "--right", "bar" };
var envVars = new Dictionary<string, string>();
await app.RunAsync(args, envVars);
var stdOut = console.ReadOutputString();
Assert.Equal("foo bar", stdOut);
```
## Debug & Preview Directives
```bash
# Suspend until debugger attaches
myapp [debug] cmd -o
# Print parsed args without executing
myapp [preview] cmd arg1 -o foo
```
Disable in production:
```csharp
new CliApplicationBuilder()
.AllowDebugMode(false)
.AllowPreviewMode(false)
.Build();
```
## .NET Framework 4.8 Notes
- CliFx targets .NET Standard 2.0+, fully compatible with net48
- `required` keyword requires C# 11+ — on net48 (LangVersion 9.0), use properties with default values and validate in `ExecuteAsync` instead
- `init` setters require C# 9+ (supported on net48 with LangVersion 9.0)
- `async Task<int> Main` requires `System.Threading.Tasks` using directive on net48