using libplctag; namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy; /// /// Default libplctag-backed . Uses ab_pccc protocol /// on top of EtherNet/IP — libplctag's PCCC layer handles the file-letter + word + bit + /// sub-element decoding internally, so our wrapper just has to forward the atomic type to /// the right Get/Set call. /// internal sealed class LibplctagLegacyTagRuntime : IAbLegacyTagRuntime { private readonly Tag _tag; public LibplctagLegacyTagRuntime(AbLegacyTagCreateParams p) { _tag = new Tag { Gateway = p.Gateway, Path = p.CipPath, PlcType = MapPlcType(p.LibplctagPlcAttribute), Protocol = Protocol.ab_eip, // PCCC-over-EIP; libplctag routes via the PlcType-specific PCCC layer 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(AbLegacyDataType type, int? bitIndex) => type switch { AbLegacyDataType.Bit => bitIndex is int bit ? _tag.GetBit(bit) : _tag.GetInt8(0) != 0, AbLegacyDataType.Int or AbLegacyDataType.AnalogInt => (int)_tag.GetInt16(0), AbLegacyDataType.Long => _tag.GetInt32(0), AbLegacyDataType.Float => _tag.GetFloat32(0), AbLegacyDataType.String => _tag.GetString(0), AbLegacyDataType.TimerElement or AbLegacyDataType.CounterElement or AbLegacyDataType.ControlElement => _tag.GetInt32(0), _ => null, }; public void EncodeValue(AbLegacyDataType type, int? bitIndex, object? value) { switch (type) { case AbLegacyDataType.Bit: if (bitIndex is int) throw new NotSupportedException( "Bit-within-word writes require read-modify-write; tracked in task #181."); _tag.SetInt8(0, Convert.ToBoolean(value) ? (sbyte)1 : (sbyte)0); break; case AbLegacyDataType.Int: case AbLegacyDataType.AnalogInt: _tag.SetInt16(0, Convert.ToInt16(value)); break; case AbLegacyDataType.Long: _tag.SetInt32(0, Convert.ToInt32(value)); break; case AbLegacyDataType.Float: _tag.SetFloat32(0, Convert.ToSingle(value)); break; case AbLegacyDataType.String: _tag.SetString(0, Convert.ToString(value) ?? string.Empty); break; case AbLegacyDataType.TimerElement: case AbLegacyDataType.CounterElement: case AbLegacyDataType.ControlElement: _tag.SetInt32(0, Convert.ToInt32(value)); break; default: throw new NotSupportedException($"AbLegacyDataType {type} not writable."); } } public void Dispose() => _tag.Dispose(); private static PlcType MapPlcType(string attribute) => attribute switch { "slc500" => PlcType.Slc500, "micrologix" => PlcType.MicroLogix, "plc5" => PlcType.Plc5, "logixpccc" => PlcType.LogixPccc, _ => PlcType.Slc500, }; } internal sealed class LibplctagLegacyTagFactory : IAbLegacyTagFactory { public IAbLegacyTagRuntime Create(AbLegacyTagCreateParams createParams) => new LibplctagLegacyTagRuntime(createParams); }