Skip to content
Snippets Groups Projects
Commit d78de86d authored by Drew Devereux's avatar Drew Devereux
Browse files

Merge branch 'MCCS-163' into 'master'

[MCCS-163] Update DeviceStateMachine with hardware power support

See merge request ska-telescope/lmc-base-classes!28
parents 7e07894c 13666a0c
No related branches found
No related tags found
No related merge requests found
Showing
with 578 additions and 184 deletions
# GitLab CI in conjunction with GitLab Runner can use Docker Engine to test and build any application. # GitLab CI in conjunction with GitLab Runner can use Docker Engine to test and build any application.
# Docker, when used with GitLab CI, runs each job in a separate and isolated container using the predefined image that is set up in .gitlab-ci.yml. # Docker, when used with GitLab CI, runs each job in a separate and isolated container using the predefined image that is set up in .gitlab-ci.yml.
# In this case we use the latest python docker image to build and test this project. # In this case we use the latest python docker image to build and test this project.
image: nexus.engageska-portugal.pt/ska-docker/ska-python-buildenv:9.3.2 image: nexus.engageska-portugal.pt/ska-docker/ska-python-buildenv:9.3.2.1
include: include:
- project: 'ska-telescope/templates-repository' - project: 'ska-telescope/templates-repository'
...@@ -29,7 +29,7 @@ stages: ...@@ -29,7 +29,7 @@ stages:
clean shell runner: clean shell runner:
stage: .pre stage: .pre
tags: tags:
- docker-executor - k8srunner
script: script:
# Gitlab CI badges creation # Gitlab CI badges creation
- rm -rf build/* - rm -rf build/*
...@@ -37,7 +37,7 @@ clean shell runner: ...@@ -37,7 +37,7 @@ clean shell runner:
build wheel for publication: # Executed on a tag build wheel for publication: # Executed on a tag
stage: build stage: build
tags: tags:
- docker-executor - k8srunner
script: script:
- python3 setup.py egg_info -b+$CI_COMMIT_SHORT_SHA sdist bdist_wheel - python3 setup.py egg_info -b+$CI_COMMIT_SHORT_SHA sdist bdist_wheel
rules: rules:
...@@ -49,7 +49,7 @@ build wheel for publication: # Executed on a tag ...@@ -49,7 +49,7 @@ build wheel for publication: # Executed on a tag
build wheel for development: # Executed on non-tagged commit build wheel for development: # Executed on non-tagged commit
stage: build stage: build
tags: tags:
- docker-executor - k8srunner
script: script:
- python3 setup.py egg_info -b+dev.$CI_COMMIT_SHORT_SHA sdist bdist_wheel - python3 setup.py egg_info -b+dev.$CI_COMMIT_SHORT_SHA sdist bdist_wheel
rules: rules:
...@@ -61,7 +61,7 @@ build wheel for development: # Executed on non-tagged commit ...@@ -61,7 +61,7 @@ build wheel for development: # Executed on non-tagged commit
unit tests: unit tests:
stage: test stage: test
tags: tags:
- docker-executor - k8srunner
script: script:
- echo $(ls -d ./dist/*.whl | grep $CI_COMMIT_SHORT_SHA) - echo $(ls -d ./dist/*.whl | grep $CI_COMMIT_SHORT_SHA)
- python3 -m pip install -U $(ls -d ./dist/*.whl | grep $CI_COMMIT_SHORT_SHA) - python3 -m pip install -U $(ls -d ./dist/*.whl | grep $CI_COMMIT_SHORT_SHA)
...@@ -74,7 +74,7 @@ unit tests: ...@@ -74,7 +74,7 @@ unit tests:
linting: linting:
stage: linting stage: linting
tags: tags:
- docker-executor - k8srunner
script: script:
- echo $(ls -d ./dist/*.whl | grep $CI_COMMIT_SHORT_SHA) - echo $(ls -d ./dist/*.whl | grep $CI_COMMIT_SHORT_SHA)
- python3 -m pip install -U $(ls -d ./dist/*.whl | grep $CI_COMMIT_SHORT_SHA) - python3 -m pip install -U $(ls -d ./dist/*.whl | grep $CI_COMMIT_SHORT_SHA)
...@@ -86,7 +86,7 @@ linting: ...@@ -86,7 +86,7 @@ linting:
publish to nexus: publish to nexus:
stage: publish stage: publish
tags: tags:
- docker-executor - k8srunner
variables: variables:
TWINE_USERNAME: $TWINE_USERNAME TWINE_USERNAME: $TWINE_USERNAME
TWINE_PASSWORD: $TWINE_PASSWORD TWINE_PASSWORD: $TWINE_PASSWORD
...@@ -105,7 +105,7 @@ release docker image: ...@@ -105,7 +105,7 @@ release docker image:
before_script: before_script:
- docker login -u $DOCKER_REGISTRY_USERNAME -p $DOCKER_REGISTRY_PASSWORD $DOCKER_REGISTRY_HOST - docker login -u $DOCKER_REGISTRY_USERNAME -p $DOCKER_REGISTRY_PASSWORD $DOCKER_REGISTRY_HOST
tags: tags:
- docker-executor - k8srunner
script: script:
- make build - make build
- make push - make push
...@@ -118,7 +118,7 @@ release docker image: ...@@ -118,7 +118,7 @@ release docker image:
pages: pages:
stage: pages stage: pages
tags: tags:
- docker-executor - k8srunner
script: script:
- cp -R build public - cp -R build public
- mv public/htmlcov/* public - mv public/htmlcov/* public
......
release=0.6.6 release=0.7.0
tag=lmcbaseclasses-0.6.6 tag=lmcbaseclasses-0.7.0
# Use SKA python image as base image # Use SKA python image as base image
FROM nexus.engageska-portugal.pt/ska-docker/ska-python-buildenv:9.3.2 AS buildenv FROM nexus.engageska-portugal.pt/ska-docker/ska-python-buildenv:9.3.2.1 AS buildenv
FROM nexus.engageska-portugal.pt/ska-docker/ska-python-runtime:9.3.2 AS runtime FROM nexus.engageska-portugal.pt/ska-docker/ska-python-runtime:9.3.2.1 AS runtime
# create ipython profile to so that itango doesn't fail if ipython hasn't run yet # create ipython profile to so that itango doesn't fail if ipython hasn't run yet
RUN ipython profile create RUN ipython profile create
# TODO: move this dependency to ska-docker/docker/tango/ska-python-buildenv/requirements.txt
RUN python3 -m pip install --user pytest-forked
# Note: working dir is `/app` which will have a copy of our repo # Note: working dir is `/app` which will have a copy of our repo
# The pip install will be a "user installation" so update path to access console scripts # The pip install will be a "user installation" so update path to access console scripts
ENV PATH=/home/tango/.local/bin:$PATH ENV PATH=/home/tango/.local/bin:$PATH
......
...@@ -17,7 +17,7 @@ SHELL = /bin/bash ...@@ -17,7 +17,7 @@ SHELL = /bin/bash
# #
DOCKER_REGISTRY_USER:=tango-example DOCKER_REGISTRY_USER:=tango-example
PROJECT = lmcbaseclasses PROJECT = lmcbaseclasses
IMAGE_FOR_DIAGRAMS = nexus.engageska-portugal.pt/ska-docker/ska-python-buildenv:9.3.2.1
# #
# include makefile to pick up the standard Make targets, e.g., 'make build' # include makefile to pick up the standard Make targets, e.g., 'make build'
# build, 'make push' docker push procedure, etc. The other Make targets # build, 'make push' docker push procedure, etc. The other Make targets
...@@ -40,10 +40,21 @@ lint: ## lint lmcbaseclasses Python code ...@@ -40,10 +40,21 @@ lint: ## lint lmcbaseclasses Python code
pylint --output-format=pylint2junit.JunitReporter src/ska > build/reports/linting.xml pylint --output-format=pylint2junit.JunitReporter src/ska > build/reports/linting.xml
test-in-docker: build ## Build the docker image and run tests inside it. test-in-docker: build ## Build the docker image and run tests inside it.
@docker run $(IMAGE):$(VERSION) make test @docker run --rm $(IMAGE):$(VERSION) make test
lint-in-docker: build ## Build the docker image and run lint inside it. lint-in-docker: build ## Build the docker image and run lint inside it.
@docker run $(IMAGE):$(VERSION) make lint @docker run --rm $(IMAGE):$(VERSION) make lint
generate-diagrams-in-docker: ## Build the docker image and generate state machine diagrams inside it.
@docker run --rm -v $(PWD):/diagrams $(IMAGE_FOR_DIAGRAMS) bash -c "cd /diagrams && make generate-diagrams-in-docker-internals"
generate-diagrams-in-docker-internals: ## Generate state machine diagrams (within a container!)
test -f /.dockerenv # ensure running in docker container
apt-get update
apt-get install --yes graphviz graphviz-dev gsfonts pkg-config
python3 -m pip install pygraphviz
cd /diagrams/docs/source && python3 draw_state_machines.py
ls -lo /diagrams/docs/source/images/
help: ## show this help. help: ## show this help.
@grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
......
...@@ -25,6 +25,13 @@ The lmc-base-classe repository contains set of eight classes as mentioned in SKA ...@@ -25,6 +25,13 @@ The lmc-base-classe repository contains set of eight classes as mentioned in SKA
## Version History ## Version History
#### 0.7.0
- Separate adminMode state machine from opState state machine
- Add support for STANDBY opState
- Add Standby() and Disable() commands to SKABaseDevice
- Breaking behavioural changes to adminMode and opState state machines
- Breaking change to `_straight_to_state` method signature
#### 0.6.6 #### 0.6.6
- Documentation bugfix - Documentation bugfix
...@@ -36,7 +43,7 @@ The lmc-base-classe repository contains set of eight classes as mentioned in SKA ...@@ -36,7 +43,7 @@ The lmc-base-classe repository contains set of eight classes as mentioned in SKA
- Refactor state machine to use pytransitions library. - Refactor state machine to use pytransitions library.
- Minor behavioural change: Off() command is accepted in every obsState, rather - Minor behavioural change: Off() command is accepted in every obsState, rather
than only EMPTY obsState. than only EMPTY obsState.
- support `straight_to_state` shortcuts to simplify test setups - support `_straight_to_state` shortcuts to simplify test setups
- Refactor of state machine testing to make it more portable - Refactor of state machine testing to make it more portable
#### 0.6.3 #### 0.6.3
......
...@@ -10,6 +10,11 @@ SKA BaseDevice ...@@ -10,6 +10,11 @@ SKA BaseDevice
:maxdepth: 2 :maxdepth: 2
.. automodule:: ska.base.base_device .. automodule:: ska.base.base_device
.. autoclass:: ska.base.DeviceStateModel
:members:
:undoc-members:
.. autoclass:: ska.base.SKABaseDevice .. autoclass:: ska.base.SKABaseDevice
:members: :members:
:undoc-members: :undoc-members:
...@@ -2,35 +2,125 @@ ...@@ -2,35 +2,125 @@
State Machine State Machine
============= =============
The state machine modules implements SKA's two fundamental state machines: the The state machine module implements three fundamental SKA state
base device state machine, and the observation state machine. machines:
Base device state machine * the admin mode state machine
------------------------- * the operational state (opState, represented in TANGO devices by TANGO
The base device state machine provides basic state needed for all devices, state) state machine
covering initialisation, off and on states, and a fault state. This state * the observation state machine.
machine is implemented by all SKA Tango devices that inherit from these LMC
base classes, though some devices with standby power modes may need to Admin mode state machine
implement further states. ------------------------
The admin mode state machine allows for transitions between the five
administrative modes:
.. figure:: images/device_state_diagram.png
:width: 80% * NOT_FITTED: this is the lowest state of readiness, representing
:alt: Diagram of the device state machine, taken from SKA design devices that cannot be deployed without some external action, such as
documentation, showing the state machine as designed plugging hardware in or updating network settings.)
* RESERVED: the device is fitted but redundant to other devices. It is
Diagram of the device state machine, taken from SKA design ready to take over should other devices fail.
documentation, showing the state machine as designed * OFFLINE: the device has been declared by SKA operations not currently
to be used for operations (or whatever other function it provides)
* MAINTENANCE: the device cannot be used for science purposes but can be
.. figure:: images/BaseDeviceStateMachine.png operationed for engineering / maintenance purposes, such as testing,
debugging, etc
* ONLINE: the device can be used for science purposes.
The admin mode state machine allows for
* any transition between the modes NOT_FITTED, RESERVED and OFFLINE
(e.g. an unfitted device being fitted as a redundant or non-redundant
device, a redundant device taking over when another device fails, etc)
* any transition between the modes OFFLINE, MAINTENANCE and ONLINE (e.g.
an online device being taken offline or put into maintenance mode to
diagnose a fault, a faulty device moving between maintenance and
offline mode as it undergoes sporadic periods of diagnosis.
Diagrams of the admin mode state machine are shown below.
.. figure:: images/AdminModeStateMachine.png
:alt: Diagram of the admin mode state machine, as designed
Diagram of the admin mode state machine, as designed
.. figure:: images/AdminModeStateMachine_autogenerated.png
:alt: Diagram of the admin mode state machine, as implemented
Diagram of the admin mode state machine, automatically generated from
the implementation. The equivalence of this diagram to the diagram
above demonstrates that the machine has been implemented as designed.
Operational state state machine
-------------------------------
The operational state (opState) machine represents the operational state
of a SKA device. It is represented in TANGO devices using the TANGO
"state", so the states used are a subset of the TANGO states: INIT,
FAULT, DISABLE, STANDBY, OFF and ON.
* INIT: the device is currently initialising
* FAULT: the device has experienced an error from which it could not
recover.
* DISABLE: the device is in its lowest state of readiness, from which
it may take some time to become fully operational. For example, if the
device manages hardware, that hardware may be switched off.
* STANDBY: the device is unready, but can be made ready quickly. For
example, if the device manages hardware, that hardware may be in a
low-power standby mode.
* OFF: the device is fully operational but is not currently in use
* ON: the device is in use
The operational state state machine allows for:
* transition from INIT or FAULT into any of the three "readiness states"
DISABLE, STANDBY and OFF.
* all transitions between these three "readiness states" DISABLE,
STANDBY and OFF.
* transition between OFF and ON.
.. figure:: images/OperationStateMachine_decoupled.png
:alt: Diagram of the operational state state machine, as designed,
ignoring coupling with admin mode
Diagram of the operational state (opState) state machine, as
designed, ignoring coupling with admin mode
Unfortunately, operational state is inextricably coupled with admin
mode: there are admin modes that imply disablement, and operational
states such as ON should not be possible in such admin modes.
To facilitate this, the entire operational state state machine is
accessible only when the admin mode is ONLINE or MAINTENANCE. When in
any other admin mode, the only permitted operational states are INIT,
FAULT and DISABLE. This constraint is implemented into the operational
state state machine by
* three extra states: INIT_ADMIN, FAULT_ADMIN and DISABLED_ADMIN
* two extra transition triggers: "admin_on" and "admin_off", which allow
for transition between INIT and INIT_ADMIN; FAULT and FAULT_ADMIN; and
DISABLE and DISABLE_ADMIN.
This implementation minimises the coupling between admin mode and
operational state, allowing the two machines to be conceptualised almost
separately.
Diagrams of the operational state state machine are shown below.
.. figure:: images/OperationStateMachine_coupled.png
:alt: Diagram of the operational state state machine, as designed,
showing coupling with admin mode
Diagram of the operational state (opState) state machine, as
designed, showing coupling with admin mode
.. figure:: images/OperationStateMachine_autogenerated.png
:width: 80% :width: 80%
:alt: Diagram of the device state machine, automatically generated :alt: Diagram of the operational state state machine, as implemented
from the state machine as specified in code.
Diagram of the device state machine, automatically generated from the Diagram of the operational state state machine, automatically
state machine as specified in code. The equivalence of this diagram to generated from the implementation. The equivalence of this diagram
the diagram previous demonstrates that the machine has been to the diagram above demonstrates that the machine has been
implemented as designed. implemented as designed.
...@@ -39,31 +129,37 @@ Observation state machine ...@@ -39,31 +129,37 @@ Observation state machine
The observation state machine is implemented by devices that manage The observation state machine is implemented by devices that manage
observations (currently only subarray devices). observations (currently only subarray devices).
.. figure:: images/ADR-8.png .. figure:: images/ObservationStateMachine_adr8.png
:width: 80% :width: 80%
:alt: Diagram of the observation state machine, as decided and published in ADR-8. :alt: Diagram of the observation state machine, as decided and
published in ADR-8.
Diagram of the observation state machine, as decided and published in ADR-8. Diagram of the observation state machine, as decided and published in
ADR-8.
.. figure:: images/ObservationStateMachine.png .. figure:: images/ObservationStateMachine_autogenerated.png
:width: 80% :width: 80%
:alt: Diagram of the observation state machine, automatically generated from :alt: Diagram of the observation state machine, automatically
the state machine as specified in code. generated from the implementation
Diagram of the observation state machine, automatically generated from Diagram of the observation state machine, automatically generated from
the state machine as specified in code. The equivalance of this the implementation. The equivalance of this diagram to the diagram
diagram to the diagram previous demonstrates that the machine has been previous demonstrates that the machine has been implemented in
implemented in conformance with ADR-8. conformance with ADR-8.
API API
--- ---
.. toctree::
:maxdepth: 2
.. automodule:: ska.base.state_machine .. automodule:: ska.base.state_machine
:members: :members:
:undoc-members: :undoc-members:
.. autoclass:: OperationStateMachine
:members:
.. autoclass:: AdminModeStateMachine
:members:
.. autoclass:: ObservationStateMachine
:members:
"""
This module draws diagrams of the state machines.
Usage:
~/ska-src/lmc-base-classes$ docker run --rm -ti -v $PWD:/app continuumio/miniconda3 bash
(base) root@293f3b699c9b:/opt/project/src/ska/base# history
$ conda install --yes pygraphviz
$ pip install transitions
$ apt-get update && apt-get install gsfonts
$ cd /app/docs/source
$ python draw_state_machines.py
(Or see the top level Makefile)
"""
import importlib
import sys
from unittest import mock
from transitions.extensions import GraphMachine
# local import, so we can run this without installing the whole package
sys.path.append("../../src/ska/base")
import state_machine
def patch_in_graph_machine():
with mock.patch("transitions.Machine", GraphMachine):
mod = sys.modules["state_machine"]
importlib.reload(mod)
def draw(machine_class):
machine_name = machine_class.__name__
graphing_options = {
"title": f"\n{machine_name}",
"show_conditions": True,
"show_state_attributes": True,
"show_auto_transitions": False,
}
machine = machine_class(None, **graphing_options)
machine.get_graph().draw(f"images/{machine_name}_autogenerated.png", prog='dot')
if __name__ == "__main__":
patch_in_graph_machine()
draw(state_machine.OperationStateMachine)
draw(state_machine.AdminModeStateMachine)
draw(state_machine.ObservationStateMachine)
docs/source/images/AdminModeStateMachine.png

8.66 KiB

docs/source/images/AdminModeStateMachine_autogenerated.png

62.1 KiB

docs/source/images/BaseDeviceStateMachine.png

119 KiB

docs/source/images/ObservationStateMachine.png

210 KiB

docs/source/images/ObservationStateMachine_autogenerated.png

233 KiB

docs/source/images/OperationStateMachine_autogenerated.png

245 KiB

docs/source/images/OperationStateMachine_coupled.png

40.6 KiB

docs/source/images/OperationStateMachine_decoupled.png

21.9 KiB

docs/source/images/device_state_diagram.png

564 KiB

...@@ -3,19 +3,22 @@ __all__ = ( ...@@ -3,19 +3,22 @@ __all__ = (
"control_model", "control_model",
"state_machine", "state_machine",
"SKAAlarmHandler", "SKAAlarmHandler",
"SKABaseDevice", "SKABaseDeviceStateModel", "SKABaseDevice",
"DeviceStateModel",
"SKACapability", "SKACapability",
"SKALogger", "SKALogger",
"SKAMaster", "SKAMaster",
"SKAObsDevice", "SKAObsDevice",
"SKASubarray", "SKASubarrayStateModel", "SKASubarrayResourceManager", "SKASubarray",
"SKASubarrayStateModel",
"SKASubarrayResourceManager",
"SKATelState", "SKATelState",
) )
# Note: order of imports is important - start with lowest in the hierarchy # Note: order of imports is important - start with lowest in the hierarchy
# SKABaseDevice, and then classes that inherit from it # SKABaseDevice, and then classes that inherit from it
from .base_device import SKABaseDevice, SKABaseDeviceStateModel from .base_device import SKABaseDevice, DeviceStateModel
from .alarm_handler_device import SKAAlarmHandler from .alarm_handler_device import SKAAlarmHandler
from .logger_device import SKALogger from .logger_device import SKALogger
from .master_device import SKAMaster from .master_device import SKAMaster
...@@ -25,5 +28,7 @@ from .tel_state_device import SKATelState ...@@ -25,5 +28,7 @@ from .tel_state_device import SKATelState
from .obs_device import SKAObsDevice from .obs_device import SKAObsDevice
from .capability_device import SKACapability from .capability_device import SKACapability
from .subarray_device import ( from .subarray_device import (
SKASubarray, SKASubarrayStateModel, SKASubarrayResourceManager SKASubarray,
SKASubarrayStateModel,
SKASubarrayResourceManager,
) )
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment