$ show libplctag/libplctag.NET
libplctag.NET is a .NET library providing two NuGet packages for industrial PLC communication via EtherNet/IP and Modbus TCP. The primary package (libplctag) offers a high-level, asynchronous, strongly-typed API built on top of the native libplctag C library, handling resource cleanup, exceptions, and event-driven operations. The second package (libplctag.NativeImport) exposes a low-level P/Invoke wrapper that manages platform-specific native binary loading and provides raw tag access. The architecture is a classic wrapper layer: the high-level Tag class encapsulates native handle lifecycle, attribute string generation, synchronous and async operations (using TaskCompletionSource with concurrent stacks to handle native callbacks), and exposes typed data accessors. The library targets a wide range of .NET frameworks (netcoreapp3.0 through net8.0, plus .NET Framework 4.7+), reflecting its industrial compatibility requirements. Some parts of the codebase (the generic Tag<M,T> and associated PlcMappers) are marked obsolete due to a planned API redesign (referenced as issue #406), indicating ongoing evolution.
The LLM identified 3 files worth highlighting for reuse:
The Tag class demonstrates a sophisticated pattern for bridging an asynchronous native API (with callbacks) into .NET async/await semantics. It uses ConcurrentStack of TaskCompletionSource instances to match native event callbacks to pending read/write/initialize operations, handles cancellation and timeouts via CancellationTokenSource, and manages native handle creation/destruction with careful dispose semantics. This pattern is reusable for any wrapper that needs to adapt a callback-based native library to .NET tasks.
The plctag class provides safe, modern C# wrappers over raw P/Invoke methods, using Span<byte> and ReadOnlySpan<byte> to expose memory-mapped buffer access without unsafe code for callers. The overloads that accept Span internally use fixed pointers to marshal to native, demonstrating a clean approach to exposing native memory operations safely to .NET consumers.
This static utility class shows a thread-safe pattern for adapting a native logging callback into a managed event. It uses a lock to lazily subscribe to the native logger and a dedicated delegate to marshal messages, ensuring that the subscription occurs only on first use and that event invocation is guarded against concurrency. The pattern is reusable for bridging any native logging subsystem to .NET events without leaking native callbacks.