Table of Contents

Class PolledConnectedDevice

Namespace
Acuit.Pinpoint.IO
Assembly
Acuit.Pinpoint.IO.Abstractions.dll

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:

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 IErrorHandler

The error handler to use when there is an unhandled exception in the background polling loop.

logger ILogger

The logger.

timeService ITimeService

The 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

TimeSpan

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

TimeSpan

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 CancellationToken

A 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:

  1. Call this base implementation.
  2. If the base implementation returns a status that is not healthy, return that status.
  3. 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

disposing bool

true when this is in response to a call to Dispose().

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 CancellationToken

A 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 CancellationToken

A 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 Exception

When 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 CancellationToken

The 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.