@@ -15,7 +15,7 @@ final class DeviceHardwareEntity {
1515 var displayName : String ?
1616 var hasInkHud : Bool = false
1717 var hasMui : Bool = false
18- @ Attribute ( . unique ) var hwModel : Int64 = 0
18+ var hwModel : Int64 = 0
1919 var hwModelSlug : String ?
2020 var key : String ?
2121 var partitionScheme : String ?
@@ -32,3 +32,112 @@ final class DeviceHardwareEntity {
3232
3333 init ( ) { }
3434}
35+
36+ /// A target-specific entry from the device-hardware catalog.
37+ ///
38+ /// Firmware targets are not a one-to-one representation of the radio hardware model reported
39+ /// over the mesh protocol: multiple targets can intentionally report the same `hwModel`.
40+ struct HardwareCatalogRecord : Equatable {
41+ let hwModel : Int64
42+ let hwModelSlug : String
43+ let platformioTarget : String
44+ let displayName : String
45+ let activelySupported : Bool
46+ let supportLevel : SupportLevel
47+ let architecture : String ?
48+
49+ init (
50+ hwModel: Int64 ,
51+ hwModelSlug: String ,
52+ platformioTarget: String ,
53+ displayName: String ,
54+ activelySupported: Bool ,
55+ supportLevel: SupportLevel ,
56+ architecture: String ? = nil
57+ ) {
58+ self . hwModel = hwModel
59+ self . hwModelSlug = hwModelSlug
60+ self . platformioTarget = platformioTarget
61+ self . displayName = displayName
62+ self . activelySupported = activelySupported
63+ self . supportLevel = supportLevel
64+ self . architecture = architecture
65+ }
66+
67+ init ( _ entity: DeviceHardwareEntity ) {
68+ self . init (
69+ hwModel: entity. hwModel,
70+ hwModelSlug: entity. hwModelSlug ?? " " ,
71+ platformioTarget: entity. platformioTarget ?? " " ,
72+ displayName: entity. displayName ?? " " ,
73+ activelySupported: entity. activelySupported,
74+ supportLevel: SupportLevel ( rawValue: entity. supportLevel) ?? . discontinued,
75+ architecture: entity. architecture
76+ )
77+ }
78+ }
79+
80+ /// Safe presentation metadata for a radio that only reports a protobuf `HardwareModel`.
81+ ///
82+ /// Target-specific values are omitted when the protocol cannot distinguish the catalog variants.
83+ struct HardwareCatalogPresentation : Equatable {
84+ let displayName : String ?
85+ let platformioTarget : String ?
86+ let activelySupported : Bool ?
87+ let supportLevel : SupportLevel ?
88+ let architecture : String ?
89+ }
90+
91+ enum HardwareCatalogResolver {
92+ static func presentation( for hwModel: Int64 , in entities: [ DeviceHardwareEntity ] ) -> HardwareCatalogPresentation ? {
93+ presentation ( for: hwModel, in: entities. map ( HardwareCatalogRecord . init) )
94+ }
95+
96+ static func presentation( for hwModel: Int64 , in records: [ HardwareCatalogRecord ] ) -> HardwareCatalogPresentation ? {
97+ let matches = records. filter { $0. hwModel == hwModel }
98+ guard !matches. isEmpty else { return nil }
99+
100+ if matches. count == 1 , let record = matches. first {
101+ return presentation ( for: record)
102+ }
103+
104+ let canonicalTargets = matches. filter {
105+ $0. platformioTarget == normalizedTarget ( from: $0. hwModelSlug)
106+ }
107+ if canonicalTargets. count == 1 , let canonical = canonicalTargets. first {
108+ return presentation ( for: canonical)
109+ }
110+
111+ // The radio protocol cannot supply target-level identity. Present the most desirable
112+ // catalog entry consistently instead of letting database/query order pick one at random.
113+ let preferred = matches. sorted ( by: isPreferred ( _: over: ) ) . first!
114+ return presentation ( for: preferred)
115+ }
116+
117+ private static func presentation( for record: HardwareCatalogRecord ) -> HardwareCatalogPresentation {
118+ HardwareCatalogPresentation (
119+ displayName: record. displayName,
120+ platformioTarget: record. platformioTarget,
121+ activelySupported: record. activelySupported,
122+ supportLevel: record. supportLevel,
123+ architecture: record. architecture
124+ )
125+ }
126+
127+ private static func normalizedTarget( from hardwareModelSlug: String ) -> String {
128+ hardwareModelSlug. lowercased ( ) . replacingOccurrences ( of: " _ " , with: " - " )
129+ }
130+
131+ private static func isPreferred( _ lhs: HardwareCatalogRecord , over rhs: HardwareCatalogRecord ) -> Bool {
132+ if lhs. supportLevel != rhs. supportLevel {
133+ return lhs. supportLevel. rawValue < rhs. supportLevel. rawValue
134+ }
135+ if lhs. activelySupported != rhs. activelySupported {
136+ return lhs. activelySupported
137+ }
138+ if lhs. displayName != rhs. displayName {
139+ return lhs. displayName. localizedStandardCompare ( rhs. displayName) == . orderedAscending
140+ }
141+ return lhs. platformioTarget. localizedStandardCompare ( rhs. platformioTarget) == . orderedAscending
142+ }
143+ }
0 commit comments