Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 47 additions & 12 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,30 @@ on:

jobs:
# ------------------------------------------------------------------
# Job 0 — dotnet format gate. Verifies the tree matches the
# repository's .editorconfig with zero diff, so formatting drift
# Job 0 — dotnet format gate. Verifies the tree matches the default
# `dotnet format` ruleset with zero diff, so formatting drift
# (indentation, brace placement, whitespace) is caught at PR time
# instead of silently accumulating into the baseline every
# subsequent branch then has to inherit. Uses the default "warn"
# severity — the same severity `dotnet format` applies with no
# --severity flag — rather than --severity info: at info severity
# dotnet format additionally tries to auto-fix long-standing Roslyn
# analyzer diagnostics (CA1859, CA1861, CA1018, ...) across the
# repo, which is a materially different (and, on this SDK/analyzer
# combination, occasionally crash-prone) undertaking from verifying
# whitespace/style formatting and is out of scope for this gate.
# subsequent branch then has to inherit. The repo does not yet ship
# a root `.editorconfig`, so the enforced ruleset is whatever the
# SDK pinned by `actions/setup-dotnet` treats as its whitespace /
# C# formatting defaults; introducing a committed `.editorconfig`
# that freezes those rules against future SDK drift is tracked as a
# follow-up (see PR description). Uses the default "warn" severity
# — the same severity `dotnet format` applies with no --severity
# flag — rather than --severity info: at info severity dotnet format
# additionally tries to auto-fix long-standing Roslyn analyzer
# diagnostics (CA1859, CA1861, CA1018, ...) across the repo, which
# is a materially different (and, on this SDK/analyzer combination,
# occasionally crash-prone) undertaking from verifying whitespace /
# style formatting and is out of scope for this gate. `--verbosity
# diagnostic` is set on the verify step so the CI log names each
# offending file and rule when the gate fires — a contributor can
# then reproduce and fix locally without scrolling for the delta.
# A second verify step covers the two csprojs that ship with the
# `dotnet new` template but are not members of `MTConnect.NET.sln`;
# the template itself is a public-surface artifact for downstream
# consumers, so it belongs under the same gate as the main tree.
# Runs on the same draft-skip gate as the other jobs and does not
# depend on them, so a formatting-only fix gets fast feedback.
# ------------------------------------------------------------------
Expand All @@ -58,8 +70,31 @@ jobs:
- name: Restore solution
run: dotnet restore MTConnect.NET.sln

- name: Verify format (dotnet format --verify-no-changes)
run: dotnet format MTConnect.NET.sln --verify-no-changes --no-restore
- name: Verify solution format (dotnet format --verify-no-changes)
run: dotnet format MTConnect.NET.sln --verify-no-changes --no-restore --verbosity diagnostic

# `MTConnect.NET.sln` does not include the two csprojs that ship
# with the `dotnet new mtconnect.net-agent` template (the
# template descriptor + the generated project's csproj under
# `content/`). They are public-surface artifacts for downstream
# template consumers, so formatting drift there is exactly what
# the gate is meant to catch. Restore + verify them explicitly
# rather than adding them to the solution (which would compile
# them on every unrelated build and complicate the template
# packaging step). Each project gets its own restore + verify
# step so a failure names the specific template in the Actions
# step-summary panel rather than in the collapsed log.
- name: Restore template descriptor
run: dotnet restore templates/mtconnect.net-agent/MTConnect-NET-Agent-Template.csproj

- name: Restore template content project
run: dotnet restore templates/mtconnect.net-agent/content/MTConnect.NET-Embedded-Agent/Agent.csproj

- name: Verify template descriptor format
run: dotnet format templates/mtconnect.net-agent/MTConnect-NET-Agent-Template.csproj --verify-no-changes --no-restore --verbosity diagnostic

- name: Verify template content project format
run: dotnet format templates/mtconnect.net-agent/content/MTConnect.NET-Embedded-Agent/Agent.csproj --verify-no-changes --no-restore --verbosity diagnostic

# ------------------------------------------------------------------
# Job 1 — unsharded unit + integration sweep on both OS legs.
Expand Down
43 changes: 43 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Contributing to MTConnect.NET

Thank you for taking the time to contribute — issues, discussions, and pull requests are all welcome. This file collects the mechanical checks a contributor is expected to run locally before pushing; the substantive discussion of what to work on lives on the [MTConnect.NET issue tracker](https://github.com/TrakHound/MTConnect.NET/issues) and on the [Documentation site](https://trakhound.github.io/MTConnect.NET/).

## Running the formatting gate locally

Every push to `master` and every non-draft pull request runs `dotnet format MTConnect.NET.sln --verify-no-changes` in CI (job `format`, defined in [`.github/workflows/dotnet.yml`](.github/workflows/dotnet.yml)). The gate rejects any whitespace / indentation / brace-placement drift, so it is worth reproducing the check before pushing:

```bash
dotnet restore MTConnect.NET.sln
dotnet format MTConnect.NET.sln # autofix in place
dotnet format MTConnect.NET.sln --verify-no-changes --verbosity diagnostic # dry-run, same as CI
```

The two `dotnet new mtconnect.net-agent` template projects live outside the solution and CI verifies them in dedicated steps; run the same commands against them if you have touched files under `templates/`:

```bash
dotnet restore templates/mtconnect.net-agent/MTConnect-NET-Agent-Template.csproj
dotnet restore templates/mtconnect.net-agent/content/MTConnect.NET-Embedded-Agent/Agent.csproj
dotnet format templates/mtconnect.net-agent/MTConnect-NET-Agent-Template.csproj --verify-no-changes --verbosity diagnostic
dotnet format templates/mtconnect.net-agent/content/MTConnect.NET-Embedded-Agent/Agent.csproj --verify-no-changes --verbosity diagnostic
```

See [`docs/testing/workflows.md`](docs/testing/workflows.md#job-0--format) for the complete gate description, the `--severity warn` rationale, and the tracked follow-ups (root `.editorconfig` + `global.json` SDK pin).

## Running the test suite locally

The default sweep runs unit and light-integration tests across the whole solution:

```bash
./tools/test.sh # Linux / macOS / Git Bash
./tools/test.ps1 # PowerShell, all platforms
```

Add `--e2e` / `-E2E` (Docker required) to also run the `Category=E2E` and `Category=RequiresDocker` workflow fixtures. `./tools/test.sh --help` (or `./tools/test.ps1 -?`) prints the full flag listing. The CI matrix and per-category filter rationale are documented on [`docs/testing/workflows.md`](docs/testing/workflows.md).

## Opening a pull request

- Keep the PR body focused on the user-facing diff — what changed, why it changed, and how a reviewer can verify it. Internal process detail belongs in the commit trailer or in follow-up comments, not in the description.
- Every push re-runs the full CI matrix on non-draft PRs. Leave a PR in draft while it is still being iterated; flip it to ready when it is ready for review.
- If your change touches the `templates/` tree, please also run the template-project format commands above — the CI gate will catch drift, but reproducing locally saves a red-CI round-trip.

Thanks again for your interest in improving MTConnect.NET.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,18 @@ This repo along with the libraries and applications are free to use and distribu

Feel free to comment, or create pull-requests for anything that could be coded, formatted, or worded better. Attention to detail and continuous improvement are important in manufacturing so they should be just as important for manufacturing software.

### Contributing

See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the local-reproduction commands the CI gate expects (formatting + tests) and the pull-request expectations. In short, before pushing:

```bash
dotnet restore MTConnect.NET.sln
dotnet format MTConnect.NET.sln --verify-no-changes --verbosity diagnostic # same as the CI `format` job
./tools/test.sh # or ./tools/test.ps1
```

The full CI gate description, the `--severity warn` rationale, and the tracked follow-ups (root `.editorconfig` + `global.json` SDK pin) are documented on [`docs/testing/workflows.md`](docs/testing/workflows.md#job-0--format).

Thanks for your interest in using these libraries and applications and feel free to contribute or give feedback.

\- Patrick
135 changes: 79 additions & 56 deletions build/MTConnect.NET-DocsGen/RouteInventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,65 +218,88 @@ private static (string? method, string? handler) SniffLambdaGate(ParenthesizedLa
// imperatively inside OnRequestReceived, so the surface is encoded
// structurally here to give the reference page meaningful columns;
// the *summary text* still flows from /// on the handler class.
//
// Each handler's parameter list is a separate `private static readonly`
// field rather than an inline nested initializer inside the
// dictionary literal. `dotnet format` cannot indent an inline
// `new EndpointParam[] { ... }` nested inside a dictionary
// collection-initializer coherently — it deepens the outer keys
// but leaves the inner block at the old depth, producing a visible
// misalignment cascade. Hoisting the arrays flattens both layers to
// the same indent depth and keeps the formatter idempotent.
private static readonly EndpointParam[] ProbeParams =
{
new("deviceType", "Query", "string", null, "Optional device-type filter."),
new("version", "Query", "string", null, "Target MTConnect Standard version of the response document."),
new("documentFormat", "Query", "string", "xml", "Response document format (xml | json | json-cppagent)."),
new("validationLevel", "Query", "int", null, "0=ignore, 1=warning, 2=remove, 3=strict."),
new("indentOutput", "Query", "bool", null, "Pretty-print the response document."),
new("outputComments", "Query", "bool", null, "Emit comments / annotations in the response document."),
};

private static readonly EndpointParam[] CurrentParams =
{
new("path", "Query", "string", null, "XPath that filters the data items included in the response."),
new("at", "Query", "ulong", null, "Sequence number anchoring the snapshot."),
new("interval", "Query", "int", null, "Streaming interval in milliseconds; switches to multipart streaming when > 0."),
new("heartbeat", "Query", "int", "10000", "Heartbeat interval for the streaming response."),
new("deviceType", "Query", "string", null, "Optional device-type filter."),
new("version", "Query", "string", null, "Target MTConnect Standard version of the response document."),
new("documentFormat", "Query", "string", "xml", "Response document format."),
new("indentOutput", "Query", "bool", null, "Pretty-print the response document."),
new("outputComments", "Query", "bool", null, "Emit comments / annotations in the response document."),
};

private static readonly EndpointParam[] SampleParams =
{
new("path", "Query", "string", null, "XPath that filters the data items included in the response."),
new("from", "Query", "ulong", null, "Sequence number lower bound."),
new("to", "Query", "ulong", null, "Sequence number upper bound."),
new("count", "Query", "int", "100", "Maximum number of observations."),
new("interval", "Query", "int", null, "Streaming interval in milliseconds; switches to multipart streaming when > 0."),
new("heartbeat", "Query", "int", "10000", "Heartbeat interval for the streaming response."),
new("deviceType", "Query", "string", null, "Optional device-type filter."),
new("version", "Query", "string", null, "Target MTConnect Standard version of the response document."),
new("documentFormat", "Query", "string", "xml", "Response document format."),
new("indentOutput", "Query", "bool", null, "Pretty-print the response document."),
new("outputComments", "Query", "bool", null, "Emit comments / annotations in the response document."),
};

private static readonly EndpointParam[] AssetsParams =
{
new("type", "Query", "string", null, "Asset type filter (e.g. CuttingTool)."),
new("removed", "Query", "bool", null, "Include removed assets when true."),
new("count", "Query", "int", null, "Maximum number of assets."),
new("documentFormat", "Query", "string", "xml", "Response document format."),
new("indentOutput", "Query", "bool", null, "Pretty-print the response document."),
};

private static readonly EndpointParam[] AssetParams =
{
new("assetId", "Route", "string", null, "Asset identifier captured from the trailing path segment."),
new("documentFormat", "Query", "string", "xml", "Response document format."),
};

private static readonly EndpointParam[] PutParams =
{
new("(form / query)", "Body", "Dictionary<string,string>", null, "DataItemId=Value entries to enqueue as observations."),
};

private static readonly EndpointParam[] PostParams =
{
new("(body)", "Body", "string", null, "Asset document payload."),
};

private static readonly IReadOnlyDictionary<string, IReadOnlyList<EndpointParam>> CeenHandlerParameters
= new Dictionary<string, IReadOnlyList<EndpointParam>>
{
["MTConnectProbeResponseHandler"] = new EndpointParam[]
{
new("deviceType", "Query", "string", null, "Optional device-type filter."),
new("version", "Query", "string", null, "Target MTConnect Standard version of the response document."),
new("documentFormat", "Query", "string", "xml", "Response document format (xml | json | json-cppagent)."),
new("validationLevel", "Query", "int", null, "0=ignore, 1=warning, 2=remove, 3=strict."),
new("indentOutput", "Query", "bool", null, "Pretty-print the response document."),
new("outputComments", "Query", "bool", null, "Emit comments / annotations in the response document."),
},
["MTConnectCurrentResponseHandler"] = new EndpointParam[]
{
new("path", "Query", "string", null, "XPath that filters the data items included in the response."),
new("at", "Query", "ulong", null, "Sequence number anchoring the snapshot."),
new("interval", "Query", "int", null, "Streaming interval in milliseconds; switches to multipart streaming when > 0."),
new("heartbeat", "Query", "int", "10000", "Heartbeat interval for the streaming response."),
new("deviceType", "Query", "string", null, "Optional device-type filter."),
new("version", "Query", "string", null, "Target MTConnect Standard version of the response document."),
new("documentFormat", "Query", "string", "xml", "Response document format."),
new("indentOutput", "Query", "bool", null, "Pretty-print the response document."),
new("outputComments", "Query", "bool", null, "Emit comments / annotations in the response document."),
},
["MTConnectSampleResponseHandler"] = new EndpointParam[]
{
new("path", "Query", "string", null, "XPath that filters the data items included in the response."),
new("from", "Query", "ulong", null, "Sequence number lower bound."),
new("to", "Query", "ulong", null, "Sequence number upper bound."),
new("count", "Query", "int", "100", "Maximum number of observations."),
new("interval", "Query", "int", null, "Streaming interval in milliseconds; switches to multipart streaming when > 0."),
new("heartbeat", "Query", "int", "10000", "Heartbeat interval for the streaming response."),
new("deviceType", "Query", "string", null, "Optional device-type filter."),
new("version", "Query", "string", null, "Target MTConnect Standard version of the response document."),
new("documentFormat", "Query", "string", "xml", "Response document format."),
new("indentOutput", "Query", "bool", null, "Pretty-print the response document."),
new("outputComments", "Query", "bool", null, "Emit comments / annotations in the response document."),
},
["MTConnectAssetsResponseHandler"] = new EndpointParam[]
{
new("type", "Query", "string", null, "Asset type filter (e.g. CuttingTool)."),
new("removed", "Query", "bool", null, "Include removed assets when true."),
new("count", "Query", "int", null, "Maximum number of assets."),
new("documentFormat", "Query", "string", "xml", "Response document format."),
new("indentOutput", "Query", "bool", null, "Pretty-print the response document."),
},
["MTConnectAssetResponseHandler"] = new EndpointParam[]
{
new("assetId", "Route", "string", null, "Asset identifier captured from the trailing path segment."),
new("documentFormat", "Query", "string", "xml", "Response document format."),
},
["MTConnectPutResponseHandler"] = new EndpointParam[]
{
new("(form / query)", "Body", "Dictionary<string,string>", null, "DataItemId=Value entries to enqueue as observations."),
},
["MTConnectPostResponseHandler"] = new EndpointParam[]
{
new("(body)", "Body", "string", null, "Asset document payload."),
},
["MTConnectProbeResponseHandler"] = ProbeParams,
["MTConnectCurrentResponseHandler"] = CurrentParams,
["MTConnectSampleResponseHandler"] = SampleParams,
["MTConnectAssetsResponseHandler"] = AssetsParams,
["MTConnectAssetResponseHandler"] = AssetParams,
["MTConnectPutResponseHandler"] = PutParams,
["MTConnectPostResponseHandler"] = PostParams,
};

// Cache of parsed /// summary text per handler-class file path, so
Expand Down
Loading
Loading