using libplctag; namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip; /// /// Default libplctag-backed . Wraps a /// instance + translates our enum into the /// GetInt32 / GetFloat32 / GetString / GetBit calls libplctag /// exposes. One runtime instance per (device, tag path); lifetime is owned by the /// driver's per-device state dict. /// internal sealed class LibplctagTagRuntime : IAbCipTagRuntime { private readonly Tag _tag; public LibplctagTagRuntime(AbCipTagCreateParams p) { _tag = new Tag { Gateway = p.Gateway, Path = p.CipPath, PlcType = MapPlcType(p.LibplctagPlcAttribute), Protocol = Protocol.ab_eip, Name = p.TagName, Timeout = p.Timeout, }; } public Task InitializeAsync(CancellationToken cancellationToken) => _tag.InitializeAsync(cancellationToken); public Task ReadAsync(CancellationToken cancellationToken) => _tag.ReadAsync(cancellationToken); public Task WriteAsync(CancellationToken cancellationToken) => _tag.WriteAsync(cancellationToken); public int GetStatus() => (int)_tag.GetStatus(); public object? DecodeValue(AbCipDataType type, int? bitIndex) => type switch { AbCipDataType.Bool => bitIndex is int bit ? _tag.GetBit(bit) : _tag.GetInt8(0) != 0, AbCipDataType.SInt => (int)(sbyte)_tag.GetInt8(0), AbCipDataType.USInt => (int)_tag.GetUInt8(0), AbCipDataType.Int => (int)_tag.GetInt16(0), AbCipDataType.UInt => (int)_tag.GetUInt16(0), AbCipDataType.DInt => _tag.GetInt32(0), AbCipDataType.UDInt => (int)_tag.GetUInt32(0), AbCipDataType.LInt => _tag.GetInt64(0), AbCipDataType.ULInt => (long)_tag.GetUInt64(0), AbCipDataType.Real => _tag.GetFloat32(0), AbCipDataType.LReal => _tag.GetFloat64(0), AbCipDataType.String => _tag.GetString(0), AbCipDataType.Dt => _tag.GetInt32(0), // seconds-since-epoch DINT; consumer widens as needed AbCipDataType.Structure => null, // UDT whole-tag decode lands in PR 6 _ => null, }; public void EncodeValue(AbCipDataType type, int? bitIndex, object? value) { switch (type) { case AbCipDataType.Bool: if (bitIndex is int) { // BOOL-within-DINT writes are routed at the driver level (AbCipDriver. // WriteBitInDIntAsync) via a parallel parent-DINT runtime so the RMW stays // serialised. If one reaches here it means the driver dispatch was bypassed — // throw so the error surfaces loudly rather than clobbering the whole DINT. throw new NotSupportedException( "BOOL-with-bitIndex writes must go through AbCipDriver.WriteBitInDIntAsync, not LibplctagTagRuntime."); } _tag.SetInt8(0, Convert.ToBoolean(value) ? (sbyte)1 : (sbyte)0); break; case AbCipDataType.SInt: _tag.SetInt8(0, Convert.ToSByte(value)); break; case AbCipDataType.USInt: _tag.SetUInt8(0, Convert.ToByte(value)); break; case AbCipDataType.Int: _tag.SetInt16(0, Convert.ToInt16(value)); break; case AbCipDataType.UInt: _tag.SetUInt16(0, Convert.ToUInt16(value)); break; case AbCipDataType.DInt: _tag.SetInt32(0, Convert.ToInt32(value)); break; case AbCipDataType.UDInt: _tag.SetUInt32(0, Convert.ToUInt32(value)); break; case AbCipDataType.LInt: _tag.SetInt64(0, Convert.ToInt64(value)); break; case AbCipDataType.ULInt: _tag.SetUInt64(0, Convert.ToUInt64(value)); break; case AbCipDataType.Real: _tag.SetFloat32(0, Convert.ToSingle(value)); break; case AbCipDataType.LReal: _tag.SetFloat64(0, Convert.ToDouble(value)); break; case AbCipDataType.String: _tag.SetString(0, Convert.ToString(value) ?? string.Empty); break; case AbCipDataType.Dt: _tag.SetInt32(0, Convert.ToInt32(value)); break; case AbCipDataType.Structure: throw new NotSupportedException("Whole-UDT writes land in PR 6."); default: throw new NotSupportedException($"AbCipDataType {type} not writable."); } } public void Dispose() => _tag.Dispose(); private static PlcType MapPlcType(string attribute) => attribute switch { "controllogix" => PlcType.ControlLogix, "compactlogix" => PlcType.ControlLogix, // libplctag treats CompactLogix under ControlLogix family "micro800" => PlcType.Micro800, "micrologix" => PlcType.MicroLogix, "slc500" => PlcType.Slc500, "plc5" => PlcType.Plc5, "omron-njnx" => PlcType.Omron, _ => PlcType.ControlLogix, }; } /// /// Default — creates a fresh /// per call. Stateless; safe to share across devices. /// internal sealed class LibplctagTagFactory : IAbCipTagFactory { public IAbCipTagRuntime Create(AbCipTagCreateParams createParams) => new LibplctagTagRuntime(createParams); }