apache / apache/plc4x

[Feature Request]: Revive the .NET port (plc4net) — net8.0, SPI3 alignment, driver runtime

Open
#2,655 1 comment 0 reactions 0 assignees View on GitHub
working on it
Dominant language
Java
Stars
1.7k
Forks
504
Avg merge
10h 55m
Merged PRs (30d)
43

Description

### What would you like to happen?

I would like to revive `plc4net`, the .NET implementation, which the README currently lists as *"not ready for usage - abandoned"*.

This follows up on my introduction of the openIndu community on `dev@plc4x.apache.org` (2026-07-24) and Christofer's reply that the .NET port never really took off after its original contributor became inactive. I am opening this as a tracking issue for the effort; a PR with the first batch of work follows.

### Why it needs reviving rather than restarting

I built and ran the existing code before touching it. The port was not merely incomplete — most of it could not work, and none of it was being exercised:

| Problem | Consequence |
|---|---|
| Every `.csproj` targeted `net452` | Could not build on Linux or macOS runners, which is why plc4net was never in the CI matrix |
| Test projects had no `Microsoft.NET.Test.Sdk` | Test suites compiled but **never executed**, so every defect below went unnoticed |
| Value classes used `new` instead of `override` | `((IPlcValue) new PlcDINT(42)).GetInt()` returned `0`. The whole value model was dead through its own interface |
| `ReadBuffer`/`WriteBuffer` sat on `Ayx.BitIO` | A net45-only package, unmaintained since 2016, that routes reads through a string of `'0'`/`'1'` chars and throws on a 32-bit field — plc4net could not read a float at all |
| `ReadUlong`/`ReadLong` consumed `2*bitLength-32` bits | Wrong bit counts on every integer read |
| The 16-bit `WriteFloat` branch was an empty block | KNX DPT 9.x values silently serialized as nothing |
| `ParseException` did not derive from `Exception` | Could not be caught |
| No driver runtime at all | No transport layer, no codec, no connection lifecycle, no driver registration |

The good news is that the **C# code generator still works**. Regenerating the KNX driver from `knxnetip.mspec` produces output byte-for-byte identical to the checked-in `.cs`, so the protocol descriptions are still a viable source for .NET.

### What I have done so far

Six commits, on top of `develop` at `a9c5517`:

| Commit | Change |
|---|---|
| `fade923` | Retarget `net452` → `net8.0`, hoist shared properties into `Directory.Build.props`, add `Microsoft.NET.Test.Sdk` |
| `d3b4632` | Make `IPlcValue` dispatch reach the concrete value types; add the accessors that were missing entirely |
| `f95f216` | Replace `Ayx.BitIO` with an in-house MSB-first `BitReader`/`BitWriter`; repair `ReadBuffer`/`WriteBuffer`; fix `ParseException` |
| `76cfe5b` | Add `.github/workflows/dotnet-platform.yml`, mirroring `python-platform.yml` (ubuntu/macos/windows) |
| `7285a3f` | Align the API with SPI3: `PlcField` → `PlcTag`, synchronous `Connect`, and a `ConnectionString` parser matching `DriverBase.URI_PATTERN` |
| `7486c4d` | Implement the driver runtime: `DriverBase`, `ConnectionBase`, `MessageCodecBase`, the transport abstraction, and a TCP transport |

`dotnet build` is clean with zero warnings and `dotnet test` reports 77 passing.

### Roadmap

1. **Code generation** — make the generator emit message parse/serialize for C# (see the open question below). This is the gate for everything else.
2. **Modbus TCP driver** — needs request/response correlation, request builders, and tag-address parsing.
3. **S7 driver** — additionally needs a COTP transport.
4. Packaging and user documentation.

### Open question I would like guidance on

This is the one decision I do not want to make unilaterally, because it determines whether the work is throwaway.

`code-generation/language/cs` currently registers only `model-template.cs.ftlh` in `CsLanguageOutput.getComplexTypeTemplates()`. There **is** an `io-template.cs.ftlh` in the same directory, but it is not registered and its body is still verbatim Java (`implements MessageInput<>`, `@Override`, `throws ParseException`, `private static final Logger`). The practical effect is that generating a protocol for C# yields data classes with no parse/serialize, so a driver's message codec has to be hand-written.

Meanwhile the Java generator has moved to the code-based generators under `org.apache.plc4x.codegeneration.language.java`, while go/c/python remain on the freemarker path.

**Should C# stay on freemarker and get a proper `io-template.cs.ftlh`, or should it follow Java to a code-based generator?**

I am willing to do either, but I would rather hear the project's intent for C# before investing in one of them. If freemarker is acceptable as an interim step, I would start with a minimal KNXnet/IP round-trip as proof, since that also serves as the specification for a code-based generator later.

### Related

- `dev@plc4x.apache.org` thread, 2026-07-24: "Introducing openIndu — offering S7/Modbus contributions to plc4net"
- PR: #2656

### Programming Languages

- [x] plc4net

### Protocols

- [x] Modbus
- [x] S7
- [x] KNXnet/IP

Contributor guide

Open the contributing guide

Research direction

Start with code-generation/language/cs/CsLanguageOutput and io-template.cs.ftlh, then compare the Java code-based generator described in the issue. Run dotnet test to confirm the 77 existing tests remain green; done requires an agreed generator direction and a minimal KNXnet/IP round-trip proof, as outlined in the roadmap.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, java
Domain
backend, devtools, networking
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.