Skip to content

Commit b0cc9c6

Browse files
committed
Revert "Use a value wrapped in ManagedCriticalState, rather than an AsyncCurrentValue"
This reverts commit f07d451. This breaks an API consumer of SwiftOCA that depends on projectedValue, so reverting for now. We may be able to reimplement more simply, otherwise we should investigate harmonizing subscription notifications with the AsyncCurrentValueSubject.
1 parent f07d451 commit b0cc9c6

File tree

5 files changed

+72
-44
lines changed

5 files changed

+72
-44
lines changed

Sources/SwiftOCA/OCA/OCF/Locking.swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,7 @@ struct Lock {
115115
}
116116
}
117117

118-
@_spi(SwiftOCAPrivate)
119-
public struct ManagedCriticalState<State> {
118+
struct ManagedCriticalState<State> {
120119
private final class LockedBuffer: ManagedBuffer<State, Lock.Primitive> {
121120
deinit {
122121
withUnsafeMutablePointerToElements { Lock.deinitialize($0) }
@@ -125,16 +124,15 @@ public struct ManagedCriticalState<State> {
125124

126125
private let buffer: ManagedBuffer<State, Lock.Primitive>
127126

128-
@_spi(SwiftOCAPrivate)
129-
public init(_ initial: State) {
127+
init(_ initial: State) {
130128
buffer = LockedBuffer.create(minimumCapacity: 1) { buffer in
131129
buffer.withUnsafeMutablePointerToElements { Lock.initialize($0) }
132130
return initial
133131
}
134132
}
135133

136-
@discardableResult @_spi(SwiftOCAPrivate)
137-
public func withCriticalRegion<R>(_ critical: (inout State) throws -> R) rethrows -> R {
134+
@discardableResult
135+
func withCriticalRegion<R>(_ critical: (inout State) throws -> R) rethrows -> R {
138136
try buffer.withUnsafeMutablePointers { header, lock in
139137
Lock.lock(lock)
140138
defer { Lock.unlock(lock) }
@@ -153,5 +151,4 @@ public struct ManagedCriticalState<State> {
153151
}
154152
}
155153

156-
@_spi(SwiftOCAPrivate)
157154
extension ManagedCriticalState: @unchecked Sendable where State: Sendable {}

Sources/SwiftOCADevice/OCC/ControlClasses/Root.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ open class OcaRoot: CustomStringConvertible, Codable, Sendable, OcaKeyPathMarker
106106
}
107107
}
108108

109+
deinit {
110+
for (_, propertyKeyPath) in allDevicePropertyKeyPaths {
111+
let property = self[keyPath: propertyKeyPath] as! (any OcaDevicePropertyRepresentable)
112+
property.finish()
113+
}
114+
}
115+
109116
enum CodingKeys: String, CodingKey {
110117
case objectNumber = "oNo"
111118
case classIdentification = "1.1"

Sources/SwiftOCADevice/OCC/PropertyTypes/BoundedDeviceProperty.swift

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
@preconcurrency
1818
import AsyncExtensions
19-
@_spi(SwiftOCAPrivate)
2019
import SwiftOCA
2120

2221
@propertyWrapper
@@ -33,10 +32,18 @@ public struct OcaBoundedDeviceProperty<
3332
public typealias Property = OcaDeviceProperty<OcaBoundedPropertyValue<Value>>
3433

3534
public var wrappedValue: OcaBoundedPropertyValue<Value> {
36-
get { storage.wrappedValue }
35+
get { storage.subject.value }
3736
nonmutating set { fatalError() }
3837
}
3938

39+
public var projectedValue: AnyAsyncSequence<Value> {
40+
async.map(\.value).eraseToAnyAsyncSequence()
41+
}
42+
43+
var subject: AsyncCurrentValueSubject<OcaBoundedPropertyValue<Value>> {
44+
storage.subject
45+
}
46+
4047
public init(
4148
wrappedValue: OcaBoundedPropertyValue<Value>,
4249
propertyID: OcaPropertyID,
@@ -56,11 +63,10 @@ public struct OcaBoundedDeviceProperty<
5663
}
5764

5865
func getJsonValue() throws -> Any {
59-
let value = storage.wrappedValue
6066
let valueDict: [String: Value] =
61-
["v": value.value,
62-
"l": value.range.lowerBound,
63-
"u": value.range.upperBound]
67+
["v": storage.subject.value.value,
68+
"l": storage.subject.value.range.lowerBound,
69+
"u": storage.subject.value.range.upperBound]
6470

6571
return valueDict
6672
}
@@ -69,7 +75,7 @@ public struct OcaBoundedDeviceProperty<
6975
object: OcaRoot,
7076
_ newValue: OcaBoundedPropertyValue<Value>
7177
) async {
72-
storage.setWithoutNotifyingSubscribers(newValue)
78+
storage.subject.send(newValue)
7379
try? await notifySubscribers(object: object, newValue.value)
7480
}
7581

Sources/SwiftOCADevice/OCC/PropertyTypes/DeviceProperty.swift

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ protocol OcaDevicePropertyRepresentable: Sendable {
2727
var setMethodID: OcaMethodID? { get }
2828
var wrappedValue: Value { get }
2929

30+
var subject: AsyncCurrentValueSubject<Value> { get }
31+
3032
func getOcp1Response() async throws -> Ocp1Response
3133
func getJsonValue() throws -> Any
3234

@@ -36,23 +38,33 @@ protocol OcaDevicePropertyRepresentable: Sendable {
3638
func set(object: OcaRoot, jsonValue: Any, device: OcaDevice) async throws
3739
}
3840

39-
fileprivate protocol ManagedCriticalStateNilRepresentable {
40-
func _setNil()
41+
extension OcaDevicePropertyRepresentable {
42+
func finish() {
43+
subject.send(.finished)
44+
}
45+
46+
var async: AnyAsyncSequence<Value> {
47+
subject.eraseToAnyAsyncSequence()
48+
}
4149
}
4250

43-
extension ManagedCriticalState: ManagedCriticalStateNilRepresentable
44-
where State: ExpressibleByNilLiteral
51+
extension AsyncCurrentValueSubject: AsyncCurrentValueSubjectNilRepresentable
52+
where Element: ExpressibleByNilLiteral
4553
{
46-
func _setNil() {
47-
withCriticalRegion { $0 = nil }
54+
func sendNil() {
55+
send(nil)
4856
}
4957
}
5058

59+
private protocol AsyncCurrentValueSubjectNilRepresentable {
60+
func sendNil()
61+
}
62+
5163
@propertyWrapper
5264
public struct OcaDeviceProperty<Value: Codable & Sendable>: OcaDevicePropertyRepresentable,
5365
Sendable
5466
{
55-
private var _value: ManagedCriticalState<Value>
67+
let subject: AsyncCurrentValueSubject<Value>
5668

5769
/// The OCA property ID
5870
public let propertyID: OcaPropertyID
@@ -65,17 +77,21 @@ public struct OcaDeviceProperty<Value: Codable & Sendable>: OcaDevicePropertyRep
6577

6678
/// Placeholder only
6779
public var wrappedValue: Value {
68-
get { _value.withCriticalRegion { $0 } }
80+
get { subject.value }
6981
nonmutating set { fatalError() }
7082
}
7183

84+
public var projectedValue: AnyAsyncSequence<Value> {
85+
async
86+
}
87+
7288
public init(
7389
wrappedValue: Value,
7490
propertyID: OcaPropertyID,
7591
getMethodID: OcaMethodID? = nil,
7692
setMethodID: OcaMethodID? = nil
7793
) {
78-
_value = ManagedCriticalState(wrappedValue)
94+
subject = AsyncCurrentValueSubject(wrappedValue)
7995
self.propertyID = propertyID
8096
self.getMethodID = getMethodID
8197
self.setMethodID = setMethodID
@@ -86,22 +102,18 @@ public struct OcaDeviceProperty<Value: Codable & Sendable>: OcaDevicePropertyRep
86102
getMethodID: OcaMethodID? = nil,
87103
setMethodID: OcaMethodID? = nil
88104
) where Value: ExpressibleByNilLiteral {
89-
_value = ManagedCriticalState(nil)
105+
subject = AsyncCurrentValueSubject(nil)
90106
self.propertyID = propertyID
91107
self.getMethodID = getMethodID
92108
self.setMethodID = setMethodID
93109
}
94110

95111
func get() -> Value {
96-
wrappedValue
97-
}
98-
99-
func setWithoutNotifyingSubscribers(_ value: Value) {
100-
_value.withCriticalRegion { $0 = value }
112+
subject.value
101113
}
102114

103115
private func setAndNotifySubscribers(object: OcaRoot, _ newValue: Value) async {
104-
_value.withCriticalRegion { $0 = newValue }
116+
subject.send(newValue)
105117
try? await notifySubscribers(object: object, newValue)
106118
}
107119

@@ -125,14 +137,12 @@ public struct OcaDeviceProperty<Value: Codable & Sendable>: OcaDevicePropertyRep
125137
}
126138

127139
func getJsonValue() throws -> Any {
128-
let value = wrappedValue
129-
130-
let jsonValue: Any = if isNil(value) {
140+
let jsonValue: Any = if isNil(subject.value) {
131141
NSNull()
132-
} else if JSONSerialization.isValidJSONObject(value) {
133-
value
142+
} else if JSONSerialization.isValidJSONObject(subject.value) {
143+
subject.value
134144
} else {
135-
try JSONEncoder().reencodeAsValidJSONObject(value)
145+
try JSONEncoder().reencodeAsValidJSONObject(subject.value)
136146
}
137147

138148
return jsonValue
@@ -145,8 +155,8 @@ public struct OcaDeviceProperty<Value: Codable & Sendable>: OcaDevicePropertyRep
145155

146156
func set(object: OcaRoot, jsonValue: Any, device: OcaDevice) async throws {
147157
if jsonValue is NSNull {
148-
if let value = _value as? any ManagedCriticalStateNilRepresentable {
149-
value._setNil()
158+
if let subject = subject as? AsyncCurrentValueSubjectNilRepresentable {
159+
subject.sendNil()
150160
} else {
151161
throw Ocp1Error.status(.badFormat)
152162
}
@@ -159,7 +169,7 @@ public struct OcaDeviceProperty<Value: Codable & Sendable>: OcaDevicePropertyRep
159169
}
160170
await setAndNotifySubscribers(object: object, objects as! Value)
161171
} else {
162-
let isValidJSONObject = JSONSerialization.isValidJSONObject(_value)
172+
let isValidJSONObject = JSONSerialization.isValidJSONObject(subject.value)
163173
if !isValidJSONObject,
164174
let jsonValue = jsonValue as? Codable
165175
{

Sources/SwiftOCADevice/OCC/PropertyTypes/VectorDeviceProperty.swift

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
//
1616

1717
import AsyncExtensions
18-
@_spi(SwiftOCAPrivate)
1918
import SwiftOCA
2019

2120
@propertyWrapper
@@ -36,10 +35,18 @@ public struct OcaVectorDeviceProperty<
3635
public typealias Property = OcaDeviceProperty<OcaVector2D<Value>>
3736

3837
public var wrappedValue: OcaVector2D<Value> {
39-
get { storage.wrappedValue }
38+
get { storage.subject.value }
4039
nonmutating set { fatalError() }
4140
}
4241

42+
public var projectedValue: AnyAsyncSequence<OcaVector2D<Value>> {
43+
async
44+
}
45+
46+
var subject: AsyncCurrentValueSubject<OcaVector2D<Value>> {
47+
storage.subject
48+
}
49+
4350
public init(
4451
wrappedValue: OcaVector2D<Value>,
4552
xPropertyID: OcaPropertyID,
@@ -62,14 +69,15 @@ public struct OcaVectorDeviceProperty<
6269
}
6370

6471
func getJsonValue() throws -> Any {
65-
let value = storage.wrappedValue
66-
let valueDict: [String: Value] = ["x": value.x, "y": value.y]
72+
let valueDict: [String: Value] =
73+
["x": storage.subject.value.x,
74+
"y": storage.subject.value.y]
6775

6876
return valueDict
6977
}
7078

7179
private func setAndNotifySubscribers(object: OcaRoot, _ newValue: OcaVector2D<Value>) async {
72-
storage.setWithoutNotifyingSubscribers(newValue)
80+
storage.subject.send(newValue)
7381
try? await notifySubscribers(object: object, newValue)
7482
}
7583

0 commit comments

Comments
 (0)