Client Application

The client application (fcfClient) is a simple utility allowing to send commands to the Device Manager from the command line. In this context we use the words commands and events as synonyms. The fcfClient uses the standard interface module stdif and the application interface module fcfif to compose the payload of the messages. The fcfClient sends the messages using CII MAL request/reply.

$ fcfClient <serviceURI> <command> ["<parameters>"]

Where
      <serviceURI> destination of the command (e.g. zpb.rr://127.0.0.1:12081)
      <command>    command to be sent to the server (e.g. Init)
      <parameters> optional parameters of the command.

Warning

The URI shall not contain the ‘/’ at the end otherwise the client will hang trying to connect to a non existing server.

Note

You should create your software based on the provided template by following the procedure in the Getting Started guide, see Getting Started.

List of Commands

The commands (events) currently supported by the fcfClient utility are:

Client commands

Command

Parameters

Init

“”

Enable

“”

Disable

“”

GetState

“”

GetStatus

“”

Setup

“”

Recover

“”

Reset

“”

GetConfig

“”

DevNames

“”

DevInfo

“”

DevConfig

“<device id>”

DevStatus

“[<device id1>, … ,<device idn>]”

SetLogLevel

“<ERROR|INFO|DEBUG|TRACE>”

Simulate

“<device id1>, … ,[<device idn>]”

StopSim

“<device id1>, … ,[<device idn>]”

Ignore

“<device id1>, … ,[<device idn>]”

StopIgn

“<device id1>, … ,[<device idn>]”

HwInit

“<device id1>, … ,[<device idn>]”

HwEnable

“<device id1>, … ,[<device idn>]”

HwDisable

“<device id1>, … ,[<device idn>]”

HwReset

“<device id1>, … ,[<device idn>]”

StartDaq

“<daq id>”

StopDaq

“<daq id>”

Exit

“”

Warning

Due to the upgrade to CII, the payload of the SETUP command cannot be defined through a JSON file. For sending SETUP and other commands is better to use the Python interface.

Note

The HW commands like HwInit or HwEnable control the state of the controller associated to the device.

Examples

Note

The following examples assume the server is listening for incoming events under the URI zpb.rr://127.0.0.1:12081 in the local host.

Enabling debug level in the server

$ fcfClient zpb.rr://127.0.0.1:12081  SetLogLevel "DEBUG"

Initialising the server

$ fcfClient zpb.rr://127.0.0.1:12081  Init ""

Moving the server to Operational state

$ fcfClient zpb.rr://127.0.0.1:12081  Enable ""

Executing a Setup command from the command line

This is not possible in this version using fcfClient utility. Please use the FCF Shell instead.

FCF Shell (CLI)

The FCF provides an experimental command shell with commands aiming to simplify the interaction with the Device Manager. The FCF shell can be invoked issuing the command fcfcli. The FCF Shell is based on a generic asynchronous shell that it is included in the IFW core libraries.

Command Line Parameters

The fcfcli offers few command line parameters. If no parameters are specified, the fcfcli will use the default name services and use nomad/consul to obtain the correct IP and port numbers of the Device Manager. The fcfcli shell commands are not necessary using the same names as the MAL interfaces with the purpose to shorten the commands names. This is also because the name of the commands are the name of the class methods in Python. Commands are asynchronous so the shell can continue being used while the answer from the previous command is not yet received.

Parameter

optional

Description

–uri

no

if the URI is specified, the supcli will use it to connect to the server

–name

no

When using nomad, one could specify the name of the service instead of the URI

–module

yes

Custom interface library

–class_name

yes

Custom command class name

–timeout

yes

Timeout for CII MAL requests in ms [default 15000ms]

–log_level

yes

log level (ERROR, INFO, DEBUG) [default ERROR]

–help

yes

Show the usage message

Warning

The fcfcli shell assumes NOMAD/CONSUL services are up and running. If this is not the case then –uri parameter shall be used instead of –name.

Note

The fcfcli shell was created for the Device Manager but since it uses the standard interface, it can be used for any server implementing this interface, although only for the standard events like init, enable, disable, etc.

fcfcli --uri zpb.rr://134.171.3.48:30269
fcfSh>?
reply> = Available command list:
 - abortdaq
 - close
 - daqstatus
 - devconfig
 - devinfo
 - devnames
 - devstatus
 - devstatus_regex
 - devtype
 ...
 ...
 - stopdaq
 - switch_off
 - switch_on

Shell History

The FCF shells keeps its own history file under $HOME/.fcfSh.txt. The history can be accessed using the arrows keys.

Shell Completion

The FCF shell provides a completion capability for the supported commands using the Tab key.

alternate text

FCF Shell command completion.

The shell completion also gives information about the parameters of each command.

alternate text

FCF Shell online help of command parameters.

Supported Shell Commands

Command

Parameters

Description

abortdaq

<daqid>

Abort DAQ acquisition

close

<shutter device>

It closes a shutter

daqstatus

<daqid>

Get DAQ acquisition status

devconfig

<device>

Get the actual configuration of a device (yaml formatting)

devinfo

Get the list of devices managed by the server with their respective types.

devnames

Get the list of devices managed by the server

devstatus

<argv>

Get the status of all devices managed by the server.

devstatus_regex

<pattern>

Get the status of all devices managed by the server and it applies a filter using regular expressions. The output to be included in the reply is the one matching the pattern.

devtype

<device>

Get the associated type of the device

disable

sends the disable (stdif) event to the connected server.

enable

sends the enable (stdif) event to the connected server.

exit

It exits the server. The clien will lose the connection

get_config

Get the server configuration (yaml formatting)

help

print the list of supported commands

hw_disable

<argv>

sends the disable directly to the device controllers. The parameter is a variable list of devices.

hw_enable

<argv>

sends the enable directly to the device controllers. The parameter is a variable list of devices.

hw_init

<argv>

sends the init directly to the device controllers. The parameter is a variable list of devices.

hw_reset

<argv>

sends the reset directly to the device controllers. The parameter is a variable list of devices.

ignore

<device>

sends the ignore event to the connected server

init

sends the init (stdif) event to the connected server.

move

<motor>,<pos> [,<type>] [,<unit>] [,<aux_motor>]

It moves a motor to a target position

move_by_name

<motor>,<name>

It moves a motor using a named position

move_by_posangle

<motor>,<angle>

It moves a device by position angle

move_by_speed

<motor>,<speed>

It moves a motor in speed

move_piezo_in _user_units

<device>, <pos1>, <pos2> [,<pos3>]

It moves a piezo device in user units

move_piezo_in _bits

<device>, <bit1>, <bit2> [,<bit3>]

It moves a piezo device in bits

open

<shutter device>

It opens a shutter

recover

sends the recover (fcfif) event to the connected server.

reset

sends the reset (stdif) event to the connected server.

setloglevel

<level>, <logger>

It configure a new log level on the server.

setup_json_file

<file>

The setup commands uses a JSON file to set run-time parameters. The contents of the JSON file shall match the defined schema.

setup_json_string

<JSON string>

The setup commands uses a JSON format to set run-time parameters. The JSON string shall match the defined schema.

simulate

<device>

sends the simulate event to the connected server

start_track

<device>,<mode>

It start tracking for using a given mode

startdaq

<daqid>

Start DAQ acquisition

state

sends the GetState (stdif) event to the connected server

status

sends the GetStatus (stdif) event to the connected server

stop

sends the stop (stdif) event to the connected server.

stop_ignore

<device>

sends the stopign event to the connected server

stop_simulate

<device>

sends the stopsim event to the connected server

stop _track

<device>

It stops tracking

stopdaq

<daqid>

Stop DAQ acquisition

switch_off

<lamp device>

It switch off a lamp or an actuator device

switch_on

<lamp device>

It switch on a lamp or an actuator device

track_offset

<device>, <offset>

It applies an offset to the position during tracking

version

It prints out the version of the FCS

write_analog

<device>, <channel>, <value>

It writes an analog output signal managed by an IODev device

write_digital

<device> <channel>, <value>

It writes a digital output signal managed by an IODev device

write_integer

<device> <channel>, <value>

It writes an integer output signal managed by an IODev device

ctrl-d

Stop the shell

Warning

Since version 5.0.0-prep1 setup_spf has been deprecated from the FCF python client library.

using devnames command

fcsSh> devnames
reply> = shutter1, lamp1, motor1, drot1, adc1, piezo1
OK

using devstatus command

fcsSh> devstatus lamp1
reply> = ['lamp1.lcs.state = Operational', 'lamp1.lcs.substate = On', 'lamp1.lcs.intensity = 0.000000', '', 'OK']
fcsSh>

using devstatus_regex command

fcsSh> devstatus_regex lamp1\.
reply> = lamp1.lcs.state = Operational
lamp1.lcs.substate = Off
lamp1.lcs.intensity = 0.000000

fcsSh>

Note

The dot character shall be escaped for the python regular expression matching.

using switch_off command

fcsSh> switch_off lamp1
fcsSh> reply> = OK setup completed.
fcsSh>

using move command

fcsSh> move motor1,50
fcsSh> reply> = OK setup completed.
fcsSh>

using setup command

This example uses a test JSON file (test.json). For the JSON schema, see fcf_setup_schema_ref.

[{
"id": "shutter1", "param": {
    "shutter": {
            "action": "OPEN"
            }
    }
},
{
"id": "lamp1", "param": {
    "lamp": {
            "action": "OFF"
            }
    }
},
{
"id": "motor1", "param": {
    "motor": {
        "action": "MOVE_ABS",
        "pos": 50,
        "unit": "UU"
            }
    }
}]
fcsSh> setup_json_file test.json
fcsSh> reply> = OK setup completed.
fcsSh>
fcsSh> setup_json_string '[{"id": "shutter1", "param": { "shutter": {"action": "OPEN"}}}]'
fcsSh> reply> = OK setup completed.
fcsSh>

using DAQ commands

These commands are the implementation of the metadaq interface. For more information, please refer to the ECS Metadaq interface module. These commands shall be executed in sequence. First the start and then the stop.

Note

The FCF requires to be in operational to handle the DAQ commands.

Warning

The FCF requires that the DATAROOT variable is defined to properly create the metadaq files.

fcsSh> startdaq science
fcsSh> reply> = science
fcsSh>
fcfSh> daqstatus science
reply> = id: science
state: DaqState.Acquiring
files: ['/scratch/DATAROOT/fcs1_science_start.fits']
keywords:
fcsSh>
fcfSh> stopdaq science
reply> = id: science
files: ['/scratch/DATAROOT/fcs1_science_start.fits', '/scratch/DATAROOT/fcs1_science_stop.fits']
keywords:
fcsSh>

These FIT files shall be created on disk and they shall contain the FCF metadata information

fcfSh> daqstatus science
reply> = id: science
state: DaqState.Succeeded
files: ['/scratch/DATAROOT/fcs1_science_start.fits', '/scratch/DATAROOT/fcs1_science_stop.fits']
keywords:
fcsSh>

Note

The FCF python library converts the output from the metadaq commands to strings such that the users can easily understand it from a shell.

FCF Python Client Library

It is possible to communicate with the Device Manager through clients developed in Python. The FCF provides a library that simplifies the interaction with the Device Manager (clib). This is probably the more flexible way to interact with the Device Manager. The clib encapsulates the creation of the payload for the Setup command by providing predefined methods.

Users might want to interact directly with the server through the FCF ICD binding methods. This is, of course possible, but it is outside the scope of this library.

Note

This Python client library was added in version 2.

Error Handling

The clib reports as a RuntimeError exceptions that may be delivered by the Device Manager.

Classes

The clib library provides internal classes to build the buffer for each device. In addition, this library provides one class that encapsulates the user interface with the DeviceManager. This class is the DevmgrCommand class. The clib also provides an asynchronous version of the same class (DevmgrAsyncCommands) which does exactly the same but it implements coroutines.

DevmgrCommand

The constructor of the DevmgrCommand class support four parameters: uri, timeout, and setup_obj.

Parameter

Description

uri

This is URI of the device manger.

timeout

The timeout is optional and has a default of one minute, expressed in milliseconds.

default: 60000[ms]

setup_obj

Dedicated setup object used when sending setup of multiple devices. By using a custom setup buffer object, users can implement more complex setups. default: None

Unless the setup_obj is provided, the class handles an internal buffer object that is used to build the payload of the Setup command. Each time the command is executed, the buffer is reset. The user can add multiple device settings into the internal buffer before executing the setup command.

The methods names of the class are shown in the tables below. They try to be self explanatory.

Public Methods for building Setup Payload

Method

parameters

close

device (string)

open

device (string)

switch_on

device (string), intensity (float) and timer (integer).

switch_off

None

move

device (string), position (float), type (string), unit (string) and aux_motor (string)

move_by_name

device (string), namepos (string)

move_by_speed

device (string), speed (float)

move_by_posangle

device (string), angle (float) ONLY for derotators.

start_track

device (string), mode (string)

stop_track

device (string)

track_offset

device (string), offset (float)

move_piezo_in_user_units

device (string)

move_piezo_in_bits

device (string)

setup_json_file

json_file (string), keep (bool)

setup_json_string

json_str (string), keep (bool)

Methods for Command Interface

Method

parameters

devstatus

device list (argv)

devstatus_regex

pattern (string)

devconfig

device (string)

devnames

None

devinfo

None

devtype

device (string)

ignore

device list (argv)

simulate

device list (argv)

stop_ignore

device list (argv)

stop_simulate

device list (argv)

hw_init

device list (argv)

hw_enable

device list (argv)

hw_disable

device list (argv)

hw_reset

device list (argv)

state

None

status

None

get_config

None

init

None

enable

None

recover

None

disable

None

reset

None

stop

None

startdaq

id (string)

stopdaq

id (string)

abortdaq

id (string)

daqstatus

id (string)

setloglevel

level (string), logger (string)

Examples

Retrieving the Status

import ifw.fcf.clib.devmgr_commands as fcs

uri = "zpb.rr://127.0.0.1:12081"
fcsif = fcs.DevMgrCommands(uri)
print(fcsif.devstatus())

['shutter1.simulated = true', 'shutter1.lcs.state = Undefined', 'shutter1.lcs.substate = Undefined',
'lamp1.simulated = true', 'lamp1.lcs.state = Undefined', 'lamp1.lcs.substate = Undefined',
'lamp1.lcs.intensity = 0.000000', 'motor1.simulated = true', 'motor1.lcs.state = Undefined',
'motor1.lcs.substate = Undefined', 'motor1.lcs.pos_target = 0.000000',
'motor1.lcs.pos_actual = 0.000000', 'motor1.lcs.vel_actual = 0.000000',
'motor1.lcs.axis_enable = false', "motor1.pos_actual_name = ''",
'motor1.pos_enc = -2147483648', '', 'OK']

Executing a single Setup

import ifw.fcf.clib.devmgr_commands as fcs

uri = "zpb.rr://127.0.0.1:12081"
fcsif = fcs.DevMgrCommands(uri)
# Move single motor1 to absolute position 100 in user units
# Fill the internal setup buffer
fcsif.move("motor1", 100)

Using a custom buffer object

import ifw.fcf.clib.devmgr_commands as fcs
import ifw.fcf.clib.setup_buffer as sbuf

uri = "zpb.rr://127.0.0.1:12081"
fcsif = fcs.DevMgrCommands(uri)
buffer = sbuf.SetupBuffer(fcsif._cii)
buffer.add_shutter_open("shutter1")
buffer.add_lamp_switch_on_with_intensity("lamp1", 50)

# Performs the setup with the custom buffer object
fcsif._setup(buffer)

Using a custom buffer object with JSON files

In this case, it is loaded first a JSON file with the initial settings. Then, one setting is overwritten. At last, one additional setting is added to the buffer before it is sent to the server.

import ifw.fcf.clib.devmgr_commands as fcs
import ifw.fcf.clib.setup_buffer as sbuf

uri = "zpb.rr://127.0.0.1:12081"
fcsif = fcs.DevMgrCommands(uri)

# Get list of devices and their types from the server. This is needed when using SPF format
# and custom setup buffers.
fcsif._init_devtype()

# initialise setup buffer object
buffer = sbuf.SetupBuffer(fcsif._cii)

# Load json file
buffer.add_json_file(myfile.json)

# Convert from JSON to CII
buffer.json2object()

# Performs the setup with the custom buffer object
fcsif._setup(buffer)

JSON Schema

JSON is used to compose the payload of the setup command. To minimize possible errors, the client python library validates the payload against a defined schema before sending the command to the server. The FCF provides a schema that covers all standard devices. Instrument implementing custom devices shall extend this schema definition.

The actions in the schema associated to each device are:

Actions Summary per Device

Device

Action

Level1

Level2

shutter

OPEN

shutter

CLOSE

lamp

ON

intensity

lamp

ON

time

lamp

OFF

actuator

ON

actuator

OFF

motor

MOVE_ABS

unit = “UU”

pos

motor

MOVE_ABS

unit = “ENC”

enc

motor

MOVE_REL

unit = “UU”

pos

motor

MOVE_REL

unit = “ENC”

enc

motor

MOVE_BY_NAME

name

motor

MOVE_BY_SPEED

speed

drot

MOVE_ABS

unit = “UU”

pos

drot

MOVE_ABS

unit = “ENC”

enc

drot

MOVE_REL

unit = “ENC”

enc

drot

MOVE_BY_NAME

name

drot

MOVE_BY_POSANG

posang

drot

MOVE_BY_SPEED

speed

drot

START_TRACK

mode

drot

START_TRACK

posang

drot

STOP_TRACK

drot

TRACK_OFFSET

offset

adc

MOVE_ABS

unit = “UU”

pos

adc

MOVE_ABS

unit = “UU”

axis

adc

MOVE_ABS

unit = “ENC”

enc

adc

MOVE_ABS

unit = “ENC”

axis

adc

MOVE_REL

unit = “UU”

pos

adc

MOVE_REL

unit = “UU”

axis

adc

MOVE_REL

unit = “ENC”

enc

adc

MOVE_REL

unit = “ENC”

axis

adc

MOVE_BY_NAME

name

adc

MOVE_BY_NAME

axis

adc

MOVE_BY_SPEED

speed

adc

MOVE_BY_SPEED

axis

adc

MOVE_BY_POSANG

posang

adc

START_TRACK

posang

adc

STOP_TRACK

piezo

SET_AUTO

piezo

SET_POS

piezo

SET_HOME

piezo

MOVE_ALL_POS

pos1

piezo

MOVE_ALL_POS

pos2

piezo

MOVE_ALL_POS

pos3

piezo

MOVE_ALL_BITS

bit1

piezo

MOVE_ALL_BITS

bit2

piezo

MOVE_ALL_BITS

bit3

iodev

SETOUT

channels

name

iodev

SETOUT

channels

signal

iodev

SETOUT

channels

value

JSON file examples

Lamp Example:

[{
{
"id": "lamp1", "param": {
    "lamp": {
            "action": "OFF"
            }
    }
},
{
"id": "lamp2", "param": {
    "lamp": {
            "action": "ON",
            "intensity": 100
            }
    }
},
{
"id": "lamp3", "param": {
    "lamp": {
            "action": "ON",
            "intensity": 100,
            "time": 3
            }
    }
},
}]

Motor Example:

[{
{
"id": "motor1", "param": {
    "motor": {
            "action": "MOVE_ABS",
            "unit": "UU",
            "pos": 40
            }
    }
},
{
"id": "motor2", "param": {
    "motor": {
            "action": "MOVE_REL",
            "unit": "UU",
            "pos": 1.5
            }
    }
},
{
"id": "motor3", "param": {
    "motor": {
            "action": "MOVE_BY_NAME",
            "name": "ON"
            }
    }
},
}]

Drot Example:

[{
{
"id": "drot1", "param": {
    "drot": {
            "action": "MOVE_ABS",
            "unit": "UU",
            "pos": 40
            }
    }
},
{
"id": "drot2", "param": {
    "drot": {
            "action": "MOVE_BY_NAME",
            "name": "ON"
            }
    }
},
{
"id": "drot3", "param": {
    "drot": {
            "action": "MOVE_BY_POSANG",
            "posang": 20
            }
    }
}
}]

Iodev Example:

[{
    "id": "iodev1", "param": {
            "iodev": {
                    "action": "SETOUT",
                    "channels": [
                    {
                        "name": "ch1",
                        "signal": "DIGITAL",
                        "value": true
                    },
                    {
                        "name": "ch2",
                        "signal": "DIGITAL",
                        "value": false
                    },
                    {
                        "name": "ch3",
                        "signal": "ANALOG",
                        "value": 2.1
                    }
                   ]
            }
    }
}]

The raw schema of the payload for the Setup command is here:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "title": "FCF Setup payload schema",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "id": {
        "type": "string",
        "description": "device identifier."
      },
      "param": {
        "$ref": "#/definitions/param"
      }
    }
  },
  "definitions": {
    "param": {
      "type": "object",
      "properties": {
        "shutter": {
          "$ref": "#/definitions/shutter"
        },
        "actuator": {
          "$ref": "#/definitions/actuator"
        },
        "lamp": {
          "$ref": "#/definitions/lamp"
        },
        "motor": {
          "$ref": "#/definitions/motor"
        },
        "drot": {
          "$ref": "#/definitions/drot"
        },
        "adc": {
          "$ref": "#/definitions/adc"
        },
        "piezo": {
          "$ref": "#/definitions/piezo"
        },
        "iodev": {
          "$ref": "#/definitions/iodev"
        }
      },
      "oneOf": [
                { "required":
                     [ "shutter" ] },
                { "required":
                     [ "actuator" ] },
                { "required":
                     [ "lamp" ] },
                { "required":
                     [ "motor" ] },
                { "required":
                     [ "drot" ] },
                { "required":
                     [ "adc" ] },
                { "required":
                     [ "piezo" ] },
                { "required":
                     [ "iodev" ] }
                ]
    },
    "shutter": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string",
          "enum": ["OPEN", "CLOSE"],
          "description": "Shutter action."
        }
      },
       "required": ["action"],
       "additionalProperties": false
    },
     "actuator": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string",
          "enum": ["ON", "OFF"],
          "description": "Actuator action."
        }
      },
       "required": ["action"],
       "additionalProperties": false
    },
    "lamp": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string",
          "enum": ["ON", "OFF"],
          "description": "Lamp action."
        },
        "intensity": {
          "type": "number",
          "minimum": 1,
          "maximum": 100,
          "description": "Lamp intensity."
        },
        "time": {
          "type": "integer",
          "minimum": 1,
          "description": "Lamp timer."
        }
      },
      "required": ["action"],
      "additionalProperties": false
    },
    "motor": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string",
          "enum": ["MOVE_ABS", "MOVE_REL", "MOVE_BY_NAME", "MOVE_BY_SPEED"],
          "description": "Motor action."
        },
        "pos": {
          "type": "number",
          "description": "Motor position in user units."
        },
        "enc": {
          "type": "integer",
          "description": "Motor position in encoders."
        },
        "unit": {
          "type": "string",
          "enum": ["UU", "ENC"],
          "description": "Motor position unit."
        },
        "name": {
          "type": "string",
          "description": "Motor named position."
        },
        "speed": {
          "type": "number",
          "description": "Motor speed."
        }
      },
      "allOf": [
                {
                   "if": {
                            "properties": {
                              "unit": { "const": "UU" },
                              "action": { "const": "MOVE_ABS"}
                            }
                          },
                   "then": {
                            "required": ["pos"],
                            "maxProperties": 3
                          }
                },
                {
                    "if": {
                            "properties": {
                              "unit": { "const": "ENC"},
                              "action": {"const": "MOVE_ABS"}
                            }
                          },
                          "then": {
                            "required": ["enc"],
                            "maxProperties": 3
                          }
                },
                {
                   "if": {
                            "properties": {
                              "unit": { "const": "UU" },
                              "action": { "const": "MOVE_REL"}
                            }
                          },
                   "then": {
                            "required": ["pos"],
                            "maxProperties": 3
                          }
                },
                {
                    "if": {
                            "properties": {
                              "unit": { "const": "ENC"},
                              "action": {"const": "MOVE_REL"}
                            }
                          },
                          "then": {
                            "required": ["enc"],
                            "maxProperties": 3
                          }
                },
                {
                  "if": {
                         "properties": { "action": { "const": "MOVE_ABS" } }
                        },
                  "then": {
                         "required": ["unit"],
                         "maxProperties": 3
                        }
                  },
                {
                  "if": {
                         "properties": { "action": { "const": "MOVE_REL" } }
                        },
                  "then": {
                         "required": ["unit"],
                         "maxProperties": 3
                        }
                  },
                {
                  "if": {
                         "properties": { "action": { "const": "MOVE_BY_SPEED" } }
                        },
                  "then": {
                         "required": ["speed"],
                         "maxProperties": 2
                        }
                },
                {
                  "if": {
                         "properties": { "action": { "const": "MOVE_BY_NAME" } }
                        },
                  "then": {
                         "required": ["name"],
                         "maxProperties": 2
                      }
                  }
               ],
      "required": ["action"],
      "additionalProperties": false
    },
    "drot": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string",
          "enum": ["MOVE_ABS", "MOVE_REL", "MOVE_BY_NAME", "MOVE_BY_SPEED", "MOVE_BY_POSANG", "START_TRACK", "TRACK_OFFSET", "STOP_TRACK"],
          "description": "Drot action."
        },
        "pos": {
          "type": "number",
          "description": "Motor position in user units."
        },
        "enc": {
          "type": "integer",
          "description": "Motor position in encoders."
        },
        "unit": {
          "type": "string",
          "enum": ["UU", "ENC"],
          "description": "Motor position unit."
        },
        "name": {
          "type": "string",
          "description": "Motor named position."
        },
        "speed": {
          "type": "number",
          "description": "Motor speed."
        },
        "posang": {
          "type": "number",
          "description": "Drot position angle."
        },
        "offset": {
          "type": "number",
          "description": "Drot tracking offset."
        },
        "mode": {
          "type": "string",
          "enum": ["ENG", "STAT", "SKY", "ELEV", "USER"],
          "description": "Drot mode."
        }
      },
      "allOf": [
            {
               "if": {
                        "properties": {
                          "unit": { "const": "UU" },
                          "action": { "const": "MOVE_ABS"}
                        }
                      },
               "then": {
                        "required": ["pos"],
                        "maxProperties": 3
                      }
            },
            {
                "if": {
                        "properties": {
                          "unit": { "const": "ENC"},
                          "action": {"const": "MOVE_ABS"}
                        }
                      },
                      "then": {
                        "required": ["enc"],
                        "maxProperties": 3
                      }
            },
            {
               "if": {
                        "properties": {
                          "unit": { "const": "UU" },
                          "action": { "const": "MOVE_REL"}
                        }
                      },
               "then": {
                        "required": ["pos"],
                        "maxProperties": 3
                      }
            },
            {
                "if": {
                        "properties": {
                          "unit": { "const": "ENC"},
                          "action": {"const": "MOVE_REL"}
                        }
                      },
                      "then": {
                        "required": ["enc"],
                        "maxProperties": 3
                      }
            },
            {
              "if": {
                     "properties": { "action": { "const": "MOVE_ABS" } }
                    },
              "then": {
                     "required": ["unit"],
                     "maxProperties": 3
                    }
              },
            {
              "if": {
                     "properties": { "action": { "const": "MOVE_REL" } }
                    },
              "then": {
                     "required": ["unit"],
                     "maxProperties": 3
                    }
              },
            {
            "if": {
                   "properties": { "action": { "const": "MOVE_BY_SPEED" } }
                  },
            "then": {
                   "required": ["speed"],
                   "maxProperties": 2
                  }
            },
            {
            "if": {
                   "properties": { "action": { "const": "MOVE_BY_NAME" } }
                  },
            "then": {
                   "required": ["name"],
                   "maxProperties": 2
                }
            },
            {
            "if": {
                   "properties": { "action": { "const": "MOVE_BY_POSANG" } }
                  },
            "then": {
                   "required": ["posang"],
                   "maxProperties": 2
                }
            },
            {
            "if": {
                   "properties": { "action": { "const": "START_TRACK" } }
                  },
            "then": {
                  "required": ["mode", "posang"],
                  "maxProperties": 3
               }
            },
            {
            "if": {
                   "properties": { "action": { "const": "STOP_TRACK" } }
                  },
            "then": {
                  "maxProperties": 1
               }
            },
            {
             "if": {
                   "properties": { "action": { "const": "TRACK_OFFSET" } }
                  },
            "then": {
                  "required": ["offset"],
                  "maxProperties": 2
             }
            }
         ],
      "required": ["action"],
      "additionalProperties": false
    },
    "adc": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string",
          "enum": ["MOVE_ABS", "MOVE_REL", "MOVE_BY_NAME", "MOVE_BY_SPEED", "MOVE_BY_POSANG", "START_TRACK", "STOP_TRACK"],
          "description": "Adc action."
        },
        "pos": {
          "type": "number",
          "description": "Motor position in user units."
        },
        "enc": {
          "type": "integer",
          "description": "Motor position in encoders."
        },
        "unit": {
          "type": "string",
          "enum": ["UU", "ENC"],
          "description": "Motor position unit."
        },
        "name": {
          "type": "string",
          "description": "Motor named position."
        },
        "speed": {
          "type": "number",
          "description": "Motor speed."
        },
        "posang": {
          "type": "number",
          "description": "Motor position angle."
        },
        "axis": {
          "type": "string",
          "enum": ["ADC1", "ADC2"],
          "description": "Adc axis."
        },
         "mode": {
          "type": "string",
          "enum": ["ENG", "OFF", "AUTO"],
          "description": "Adc mode."
        }
      },
      "allOf": [
            {
               "if": {
                        "properties": {
                          "unit": { "const": "UU" },
                          "action": { "const": "MOVE_ABS"}
                        }
                      },
               "then": {
                        "required": ["axis", "pos"],
                        "maxProperties": 4
                      }
            },
            {
                "if": {
                        "properties": {
                          "unit": { "const": "ENC"},
                          "action": {"const": "MOVE_ABS"}
                        }
                      },
                      "then": {
                        "required": ["axis", "enc"],
                        "maxProperties": 4
                      }
            },
            {
               "if": {
                        "properties": {
                          "unit": { "const": "UU" },
                          "action": { "const": "MOVE_REL"}
                        }
                      },
               "then": {
                        "required": ["axis", "pos"],
                        "maxProperties": 4
                      }
            },
            {
                "if": {
                        "properties": {
                          "unit": { "const": "ENC"},
                          "action": {"const": "MOVE_REL"}
                        }
                      },
                      "then": {
                        "required": ["axis", "enc"],
                        "maxProperties": 4
                      }
            },
            {
            "if": {
                   "properties": { "action": { "const": "MOVE_ABS" } }
                  },
            "then": {
                   "required": ["axis", "unit"],
                   "minProperties": 4
                  }
            },
            {
            "if": {
                   "properties": { "action": { "const": "MOVE_REL" } }
                  },
            "then": {
                   "required": ["axis", "unit"],
                   "minProperties": 4
                  }
            },
            {
            "if": {
                   "properties": { "action": { "const": "MOVE_BY_SPEED" } }
                  },
            "then": {
                   "required": ["axis", "speed"],
                   "maxProperties": 3
                  }
            },
            {
            "if": {
                   "properties": { "action": { "const": "MOVE_BY_NAME" } }
                  },
            "then": {
                   "required": ["axis", "name"],
                   "maxProperties": 3
                }
            },
            {
            "if": {
                   "properties": { "action": { "const": "MOVE_BY_POSANG" } }
                  },
            "then": {
                   "required": ["posang"],
                   "maxProperties": 2
                }
            },
            {
            "if": {
                   "properties": { "action": { "const": "START_TRACK" } }
                  },
            "then": {
                  "required": ["posang"],
                  "maxProperties": 2
               }
            },
            {
            "if": {
                   "properties": { "action": { "const": "STOP_TRACK" } }
                  },
            "then": {
                  "maxProperties": 1
               }
            }
         ],
      "required": ["action"],
      "additionalProperties": false
    },
    "piezo": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string",
          "enum": ["SET_AUTO", "SET_POS", "SET_HOME", "MOVE_ALL_BITS", "MOVE_ALL_POS"],
          "description": "Piezo action."
        },
        "pos1": {
          "type": "number",
          "description": "Piezo position 1 in volts."
        },
        "pos2": {
          "type": "number",
          "description": "Piezo position 2 in volts."
        },
        "pos3": {
          "type": "number",
          "description": "Piezo position 3 in volts."
        },
        "bit1": {
          "type": "integer",
          "description": "Piezo position 1 in bits."
        },
        "bit2": {
          "type": "integer",
          "description": "Piezo position 2 in bits."
        },
        "bit3": {
          "type": "integer",
          "description": "Piezo position 3 in bits."
        }
      },
      "required": ["action"],
      "additionalProperties": false
    },
    "iodev": {
      "type": "object",
      "properties": {
        "action": {
          "type": "string",
          "enum": ["SETOUT"],
          "description": "IODev action."
           },
         "channels": {
             "type": "array",
              "items": {
                 "type": "object",
                 "properties": {
                    "name": {
                        "name": "string"
                      },
                     "signal": {
                         "type": "string",
                         "enum": ["DIGITAL","ANALOG", "INTEGER"],
                         "description": "Signal type."
                     },
                    "value": {
                        "type": ["boolean","integer","number"]
                      }
                    },
                 "required": ["name","value"],
                "additionalProperties": false
                }
            }
         },
      "required": ["action"],
      "additionalProperties": false
    }
  }
}