@@ -27,7 +27,7 @@ import OpenCombine
2727import SystemPackage
2828
2929private extension Ocp1Error {
30- var connectionState : Ocp1ConnectionState ? {
30+ var ocp1ConnectionState : Ocp1ConnectionState ? {
3131 switch self {
3232 case . notConnected:
3333 . notConnected
@@ -75,7 +75,7 @@ private extension Errno {
7575
7676private extension Error {
7777 var ocp1ConnectionState : Ocp1ConnectionState {
78- ( self as? Ocp1Error ) ? . connectionState ?? . connectionFailed
78+ ( self as? Ocp1Error ) ? . ocp1ConnectionState ?? . connectionFailed
7979 }
8080
8181 var _isRecoverableConnectionError : Bool {
@@ -109,7 +109,8 @@ private extension Ocp1ConnectionState {
109109extension Ocp1Connection {
110110 /// start receiveMessages/keepAlive monitor task
111111 private func _startMonitor( ) {
112- let monitor = Monitor ( self )
112+ connectionID &+= 1
113+ let monitor = Monitor ( self , id: connectionID)
113114 monitorTask = Task {
114115 try await monitor. run ( )
115116 }
@@ -160,18 +161,20 @@ extension Ocp1Connection {
160161 }
161162 }
162163
163- func updateConnectionState ( _ connectionState: Ocp1ConnectionState ) {
164+ private func _updateConnectionState ( _ connectionState: Ocp1ConnectionState ) {
164165 logger. trace ( " _updateConnectionState: \( _connectionState. value) => \( connectionState) " )
165- if connectionState == . connected {
166- logger. info ( " connected to \( self ) " )
167- }
168166 _connectionState. send ( connectionState)
169167 }
170168
169+ func markConnectionConnected( ) {
170+ logger. info ( " connected to \( self ) " )
171+ _updateConnectionState ( . connected)
172+ }
173+
171174 private func _didConnectDevice( ) async throws {
172175 if !isDatagram {
173176 // otherwise, set connected state when we receive first keepAlive PDU
174- updateConnectionState ( . connected )
177+ markConnectionConnected ( )
175178 }
176179
177180 _startMonitor ( )
@@ -190,13 +193,13 @@ extension Ocp1Connection {
190193 }
191194
192195 public func connect( ) async throws {
193- updateConnectionState ( . connecting)
196+ _updateConnectionState ( . connecting)
194197
195198 do {
196199 try await _connectDeviceWithTimeout ( )
197200 } catch {
198201 logger. debug ( " connection failed: \( error) " )
199- updateConnectionState ( error. ocp1ConnectionState)
202+ _updateConnectionState ( error. ocp1ConnectionState)
200203 throw error
201204 }
202205
@@ -243,7 +246,7 @@ extension Ocp1Connection {
243246 public func disconnect( ) async throws {
244247 await removeSubscriptions ( )
245248
246- updateConnectionState ( . notConnected)
249+ _updateConnectionState ( . notConnected)
247250
248251 let clearObjectCache = !options. flags. contains ( . retainObjectCacheAfterDisconnect)
249252 try await _disconnectDevice ( clearObjectCache: clearObjectCache)
@@ -253,32 +256,8 @@ extension Ocp1Connection {
253256// MARK: - reconnection handling
254257
255258extension Ocp1Connection {
256- enum ReconnectionPolicy {
257- /// do not try to automatically reconnect on connection failure
258- case noReconnect
259- /// try to reconnect in the keepAlive monitor task
260- case reconnectInMonitor
261- /// try to reconnect before sending the next message
262- case reconnectOnSend
263- }
264-
265- ///
266- /// Re-connection logic is as follows:
267- ///
268- /// * If the connection has a heartbeat, then automatic reconnection is only
269- /// managed in the / heartbeat task
270- ///
271- /// * If the connection does not have a heartbeat, than automatic
272- /// reconnection is managed when / sending a PDU
273- ///
274- private var _reconnectionPolicy : ReconnectionPolicy {
275- if !options. flags. contains ( . automaticReconnect) {
276- . noReconnect
277- } else if heartbeatTime == . zero {
278- . reconnectOnSend
279- } else {
280- . reconnectInMonitor
281- }
259+ private var _automaticReconnect : Bool {
260+ options. flags. contains ( . automaticReconnect)
282261 }
283262
284263 /// reconnect to the OCA device with exponential backoff, updating
@@ -287,7 +266,7 @@ extension Ocp1Connection {
287266 var lastError : Error ?
288267 var backoff : Duration = options. reconnectPauseInterval
289268
290- updateConnectionState ( . reconnecting)
269+ _updateConnectionState ( . reconnecting)
291270
292271 logger
293272 . trace (
@@ -313,69 +292,22 @@ extension Ocp1Connection {
313292
314293 if let lastError {
315294 logger. debug ( " reconnection abandoned: \( lastError) " )
316- updateConnectionState ( lastError. ocp1ConnectionState)
295+ _updateConnectionState ( lastError. ocp1ConnectionState)
317296 throw lastError
318297 } else if !isDatagram && !isConnected {
319298 logger. trace ( " reconnection abandoned after too many tries " )
320- updateConnectionState ( . notConnected)
299+ _updateConnectionState ( . notConnected)
321300 throw Ocp1Error . notConnected
322301 }
323302 }
324303
325- private var _needsReconnectOnSend : Bool {
326- guard _reconnectionPolicy == . reconnectOnSend else { return false }
304+ func onMonitorError ( id : Int , _ error : Error ) async throws {
305+ _updateConnectionState ( error . ocp1ConnectionState )
327306
328- switch _connectionState. value {
329- case . notConnected:
330- fallthrough
331- case . connectionTimedOut:
332- fallthrough
333- case . connectionFailed:
334- return true
335- default :
336- return false
337- }
338- }
339-
340- func willSendMessage( ) async throws {
341- guard _needsReconnectOnSend else { return }
342- try await reconnectDeviceWithBackoff ( )
343- }
344-
345- func didSendMessage( error: Ocp1Error ? = nil ) async throws {
346- if error == nil {
347- lastMessageSentTime = Monitor . now
348- }
349-
350- if _reconnectionPolicy != . reconnectInMonitor, let error,
351- let connectionState = error. connectionState
352- {
353- logger
354- . debug (
355- " failed to send message: error \( error) , new connection state \( connectionState) ; disconnecting "
356- )
357- if isConnected {
358- updateConnectionState ( connectionState)
359- try await _disconnectDeviceAfterConnectionFailure ( )
360- }
361- }
362- }
363-
364- private func _onMonitorError( _ error: Error ) async throws {
365- updateConnectionState ( error. ocp1ConnectionState)
366-
367- if error. _isRecoverableConnectionError, _reconnectionPolicy == . reconnectInMonitor {
368- logger. trace ( " expiring connection after receiving error \( error) " )
307+ if _automaticReconnect, error. _isRecoverableConnectionError {
308+ logger. trace ( " monitor task \( id) disconnecting: \( error) " )
369309 try await _disconnectDeviceAfterConnectionFailure ( )
370310 Task . detached { try await self . reconnectDeviceWithBackoff ( ) }
371311 }
372312 }
373-
374- func onKeepAliveMonitorError( _ error: Error ) async throws {
375- try await _onMonitorError ( error)
376- }
377-
378- func onReceiveMessageMonitorError( _ error: Error ) async throws {
379- try await _onMonitorError ( error)
380- }
381313}
0 commit comments