Architecture Overview
Relevant source files
The following files were used as context for generating this wiki page:
The Pulsar Dome Driver is an ASCOM-compliant driver designed to control a Pulsar dome via serial communication. Its architecture emphasizes robustness and reliability through a multi-layered system of status polling, command execution watchdogs, and an automated reset mechanism for fault recovery. The driver also integrates with external systems for status reporting and notifications, including an MQTT broker and the Goodnight System (GNS).
Core components are managed by the main DomeDriver class, which handles ASCOM interface implementation, connection management, and orchestration of all dome operations. Configuration is persisted in the ASCOM Profile (Windows Registry) and managed through the ConfigManager class and a dedicated SettingsForm UI.
Core Components
The driver is composed of several key classes that collaborate to provide its functionality. The main DomeDriver class acts as the central orchestrator.
This diagram shows the primary classes and their relationships. DomeDriver holds instances of most other helper classes to delegate tasks like configuration, communication, and logging.
Sources: Driver/Pulsar_DomeDriver.cs, Config/ConfigManager.cs, Helper/SerialPortGuard.cs, MQTT/MqttPublisher.cs, Helper/ActionWatchdog.cs, GNS/GNS.cs, UI/SettingsForm.cs, Diagnostics/FileLogger.cs
Connection and Communication
Communication with the dome controller is handled exclusively over a serial port. The architecture includes several layers to ensure thread-safe and reliable command dispatch.
Connection Lifecycle
The connection process is managed by the Connected property in the DomeDriver class.
- On Connect (
settotrue):- The
ConnectControllermethod is called. - It opens the configured serial port.
- A
SerialPortGuardinstance is created to manage access to the port. - A "PULSAR" ping command is sent to verify the controller is responsive.
- If successful, the background polling loop and system watchdog are started.
- If configured, an MQTT connection is established.
- The
- On Disconnect (
settofalse):- The polling loop and system watchdog are stopped.
- The MQTT connection is terminated.
- The
DisconnectControllermethod closes the serial port and disposes theSerialPortGuard.
Sources: Driver/Pulsar_DomeDriver.cs:175-263
Serial Communication via SerialPortGuard
Direct access to the SerialPort object is prevented. Instead, all serial commands are funneled through the SerialPortGuard class. This class uses a SemaphoreSlim to ensure that only one command is sent at a time, preventing race conditions and interleaved messages.
The Send method in SerialPortGuard is responsible for:
- Acquiring a lock (
_gate.Wait()). - Setting a
_commandInProgressflag via a callback. - Flushing serial buffers to ensure a clean state.
- Writing the command string followed by a carriage return (
\r). - If a response is expected, reading from the port until a terminator is seen or a timeout occurs.
- Clearing the
_commandInProgressflag. - Releasing the lock (
_gate.Release()).
Sources: Helper/SerialPortGuard.cs:27-101
The following diagram illustrates the sequence for sending a command and receiving a response.
State Management and Polling
The driver maintains the dome's current state by continuously polling the controller. This ensures that ASCOM properties like Azimuth, ShutterStatus, and AtHome are always up-to-date.
Polling Loop
The PollLoopAsync task runs in the background when the driver is connected.
- It runs in a continuous
whileloop, broken only by aCancellationToken. - In each cycle, it calls
DomeStatus(). - It then waits for a configurable interval (
_config._pollingIntervalMs). - The timestamp of the last successful poll (
_lastPollTimestamp) is updated in each cycle.
Sources: Driver/Pulsar_DomeDriver.cs:484-578
System Watchdog
A separate watchdog task, StartWatchdog, runs to monitor the health of the polling loop itself.
- It checks every 5 seconds (
_systemWatchdogInterval). - It compares the current time with
_lastPollTimestamp. - If the time elapsed exceeds a threshold (10 seconds), it assumes the polling loop has stalled.
- It then forcefully stops and restarts the polling loop to recover.
- It also publishes a periodic heartbeat message to the
Dome/WatchdogMQTT topic.
This two-tiered polling and watchdog system ensures that the driver remains synchronized with the hardware and can recover from transient communication freezes.
Sources: Driver/Pulsar_DomeDriver.cs:580-630
The overall polling and watchdog flow is shown below.
Sources: Driver/Pulsar_DomeDriver.cs:443-652
Command Execution and Action Watchdog
Long-running dome actions (e.g., slewing, opening the shutter) are managed by a generic command execution pattern that incorporates a dedicated ActionWatchdog.
ExecuteDomeCommand
This private helper method in DomeDriver provides a standardized way to initiate and monitor an action.
- Sets a
ForceBusyflag and records the commandintent. - Stops the main polling loop temporarily to prevent interference.
- Sends the command to the controller (e.g., "OPEN", "GO H").
- If the command is acknowledged, it starts an
ActionWatchdog. - Restarts the main polling loop, which will now monitor for the command's completion.
Sources: Driver/Pulsar_DomeDriver.cs:1612-1685
ActionWatchdog
The ActionWatchdog class is crucial for reliability. When started, it monitors a specific action for a given timeout.
- It runs in its own task.
- It periodically invokes a
checkStatusfunction (passed fromExecuteDomeCommand) to determine if the action has succeeded, failed, or is still in progress. - On Success: The watchdog exits cleanly. The polling loop's completion check or a direct call to
MarkSuccess()can trigger this. - On Timeout/Failure: If the action doesn't complete within the specified timeout, the watchdog invokes the
_resetRoutine(ResetRoutineAsyncinDomeDriver) to attempt a recovery. It also publishes failure messages to MQTT.
This mechanism prevents the driver from getting stuck in a Slewing state indefinitely if the controller fails to report completion.
The sequence for opening the shutter is a good example of this process.
Error Handling and Reset Mechanism
The driver features a sophisticated, multi-stage reset routine to recover from hardware or communication failures detected by the ActionWatchdog or other error conditions.
ResetRoutineAsync
This is the core recovery function. It is triggered when a command times out or fails. The routine proceeds as follows:
- Stop Polling: The main polling loop is stopped to prevent further commands.
- Attempt Soft Reset: If enabled (
SoftResetconfig flag), it sends a "RESTART" command to the controller. It then waits and checks the shutter status to see if the controller successfully rebooted. - Attempt Hard Reset: If the soft reset is disabled or fails, and if hard reset is enabled (
HardResetconfig flag), it proceeds to the next stage.- It disconnects the serial port.
- It executes an external application (
ResetExe) with "OFF" parameters to power-cycle the dome controller via an external relay. - It waits for a
CycleDelay. - It executes the same application with "ON" parameters.
- It waits for a
ResetDelayfor the controller to boot. - It attempts to reconnect to the serial port and ping the controller.
- Replay Command: If either reset method succeeds, the driver attempts to re-issue the last command that failed (
ReplayLastCommand). - Unrecoverable Failure: If the hard reset also fails, an alarm is raised via GNS and MQTT, and the driver remains in a failed state.
Configuration
All driver settings are managed by the ConfigManager class, which reads from and writes to the ASCOM Profile (stored in the Windows Registry). A graphical user interface, SettingsForm, is provided for easy configuration.
| Setting | Type | UI Control | Description | Source File |
|---|---|---|---|---|
SerialPort | string | comboBoxSerialPort | The COM port for the dome controller. | Config/ConfigManager.cs:81 |
LogLocation | string | txtBoxLogLocation | The directory path for log files. | Config/ConfigManager.cs:121 |
DebugLog | bool | chkBoxDebugLog | Enables detailed debug-level logging. | Config/ConfigManager.cs:221 |
TraceLog | bool | chkBoxTraceLog | Enables verbose trace-level logging for serial traffic. | Config/ConfigManager.cs:233 |
UseMQTT | bool | chkBoxMQTT | Enables/disables the MQTT integration. | Config/ConfigManager.cs:245 |
UseGNS | bool | chkBoxGNS | Enables/disables Goodnight System notifications. | Config/ConfigManager.cs:257 |
SoftReset | bool | checkBoxInternalReset | Enables the software "RESTART" command for recovery. | Config/ConfigManager.cs:87 |
HardReset | bool | checkBoxExternalReset | Enables the external power-cycle reset for recovery. | Config/ConfigManager.cs:99 |
ResetExe | string | txtBoxExternalExe | Path to the executable used for hard reset. | Config/ConfigManager.cs:127 |
ResetOnParameters | string | txtBoxOnParameters | Command-line arguments for the reset executable (ON state). | Config/ConfigManager.cs:133 |
ResetOffParameters | string | txtBoxOffParameters | Command-line arguments for the reset executable (OFF state). | Config/ConfigManager.cs:139 |
ShutterTimeout | int | txtBoxShutterTimeout | Timeout in milliseconds for shutter open/close operations. | Config/ConfigManager.cs:169 |
RotationTimeout | int | txtBoxRotationTimeout | Timeout in milliseconds for dome rotation/homing operations. | Config/ConfigManager.cs:187 |
Sources: Config/ConfigManager.cs, UI/SettingsForm.cs
External Integrations
The driver can communicate with other systems for status reporting and notifications.
MQTT
If UseMQTT is enabled, the MqttPublisher class is initialized. It connects to a local MQTT broker and publishes messages on various topics:
Dome/DriverStatus: Driver connection status ("Driver connected", "Driver disconnected").Dome/Dome/Status: Dome movement status and completion messages.Dome/Shutter/Status: Shutter movement status and completion messages.Dome/Alarm: Published when an unrecoverable error occurs.Dome/Watchdog: A periodic heartbeat message to confirm the driver is alive.
Sources: MQTT/MqttPublisher.cs, Driver/Pulsar_DomeDriver.cs:85-91, Driver/Pulsar_DomeDriver.cs:1300-1335
GNS (Goodnight System)
If UseGNS is enabled, the GNS class sends formatted messages to a named pipe called GNSBridge. This allows integration with the Goodnight System for displaying status messages and alarms to the user. Different message types (Message, New, Alarm, Stop, Cease) are used to control the GNS display.
Sources: GNS/GNS.cs, Driver/Pulsar_DomeDriver.cs:94-95