using System; using System.Globalization; using Avalonia.Data.Converters; using Avalonia.Media; namespace Junction.App.Converters { /// /// Maps a machine's ProtocolId ("mtconnect", future "opcua"/"fanuc", …) to a vector /// glyph () for a PathIcon. Keeps view-models framework-agnostic: /// the icon decision lives entirely in the view layer, mirroring /// . Unknown/empty ids fall back to a generic glyph. /// Glyphs are line-only 24×24 path data (no arcs/curves) so they always parse and read /// as distinct filled shapes. No external image assets — net48 packaging stays a single exe set. /// public sealed class ProtocolIdToIconConverter : IValueConverter { public static readonly ProtocolIdToIconConverter Instance = new ProtocolIdToIconConverter(); // Signal-strength bars — a live telemetry feed. private const string MtconnectData = "M4,14 H7 V20 H4 Z M10,10 H13 V20 H10 Z M16,5 H19 V20 H16 Z"; // Stacked diamonds — the OPC UA layered address space. private const string OpcuaData = "M12,2 L20,7 L12,12 L4,7 Z M12,13 L20,18 L12,23 L4,18 Z"; // Plus/cross — an industrial CNC controller marker. private const string FanucData = "M10,3 H14 V10 H21 V14 H14 V21 H10 V14 H3 V10 H10 Z"; // Octagon node — generic / unknown protocol. private const string FallbackData = "M8,4 H16 L20,8 V16 L16,20 H8 L4,16 V8 Z"; private static Geometry? _mtconnect; private static Geometry? _opcua; private static Geometry? _fanuc; private static Geometry? _fallback; /// /// Pure lookup of the raw path-data string for a protocol id. View-framework-free and /// side-effect-free so it is unit-testable without an Avalonia platform. Known ids return a /// distinct non-empty string; anything else returns the generic fallback. Never throws. /// public static string PathDataFor(string? protocolId) { switch (protocolId == null ? null : protocolId.Trim().ToLowerInvariant()) { case "mtconnect": return MtconnectData; case "opcua": return OpcuaData; case "fanuc": return FanucData; default: return FallbackData; } } public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { switch (PathDataFor(value as string)) { case MtconnectData: return _mtconnect ?? (_mtconnect = Geometry.Parse(MtconnectData)); case OpcuaData: return _opcua ?? (_opcua = Geometry.Parse(OpcuaData)); case FanucData: return _fanuc ?? (_fanuc = Geometry.Parse(FanucData)); default: return _fallback ?? (_fallback = Geometry.Parse(FallbackData)); } } public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => throw new NotSupportedException(); } }