Class PolledConnectedDevice
A base class for implementing IConnectedDevice and IHealthCheck for devices that should be regularly polled for status.
public abstract class PolledConnectedDevice : ConnectedDevice, IConnectedDevice, IHealthCheck, IDisposable, IAsyncDisposable
- Inheritance
-
PolledConnectedDevice
- Implements
- Inherited Members
Remarks
Derived classes must do the following:
- Follow the instructions listed in the remarks for ConnectedDevice, with the following notes:
- In the DoConnectAsync(CancellationToken) and DoDisconnectAsync(CancellationToken) implementations, be sure to call the base implementations, which start and stop the background polling loop, respectively.
- The default health check will not only verify DoConnectAsync(CancellationToken) success, but also that the polling loop is running and that the latest poll completed successfully. Optionally override ConnectedCheckHealthAsync(CancellationToken) to add additional checks.
- Implement PerformPollAsync(CancellationToken).
See the remarks for the referenced members for more details.
Constructors
PolledConnectedDevice(IErrorHandler, ILogger, ITimeService)
Initializes a new instance of the PolledConnectedDevice class.
protected PolledConnectedDevice(IErrorHandler errorHandler, ILogger logger, ITimeService timeService)
Parameters
errorHandler
IErrorHandlerThe error handler to use when there is an unhandled exception in the background polling loop.
logger
ILoggerThe logger.
timeService
ITimeServiceThe time service.
Exceptions
- ArgumentNullException
errorHandler
is null.- ArgumentNullException
logger
is null.- ArgumentNullException
timeService
is null.
Properties
AfterPollErrorDelay
Gets or sets how long to wait after a polling error before initiating the next poll.
[Display(Description = "PolledConnectedDevice_AfterPollErrorDelay_Description", ResourceType = typeof(Resources))]
[GreaterThanOrEqualTo(typeof(TimeSpan), "0:00:00")]
public TimeSpan AfterPollErrorDelay { get; set; }
Property Value
Remarks
The actual delay time after a polling error will be the maximum of this value or the time left before the next poll time, per PollingPeriod, so this effectively enforces a minimum delay time after a polling error.
PollingPeriod
Gets or sets the polling period.
[Display(Description = "PolledConnectedDevice_PollingPeriod_Description", ResourceType = typeof(Resources))]
[GreaterThanOrEqualTo(typeof(TimeSpan), "0:00:00")]
public TimeSpan PollingPeriod { get; set; }
Property Value
Remarks
The effectively sets how long to wait after each poll before initiating the next poll. If polling itself takes longer that this period, then there will be no delay before initiating the next poll. Setting this to Zero will result in polling occurring as fast as possible.
Methods
ConnectedCheckHealthAsync(CancellationToken)
Perform a health check on a connected device.
protected override 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
This implementation augments the default ConnectedDevice health check with:
- If no polls have occurred since the last health check, an unhealthy "Polling is not occurring" result will be reported.
- If the last poll failed, an unhealthy "Poll failed" result will be reported.
If the poll performed by PerformPollAsync(CancellationToken) is adequate to establish device health, then this default behavior will suffice and this method need not be overridden.
If additional device health checks that are not normally performed during regular polls are necessary (e.g., checking that a device is in a "Run" mode), then this method should be overridden with an implementation like this:
- Call this base implementation.
- If the base implementation returns a status that is not healthy, return that status.
- Otherwise, perform whatever additional device health checks are appropriate, returning the determined health status.
See the remarks for ConnectedCheckHealthAsync(CancellationToken) for additional notes about implementing an override for this method, keeping in mind that while the ConnectedCheckHealthAsync(CancellationToken) base implementation need not be called, this base implementation should be called as described above.
NOTE: This implementation assumes that device polling will occur more frequently than health checks. If this might not be true, then this method should be overriden with alternate logic, and this default implementation should not be called.
Exceptions
- Exception
The device is unhealthy.
Dispose(bool)
Closes and releases all resources used by the object.
protected override 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.
DisposeAsyncCore()
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously.
protected override 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 override 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 override 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.
OnPollCompleted(Exception)
Called when a poll completes, either successfully or with an error.
protected virtual void OnPollCompleted(Exception exception)
Parameters
exception
ExceptionWhen not null, the error representing the failed poll attempt; null for successful polls.
Remarks
A connected device lock (i.e., via ConnectedDeviceLockAsync(CancellationToken)) will not exist when this is called.
This will be called from a worker thread.
PerformPollAsync(CancellationToken)
Performs a poll.
protected abstract Task PerformPollAsync(CancellationToken cancellationToken)
Parameters
cancellationToken
CancellationTokenThe cancellation token, which indicates that the polling loop has been requested to stop.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
Derived classes must implement this method to actually perform the poll. If the poll fails (e.g., due to an error communicating with the device), then an exception representing the failure should be thrown (either synchronously or asynchronously as a faulted task). Note that any exception from this method will be interpreted as a poll failure, not as an unexpected application error. Best practice is to only allow known poll failure exceptions to be thrown from this method, and use IErrorHandler.HandleUnexpectedException(Exception) to handle any other unexpected exceptions within this method.
PolledConnectedDevice will log a "New or changed poll error" error entry via the ILogger when a poll attempt fails, while suppressing repeated log entries. An entry will be logged only when a poll attempt fails after the previous poll succeeded, or when a poll attempt fails with a different combined exception message (i.e., via MessagesHierarchyAsSingleLine(Exception)) as compared to that of the previous poll attempt. When a poll attempt succeeds after the previous attempt had failed, a "Poll error cleared" information message will be logged.
The implementation must not call ConnectedDeviceLockAsync(CancellationToken), as a lock will already exist when this gets called.
This will be called from a worker thread and will never be called before a previous call completes.
Exceptions
- Exception
The poll attempt failed.