The General Principles

Drivers are system components that are responsible for hiding all device dependencies from applications via a standard interface.

On the surface, you may say "Of course!", but things can become subtle. Let's say your dome control system derives its ShutterStatus from some sensors and the motor state. Now an application calls Dome.OpenShutter() and then starts checking Dome.ShutterStatus to monitor the operation. But in your system, the dome takes a half second to start moving, so your driver reports ShutterClosed for a half second. This is a lie to the application, in fact the shutter is in ShutterOpening state. The application will be faced with a result it doesn't expect: "I asked it to open the shutter, it didn't raise an exception, so I should be seeing ShutterOpening or, if it's super fast, ShutterOpen. What happened?" It is 100% reasonable for an application to flag a problem when faced with such conflicting info. The real problem is that your driver did not hide the specifics of the shutter control system from the application. Immediately after the call to OpenShutter() returns, the only correct states are ShutterOpening with Slewing true, or ShutterOpen with Slewing false. There must not be any "little bit of time" during which the device will lie to the application through its interface.

It's important to consider what aspects of your target device are exposed to clients. With this in mind, you should review the relevant specification to make sure you aren't exposing low-level limitations or unique requirements to clients. Some of the other sections listed on the left address a few specific instances of this that seem to come up commonly.

Do it correctly or raise an exception.

For Applications

For app developers the consequences of this principle relate to expectations. If you make a call to a method and it returns normally, you can be 100% certain that the device accepted responsibility for doing what you requested, it started the process, and it expects to be able to complete the request. ASCOM operations are asynchronous(*) so calling a method starts the process. Each method is associated with a completion property which indicates when the process completes later.

For example, Dome.SlewToAzimuth() starts the dome moving, and Dome.Slewing starts out True, then later changes to False when the dome rotation to the new azimuth completes successfully. If the process fails for any reason, you will get a run-time error, an exception. This includes both the initial method call as well as reading the completion property. Using the Dome.SlewToAzimuth() method example, if it successfully starts, then later fails, reading Dome.Slewing will not return a value, but instead result in a raised exception. The intepretation is "I cannot tell you the answer because something went wrong."

It is the application's responsibility to avoid asking a device to do impossible tasks, within its abaility. One application developer's approach, for example, is to provide the application with its own horizon curve and altitude limit that must be set to prevent asking the mount to do impossible things and to prevent observing through trees, buildings, etc. However, as described below, it is the driver's responsibility to protect itself against requests that would cause damage or degrade performance.

As an application developer, then, you must be prepared to get an exception back from all members of all APIs, including both methods and properties. This is consistent with computing in general within modern ecosystems which use exceptions for error signalling.

For Drivers

This is the basic "contract" between a driver and its client application. Your driver absolutely must either do what it is asked (including providing a correct and current answer) or raise an exception for the client to trap. Unless the client gets an exception, it will always assume you have done what it requested and continue in its logic under that assumption. This means that the driver must not return "illegal" (sentinel) values for properties in lieu of signaling an error, or "try" to do something and return whether it was done or not. Self-destructive behavior is also prohibited (see below). All modern programming environments operate using out-of-band run time exception signalling.

Here too, things can be subtle. Using Telescope.SlewToCoordinatesAsync() as an example. It is suposed to start the slew (at which time Slewing becomes true): If the mount knows at that time that it cannot possibly reach the requested coordinates due to the destination being outside the mount's configured or physical limits, then SlewToCoordinatesAsync() must immediately raise an exception "I can't possibly do this. It's beyond my limits". That leaves unforeseen error conditions during the slew... cable snag, wall contact, insufficient power to overcome weight or friction, etc. In that case, if such a condition occurs, the mount is simply in an error state requiring some sort of operator intervention. Any call to the compromised logic in the driver must raise an exception "I can't give you the answer or do what you want because I have a serious problem that needs your attention". For example, it would be fine for the driver to return its name or version while in an error state, but if a slew fails then Telescope.Slewing, RightAscension, Declination, Altitude, Azimuth, etc. must raise an exception.

Self Protection of Devices

A device should protect itself against an application's request that would cause damage or degrade its performance. The device must refuse such requests by raising an exception.

For example, if an app asks a mount to slew to coordinates that would result in a collision with the pier or a wall, the mount should refuse to accept the request, and instead raise an exception. Less obviously, consider a mount with an internal pointing model. It may be that an app calling SyncToCoordinates() would corrupt the pointing model, degrading its accuracy. In this case, the mount should refuse the call to SyncToCoordinates() with an error message something like "Cannot sync because a pointing model is in effect. consider rebuilding the model to restore accuracy.