Actor#

basecam includes a default implementation of an SDSS-style actor to provide an interface for the camera system. An actor is just a server of some kind (TCP/IP or other) that accepts commands directed to the camera system, performs the commanded action, and replies to the user. basecam uses CLU to provide the actor functionality.

The default actor implementation uses a JSON actor that receives commands that resemble Unix terminal line commands and replies with a JSON object. Creating an actor for a given camera system is easy

from basecam.actor import CameraActor

class Actor(CameraActor):
    pass

host = 'localhost'
port = 8888
camera_system = CameraSystem()
actor = Actor(camera_system, host=host, port=port)
await actor.setup()
await actor.serve_forever()

camera_system must be an instantiated camera system. At this point the actor will be running on port 8888 of localhost and a client can connect to it over telnet or open a socket and issue commands.

$ telnet 127.0.0.1 8888
status
{
"header": {
    "command_id": 0,
    "commander_id": "8a278303-19bc-4d3b-ba33-a0fc33fdb267",
    "message_code": ">",
    "sender": "flicamera"
},
"data": {}
}
{
    "header": {
        "command_id": 0,
        "commander_id": "8a278303-19bc-4d3b-ba33-a0fc33fdb267",
        "message_code": "i",
        "sender": "flicamera"
    },
    "data": {
        "status": {
            "camera": "gfa0",
            "model": "MicroLine ML4240",
            "serial": "ML0112718",
            "fwrev": 0,
            "hwrev": 0,
            "hbin": 1,
            "vbin": 1,
            "visible_area": [
                0,
                0,
                2048,
                2048
            ],
            "image_area": [
                0,
                0,
                2048,
                2048
            ],
            "temperature_ccd": -25.0,
            "temperature_base": -10.0,
            "exposure_time_left": 0,
            "cooler_power": 60.0
        }
    }
}
{
    "header": {
        "command_id": 0,
        "commander_id": "8a278303-19bc-4d3b-ba33-a0fc33fdb267",
        "message_code": ":",
        "sender": "flicamera"
    },
    "data": {}
}

Note that the replies include an empty message with code > indicating that the command is running, and : when the command is done. See the message codes for more details.

It’s possible to build a camera actor with the same functionality but a different CLU base actor, for example AMQPActor or LegacyActor. To create a camera actor class from a different base actor

from clu import AMQPActor
from basecam.actor import BaseCameraActor

class NewActor(BaseCameraActor, AQMPActor):
    pass

The order of the imports is important, always subclass from BaseCameraActor first, and then from the specific CLU base actor. Then initialise the new actor with the parameters necessary for the CLU base actor user.

Default commands#

The following commands are provided by default for any camera actor. They cover all the default functionality provided by basecam. Some commands such as binning are only available if the camera system includes the corresponding mixin. basecam will automatically detect if that’s the case and add the command.

basecam#

basecam [OPTIONS] COMMAND [ARGS]...

area#

Controls the camera image area.

If called without an image area value, returns the current value. The image area must have the format (x0, x1, y0, y1) and be 1-indexed.

basecam area [OPTIONS] [CAMERAS]... [AREA]...

Options

-r, --reset#

Restores the original image area.

Default:

False

Arguments

CAMERAS#

Optional argument(s)

AREA#

Optional argument(s)

binning#

Controls the camera binning.

If called without a binning value, returns the current value.

basecam binning [OPTIONS] [CAMERAS]... [BINNING]...

Arguments

CAMERAS#

Optional argument(s)

BINNING#

Optional argument(s)

expose#

Exposes and writes an image to disk.

basecam expose [OPTIONS] [CAMERA_NAMES]... [EXPTIME]

Options

--object#

Takes an object exposure.

Default:

True

--flat#

Takes a flat exposure.

Default:

False

--dark#

Takes a dark exposure.

Default:

False

--bias#

Takes a bias exposure.

Default:

False

-f, --filename <filename>#

Filename of the imaga to save.

-n, --num <num>#

Sequence number for this exposure filename.

-s, --stack <stack>#

Number of images to stack.

Default:

1

-c, --count <count>#

Number of exposures to take.

--no-postprocess#

Skip the post-process step, if defined.

Arguments

CAMERA_NAMES#

Optional argument(s)

EXPTIME#

Optional argument

get-command-model#

Returns a dictionary representation of the command using unclick.

basecam get-command-model [OPTIONS] [COMMAND_NAME]

Arguments

COMMAND_NAME#

Optional argument

help#

Shows the help.

basecam help [OPTIONS] [PARSER_COMMAND]

Arguments

PARSER_COMMAND#

Optional argument

list#

Lists cameras connected to the camera system.

basecam list [OPTIONS]

Options

--available#

Lists available cameras.

ping#

Pings the actor.

basecam ping [OPTIONS]

reconnect#

Reconnects a camera.

basecam reconnect [OPTIONS] [CAMERAS]...

Options

-t, --timeout <timeout>#

Seconds to wait until disconnect or reconnect command times out.

Default:

5.0

Arguments

CAMERAS#

Optional argument(s)

set-default#

Set default cameras.

basecam set-default [OPTIONS] [CAMERAS]...

Options

-f, --force#

Forces a camera to be set as default even if it is not connected.

Arguments

CAMERAS#

Optional argument(s)

shutter#

Controls the camera shutter.

If called without a shutter position flag, returns the current position of the shutter.

basecam shutter [OPTIONS] [CAMERAS]...

Options

--open#

Open the shutter.

--close#

Close the shutter.

Arguments

CAMERAS#

Optional argument(s)

status#

Returns the status of a camera.

basecam status [OPTIONS] [CAMERAS]...

Arguments

CAMERAS#

Optional argument(s)

temperature#

Controls the camera temperature.

If called without a temperature value, returns the current temperature.

basecam temperature [OPTIONS] [CAMERAS]... [TEMPERATURE]

Arguments

CAMERAS#

Optional argument(s)

TEMPERATURE#

Optional argument

Expose post-process hook#

In expose sometimes one wants to perform a final post-process step after the image has been written to disk but before the command is finished. For example, we may want to analyse the image and report the mean value. The expose command includes a post-process hook that allows to set a callback to call when the the exposure has been written

async def report_median(command, exposure):
    mean = exposure.data.mean()
    command.info(text=f"Image mean value is {mean:.2f}")

class MyActor(CameraActor):

    def __init__(self, *args, **kwargs):
        ...
        self.context_obj['post_process_callback'] = report_median

Here we use the context_obj dictionary that CLU passes to all the commands and we set the post_process_callback parameter with the coroutine we want to call. The hook in expose will then invoke the callback with the command and the Exposure object.

Adding new commands#

To add new commands to the actor command parser import the camera_parser and define new commands

import asyncio
import click
from basecam.actor.commands import camera_parser

@camera_parser.command()
@click.option('--now', is_flag=True, help='Reboot without delay')
async def reboot(command, cameras, now):
    if not now:
        asyncio.sleep(1.0)
    for camera in cameras:
        await camera.reboot()
        command.info(reboot={'camera': camera.name, 'text': 'Reboot started'})
    command.finish()

The new actor command always receives a CLU Command as the first argument and a list of connected cameras as the second argument. It’s possible to access the actor instance as command.actor and the camera system as command.actor.camera_system. For more details, refer to CLU’s parser documentation.

Schema#

basecam defines a data model for the actor replies as a JSONSchema file. A summary of the schema is given below. When a command issues a reply, the contents are validated against the schema and an error will be generated if the validation fails. The message is not output in that case.

It’s possible to opt out of the schema validation by instantiating the CameraActor (or any other subclass of BaseCameraActor) with schema=None.

When adding new commands, you will need to extend the schema and pass it to the camera actor. To do so, first download the default schema and extend it. For our reboot example we would need to add the following text

"reboot": {
    "type": "object",
    "properties": {
    "camera": { "type": "string" },
    "text": { "type" "string" },
    "additionalProperties": false
}

Then do

actor = CameraActor(camera_system, schema='schema.json', host=..., port=...)

Alternatively it’s also possible to use get_schema to get the basecam schema and then append to it

from basecam.actor import get_schema

schema = get_schema()
schema['properties']['reboot'] = {
    "type": "object",
    "properties": {
    "camera": { "type": "string" },
    "text": { "type" "string" },
    "additionalProperties": false
}

actor = CameraActor(camera_system, schema=schema, ...)

An actor command can also manually opt out of validating a specific message by passing validate=False

command.info(reboot={'camera': camera.name, 'text': 'Reboot started'}, validate=False)

Default schema#

type

object

properties

  • error

An error message from the system or one of the cameras

oneOf

type

object

properties

  • camera

type

string

  • error

type

string

additionalProperties

False

type

string

  • default_cameras

Current default cameras

type

array

items

type

string

  • cameras

Connected cameras

type

array

items

type

string

  • camera_connected

Name or UID of a newly connected camera

type

string

  • camera_disconnected

Name or UID of a newly disconnected camera

type

string

  • status

Status parameters for the given camera

type

object

properties

  • camera

type

string

additionalProperties

True

  • exposure_state

Status of current exposure

type

object

properties

  • camera

type

string

  • state

Current stage of the exposure

type

string

enum

idle, integrating, reading, done, aborted, failed, post_processing, post_process_failed

  • image_type

type

string

  • exposure_time

Exposure time for a single exposure (0 if none, short, or unknown)

type

number

  • remaining_time

Remaining time for the current exposure (0 if none, short, or unknown)

type

number

  • current_stack

Current exposure in the stack (0 if not exposing or value not updated)

type

integer

  • n_stack

Total number of exposures in the stack (0 if not exposing or value not updated)

type

integer

  • error

Description of an error while exposing or post-processing

type

string

additionalProperties

False

  • temperature

type

object

properties

  • camera

type

string

  • ccd_temp

Temperature of the CCD

type

number

additionalProperties

False

  • filename

Last written file

type

object

properties

  • camera

type

string

  • filename

type

string

additionalProperties

False

  • binning

Binning status in pixels

type

object

properties

  • camera

type

string

  • horizontal

type

integer

  • vertical

type

integer

additionalProperties

False

  • area

Area read from the CCD chip, in pixels

oneOf

type

object

properties

  • camera

type

string

  • area

type

array

maxLength

4

minLength

4

items

type

integer

additionalProperties

False

type

array

maxLength

5

minLength

5

items

type

string

type

integer

type

integer

type

integer

type

integer

  • shutter

Status of the shutter

properties

  • camera

type

string

  • shutter

type

string

enum

open, closed

additionalProperties

False

additionalProperties

False