using System.Text.Json; namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions; /// /// The Galaxy () tag reference: the attributeRef a Galaxy raw tag's /// TagConfig carries, tag_name.AttributeName, which the driver dials through MXAccess. One parser for the /// driver (which dials it) and the address-space composer (which names the AVEVA event source it implies, Gitea #886), /// so the two can never disagree about which tags carry one. /// public static class GalaxyAttributeRef { /// The TagConfig key (camelCase; matched case-insensitively). public const string Key = "attributeRef"; /// /// Read the Galaxy dialled address from a tag's TagConfig JSON — the camelCase attributeRef key (the v3 /// rename of the pre-v3 PascalCase FullName). Case-insensitive on the property name for authoring tolerance; /// returns null on an absent/blank/non-string value or an unparseable blob. /// /// The tag's TagConfig JSON. /// The attributeRef, or null. public static string? TryRead(string? tagConfigJson) { if (string.IsNullOrWhiteSpace(tagConfigJson)) return null; try { using var doc = JsonDocument.Parse(tagConfigJson); if (doc.RootElement.ValueKind != JsonValueKind.Object) return null; foreach (var prop in doc.RootElement.EnumerateObject()) { if (!prop.NameEquals(Key) && !string.Equals(prop.Name, Key, StringComparison.OrdinalIgnoreCase)) continue; if (prop.Value.ValueKind != JsonValueKind.String) return null; var v = prop.Value.GetString(); return string.IsNullOrWhiteSpace(v) ? null : v; } } catch (JsonException) { // Malformed blob: no dialable address. } return null; } /// /// The AVEVA alarm reference of a native-alarm tag, or null: the tag's attributeRef when its /// driver is the Galaxy driver, otherwise null (no other driver's alarms are stored by AVEVA under a Galaxy name). /// /// The owning driver's DriverType. /// The tag's TagConfig JSON. /// The Galaxy alarm reference (tag_name.AlarmAttribute), or null. public static string? AlarmReferenceOf(string? driverType, string? tagConfigJson) => string.Equals(driverType, DriverTypeNames.Galaxy, StringComparison.Ordinal) ? TryRead(tagConfigJson) : null; /// /// The Galaxy object (tag_name) a reference belongs to: the text before the first ., or the whole /// reference when it has none. This is AVEVA's Source_Object for the object's events and its attributes' /// alarms. Null for a blank reference or one that starts with a .. /// /// A Galaxy reference, e.g. Oven_007.TempHiAlarm. /// The object name, e.g. Oven_007. public static string? ObjectNameOf(string? reference) { if (string.IsNullOrWhiteSpace(reference)) return null; var trimmed = reference.Trim(); var dot = trimmed.IndexOf('.'); return dot switch { < 0 => trimmed, 0 => null, _ => trimmed[..dot], }; } }