TomNewChao opened a new pull request, #2656: URL: https://github.com/apache/plc4x/pull/2656
Revives `plc4net`, which the README currently lists as *"not ready for usage - abandoned"*, to the point where it builds on all three platforms, has an executing test suite, and has a driver runtime aligned with SPI3. Relates to #2655. Follows the `dev@` thread from 2026-07-24 where Christofer suggested a draft PR as the way to get started. ## This is a draft — I am asking for direction, not for a merge It is deliberately larger than a normal PR because it is the whole picture of where the port stands. I would rather show it once and be told "this shape is wrong" than land five PRs in the wrong shape. Once the direction is confirmed I am happy to split it into merge-sized pieces in whatever grouping you prefer. **The single most useful thing you can tell me is the answer to the generator question at the bottom.** ## Reviewing this The six commits are independent and each stands alone. Reviewing commit by commit is much easier than reviewing the combined diff. | Commit | Change | Size | |---|---|---| | `fade923` | Retarget `net452` → `net8.0`; hoist shared properties into `Directory.Build.props`; add `Microsoft.NET.Test.Sdk` | 61+/37− | | `d3b4632` | Make `IPlcValue` dispatch reach the concrete value types; add missing accessors | 466+/148− | | `f95f216` | Replace `Ayx.BitIO` with an in-house `BitReader`/`BitWriter`; repair `ReadBuffer`/`WriteBuffer`; fix `ParseException` | 693+/146− | | `76cfe5b` | Add `.github/workflows/dotnet-platform.yml` (ubuntu/macos/windows) | 120+ | | `7285a3f` | Align the API with SPI3: `PlcField` → `PlcTag`, synchronous `Connect`, `ConnectionString` | 322+/177− | | `7486c4d` | Implement the driver runtime and a TCP transport | 2082+ | ### Why each one is needed **`fade923` — net8.0.** `net452` builds fine on Windows but cannot build on Linux or macOS runners, which is why plc4net was never in the CI matrix. This commit also adds `Microsoft.NET.Test.Sdk` to the test projects; without it the suites compiled but **never executed**. That single omission is why everything below went unnoticed. **`d3b4632` — the value model was dead through its own interface.** Subclasses declared `public new bool GetBool()` rather than `override`. In C# `new` is compile-time name hiding and does not participate in virtual dispatch, so a caller holding an `IPlcValue` — which is how the API hands values out — landed on `PlcValueAdapter`'s defaults. `((IPlcValue) new PlcDINT(42)).GetInt()` returned `0`. `PlcBOOL`, `PlcSTRING`, `PlcCHAR`, `PlcWCHAR` and `PlcWSTRING` additionally stored a value but never exposed it at all. **`f95f216` — the bit codec.** `Ayx.BitIO` is net45-only and unmaintained since 2016; internally it round-trips reads through a `StringBuilder` of `'0'`/`'1'` characters and throws on a 32-bit field, so plc4net could not read a float. It is replaced with an MSB-first `BitReader`/`BitWriter` (~240 lines, no dependencies). Fixing the codec then exposed the layer above it: `ReadUlong`/`ReadLong` consumed `2*bitLength-32` bits, `WriteFloat`/`WriteDouble` passed arguments in the wrong order, the 16-bit float branch was an empty block, `ReadDouble(32)` always threw, and `ReadString`/`ReadByteArray`/`WriteString` were `NotImplementedException`. `ParseException` did not derive from `Exception`. The KNX DPT 9.x 16-bit format is implemented explicitly rather than as IEEE-754 half. One test documents that the format is coarse at the top of its range (step 327.68 at exponent 15) so the next person does not read it as a rounding bug. **`7285a3f` — SPI3 alignment.** `PlcField` → `PlcTag`, `ConnectAsync` → synchronous `Connect`, and a `ConnectionString` parser whose grammar is character-for-character the Java `DriverBase.URI_PATTERN`. **`7486c4d` — the driver runtime.** `DriverBase` (connection-string dispatch, transport selection, supported-transport enforcement), `ConnectionBase`, `MessageCodecBase<T>`, the transport abstraction, and a TCP transport. ## Relationship to the Java SPI3 contracts | plc4net | plc4j | |---|---| | `spi/drivers/DriverBase.cs` | `plc4j/spi/drivers/.../spi/drivers/DriverBase.java` | | `spi/drivers/MessageCodecBase.cs` | `plc4j/spi/drivers/.../spi/drivers/MessageCodecBase.java` | | `spi/transports/ITransportInstance.cs` | `plc4j/transports/api/.../spi/transports/api/TransportInstance.java` | | `api/api/ConnectionString.cs` | `DriverBase.URI_PATTERN` plus parameter parsing | ### Four places I deliberately diverged Flagging these because they are the most likely thing to be objected to, and I would rather discuss them than have them found in review. 1. **`ConnectionString` is a public type in the API, not the SPI.** It has no SPI dependency, and `PlcDriverManager` — which lives in the API — needs it to route the protocol code. 2. **Getters became C# properties** (`IsOpen`, `ProtocolCode`). .NET convention. 3. **`ITransportInstance` extends `IDisposable`.** .NET resource convention; enables `using`. 4. **The TCP read loop is `async`/`await`, not a virtual thread.** .NET has no virtual threads; the async socket path is the equivalent. If you would rather have a literal transliteration of the Java API, say so and I will change these — but the .NET ecosystem expectations argued for the above. ## What is not here - **Generated code has no parse/serialize** — see the question below. This is the reason there is still no real driver. - No request/response correlation or timeouts. - Only a TCP transport. No UDP, serial, TLS, or COTP (S7 needs COTP). - Request builders are interfaces only; no implementations yet. - No tag-address parsing framework. - KNXnet/IP is still generated models only, not a driver. ## Verification - `dotnet build plc4net/plc4net.sln` — 0 errors, 0 warnings - `dotnet test plc4net/plc4net.sln` — 77 passing (76 SPI, 1 KNX) - The C# generator was re-run against `knxnetip.mspec`; output is byte-for-byte identical to the checked-in `.cs`, so this PR does not drift from the protocol descriptions Value-model tests assert exclusively through `IPlcValue` rather than the concrete types. That is deliberate: the `new`/`override` defect was invisible to tests bound to concrete types and only visible through the interface. CI is the one thing I cannot verify from here — `dotnet-platform.yml` has never run in this repository's environment. If it fails I will fix it on the branch. ## The question I would most like answered `CsLanguageOutput.getComplexTypeTemplates()` registers only `model-template.cs.ftlh`. An `io-template.cs.ftlh` exists beside it but is not registered, and its body is still verbatim Java (`implements MessageInput<>`, `@Override`, `throws ParseException`). So generating a protocol for C# yields data classes and nothing that can parse or serialize a frame. Java has since moved to the code-based generators under `org.apache.plc4x.codegeneration.language.java`; go, c and python are still on freemarker. **Should C# stay on freemarker and get a real `io-template.cs.ftlh`, or follow Java to a code-based generator?** I will do either. If freemarker is acceptable as an interim, I would start with a minimal KNXnet/IP round-trip as proof — which doubles as the specification for a code-based generator later, so neither path wastes the other's work. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
