Skip to content

Commit 665b699

Browse files
committed
serialize OcaGroup members as object numbers, not full objects
The default @OcaDeviceProperty serialization for the members property attempted to JSON-encode the full OcaRoot objects, which is both incorrect and could cause serialization failures. Override serialize() and deserialize() to encode members as [OcaONo] and resolve them back on deserialization, matching the wire protocol representation.
1 parent 222f109 commit 665b699

File tree

1 file changed

+60
-0
lines changed
  • Sources/SwiftOCADevice/OCC/ControlClasses/Agents

1 file changed

+60
-0
lines changed

Sources/SwiftOCADevice/OCC/ControlClasses/Agents/Group.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,66 @@ open class OcaGroup<Member: OcaRoot>: OcaAgent {
8787
try? await notifySubscribers(actionObjects: members, changeType: .itemDeleted)
8888
}
8989

90+
private static var _membersPropertyID: OcaPropertyID { OcaPropertyID("3.1") }
91+
92+
override open func serialize(
93+
flags: OcaRoot.SerializationFlags = [],
94+
isIncluded: OcaRoot.SerializationFilterFunction? = nil
95+
) throws -> [String: any Sendable] {
96+
// exclude members from super.serialize() to avoid encoding full OcaRoot objects
97+
let membersPropertyID = Self._membersPropertyID
98+
let wrappedFilter: OcaRoot.SerializationFilterFunction = { object, propertyID, value in
99+
if propertyID == membersPropertyID { return false }
100+
return isIncluded?(object, propertyID, value) ?? true
101+
}
102+
103+
var jsonObject = try super.serialize(flags: flags, isIncluded: wrappedFilter)
104+
105+
// encode members as object numbers
106+
if isIncluded?(self, membersPropertyID, members) ?? true {
107+
jsonObject[membersPropertyID.description] = members.map(\.objectNumber)
108+
}
109+
110+
return jsonObject
111+
}
112+
113+
override open func deserialize(
114+
jsonObject: [String: Sendable],
115+
flags: DeserializationFlags = []
116+
) async throws {
117+
guard let deviceDelegate else { throw Ocp1Error.notConnected }
118+
119+
let membersKey = Self._membersPropertyID.description
120+
121+
// extract members before calling super to avoid needless deserialization
122+
let memberONos = jsonObject[membersKey] as? [OcaONo]
123+
var filteredJsonObject = jsonObject
124+
filteredJsonObject.removeValue(forKey: membersKey)
125+
126+
try await super.deserialize(jsonObject: filteredJsonObject, flags: flags)
127+
128+
guard let memberONos else {
129+
if flags.contains(.ignoreDecodingErrors) { return }
130+
else { throw Ocp1Error.status(.badFormat) }
131+
}
132+
133+
let resolvedMembers: [Member] = try await memberONos.asyncCompactMap { @Sendable memberONo in
134+
guard let object = await deviceDelegate.objects[memberONo] else {
135+
if flags.contains(.ignoreUnknownObjectNumbers) { return nil }
136+
else { throw Ocp1Error.objectNotPresent(memberONo) }
137+
}
138+
139+
guard let member = object as? Member else {
140+
if flags.contains(.ignoreObjectClassMismatches) { return nil }
141+
else { throw Ocp1Error.objectClassMismatch }
142+
}
143+
144+
return member
145+
}
146+
147+
try await set(members: resolvedMembers)
148+
}
149+
90150
override open func handleCommand(
91151
_ command: Ocp1Command,
92152
from controller: any OcaController

0 commit comments

Comments
 (0)