using System.ComponentModel.DataAnnotations; namespace ZB.MOM.WW.OtOpcUa.Driver.S7; /// /// Siemens S7 native (S7comm / ISO-on-TCP port 102) driver configuration. Bound from the /// driver's DriverConfig JSON at DriverHost.RegisterAsync. Unlike the Modbus /// driver the S7 driver uses the PLC's *native* protocol — port 102 ISO-on-TCP rather /// than Modbus's 502, and S7-specific area codes (DB, M, I, Q) rather than holding- /// register / coil tables. /// /// /// /// The driver requires PUT/GET communication enabled in the TIA Portal /// hardware config for S7-1200/1500. The factory default disables PUT/GET access, /// so a driver configured against a freshly-flashed CPU will see a hard error /// (S7.Net surfaces it as Plc.ReadAsync returning ErrorCode.Accessing). /// The driver maps that specifically to BadNotSupported and flags it as a /// configuration alert rather than a transient fault — blind Polly retry is wasted /// effort when the PLC will keep refusing every request. /// /// /// See docs/v2/driver-specs.md §5 for the full specification. /// /// public sealed class S7DriverOptions { /// PLC IP address or hostname. public string Host { get; init; } = "127.0.0.1"; /// TCP port. ISO-on-TCP is 102 on every S7 model; override only for unusual NAT setups. public int Port { get; init; } = 102; /// /// CPU family. Determines the ISO-TSAP slot byte that S7.Net uses during connection /// setup — pick the family that matches the target PLC exactly. /// public S7CpuType CpuType { get; init; } = S7CpuType.S71500; /// /// Hardware rack number. Almost always 0; relevant only for distributed S7-400 racks /// with multiple CPUs. /// public short Rack { get; init; } = 0; /// /// CPU slot. Conventions per family: S7-300 = slot 2, S7-400 = slot 2 or 3, /// S7-1200 / S7-1500 = slot 0 (onboard PN). S7.Net uses this to build the remote /// TSAP. Wrong slot → connection refused during handshake. /// public short Slot { get; init; } = 0; /// Connect + per-operation timeout. public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(5); /// Pre-declared tag map. S7 has a symbol-table protocol but S7.Net does not expose it, so the driver operates off a static tag list configured per-site. Address grammar documented in S7AddressParser (PR 63). public IReadOnlyList Tags { get; init; } = []; /// /// Background connectivity-probe settings. When enabled, the driver runs a tick loop /// that issues S7.Net.Plc.ReadStatusAsync (a CPU-status PDU) every /// and raises OnHostStatusChanged on /// Running ↔ Stopped transitions. /// public S7ProbeOptions Probe { get; init; } = new(); /// /// Timeout for the AdminUI Test Connect probe, in seconds. The AdminUI clamps to a /// 60s server-side maximum; this default is what the form pre-fills for new instances. /// [Display(Name = "Probe timeout (seconds)", Description = "Connection test timeout. Default 5s.", GroupName = "Diagnostics")] [Range(1, 60)] public int ProbeTimeoutSeconds { get; init; } = 5; } public sealed class S7ProbeOptions { /// Gets or sets a value indicating whether probing is enabled. public bool Enabled { get; init; } = true; /// Gets or sets the probe interval. public TimeSpan Interval { get; init; } = TimeSpan.FromSeconds(5); /// Gets or sets the probe timeout. public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(2); // Driver.S7-012: ProbeAddress was configured and documented but was never read by the // probe loop. ProbeLoopAsync uses S7.Net's ReadStatusAsync (a CPU-status PDU), not a // DB/Merker read — it does not consume an explicit address. Rather than ship dead config // surface, ProbeAddress has been removed. The liveness check is purely ReadStatusAsync-based. } /// /// One S7 variable as exposed by the driver. Addresses use S7.Net syntax — see /// S7AddressParser (PR 63) for the grammar. /// /// Tag name; OPC UA browse name + driver full reference. /// S7 address string, e.g. DB1.DBW0, M0.0, I0.0, QD4. Grammar documented in S7AddressParser (PR 63). /// Logical data type — drives the underlying S7.Net read/write width. /// When true the driver accepts writes for this tag. /// For DataType = String: S7-string max length. Default 254 (S7 max). /// /// Per docs/v2/plan.md decisions #44, #45, #143 — flag a tag as safe to replay on /// write timeout / failure. Default false; writes do not auto-retry. Safe candidates /// on S7: DB word/dword set-points holding analog values, configuration DBs where the same /// value can be written again without side-effects. Unsafe: M (merker) bits or Q (output) /// coils that drive edge-triggered routines in the PLC program. /// public sealed record S7TagDefinition( string Name, string Address, S7DataType DataType, bool Writable = true, int StringLength = 254, bool WriteIdempotent = false); public enum S7DataType { Bool, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float32, Float64, String, DateTime, }