You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by "TomNewChao (via GitHub)" <gi...@apache.org> on 2026/07/26 09:25:37 UTC
[PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] 【WIP】feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "sruehl (via GitHub)" <gi...@apache.org>.
sruehl commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5338957127
@TomNewChao are you still working on this?
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "chrisdutz (via GitHub)" <gi...@apache.org>.
chrisdutz commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5101319099
Well we shouldn't postpone the release too long. If something usable is done soon, sure. Otherwise the next release could be done any time.
Is Abrisskalender a quite streamlined process (as sing as it's part of the monorepo.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "sruehl (via GitHub)" <gi...@apache.org>.
sruehl commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5104067448
the target ist not really a issue, it can stay on develop and it just stay open...
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#discussion_r3655399932
##########
plc4net/api/api/ConnectionString.cs:
##########
@@ -0,0 +1,184 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using org.apache.plc4net.exceptions;
+
+namespace org.apache.plc4net.api
+{
+ /// <summary>
+ /// A parsed PLC4X connection string:
+ /// <c>{protocol-code}(:{transport-code})?://{transport-config}(?{parameter-string})?</c>
+ /// </summary>
+ /// <remarks>
+ /// The grammar is kept identical to the Java SPI3 <c>DriverBase.URI_PATTERN</c> so the
+ /// same connection string addresses the same device from either language. Examples:
+ /// <c>s7://192.168.0.1</c>, <c>s7:cotp://10.0.0.5:102?remote-rack=0&remote-slot=1</c>,
+ /// <c>modbus-tcp://10.0.0.9:502?unit-identifier=1</c>.
+ /// </remarks>
+ public sealed class ConnectionString
+ {
+ private static readonly Regex UriPattern = new Regex(
+ @"^(?<protocolCode>[a-z0-9\-]*)(:(?<transportCode>[a-z0-9\-]*))?://(?<transportConfig>[^?]*)(\?(?<paramString>.*))?$",
+ RegexOptions.Compiled);
+
+ // Masks the value of any query parameter whose name looks like it carries a
+ // credential, so connection strings can be logged safely.
+ private static readonly Regex SecretParamPattern = new Regex(
+ @"([?&][^=&]*(?:password|passwd|secret|token)[^=&]*=)[^&]*",
+ RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+ private ConnectionString(
+ string protocolCode,
+ string transportCode,
+ string transportConfig,
+ string paramString,
+ IReadOnlyDictionary<string, string> parameters)
+ {
+ ProtocolCode = protocolCode;
+ TransportCode = transportCode;
+ TransportConfig = transportConfig;
+ ParamString = paramString;
+ Parameters = parameters;
+ }
+
+ /// <summary>e.g. "s7", "modbus-tcp".</summary>
+ public string ProtocolCode { get; }
+
+ /// <summary>e.g. "tcp", "cotp". Null when the string relies on the driver's default.</summary>
+ public string TransportCode { get; }
+
+ /// <summary>The address the transport consumes, e.g. "192.168.0.1:102".</summary>
+ public string TransportConfig { get; }
+
+ /// <summary>The raw query string, without the leading '?'. Empty when absent.</summary>
+ public string ParamString { get; }
+
+ /// <summary>Query parameters, parsed. Keys are case-insensitive.</summary>
+ public IReadOnlyDictionary<string, string> Parameters { get; }
+
+ public static ConnectionString Parse(string connectionString)
+ {
+ if (string.IsNullOrWhiteSpace(connectionString))
+ {
+ throw new PlcConnectionException("Connection string must not be empty.");
+ }
+
+ var match = UriPattern.Match(connectionString);
+ if (!match.Success)
+ {
+ throw new PlcConnectionException(
+ "Connection string doesn't match the format " +
+ "'{protocol-code}(:{transport-code})?://{transport-config}(?{parameter-string})?'");
+ }
+
+ var protocolCode = match.Groups["protocolCode"].Value;
+ if (string.IsNullOrEmpty(protocolCode))
+ {
+ throw new PlcConnectionException("Connection string is missing the protocol code.");
+ }
+
+ var transportGroup = match.Groups["transportCode"];
+ var transportCode = transportGroup.Success && transportGroup.Value.Length > 0
+ ? transportGroup.Value
+ : null;
+
+ var paramString = match.Groups["paramString"].Success
+ ? match.Groups["paramString"].Value
+ : string.Empty;
+
+ return new ConnectionString(
+ protocolCode,
+ transportCode,
+ match.Groups["transportConfig"].Value,
+ paramString,
+ ParseParameters(paramString));
+ }
+
+ private static IReadOnlyDictionary<string, string> ParseParameters(string paramString)
+ {
+ var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ if (string.IsNullOrEmpty(paramString))
+ {
+ return result;
+ }
+
+ foreach (var pair in paramString.Split('&'))
+ {
+ if (pair.Length == 0)
+ {
+ continue;
+ }
+ var separator = pair.IndexOf('=');
+ if (separator < 0)
+ {
+ // A bare flag, e.g. "?verbose" — treat as present-and-true.
+ result[Uri.UnescapeDataString(pair)] = "true";
+ continue;
+ }
+ var key = Uri.UnescapeDataString(pair.Substring(0, separator));
+ var value = Uri.UnescapeDataString(pair.Substring(separator + 1));
+ result[key] = value;
+ }
Review Comment:
Parameter decoding uses Uri.UnescapeDataString, which does not translate '+' into spaces. Java’s SPI3 configuration parsing URL-decodes parameter values (URLDecoder.decode(..., UTF_8)), so a connection string containing application/x-www-form-urlencoded style spaces (e.g. "name=a+b") will be interpreted differently in plc4net vs plc4j.
##########
plc4net/transports/tcp/TcpTransportInstance.cs:
##########
@@ -0,0 +1,336 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+using org.apache.plc4net.spi.transports;
+
+namespace org.apache.plc4net.transports.tcp
+{
+ /// <summary>
+ /// TCP transport: one background read loop per connection feeding a ring buffer.
+ /// </summary>
+ /// <remarks>
+ /// The Java SPI3 transport uses a virtual thread per connection blocked in a socket
+ /// read. .NET has no virtual threads, but async I/O gives the same result: the read
+ /// loop awaits <c>ReceiveAsync</c> and holds no thread while idle. The observable
+ /// contract is the same — the loop fills the ring buffer and then invokes the data
+ /// listener.
+ /// </remarks>
+ public class TcpTransportInstance : BaseTransportInstance, IAsyncTransportInstance
+ {
+ private static readonly byte[] EmptyBytes = new byte[0];
+
+ private readonly Socket _socket;
+ private readonly RingBuffer _ringBuffer;
+ private readonly object _readLock = new object();
+ private readonly object _writeLock = new object();
+ private readonly CancellationTokenSource _shutdown = new CancellationTokenSource();
+ private readonly Task _readLoop;
+
+ private int _open = 1;
+ private volatile Action _dataListener;
+ private volatile Action<Exception> _disconnectListener;
+
+ public TcpTransportInstance(IPEndPoint remoteAddress, TcpTransportConfiguration configuration)
+ : base(configuration)
+ {
+ if (remoteAddress == null)
+ {
+ throw new ArgumentNullException(nameof(remoteAddress));
+ }
+
+ _ringBuffer = new RingBuffer(configuration.ReceiveBufferSize);
+
+ Socket socket = null;
+ try
+ {
+ socket = new Socket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+
+ if (!string.IsNullOrEmpty(configuration.LocalAddress))
+ {
+ socket.Bind(new IPEndPoint(IPAddress.Parse(configuration.LocalAddress), configuration.LocalPort));
+ }
+
+ socket.NoDelay = configuration.TcpNoDelay;
+ socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, configuration.KeepAlive);
+ if (configuration.SendBufferSize > 0)
+ {
+ socket.SendBufferSize = configuration.SendBufferSize;
+ }
+ if (configuration.ReceiveBufferSize > 0)
+ {
+ socket.ReceiveBufferSize = configuration.ReceiveBufferSize;
+ }
+
+ Connect(socket, remoteAddress, configuration.ConnectTimeout);
+
+ _socket = socket;
+ RemoteAddress = remoteAddress;
+ LocalAddress = socket.LocalEndPoint as IPEndPoint;
+ }
+ catch (Exception e)
+ {
+ socket?.Dispose();
+ throw new TransportException(
+ $"Failed to connect to {remoteAddress.Address}:{remoteAddress.Port} - {e.Message}", e);
+ }
+
+ // Started last so a throw during setup cannot leak a running loop.
+ _readLoop = Task.Run(RunReadLoopAsync);
+ }
+
+ public IPEndPoint RemoteAddress { get; }
+
+ public IPEndPoint LocalAddress { get; }
+
+ /// <summary>
+ /// Connects with a bounded wait. ConnectAsync has no timeout overload, so the
+ /// timeout is applied by racing it and disposing the socket on expiry — closing
+ /// the socket is what aborts an in-flight connect.
+ /// </summary>
+ private static void Connect(Socket socket, IPEndPoint remoteAddress, int connectTimeoutMillis)
+ {
+ var connectTask = socket.ConnectAsync(remoteAddress);
+ if (!connectTask.Wait(connectTimeoutMillis))
+ {
+ throw new TimeoutException(
+ $"Connection to {remoteAddress.Address}:{remoteAddress.Port} timed out after {connectTimeoutMillis} ms.");
+ }
+ // Surface any connect error (Wait already completed, so this cannot block).
+ connectTask.GetAwaiter().GetResult();
+ }
+
+ public override bool IsOpen => Volatile.Read(ref _open) == 1 && _socket.Connected;
+
+ public override int GetNumBytesAvailable()
+ {
+ lock (_readLock)
+ {
+ return IsOpen || _ringBuffer.AvailableForReading > 0
+ ? _ringBuffer.AvailableForReading
+ : 0;
+ }
+ }
+
+ public override byte[] PeekReadableBytes(int numBytes)
+ {
+ if (numBytes <= 0)
+ {
+ return EmptyBytes;
+ }
+ lock (_readLock)
+ {
+ if (_ringBuffer.AvailableForReading < numBytes)
+ {
+ throw new TransportException(
+ $"Requested {numBytes} bytes but only {_ringBuffer.AvailableForReading} available");
+ }
+ return _ringBuffer.Peek(numBytes);
+ }
+ }
+
+ public override byte[] Read(int numBytes)
+ {
+ if (numBytes <= 0)
+ {
+ return EmptyBytes;
+ }
+ lock (_readLock)
+ {
+ if (_ringBuffer.AvailableForReading < numBytes)
+ {
+ throw new TransportException(
+ $"Requested {numBytes} bytes but only {_ringBuffer.AvailableForReading} available");
+ }
+ return _ringBuffer.Read(numBytes);
+ }
+ }
+
+ public override void Write(byte[] bytes)
+ {
+ if (bytes == null || bytes.Length == 0)
+ {
+ return;
+ }
+
+ lock (_writeLock)
+ {
+ EnsureOpen();
+ try
+ {
+ var offset = 0;
+ while (offset < bytes.Length)
+ {
+ offset += _socket.Send(bytes, offset, bytes.Length - offset, SocketFlags.None);
+ }
+ }
+ catch (ObjectDisposedException) when (Volatile.Read(ref _open) == 0)
+ {
+ // A concurrent Close() disposed the socket mid-write: normal shutdown.
+ }
+ catch (SocketException e)
+ {
+ throw new TransportException("Failed to write data", e);
+ }
+ }
+ }
+
+ public override void Close()
+ {
+ // Run the shutdown exactly once even under concurrent/repeated calls.
+ if (Interlocked.CompareExchange(ref _open, 0, 1) != 1)
+ {
+ return;
+ }
+
+ // Deliberately takes no locks: closing the socket is what unblocks the read
+ // loop. Taking _writeLock first would deadlock against a blocked writer.
+ _shutdown.Cancel();
+ try
+ {
+ _socket.Shutdown(SocketShutdown.Both);
+ }
+ catch (SocketException)
+ {
+ // Already torn down by the peer; nothing to do.
+ }
+ catch (ObjectDisposedException)
+ {
+ }
+ finally
+ {
+ _socket.Dispose();
+ // Do not wait on the read loop when Close() is called from inside it
+ // (e.g. a disconnect listener closing the connection).
+ if (_readLoop != null && !_readLoop.IsCompleted && Task.CurrentId != _readLoop.Id)
+ {
+ _readLoop.Wait(TimeSpan.FromSeconds(1));
+ }
Review Comment:
Close() attempts to avoid waiting on the read loop when called from inside it by comparing Task.CurrentId to _readLoop.Id. In async/await code, Task.CurrentId is often null for continuations, so this check can fail and Close() may synchronously wait on its own read-loop task (potential deadlock / 1s stall) when invoked from a data/disconnect listener.
##########
plc4net/api/api/ConnectionString.cs:
##########
@@ -0,0 +1,184 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using org.apache.plc4net.exceptions;
+
+namespace org.apache.plc4net.api
+{
+ /// <summary>
+ /// A parsed PLC4X connection string:
+ /// <c>{protocol-code}(:{transport-code})?://{transport-config}(?{parameter-string})?</c>
+ /// </summary>
+ /// <remarks>
+ /// The grammar is kept identical to the Java SPI3 <c>DriverBase.URI_PATTERN</c> so the
+ /// same connection string addresses the same device from either language. Examples:
+ /// <c>s7://192.168.0.1</c>, <c>s7:cotp://10.0.0.5:102?remote-rack=0&remote-slot=1</c>,
+ /// <c>modbus-tcp://10.0.0.9:502?unit-identifier=1</c>.
+ /// </remarks>
+ public sealed class ConnectionString
+ {
+ private static readonly Regex UriPattern = new Regex(
+ @"^(?<protocolCode>[a-z0-9\-]*)(:(?<transportCode>[a-z0-9\-]*))?://(?<transportConfig>[^?]*)(\?(?<paramString>.*))?$",
+ RegexOptions.Compiled);
+
+ // Masks the value of any query parameter whose name looks like it carries a
+ // credential, so connection strings can be logged safely.
+ private static readonly Regex SecretParamPattern = new Regex(
+ @"([?&][^=&]*(?:password|passwd|secret|token)[^=&]*=)[^&]*",
+ RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+ private ConnectionString(
+ string protocolCode,
+ string transportCode,
+ string transportConfig,
+ string paramString,
+ IReadOnlyDictionary<string, string> parameters)
+ {
+ ProtocolCode = protocolCode;
+ TransportCode = transportCode;
+ TransportConfig = transportConfig;
+ ParamString = paramString;
+ Parameters = parameters;
+ }
+
+ /// <summary>e.g. "s7", "modbus-tcp".</summary>
+ public string ProtocolCode { get; }
+
+ /// <summary>e.g. "tcp", "cotp". Null when the string relies on the driver's default.</summary>
+ public string TransportCode { get; }
+
+ /// <summary>The address the transport consumes, e.g. "192.168.0.1:102".</summary>
+ public string TransportConfig { get; }
+
+ /// <summary>The raw query string, without the leading '?'. Empty when absent.</summary>
+ public string ParamString { get; }
+
+ /// <summary>Query parameters, parsed. Keys are case-insensitive.</summary>
+ public IReadOnlyDictionary<string, string> Parameters { get; }
+
+ public static ConnectionString Parse(string connectionString)
+ {
+ if (string.IsNullOrWhiteSpace(connectionString))
+ {
+ throw new PlcConnectionException("Connection string must not be empty.");
+ }
+
+ var match = UriPattern.Match(connectionString);
+ if (!match.Success)
+ {
+ throw new PlcConnectionException(
+ "Connection string doesn't match the format " +
+ "'{protocol-code}(:{transport-code})?://{transport-config}(?{parameter-string})?'");
+ }
+
+ var protocolCode = match.Groups["protocolCode"].Value;
+ if (string.IsNullOrEmpty(protocolCode))
+ {
+ throw new PlcConnectionException("Connection string is missing the protocol code.");
+ }
+
+ var transportGroup = match.Groups["transportCode"];
+ var transportCode = transportGroup.Success && transportGroup.Value.Length > 0
+ ? transportGroup.Value
+ : null;
+
+ var paramString = match.Groups["paramString"].Success
+ ? match.Groups["paramString"].Value
+ : string.Empty;
+
+ return new ConnectionString(
+ protocolCode,
+ transportCode,
+ match.Groups["transportConfig"].Value,
+ paramString,
+ ParseParameters(paramString));
+ }
+
+ private static IReadOnlyDictionary<string, string> ParseParameters(string paramString)
+ {
+ var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ if (string.IsNullOrEmpty(paramString))
+ {
+ return result;
+ }
+
+ foreach (var pair in paramString.Split('&'))
+ {
+ if (pair.Length == 0)
+ {
+ continue;
+ }
+ var separator = pair.IndexOf('=');
+ if (separator < 0)
+ {
+ // A bare flag, e.g. "?verbose" — treat as present-and-true.
+ result[Uri.UnescapeDataString(pair)] = "true";
+ continue;
+ }
+ var key = Uri.UnescapeDataString(pair.Substring(0, separator));
+ var value = Uri.UnescapeDataString(pair.Substring(separator + 1));
+ result[key] = value;
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Looks up a parameter, returning <paramref name="defaultValue"/> when absent.
+ /// </summary>
+ public string GetParameter(string name, string defaultValue = null)
+ {
+ return Parameters.TryGetValue(name, out var value) ? value : defaultValue;
+ }
+
+ public int GetIntParameter(string name, int defaultValue)
+ {
+ var raw = GetParameter(name);
+ return int.TryParse(raw, out var value) ? value : defaultValue;
+ }
Review Comment:
GetIntParameter uses int.TryParse(string) which is culture-sensitive. Connection-string numeric parsing should be culture-invariant (Java uses Integer.parseInt on the decoded string), otherwise locales that use non-ASCII digits or different separators can cause unexpected fallback to defaultValue.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5103954250
Sorry, I accidentally hit the close button — reopened immediately. That was not intentional.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5091156899
Thanks for the pointers — the code generation one lands on the open question in the description, and with an option I hadn't considered.
The bit buffers were the same call in miniature: I dropped Ayx.BitIO and wrote the reader/writer rather than hunting for a closer package. That you rewrote that layer in SPI3 for the same reason is useful — I'll take SPI3 as the reference to follow rather than a source to copy line by line, and say so where .NET pushes a different shape.
On a pure .NET toolchain: agreed, and for the reason you give. Removing the Java dependency for .NET developers is worth more than finishing the freemarker templates. I had a look at the antlr4 grammar and the parser side does look straightforward — the work sits above it, in the type model.
Slack sounds like the right place for the rest — yes please, and thanks for the offer. I'm on UTC+8, so your working day runs from my afternoon into my evening; that lands inside sensible hours on both ends.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [WIP] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5541436030
> Couldn't you just delete the generated files ... they should be built as part of the built itself, right?
>
> And just be carefull to not re-commit them. ... Could add a temp gitIgnore rule.
Not quite, actually — the build doesn't regenerate the models today. They're committed, and rebuilt only under -Pupdate-generated-code, same as plc4j and the other ports. So dotnet build just compiles what's checked in — there's no generator step to lean on yet.
I'm already set on making this reviewable: the plan I gave sruehl splits it into per-driver PRs, and that's what gets each piece under the bot limits.
Generating at build instead of committing is a bigger move than the gitignore rule suggests. It turns tools/code-gen into a hard build dependency of every driver — today a generator bug only trips the isolated drift check, not the whole build — and it's the generate-on-build model you moved plc4j away from for reproducible builds. It'd also make plc4net the only port not committing its generated code.
There's a real case for it — the drift-check job and the noisy regenerated diffs on generator PRs only exist because the files are committed. I'll start the per-driver split regardless — I'd rather take up build-time generation separately once the split's landed, if it's still needed then. Given the scope and the reproducibility point, I'd like to hear more of your thinking here before deciding.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [WIP] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "sruehl (via GitHub)" <gi...@apache.org>.
sruehl commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5541817303
are those then stacked PRs?
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [WIP] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5540389827
> I wonder if this could utilize https://docs.github.com/en/pull-requests/how-tos/stacked-pull-requests to seperate the generated code from this PR. This way we had the slim base of just the changes and then a stacked pull request adding the generated code. ATM it is for example not possible to do CoPilot reviews as it just bails due to size
Thanks — the size problem is real. Copilot has refused it since it crossed 20k lines, and 651 files is a lot to put in front of a human.
What this PR is right now — an end-to-end revival spike, not a finished contribution. What I'm driving toward is S7 and Modbus working against real hardware: S7 passes on an S7-1214C now, Modbus is next, both within a week I'd expect. Until that path is proven I don't want to freeze the module boundaries, so decomposing now would be guesswork.
Once it's proven — split into develop-targeted PRs, each Copilot-sized and reviewable on its own: the SPI3 runtime, then the pure-.NET code generator, then the transports, then one per driver. The generated model classes (424 of the 651 files) travel with their driver — regenerable from mspec, not something to read line by line.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "chrisdutz (via GitHub)" <gi...@apache.org>.
chrisdutz commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5102176112
Now that I had the time to catch up with everything else after my holiday yesterday, now I had the time to thorougly read your initial message.
So the PLC4Net doesn't have to be identical to the Java version. It was allways our goal to make the libratries feel natural in their normal ecosystem. With Java we're using CompletableFutures, with Go we're using channels ... whatever is the best way to do things in the target language.
What we do try to keep, is the general usage pattern: So if connections are synchronous in Java, it would sort of be bad if it wasn't in other languages. Also if the naming of things was kept ... like the usage of "Tag" and "Query" and that you can add a Tag or a TagString, ....
Admittedly I don't quite understand what you mean with your first divergence: "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." Could you please explain that?
I think when I did the first work on PLC4Net, I chose KNX as this used the most of our mspec-functionality ... I thought if I get this driver working, the rest will most probably also work. Usually when starting on a new language I started with Modbus as this is the simplest one of them all for which a test-bench is cheaply available. KNX also had this super odd encoding for 16 bit float which isn't really a full half-precision-iec float. In the new SPI3 java version, I think I used a dedicated float implementation:
```
['PDT_KNX_FLOAT' REAL
[simple float 16 value floatEncoding='"KNXFloat"']
]
```
The implementation is here:
plc4j/spi/buffers/byte/src/main/java/org/apache/plc4x/java/spi/buffers/bytebased/encoding/EncodingKnxFloat.java
However, I built things in SPI3 so theoretically a protocol module could bring along it's own Encoding implementation by putting the class in the driver module and registering it via a org.apache.plc4x.java.spi.buffers.bytebased.encoding.Encoding property file.
As you could see I never really got very far with the .Net version as the person that claimed to want to help decided to go away. Usually my process was that I hand wrote some types from Modbus in the target language, then I copied the java templates and started adjusting them till the output matched the hand-written one. the data-io template you found was simply one copied but never touched by me as the work stopped before I had time to do that.
As you saw on the discussion list, we're currently considering splitting out Go and also creating go-native code generators. Possibly this is something you would also like to do. Initially the idea was to have one system for generating code. One system anyone would understand and maintaining would therefore be easier. Also is it one thing to understand a programming language, but it's a totally different thing to understand the tooling available in a language, the best practices and how to nicely integrate this into a smoothly running build. For java and maven we knew how to do this, for the others not so much ;-) So the current template+maven approch was simply a quick win that we knew how to do.
However, have I found out with my own closed-source work, that there are huge benefits for running this in a tool-native fashion ... then I guess only the resolution of the protocol modules would need to be implemented and the code-gen is built per language. The mspec format is documented in an Antlr4 grammar for which there should be tooling in most languages. The core mspec format is described here:
code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4
The expression syntax used in the little expression blocks inside are documented here:
code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/expression/Expression.g4
I hope I manged to answer your questions ... if not ... don't hesitate to ask ... ideally here ... if it has to be quickly and you see I'm online in Slack, just ask there.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [WIP] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5538471294
> As i just updated the prerequisite-check script to check for .Net 7 instead of 4.2.1 ... possibly your PR should also ensure that we check for the right version ... also please update the README.md in the root of the project. Here the .Net related section should not be forgotten.
Thanks for the heads-up.
This PR takes plc4net off net452 entirely — the projects now target net8.0 (LTS), which is what finally let the module build on the Linux and macOS CI runners. I've aligned both places you mentioned:
- src/main/script/prerequisiteCheck.groovy — picked up your 6879d954c and raised the checkDotnet() floor to 8.0.0; also refreshed the inline comment, which still described the old net452 / C# 11 rationale.
- Root README.md — reworked the .NET section: dropped the "abandoned" marker, changed the SDK requirement from 7.0 to 8.0, and removed the ".NET Framework 4.5.2 targeting pack" / Mono paragraph, which no longer applies now that nothing targets net452.
I've also merged current develop and refreshed the PR description, which had fallen behind the branch.
BTW, we're verifying S7 and Modbus against an S7-1214C at the moment — should be done within a week.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [spike] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-6409443780
Closing this spike. The corresponding parts of this branch have been superseded by smaller PRs that are now merged: #2761, #2771, #2774, #2783, #2784, #2785, #2788, #2792, #2812, #2813, #2814, #2815, #2818 and #2825.
The branch feature/plc4net-revival is left unchanged, and #2655 remains the tracking issue.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#discussion_r3655768567
##########
.github/workflows/dotnet-platform.yml:
##########
@@ -0,0 +1,120 @@
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+name: ".Net Platform Compatibility"
+
+on:
+ push:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
+ pull_request:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
Review Comment:
The path filter `plc4net**` is likely not matching the intended directory tree (usually `plc4net/**`). If this glob doesn’t match as expected, CI may not run on PLC4Net-only changes.
##########
.github/workflows/dotnet-platform.yml:
##########
@@ -0,0 +1,120 @@
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+name: ".Net Platform Compatibility"
+
+on:
+ push:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
+ pull_request:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
+ workflow_dispatch:
+ inputs:
+ forceUpdates:
+ description: "Forces a snapshot update"
+ required: false
+ default: 'false'
+
+permissions:
+ contents: read
+
+env:
+ DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
+
+jobs:
+ test:
+ strategy:
+ matrix:
+ os: [ ubuntu-latest, macos-latest, windows-latest ]
+ fail-fast: false
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v7
+
+ - name: Setup .Net
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Setup Java
+ uses: actions/setup-java@v5
+ with:
+ distribution: 'adopt'
+ java-package: jdk
+ java-version: 21
Review Comment:
The `actions/setup-java` `distribution: 'adopt'` value has been deprecated/removed in typical GitHub hosted runner setups, and can cause the workflow to fail to install Java. Prefer a currently supported distribution such as `temurin` (or whichever the repo standardizes on).
##########
plc4net/plc4net.sln:
##########
@@ -1,24 +1,5 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
-
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
+
+Microsoft Visual Studio Solution File, Format Version 12.00
Review Comment:
The solution file now appears to start with an empty first line (containing only a BOM), pushing the required `Microsoft Visual Studio Solution File...` marker to line 2. Visual Studio/MSBuild tooling is sensitive to the marker being on the first line; this may make the `.sln` unreadable. Ensure the BOM (if present) is on the same line as the marker and remove any leading blank line.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5103845018
> Now that I had the time to catch up with everything else after my holiday yesterday, now I had the time to thorougly read your initial message.在昨天假期过后,我有时间重新整理了一下其他的事情,现在我有时间仔细阅读了你们发来的原始消息。
>
> So the PLC4Net doesn't have to be identical to the Java version. It was allways our goal to make the libratries feel natural in their normal ecosystem. With Java we're using CompletableFutures, with Go we're using channels ... whatever is the best way to do things in the target language.因此,PLC4Net 不必与 Java 版本完全相同。我们的目标始终是让这些库在各自的生态系统中能够自然融合。对于 Java 语言,我们使用 CompletableFutures;而对于 Go 语言,则使用通道……无论哪种方式都能达到目标语言的最佳效果。
>
> What we do try to keep, is the general usage pattern: So if connections are synchronous in Java, it would sort of be bad if it wasn't in other languages. Also if the naming of things was kept ... like the usage of "Tag" and "Query" and that you can add a Tag or a TagString, ....我们努力保留的是这些通用的使用模式:如果在 Java 中连接是同步的,那么在其他语言中这种特性就可能会变得不好用。另外,如果能够保持一些命名规范的话……比如继续使用“Tag”和“Query”这样的命名方式,并且可以添加“Tag”或“TagString”这样的元素……
>
> Admittedly I don't quite understand what you mean with your first divergence: "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." Could you please explain that?诚然,我不太理解你所说的“第一次分歧”是什么意思。你的意思是:“ConnectionString 是 API 中的公共类型,而不是 SPI 中的类型。它没有任何 SPI 依赖关系,而位于 API 中的 PlcDriverManager 则需要使用它来配置协议代码。”请能解释一下这一点吗?
>
> I think when I did the first work on PLC4Net, I chose KNX as this used the most of our mspec-functionality ... I thought if I get this driver working, the rest will most probably also work. Usually when starting on a new language I started with Modbus as this is the simplest one of them all for which a test-bench is cheaply available. KNX also had this super odd encoding for 16 bit float which isn't really a full half-precision-iec float. In the new SPI3 java version, I think I used a dedicated float implementation:我记得在第一次开发 PLC4Net 项目时,我选择了 KNX 作为协议选择,因为 KNX 利用了我们 MSpec 功能的最多部分……我认为,如果我能让这个驱动程序正常工作,那么其他协议也很有可能会顺利实现。通常,在开始使用一种新的协议时,我会从 ModBus 开始尝试,因为 ModBus 是最简单的协议之一,相关的测试平台也很便宜。而 KNX 则采用了一种非常特殊的 16 位浮点�
��码方式,实际上并不是真正的半精度 IEC 浮点编码。在新的 SPI3 Java 版本中,我认为我们使用了专门的浮点实现方式来处理这种编码问题。
>
> ```
> ['PDT_KNX_FLOAT' REAL
> [simple float 16 value floatEncoding='"KNXFloat"']
> ]
> ```
>
> The implementation is here:实施工作已经开始了: plc4j/spi/buffers/byte/src/main/java/org/apache/plc4x/java/spi/buffers/bytebased/encoding/EncodingKnxFloat.java
>
> However, I built things in SPI3 so theoretically a protocol module could bring along it's own Encoding implementation by putting the class in the driver module and registering it via a org.apache.plc4x.java.spi.buffers.bytebased.encoding.Encoding property file.不过,我使用的是 SPI3 框架来构建应用程序。因此,理论上,一个协议模块可以通过在驱动模块中引入自己的编码实现,并通过 org.apache.plc4x.java.spi.buffers.bytebased.encoding.Encoding 属性文件来注册该编码实现。
>
> As you could see I never really got very far with the .Net version as the person that claimed to want to help decided to go away. Usually my process was that I hand wrote some types from Modbus in the target language, then I copied the java templates and started adjusting them till the output matched the hand-written one. the data-io template you found was simply one copied but never touched by me as the work stopped before I had time to do that.如您所见,那个.NET 版本的工作我并没有取得太大进展。那个声称愿意帮忙的人后来就离开了。通常我的工作流程是:先用目标语言手动编写一些 Modbus 协议的数据类型,然后复制 Java 模板,并不断调整它们,直到输出结果与手动编写的结果一致。您看到的那个数据输入输出模板就是直接复制过来的,但我并没有对其进行任何修改,因为在我完成之前,工作就停止了。
>
> As you saw on the discussion list, we're currently considering splitting out Go and also creating go-native code generators. Possibly this is something you would also like to do. Initially the idea was to have one system for generating code. One system anyone would understand and maintaining would therefore be easier. Also is it one thing to understand a programming language, but it's a totally different thing to understand the tooling available in a language, the best practices and how to nicely integrate this into a smoothly running build. For java and maven we knew how to do this, for the others not so much ;-) So the current template+maven approch was simply a quick win that we knew how to do.正如你在讨论帖中看到的,我们目前正在考虑将 Go 语言分离出来,同时开发专门针对 Go 语言的代码生成工具。也许这也是你希望做的事情。最初的想法是建立一个统一的代码生成系统,这样大家就能更容易理解和维护
这个系统。不过,理解一种编程语言是一回事,而了解该语言提供的工具、最佳实践以及如何将它们很好地整合到流畅运行的构建过程中则是另一回事。对于 Java 和 Maven 来说,我们懂得如何做到这一点,但对于其他语言来说则不太容易哦;-)所以,目前采用模板+Maven 的方法只是我们能够成功实现的一个例子而已。
>
> However, have I found out with my own closed-source work, that there are huge benefits for running this in a tool-native fashion ... then I guess only the resolution of the protocol modules would need to be implemented and the code-gen is built per language. The mspec format is documented in an Antlr4 grammar for which there should be tooling in most languages. The core mspec format is described here:不过,通过我自己使用开源框架进行开发的经验来看,以工具原生的方式来实现这一功能确实有着巨大的优势……我认为,只需要实现协议模块的相关功能,然后针对每种语言分别生成相应的代码即可。mspec 格式有相应的文档说明,它遵循 Antlr4 语法规范,因此大多数语言都应该有相应的工具来支持这种格式。关于 mspec 核心格式的描述可以在这里找到: code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4代码
生成/协议基础 MSpec 源码目录结构如下: src/main/antlr4/ org/apache/plc4x/plugins/代码生成器/ language/MSpec/MSpec.g4 The expression syntax used in the little expression blocks inside are documented here:这些小表达式块中使用的表达式语法在这里有详细说明: code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/expression/Expression.g4代码生成/协议基础 MSpec 源码目录结构如下: src/main/antlr4/ org/apache/plc4x/plugins/代码生成器 -> language/MSpec -> expression/Expression.g4
>
> I hope I manged to answer your questions ... if not ... don't hesitate to ask ... ideally here ... if it has to be quickly and you see I'm online in Slack, just ask there.希望我已经回答了你的问题……如果没有回答到的话,请随时提问。如果情况需要快速处理,而且你看到我正在 Slack 上在线,那就直接在那里提问吧。
Thanks — knowing the goal is to make the libraries feel natural in their normal
ecosystem while keeping the usage pattern and the naming settles several things I
was unsure about. Let me answer your question first, then lay out where I would
like to take this.
On the ConnectionString divergence — it does not hold up
The three states side by side:
(*) = the full connection-string parser
protocol code + transport code + host + port + query params + URL decoding
(1) and (2) are the same shape. I broke that in (3).
What happened: I read "Uri cannot parse the whole s7:cotp://host form" — which
is true, Host comes back empty and Port -1 — as "Uri cannot be used here"
the manager only ever needs the protocol code, and I had never checked what Java
actually does there. Both behave identically:
java.net.URI System.Uri
scheme / host Scheme / Host
s7://192.168.0.1 s7 / 192.168.0.1 s7 / 192.168.0.1
s7:cotp://10.0.0.5:102 s7 / null s7 / ""
modbus-tcp://10.0.0.9:502?u=1 modbus-tcp / 10.0.0.9 modbus-tcp / 10.0.0.9
opcua:tcp://localhost:4840/milo opcua / null opcua / ""
Java has exactly the same limitation on the two-scheme form and lives with it,
because DefaultPlcDriverManager only ever takes the scheme. So the thing I
treated as a blocker was never one.
Counting the consumers settles which module the type belongs in:
api/PlcDriverManager.cs:60 ConnectionString.Parse(..) 1 site <- the line I added
spi/drivers/DriverBase.cs:83 ConnectionString parameter 3 sites <- genuine; these
spi/drivers/DriverBase.cs:101 ConnectionString.Parse(..) need transport code,
spi/drivers/DriverBase.cs:130 ResolveTransportCode(..) host, port, params
Revert that one line and the type has no consumers left in the api module. So it
moves down next to DriverBase and the manager goes back to Uri.Scheme. I will
fix that.
On the general approach
My intent throughout has been to follow how the Go and Java modules are u
the project keeps one consistent shape. Your point about the usage pattern and
the naming is a fair hit — IPlcReadRequestBuilder still exposes
AddItem(name, fieldQuery), which is both the old "field" wording and missing
the Tag/TagString pair. I will align it with addTag/addTagAddress. Query I
would rather leave until there is browse support behind it — plc4net currently
has no PlcBrowser and no browse request at all, so adding the type on its own
would be the name without the capability.
On KNX
I did not choose it, I inherited it — plc4net/drivers/knxnetip/src already
carries the generated model, so finishing that capability looked like the
first step. My own roadmap is different: Modbus, then S7, then OPC UA, because
what I actually need is to reach PLCs from several vendors and feed them into an
IoT platform. Your note that you normally start with Modbus matches where
heading anyway.
On tool-native code generation
I agree with building it per language. Each language has its own idioms, tooling
and best practices, and a tool-native generator fits that far better than one
shared toolchain. Thanks for the two grammar pointers — I have looked at them and
they seem very tractable from .NET, so the part left to work out is the
resolution of the protocol modules you mentioned.
Where I would like to go next
1. Get the CLA filed.
2. Start with Modbus and prove the path end to end.
3. Extend outwards to the protocols the PLCs I work with actually speak.
One request
This PR is really me probing for direction rather than proposing something
finished, and properly absorbing this project is going to take me a while
you consider creating a feature/plc4net branch I could target instead of
develop? contributing.adoc already describes feature branches with that
prefix, and it would let this land in reviewable increments without any o
touching the 1.0.0 release — which I think also answers @sruehl's question above.
Happy to work that way if it suits you.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [WIP] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5542005851
> are those then stacked PRs?
Not the GitHub stacked-PR mechanism, no — each targets develop directly, opened one after the previous merges. They're dependency-ordered either way (each needs the last one's code to compile), so a formal stack wouldn't buy much here; it mainly helps when multiple people need to review different layers in parallel, which isn't the situation with one contributor. Happy to switch to literal stacking if you'd rather review them without waiting on each merge.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "chrisdutz (via GitHub)" <gi...@apache.org>.
chrisdutz commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5089292156
Wow ... quite a bit happened here during my few days off :-)
So one thing I wanted to point out: Even if in Java several external dependencies existed for various things (such as BitBuffers etc), I still rewrote everything from scratch in SPI3. One the one side, this way I could build a perfect fit for PLC4X, but also I thought this would be a perfect template for porting to other languages as there is no need to fins a "suitable but mostly not 100% replacement".
Another thing I noticed: We currently rely on the plc4x build-tools maven plugin to generate code. In my commercial offering I decided to give something else a try: I built an mspec parser and code-generator that fits the target language (in that case Rust) ... possibly it might be worth investigating the options to use a pure dotnet code generation tool ... the antlr4 grammar for mspec should help a lot with that. In the end a pure dotnet toolchain would eliminate the dependency on Java for dotnet developers while still being able to call the build from the overall maven reactor.
I think the API diverged quite a bit since it was created several years ago, but i think I saw you already picked up some of these changes.
If you need any help ...don't hesitate pinging me. I can also invite you in the plc4x slack channel, if you want more instant feedback (during sensible EU times ;-) )
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "chrisdutz (via GitHub)" <gi...@apache.org>.
chrisdutz commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5101932622
Also ... as this is considered a significant contribution .... before we can merge this you would need to file an ICLA with apache: https://www.apache.org/licenses/icla.pdf ... if you are doing this work as part of your day-job you should also consider your company signing a CCLA https://www.apache.org/licenses/cla-corporate.pdf
Possibly worth doing that now so it's not going to delay things once your work is ready to merge.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [WIP] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "chrisdutz (via GitHub)" <gi...@apache.org>.
chrisdutz commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5540530682
Couldn't you just delete the generated files ... they should be built as part of the built itself, right?
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [WIP] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5652691189
Quick update: physical Modbus RTU verification is currently blocked by the RS-485 hardware setup.
An independent raw-serial test sends a valid request, but receives no response either, so there is currently no evidence that this is a plc4net driver issue. I still need a known-good USB-to-RS485 adapter or an oscilloscope to isolate the converter from the CM1241.
My proposal is to keep this limitation documented and proceed with the smaller, dependency-ordered PRs. Physical RTU verification can follow separately once the hardware link is confirmed.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "chrisdutz (via GitHub)" <gi...@apache.org>.
chrisdutz commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5091316641
As it's challenging to get github-user-to-email-addresses ... please send me the address I should send the invite to cdutz@apache.org
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#discussion_r3658848118
##########
plc4net/api/PlcDriverManager.cs:
##########
@@ -1,104 +1,84 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
using System;
using System.Collections.Generic;
-using System.Threading.Tasks;
using org.apache.plc4net.api;
using org.apache.plc4net.api.authentication;
using org.apache.plc4net.exceptions;
namespace org.apache.plc4net
{
/// <summary>
- /// Manages connections to PLCs
+ /// Registry of PLC protocol drivers. A driver registers itself here;
+ /// connection requests are dispatched to the driver whose protocol code
+ /// matches the connection string scheme.
/// </summary>
public class PlcDriverManager
{
- /// <summary>
- /// Singleton instance of the manager
- /// </summary>
private static PlcDriverManager _instance;
Review Comment:
The singleton initialization is not thread-safe: concurrent calls to `Instance` can construct multiple `PlcDriverManager` instances and lose registrations. Use a thread-safe singleton pattern (e.g., `static readonly PlcDriverManager Instance = new PlcDriverManager();` or `Lazy<PlcDriverManager>`) to guarantee single instantiation.
##########
.github/workflows/dotnet-platform.yml:
##########
@@ -0,0 +1,120 @@
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+name: ".Net Platform Compatibility"
+
+on:
+ push:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
+ pull_request:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
+ workflow_dispatch:
+ inputs:
+ forceUpdates:
+ description: "Forces a snapshot update"
+ required: false
+ default: 'false'
+
+permissions:
+ contents: read
+
+env:
+ DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
+
+jobs:
+ test:
+ strategy:
+ matrix:
+ os: [ ubuntu-latest, macos-latest, windows-latest ]
+ fail-fast: false
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v7
+
+ - name: Setup .Net
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Setup Java
+ uses: actions/setup-java@v5
+ with:
+ distribution: 'adopt'
+ java-package: jdk
+ java-version: 21
Review Comment:
The `setup-java` distribution value `adopt` has been deprecated in favor of maintained distributions (commonly `temurin`). To reduce CI fragility, switch to a currently supported distribution name that `actions/setup-java` documents.
##########
plc4net/api/PlcDriverManager.cs:
##########
@@ -1,104 +1,84 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
using System;
using System.Collections.Generic;
-using System.Threading.Tasks;
using org.apache.plc4net.api;
using org.apache.plc4net.api.authentication;
using org.apache.plc4net.exceptions;
namespace org.apache.plc4net
{
/// <summary>
- /// Manages connections to PLCs
+ /// Registry of PLC protocol drivers. A driver registers itself here;
+ /// connection requests are dispatched to the driver whose protocol code
+ /// matches the connection string scheme.
/// </summary>
public class PlcDriverManager
{
- /// <summary>
- /// Singleton instance of the manager
- /// </summary>
private static PlcDriverManager _instance;
- /// <summary>
- /// Get the singleton instance
- /// </summary>
- public static PlcDriverManager Instance => _instance ?? (_instance = new PlcDriverManager());
-
- /// <summary>
- /// Dictionary for the drivers
- /// </summary>
- private readonly Dictionary<string, IPlcDriver> _drivers;
+ // Protocol codes are matched case-insensitively, the same way
+ // DefaultTransportManager matches transport codes.
+ private readonly Dictionary<string, IPlcDriver> _drivers
+ = new Dictionary<string, IPlcDriver>(StringComparer.OrdinalIgnoreCase);
- /// <summary>
- /// Private constructor for the singleton driver manager.
- /// </summary>
private PlcDriverManager()
{
- _drivers = new Dictionary<string, IPlcDriver>();
-
- /*
- * TODO: Implement some mechanism to provide drivers -> MEF?
- */
}
- /// <summary>
- /// Get the connection to the a PLC identified by the URL
- /// </summary>
- /// <param name="url">URL including the schema to connect to the PLC</param>
- /// <param name="authentication">Authentication to use</param>
- /// <returns>Created PLC connection</returns>
- public async Task<IPlcConnection> GetConnection(string url, IPlcAuthentication authentication)
+ public static PlcDriverManager Instance => _instance ?? (_instance = new PlcDriverManager());
Review Comment:
The singleton initialization is not thread-safe: concurrent calls to `Instance` can construct multiple `PlcDriverManager` instances and lose registrations. Use a thread-safe singleton pattern (e.g., `static readonly PlcDriverManager Instance = new PlcDriverManager();` or `Lazy<PlcDriverManager>`) to guarantee single instantiation.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [WIP] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5541470394
> > Couldn't you just delete the generated files ... they should be built as part of the built itself, right?
> > And just be carefull to not re-commit them. ... Could add a temp gitIgnore rule.
>
> Not quite, actually — the build doesn't regenerate the models today. They're committed, and rebuilt only under `-Pupdate-generated-code`, same as plc4j and the other ports. So dotnet build just compiles what's checked in — there's no generator step to lean on yet.
>
> I'm already set on making this reviewable: the plan I gave sruehl splits it into per-driver PRs, and that's what gets each piece under the bot limits.
>
> Generating at build instead of committing is a bigger move than the gitignore rule suggests. It turns tools/code-gen into a hard build dependency of every driver — today a generator bug only trips the isolated drift check, not the whole build — and it's the generate-on-build model you moved plc4j away from for reproducible builds. It'd also make plc4net the only port not committing its generated code.
>
> There's a real case for it — the drift-check job and the noisy regenerated diffs on generator PRs only exist because the files are committed. I'll start the per-driver split regardless — I'd rather take up build-time generation separately once the split's landed, if it's still needed then. Given the scope and the reproducibility point, I'd like to hear more of your thinking here before deciding.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [spike] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao closed pull request #2656: [spike] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
URL: https://github.com/apache/plc4x/pull/2656
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#discussion_r3655094212
##########
plc4net/transports/tcp/TcpTransport.cs:
##########
@@ -0,0 +1,205 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Net;
+using org.apache.plc4net.spi.transports;
+
+namespace org.apache.plc4net.transports.tcp
+{
+ /// <summary>
+ /// The "tcp" transport.
+ /// </summary>
+ public class TcpTransport : ITransport
+ {
+ public string TransportCode => "tcp";
+
+ public string TransportName => "TCP/IP Socket Transport";
+
+ public ITransportConfiguration CreateConfiguration(IReadOnlyDictionary<string, string> parameters)
+ {
+ var configuration = new TcpTransportConfiguration();
+ if (parameters == null)
+ {
+ return configuration;
+ }
+
+ // Transport options are prefixed with the transport code in the connection
+ // string ("tcp.connect-timeout=..."), matching the Java side. The unprefixed
+ // form is accepted too, since drivers with a single transport commonly use it.
+ configuration.ConnectTimeout = GetInt(parameters, "connect-timeout", configuration.ConnectTimeout);
+ configuration.TcpNoDelay = GetBool(parameters, "tcp-no-delay", configuration.TcpNoDelay);
+ configuration.KeepAlive = GetBool(parameters, "keep-alive", configuration.KeepAlive);
+ configuration.SendBufferSize = GetInt(parameters, "send-buffer-size", configuration.SendBufferSize);
+ configuration.ReceiveBufferSize = GetInt(parameters, "receive-buffer-size", configuration.ReceiveBufferSize);
+ configuration.LocalAddress = GetString(parameters, "local-address", configuration.LocalAddress);
+ configuration.LocalPort = GetInt(parameters, "local-port", configuration.LocalPort);
+ configuration.DefaultPort = GetInt(parameters, "default-port", configuration.DefaultPort);
Review Comment:
TcpNoDelay option lookup will likely never find the intended prefixed key. GetString() already prefixes with "tcp." (e.g. "tcp.connect-timeout"), but CreateConfiguration passes "tcp-no-delay" which becomes "tcp.tcp-no-delay" when prefixed. This contradicts the comment and makes it hard/impossible to set via connection-string parameters.
##########
plc4net/spi/spi/transports/RingBuffer.cs:
##########
@@ -0,0 +1,136 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+using System;
+
+namespace org.apache.plc4net.spi.transports
+{
+ /// <summary>
+ /// Fixed-capacity byte ring buffer sitting between a transport's read loop and the
+ /// protocol codec that drains it.
+ /// </summary>
+ /// <remarks>
+ /// The codec needs to inspect a message header before it knows how long the message
+ /// is, so this supports <see cref="Peek"/> (look without consuming) alongside
+ /// <see cref="Read"/>. Callers are expected to hold the transport's read lock; this
+ /// type is not internally synchronised.
+ /// </remarks>
+ public class RingBuffer
+ {
+ private readonly byte[] _buffer;
+ private int _readPosition;
+ private int _writePosition;
+ private int _count;
+
+ public RingBuffer(int capacity)
+ {
+ if (capacity <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "Capacity must be positive.");
+ }
+ _buffer = new byte[capacity];
+ }
+
+ public int Capacity => _buffer.Length;
+
+ public int AvailableForReading => _count;
+
+ public int RemainingForWriting => _buffer.Length - _count;
+
+ public void Write(byte[] data)
+ {
+ if (data == null)
+ {
+ throw new ArgumentNullException(nameof(data));
+ }
+ Write(data, 0, data.Length);
+ }
+
+ public void Write(byte[] data, int offset, int length)
+ {
+ if (data == null)
+ {
+ throw new ArgumentNullException(nameof(data));
+ }
+ if (offset < 0 || length < 0 || offset + length > data.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length));
+ }
+ if (length > RemainingForWriting)
+ {
+ throw new TransportException(
+ $"Ring buffer overflow: tried to write {length} bytes, {RemainingForWriting} free.");
+ }
+
+ for (var i = 0; i < length; i++)
+ {
+ _buffer[_writePosition] = data[offset + i];
+ _writePosition = (_writePosition + 1) % _buffer.Length;
+ }
+ _count += length;
+ }
Review Comment:
RingBuffer.Write copies incoming data one byte at a time and does a modulo operation per byte. This is on the transport hot path (TcpTransportInstance writes every receive into the ring buffer), so it can become a throughput bottleneck under sustained traffic.
##########
plc4net/api/PlcDriverManager.cs:
##########
@@ -1,104 +1,77 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
-using System;
using System.Collections.Generic;
-using System.Threading.Tasks;
using org.apache.plc4net.api;
using org.apache.plc4net.api.authentication;
using org.apache.plc4net.exceptions;
namespace org.apache.plc4net
{
/// <summary>
- /// Manages connections to PLCs
+ /// Registry of PLC protocol drivers. A driver registers itself here;
+ /// connection requests are dispatched to the driver whose protocol code
+ /// matches the connection string scheme.
/// </summary>
public class PlcDriverManager
{
- /// <summary>
- /// Singleton instance of the manager
- /// </summary>
private static PlcDriverManager _instance;
- /// <summary>
- /// Get the singleton instance
- /// </summary>
- public static PlcDriverManager Instance => _instance ?? (_instance = new PlcDriverManager());
-
- /// <summary>
- /// Dictionary for the drivers
- /// </summary>
- private readonly Dictionary<string, IPlcDriver> _drivers;
+ private readonly Dictionary<string, IPlcDriver> _drivers
+ = new Dictionary<string, IPlcDriver>();
- /// <summary>
- /// Private constructor for the singleton driver manager.
- /// </summary>
private PlcDriverManager()
{
- _drivers = new Dictionary<string, IPlcDriver>();
-
- /*
- * TODO: Implement some mechanism to provide drivers -> MEF?
- */
}
- /// <summary>
- /// Get the connection to the a PLC identified by the URL
- /// </summary>
- /// <param name="url">URL including the schema to connect to the PLC</param>
- /// <param name="authentication">Authentication to use</param>
- /// <returns>Created PLC connection</returns>
- public async Task<IPlcConnection> GetConnection(string url, IPlcAuthentication authentication)
+ public static PlcDriverManager Instance => _instance ?? (_instance = new PlcDriverManager());
+
+ public void RegisterDriver(IPlcDriver driver)
{
- var plcDriver = GetDriver(url);
- var connection = await plcDriver.ConnectAsync(url, authentication);
-
- //TODO: Does the driver method already connect or is a separate connect needed?
- //TODO: Should we do it like this?
- if (!connection.IsConnected)
- {
- await connection.ConnectAsync();
- }
+ _drivers[driver.ProtocolCode] = driver;
+ }
- return connection;
+ public IPlcDriver GetDriver(string connectionString)
+ {
+ var parsed = ConnectionString.Parse(connectionString);
+ return GetDriverByCode(parsed.ProtocolCode);
}
- public IPlcDriver GetDriver(string url)
+ public IPlcDriver GetDriverByCode(string protocolCode)
{
- try
+ if (_drivers.TryGetValue(protocolCode.ToLowerInvariant(), out var driver))
{
- Uri plcUri = new Uri(url);
- var proto = plcUri.Scheme;
-
- _drivers.TryGetValue(proto, out var plcDriver);
+ return driver;
Review Comment:
Driver registration and lookup use different key casing. RegisterDriver stores driver.ProtocolCode as-is, but GetDriverByCode lowercases the lookup key; this will fail for any protocol code not already lowercase (or if a driver uses mixed case).
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5091169853
> Wow ... quite a bit happened here during my few days off :-)
>
> So one thing I wanted to point out: Even if in Java several external dependencies existed for various things (such as BitBuffers etc), I still rewrote everything from scratch in SPI3. One the one side, this way I could build a perfect fit for PLC4X, but also I thought this would be a perfect template for porting to other languages as there is no need to fins a "suitable but mostly not 100% replacement".
>
> Another thing I noticed: We currently rely on the plc4x build-tools maven plugin to generate code. In my commercial offering I decided to give something else a try: I built an mspec parser and code-generator that fits the target language (in that case Rust) ... possibly it might be worth investigating the options to use a pure dotnet code generation tool ... the antlr4 grammar for mspec should help a lot with that. In the end a pure dotnet toolchain would eliminate the dependency on Java for dotnet developers while still being able to call the build from the overall maven reactor.
>
> I think the API diverged quite a bit since it was created several years ago, but i think I saw you already picked up some of these changes.
>
> If you need any help ...don't hesitate pinging me. I can also invite you in the plc4x slack channel, if you want more instant feedback (during sensible EU times ;-) )
Thanks for the pointers — the code generation one lands on the open question in the description, and with an option I hadn't considered.
The bit buffers were the same call in miniature: I dropped Ayx.BitIO and wrote the reader/writer rather than hunting for a closer package. That you rewrote that layer in SPI3 for the same reason is useful — I'll take SPI3 as the reference to follow rather than a source to copy line by line, and say so where .NET pushes a different shape.
On a pure .NET toolchain: agreed, and for the reason you give. Removing the Java dependency for .NET developers is worth more than finishing the freemarker templates. I had a look at the antlr4 grammar and the parser side does look straightforward — the work sits above it, in the type model.
Slack sounds like the right place for the rest — yes please, and thanks for the offer. I'm on UTC+8, so your working day runs from my afternoon into my evening; that lands inside sensible hours on both ends.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "chrisdutz (via GitHub)" <gi...@apache.org>.
chrisdutz commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5104035164
Hi Tom,
So you're proposing to build a generic "PLC4X Connection String Parser" component that know how to deal with all types of PLC4X connection strings? I fully agree that's probably the cleanest approach. In my ToddySoft implementation I think I also did it that way. The PLC4J SPI3 was more a migration that a rewrite. So Yeah ... I agree that possibly also for PLC4J such a central component would be a good option.
Nothing I keeping you from creating that branch and I think in the PR can't you simply select that as target? Or would someone with commit rights need to create that branch first? If that is the case, I'll be happy to create it.
Regarding naming inconsistencies: Yes ... we did some cleaning up in the API some time ago and I recall not bothering to update PLC4Net as it was considered abandoned and we generally left it in there as it contained the glue to integrate it into the overall reactor build if someone decided to want to work on this in the future (So we were waiting for you ;-) )
Chris
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "sruehl (via GitHub)" <gi...@apache.org>.
sruehl commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5100954120
@chrisdutz should that be part of 1.0.0?
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] 【WIP】feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5364589271
> @TomNewChao are you still working on this?
Yeah, sorry, I'm on vacation right now. I'll get back to work as soon as I get back.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "chrisdutz (via GitHub)" <gi...@apache.org>.
chrisdutz commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5104091803
Good point @sruehl I mean ... currently the part of the code-base nobody is using or working on plc4net ... so generally it would even be safe to work on plc4net on develop directly as long as it doesn't break the build.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao closed pull request #2656: feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
URL: https://github.com/apache/plc4x/pull/2656
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(plc4net): revive the .NET port — net8.0, SPI3 alignment, driver runtime
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5104164290
> Hi Tom,
>
> So you're proposing to build a generic "PLC4X Connection String Parser" component that know how to deal with all types of PLC4X connection strings? I fully agree that's probably the cleanest approach. In my ToddySoft implementation I think I also did it that way. The PLC4J SPI3 was more a migration that a rewrite. So Yeah ... I agree that possibly also for PLC4J such a central component would be a good option.
>
> Nothing I keeping you from creating that branch and I think in the PR can't you simply select that as target? Or would someone with commit rights need to create that branch first? If that is the case, I'll be happy to create it.
>
> Regarding naming inconsistencies: Yes ... we did some cleaning up in the API some time ago and I recall not bothering to update PLC4Net as it was considered abandoned and we generally left it in there as it contained the glue to integrate it into the overall reactor build if someone decided to want to work on this in the future (So we were waiting for you ;-) )
>
> Chris
On the ConnectionString — I was actually proposing something smaller: just moving it from the api module down to the spi module, next to DriverBase, matching how plc4j splits it. But the idea of a central component is worth discussing once the basics are in place.
On the branch @sruehl — understood, and keeping it on develop is simpler. I will keep the build green and keep the PR open as the tracking point.
Thanks
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [WIP] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "chrisdutz (via GitHub)" <gi...@apache.org>.
chrisdutz commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5537968198
As i just updated the prerequisite-check script to check for .Net 7 instead of 4.2.1 ... possibly your PR should also ensure that we check for the right version ... also please update the README.md in the root of the project. Here the .Net related section should not be forgotten.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [WIP] feat(plc4net): revive the .NET port — net8.0, SPI3 runtime, pure-.NET mspec generator
Posted by "sruehl (via GitHub)" <gi...@apache.org>.
sruehl commented on PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#issuecomment-5538930338
I wonder if this could utilize https://docs.github.com/en/pull-requests/how-tos/stacked-pull-requests to seperate the generated code from this PR. This way we had the slim base of just the changes and then a stacked pull request adding the generated code. ATM it is for example not possible to do CoPilot reviews as it just bails due to size
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [WIP] feat(plc4net): revive the .NET port
Posted by "TomNewChao (via GitHub)" <gi...@apache.org>.
TomNewChao commented on code in PR #2656:
URL: https://github.com/apache/plc4x/pull/2656#discussion_r3892345980
##########
.github/workflows/dotnet-platform.yml:
##########
@@ -0,0 +1,120 @@
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+name: ".Net Platform Compatibility"
+
+on:
+ push:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
+ pull_request:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
+ workflow_dispatch:
+ inputs:
+ forceUpdates:
+ description: "Forces a snapshot update"
+ required: false
+ default: 'false'
+
+permissions:
+ contents: read
+
+env:
+ DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
+
+jobs:
+ test:
+ strategy:
+ matrix:
+ os: [ ubuntu-latest, macos-latest, windows-latest ]
+ fail-fast: false
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v7
+
+ - name: Setup .Net
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Setup Java
+ uses: actions/setup-java@v5
+ with:
+ distribution: 'adopt'
+ java-package: jdk
+ java-version: 21
Review Comment:
The workflow no longer uses actions/setup-java — plc4net builds with the .NET SDK only (no JDK, no Maven). Fixed.
##########
.github/workflows/dotnet-platform.yml:
##########
@@ -0,0 +1,120 @@
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+name: ".Net Platform Compatibility"
+
+on:
+ push:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
+ pull_request:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
+ workflow_dispatch:
+ inputs:
+ forceUpdates:
+ description: "Forces a snapshot update"
+ required: false
+ default: 'false'
+
+permissions:
+ contents: read
+
+env:
+ DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
+
+jobs:
+ test:
+ strategy:
+ matrix:
+ os: [ ubuntu-latest, macos-latest, windows-latest ]
+ fail-fast: false
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v7
+
+ - name: Setup .Net
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Setup Java
+ uses: actions/setup-java@v5
+ with:
+ distribution: 'adopt'
+ java-package: jdk
+ java-version: 21
Review Comment:
The workflow no longer uses actions/setup-java — plc4net builds with the .NET SDK only (no JDK, no Maven). Fixed.
##########
.github/workflows/dotnet-platform.yml:
##########
@@ -0,0 +1,120 @@
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+name: ".Net Platform Compatibility"
+
+on:
+ push:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
+ pull_request:
+ branches: [ "develop", "plc4net" ]
+ paths:
+ - code-generation/**
+ - protocols/**
+ - plc4net**
Review Comment:
The path filters and the license-header glob both use plc4net/** in the current workflow. Fixed.
--
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: commits-unsubscribe@plc4x.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org