Class ConnectedDevice
A base class for implementing IConnectedDevice and IHealthCheck.
public abstract class ConnectedDevice : IConnectedDevice, IHealthCheck, IDisposable, IAsyncDisposable
- Inheritance
-
ConnectedDevice
- Implements
- Derived
- Inherited Members
Remarks
Derived classes must do the following:
- Implement DoConnectAsync(CancellationToken) and DoDisconnectAsync(CancellationToken). Implementations can assume that they will never be called concurrently.
- Call OnExternalDeviceClosedConnection(Exception) if the connection is closed from the external device (but not when the connection is closed due to DoDisconnectAsync(CancellationToken)).
- Override Dispose(bool) and DisposeAsyncCore() as needed (when overriding one, always override the other as well).
- In public methods that work with the connected device, use ConnectedDeviceLockAsync(CancellationToken) to acquire a lock.
- To implement device health checks (beyond DoConnectAsync(CancellationToken) success), override ConnectedCheckHealthAsync(CancellationToken), and optionally call ConnectedHealthIsHealthy() from public methods that work with the connected device when they succeed.
See the remarks for the referenced members for more details.
Constructors
ConnectedDevice(ILogger)
Initializes a new instance of the ConnectedDevice class.
[CLSCompliant(false)]
protected ConnectedDevice(ILogger logger)
Parameters
logger
ILoggerThe logger.
Exceptions
- ArgumentNullException
logger
is null.
Methods
CheckHealthAsync(HealthCheckRegistration, CancellationToken)
Runs the health check, returning the status of the component being checked.
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckRegistration registration, CancellationToken cancellationToken = default)
Parameters
registration
HealthCheckRegistrationThe health check registration associated with the current execution.
cancellationToken
CancellationTokenA CancellationToken that can be used to cancel the health check.
Returns
- Task<HealthCheckResult>
A Task<TResult> that completes when the health check has finished, yielding the status of the component being checked.
ConnectAsync(CancellationToken)
Connects to the device.
public Task<IDeviceConnection> ConnectAsync(CancellationToken cancellationToken = default)
Parameters
cancellationToken
CancellationTokenA CancellationToken that can request canceling the connection attempt.
Returns
- Task<IDeviceConnection>
A task that represents the asynchronous operation. The value of its Result property contains a IDeviceConnection representing the established connection.
Remarks
This is safe to call even if there is already an active connection, in which case it will do nothing and return the same IDeviceConnection as from the active connection.
Implementations must ensure that the returned IDeviceConnection is fully implemented, including handling callback registrations and indicating when the connection is closed for any reason.
Upon a successful connection, Connected should be raised.
This must be thread-safe: asynchronous implementations must ensure multiple simultaneous calls to this are handled correctly.
Exceptions
- Exception
The connection could not be established. Specific exception types will depend on the implementation.
ConnectedCheckHealthAsync(CancellationToken)
Perform a health check on a connected device.
protected virtual Task<HealthCheckResult> ConnectedCheckHealthAsync(CancellationToken cancellationToken)
Parameters
cancellationToken
CancellationTokenA CancellationToken that can be used to cancel the health check.
Returns
- Task<HealthCheckResult>
A Task<TResult> that completes when the health check has finished, yielding the status of the component being checked.
Remarks
ConnectedDevice implements IHealthCheck by default to simply confirm that a connection exists (or can be made) to the device; it basically reflects the status of the DoConnectAsync(CancellationToken) implementation's latest result. If that is sufficient for a device implementation, then this method should not be overridden and ConnectedHealthIsHealthy() need never be called.
This method should be overridden if there are additional actions needed to verify device health, beyond DoConnectAsync(CancellationToken) connectivity. For example:
- A device implementation might "connect" to a device by simply opening a serial port for communication. Therefore, the default health check only represents the status of the local serial port. In this case, it would probably be appropriate to override this method to perform some kind of communication with the device to confirm that the device is there and is responsive.
- A device might need to be in a certain state to operate properly, such as a PLC that needs to be in "Run" mode. In this case, this method could be overridden to read the status from the device and confirm it is in the correct state.
Override implementations:
- Must not call ConnectedDeviceLockAsync(CancellationToken), as a lock will already exist when this gets called.
- Can simply throw a synchronous exception or complete the returned task with a fault exception to indicate an unhealthy status, which will cause an unhealthy "Connection problem" result to be reported.
- Can return a HealthCheckResult result indicating a specific health status and a specific description.
- Should not call ConnectedHealthIsHealthy() to indicate a healthy status, but rather return a Healthy result.
- Need not call the base implementation, as it simply returns a Healthy result.
Exceptions
- Exception
The device is unhealthy.Specific exception types will depend on any override(s).
ConnectedDeviceLockAsync(CancellationToken)
Ensures the device is connected and acquires a lock on the device connection.
protected Task<IDisposable> ConnectedDeviceLockAsync(CancellationToken cancellationToken)
Parameters
cancellationToken
CancellationTokenA CancellationToken that can request canceling waiting for the lock and connecting to the device.
Returns
- Task<IDisposable>
A task that represents the asynchronous operation. The value of its Result property contains a disposable that releases the lock when disposed.
Exceptions
- Exception
The connection could not be established. Specific exception types will depend on the implementation of DoConnectAsync(CancellationToken), any OnConnectedAsync() override(s), and any Connected event handlers.
ConnectedHealthIsHealthy()
Derived classes can call this at any time to indicate that the health of the connection with the device was confirmed to be healthy.
protected void ConnectedHealthIsHealthy()
Remarks
When this has been called at least once since the last health check, then the call to ConnectedCheckHealthAsync(CancellationToken) during the next health check will be skipped. This is typically called from public methods that work with the connected device once they succeed, when the event is enough to verify device health.
This need only be called when ConnectedCheckHealthAsync(CancellationToken) is overridden with custom health check logic, as this is only used to determine when the call to ConnectedCheckHealthAsync(CancellationToken) can be skipped. Typically this is used when ConnectedCheckHealthAsync(CancellationToken) is simply used to confirm that the device is there and responds to communication, and thus can be skipped when other successful communication with the device is occurring. This should typically not be used when ConnectedCheckHealthAsync(CancellationToken) performs a check that is not performed by other public methods (e.g., checking that a PLC is in "Run" mode).
DisconnectAsync(CancellationToken)
Disconnects from the device.
public Task DisconnectAsync(CancellationToken cancellationToken = default)
Parameters
cancellationToken
CancellationTokenA CancellationToken that can request that the disconnection process no longer be graceful.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
This is safe to call even if there is not an active connection.
When cancellationToken
is signaled, it indicates that the disconnection should still occur, but not gracefully.
The implementation should complete its disconnection process as directly as possible, but the returned Task should
still reflect a RanToCompletion status (not Canceled).
Upon a successful connection, Disconnected should be raised.
This must be thread-safe: asynchronous implementations must ensure multiple simultaneous calls to this are handled correctly.
Dispose()
Closes and releases all resources used by the object.
public void Dispose()
Dispose(bool)
Closes and releases all resources used by the object.
protected virtual void Dispose(bool disposing)
Parameters
Remarks
Derived classes should override this when they have any resources that should be disposed.
When Dispose() is called on the object, Dispose(bool) will be called with true for the parameter value.
When DisposeAsync() is called on the object, DisposeAsyncCore() will be called, and then Dispose(bool) will be called with false for the parameter value.
DisposeAsync()
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously.
public ValueTask DisposeAsync()
Returns
- ValueTask
A task that represents the asynchronous operation.
DisposeAsyncCore()
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously.
protected virtual ValueTask DisposeAsyncCore()
Returns
- ValueTask
A task that represents the asynchronous operation.
Remarks
Derived classes should override this when they have any resources that should be disposed.
When Dispose() is called on the object, Dispose(bool) will be called with true for the parameter value.
When DisposeAsync() is called on the object, DisposeAsyncCore() will be called, and then Dispose(bool) will be called with false for the parameter value.
DoConnectAsync(CancellationToken)
Connects to the device.
protected abstract Task DoConnectAsync(CancellationToken cancellationToken)
Parameters
cancellationToken
CancellationTokenA CancellationToken that can request canceling the connection attempt.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
This might be called from any thread, but it and DoDisconnectAsync(CancellationToken) will never be called concurrently.
Exceptions
- Exception
The connection could not be established. Specific exception types will depend on the implementation.
DoDisconnectAsync(CancellationToken)
Disconnects from the device.
protected abstract Task DoDisconnectAsync(CancellationToken cancellationToken)
Parameters
cancellationToken
CancellationTokenA CancellationToken that can request that the disconnection process no longer be graceful.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
When cancellationToken
is signaled, it indicates that the disconnection should still occur, but not gracefully.
The implementation should complete its disconnection process as directly as possible, but the returned Task should
still reflect a RanToCompletion status (not Canceled).
This might be called from any thread, but it and DoConnectAsync(CancellationToken) will never be called concurrently.
OnConnectedAsync()
Called after DoConnectAsync(CancellationToken) completes successfully.
protected virtual Task OnConnectedAsync()
Returns
- Task
A task that represents the asynchronous operation.
OnDisconnectedAsync()
Called after DoDisconnectAsync(CancellationToken) completes successfully.
protected virtual Task OnDisconnectedAsync()
Returns
- Task
A task that represents the asynchronous operation.
OnDisconnectingAsync(CancellationToken)
Called before DoDisconnectAsync(CancellationToken) will be called.
protected virtual Task OnDisconnectingAsync(CancellationToken cancellationToken)
Parameters
cancellationToken
CancellationTokenA CancellationToken that can request that the disconnection process no longer be graceful.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
When cancellationToken
is signaled, it indicates that the disconnection should still occur, but not gracefully.
The returned Task should reflect a RanToCompletion status (not Canceled).
OnExternalDeviceClosedConnection(Exception)
Derived classes should call this when the external device closes the connection, but being careful not to call it while disconnection is underway.
protected virtual void OnExternalDeviceClosedConnection(Exception closeError)
Parameters
closeError
ExceptionThe exception representing the connection lost error.
Remarks
closeError
is required, since CloseError should only be null when
the connection is still open or when it was closed via DisconnectAsync(CancellationToken). If there was no exception
that raised the lost connection, then provide a created exception. DeviceClosedConnectionException can be used for this, which will
provide a message like "The device closed the connection."
Exceptions
- ArgumentNullException
closeError
is null.- InvalidOperationException
There is no active connection, or a connection or disconnection process is in progress.
Events
Connected
Raised when the device is connected.
public event EventHandler Connected
Event Type
Disconnected
Raised when the device is disconnected.
public event EventHandler Disconnected