_images/alpaca128.png

Introduction to Alpaca Drivers

Writing a successful device driver requires, first and foremost, a clear understanding of the role of a driver, and by extension, the responsibilities of the driver and the developer. There are subtleties and easily missed aspects. We can’t overstress the importance of starting a driver project with a clear view of the landscape. These documents will give you a good overview of driver development.

  1. ASCOM Initiative web site All things ASCOM and Alpaca

  2. Alpaca Developers Info See the Design Principles sections: General Principles, Asynchronous APIs, and Exceptions.

  3. Alpaca API Reference (PDF) General specifications for Alpaca network protocol and API

  4. Master Generic ASCOM Device Interface Specifications

Alpaca Device and Driver Architecture

An “Alpaca device” consists of a server which can host one or more drivers for multiple ASCOM devices of multiple types. For example, a single Alpaca device could provide the HTTP/REST communications for two ASCOM cameras and an ASCOM mount, all through the same IP address and port.

The device’s internal server is an HTTP (web) server that apps talk to using the Alpaca HTTP/REST protocol, and which dispatches endpoint requests as calls to the drivers for each device type and instance.

An ASCOM device driver consists of a set of responders for each Alpaca REST endpoint (represented by a unique URI), including the ones common to all ASCOM devices like Description and DriverVersion, as well as the ones specific to the ASCOM device type like Rotator.MoveMechanical(). Incoming REST requests are routed to their respective responders. The responder is responsible for performing the action or accessing the data represented by the endpoint.

Important

Each member of the ASCOM interface for a device is mapped one-for-one to an Alpaca REST endpoint and thence to a responder for that endpoint. This one-for-one mapping makes interoperation between ASCOM Alpaca and classic ASCOM/COM possible.

Each endpoint URI contains both the device type (its device name) and also a device number (the particular instance of that device type). The server is responsible for calling the responder for the device type and instance. Typically this is done via a routing table.

The Alpaca device’s server is additionally responsible for responding to Alpaca Discovery multicasts coming from clients. This is a really simple mechanism. It sends back a simple JSON response {AlpacaPort: *n*}. Together with this port number, and the server’s IP address in the HTTP response packet, the client now knows now to talk to the Alpaca server.

Once a client has found the Alpaca device, it can talk to its internal server to determine the types of ASCOM devices served, and the number of instances of each ASCOM device that are available (and some other metadata). This is done through three Management endpoints. These are typically used by client apps to select a specific device served [1]

Finally, device settings and configuration are optionally provided with a set of HTML web pages via the setup endpoint. Alternatively, for lightweight applications (like this sample) an alternative is to use a simple text config file.

Hint

For details see Alpaca API Reference (PDF)

Sample Driver Organization

In this sample the HTTP server function is provided by the lightweight Python wsgiref.simple_server combined with the The Falcon Web Framework. These two components together provide the REST API engine and endpoint URI-to-responder routing. The HTTP server and the Falcon back-end application are created on the main thread at app startup and run “forever”.

The responders for each Alpaca device API are kept in separate modules, one for the endpoints common to all device types, and the other for the device-specific endpoints. In this sample, these are common.py and rotator.py.

Note

Your main development effort will focus on the device-specific responder classes. Metadata elsewhere can be tailored quickly.

Routing of incoming requests to the responder classes is done with a simple function app.init_routes() which inspects each .py module containing responder classes, finds each class, constructs the endpoint URI template and then enters the URI and responder class into the Falcon routing table. Thus you do not need to manually create routes for responders. See The Falcon Web Framework.

Alpaca discovery is provided by a simple engine running in a separate thread. It is started at app startup, and runs “forever”. You should not need to edit this.

Management API is provided in a separate module and routing for the management endpoits is set up at app startup.

Logging is provided by the standard Python logger engine, with customizations for the logging format including ISO-8601/UTC time stamps and logging to a file (and optionally stdout). In addition, the HTTP server’s logging output, normally coming at the end of a request, is replaced with an HTTP request log at the beginning of the request so that it is in context with logged messages that may appear during processing of requests. The HTTP server is allowed to write the post-request log line for non-200 (OK) HTTP responses.

Finally the setup endpoint simply displays a static web page. Configuration for this lightweight sample uses a config file in Tom's Obvious Minimal Language. For details see Device Configuration Support. Of course you can provide your own web pages, or get really fancy and use Falcon support for Jinja-2.

Asynchronous Operations

All time-consuming device operations, such as slewing a mount, are implemented in Alpaca as asynchronous operations. While you may be familiar with async programming with an async/await type feature, the Alpaca base model is one of explicit endpoints acting as initiators and completion properties.

Attention

  • Your device and the responders in the driver must return promptly to every call.

  • This may surprise you, but if your device runs into trouble after successfully starting an operation, you should raise an exception when the client app later asks for the status of that operation. See Asynchronous APIs.

Handling Exceptions

It’s vital that your driver implement the prime directive for distributed systems:

Do it right or raise an Exception – ASCOM Initiative

For a detailed description of this vital principle as it applies to ASCOM and Alpaca, read through Exceptions in ASCOM. It will only take a few minutes. We’ve tried to make this as TL:DR-proof as we could.

Alpaca Exceptions

The JSON responses to all Alpaca requests include ErrorNumber and ErrorMessage members. If ErrorNumber is 0 then the client considers the request to have been a success (the ErrorMessage is ignored). Otherwise, a non-zero ErrorNumber in the JSON response tells the client that an Alpaca exception was raised (see Exceptions - Classes Defining Alpaca Exceptions). Alpaca API Reference (PDF) (Sec. 2.8) describes these Alpaca exceptions. Each one has a specific error number. The accompanying error message defaults to a generic descriptive message but you can override the message with something more detailed and helpful (recommended) when you instantiate the Apaca Exception class.

Note

These Alpaca exception classes should not be confused with raised Python exceptions such as ZeroDivisionError and RunTimeError which you can raise for internal device failures. See the next section.

Python Exceptions

Within your driver, your code may raise Python Exceptions. So how do you communicate a Python exception through your Alpaca API responder and back to the client? The Alpaca API Reference (PDF) specifies that the Alpaca DriverException should be used for all problems within the device and driver code. In this sample, the DriverException class is unique in that it accepts a Python exception object as would come from the ex of except Exception as ex:. See DriverException and Alpaca Exceptions.

Tip

The built-in exception handling in this template/sample is detailed in the Developer Roadmap, specifically Alpaca Exceptions.

Making this sample into your driver

When using this sample to make your own Alpaca device driver, follow this general set of steps.

Important

The Master Generic ASCOM Device Interface Specifications are the final word in interface definition, data types, exceptions, and behavior. Experiment with the Alpaca Omni Simulator OpenAPI interface to see how each endpoint is supposed to work.

  1. Familiarize yourself with The Falcon Web Framework specifically how incoming REST requests are routed to responders with the Request and Response objects.

  2. Run this sample, using the Conform Universal tool to generate traffic to all of the Rotator endpoints. Walk through the app startup in the Application Startup and Exception Handling with the debugger. See how the API endpoint URIs are registered to the responder classes in the init_routes() function. Walk through a GET request, then a PUT request. See how the Alpaca JSON responses are created by the PropertyResponse and MethodResponse classes. Look how the simulated rotator machine is started and runs in a separate class. Observe how locks are used to prevent conflicts in accesses between threads. In short, become very familiar with how this simulated device works.

  3. Using Rotator - Device-Specific Responders as a guide, and one of the Template Responder Class Modules provided create a module containing responder classes for each Alpaca endpoint of your device. Using the one for your device will be a big time saver!! Of course, if you’re making a Rotator driver you can use Rotator - Device-Specific Responders as a starting point.

  4. Look in Shared Utility Functions and Metadata for the DeviceMetadata static class. Edit the fields for your device. Generate your own unique ID using the Online GUID / UUID Generator.

  5. Adjust the user configuration file (config.toml) for the Title, IP/Port etc. Use this to store your device’s settings as well. See Device Configuration Support.

  6. Develop the low-level code to control your device. Try to design it so that it provides variables and functions that can be used by the Alpaca methods and properties. Obviously this is going to be the major portion of your work, followed by the time required to create the module containing the Alpaca endpoint responder classes (step 2 above).

  7. Wire up the device control code to the endpoint responder classes.

  8. Test and fix until your device passes the full Conform Universal tool’s test.

  9. Use the Alpaca Protocol Tester in ConformU to check your driver at the Alpaca protocol level (as opposed to the operational tests provided by the Conformance checker.)