From 8b4af12f8000cd5048cfe7a30af0c6f8cc753118 Mon Sep 17 00:00:00 2001 From: George Ryall Date: Thu, 16 May 2024 16:34:33 +0100 Subject: [PATCH 01/13] Creating template file structure in support submodule --- .gitattributes | 29 +++++++++++++ .gitignore | 21 +++++++++ LICENCE | 30 +++++++++++++ Makefile | 37 ++++++++++++++++ attocubeSup/Makefile | 10 +++++ attocubeSup/attocube.db | 19 +++++++++ attocubeSup/attocube.proto | 18 ++++++++ configure/CONFIG | 29 +++++++++++++ configure/CONFIG_SITE | 43 +++++++++++++++++++ configure/Makefile | 8 ++++ configure/RELEASE | 5 +++ configure/RULES | 6 +++ configure/RULES_DIRS | 2 + configure/RULES_TOP | 3 ++ documentation/devattocube.html | 78 ++++++++++++++++++++++++++++++++++ 15 files changed, 338 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENCE create mode 100644 Makefile create mode 100644 attocubeSup/Makefile create mode 100644 attocubeSup/attocube.db create mode 100644 attocubeSup/attocube.proto create mode 100644 configure/CONFIG create mode 100644 configure/CONFIG_SITE create mode 100644 configure/Makefile create mode 100644 configure/RELEASE create mode 100644 configure/RULES create mode 100644 configure/RULES_DIRS create mode 100644 configure/RULES_TOP create mode 100644 documentation/devattocube.html diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9585212 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,29 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.c text +*.cpp text +*.h text +*.hpp text +*.java text +*.sh text eol=lf +*.bat text +*.cmd text +*.db text +*.dbd text +*.template text +*.substitutions text +*.py text +*.rst text +*.uxf text + +# Declare files that will always have CRLF line endings on checkout. +*.sln text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.class binary +*.vi binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..43df806 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +O.*/ +/db +/bin +/dbd +/include +/lib +/templates +test-reports +envPaths +cdCommands +dllPath.bat +runIOC.bat +runIOC.sh +relPaths.sh +*.tag +/data/ +/doc/ +*_info_positions.req +*_info_settings.req +*.py[cod] +__pycache__/ diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..45da810 --- /dev/null +++ b/LICENCE @@ -0,0 +1,30 @@ +BSD 3-Clause License + +Copyright (c) 2024, Science and Technology Facilities Council +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..efd5f1f --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +# Makefile for Asyn attocube support +# +# Created by ooy64575 on Thu May 16 16:34:28 2024 +# Based on the Asyn streamSCPI template + +TOP = . +include $(TOP)/configure/CONFIG + +DIRS += configure +DIRS += $(wildcard *Sup) +DIRS += $(wildcard *App) +DIRS += $(wildcard *Top) +DIRS += $(wildcard iocBoot) + +# The build order is controlled by these dependency rules: + +# All dirs except configure depend on configure +$(foreach dir, $(filter-out configure, $(DIRS)), \ + $(eval $(dir)_DEPEND_DIRS += configure)) + +# Any *App dirs depend on all *Sup dirs +$(foreach dir, $(filter %App, $(DIRS)), \ + $(eval $(dir)_DEPEND_DIRS += $(filter %Sup, $(DIRS)))) + +# Any *Top dirs depend on all *Sup and *App dirs +$(foreach dir, $(filter %Top, $(DIRS)), \ + $(eval $(dir)_DEPEND_DIRS += $(filter %Sup %App, $(DIRS)))) + +# iocBoot depends on all *App dirs +iocBoot_DEPEND_DIRS += $(filter %App,$(DIRS)) + +# Add any additional dependency rules here: + +include $(TOP)/configure/RULES_TOP + +ioctests: + .\system_tests\run_tests.bat diff --git a/attocubeSup/Makefile b/attocubeSup/Makefile new file mode 100644 index 0000000..6e4342f --- /dev/null +++ b/attocubeSup/Makefile @@ -0,0 +1,10 @@ +TOP=.. +include $(TOP)/configure/CONFIG +#======================================= + +# Install .dbd and .db files +DB += attocube.db +DATA += attocube.proto + +#======================================= +include $(TOP)/configure/RULES diff --git a/attocubeSup/attocube.db b/attocubeSup/attocube.db new file mode 100644 index 0000000..4c7f7b3 --- /dev/null +++ b/attocubeSup/attocube.db @@ -0,0 +1,19 @@ +record(bo, "$(P)SIM") +{ + field(SCAN, "Passive") + field(DTYP, "Soft Channel") + field(ZNAM, "NO") + field(ONAM, "YES") + field(VAL, "$(RECSIM=0)") + field(PINI, "YES") +} + +record(bo, "$(P)DISABLE") +{ + field(DESC, "Disable comms") + field(PINI, "YES") + field(VAL, "$(DISABLE=0)") + field(OMSL, "supervisory") + field(ZNAM, "COMMS ENABLED") + field(ONAM, "COMMS DISABLED") +} diff --git a/attocubeSup/attocube.proto b/attocubeSup/attocube.proto new file mode 100644 index 0000000..3326465 --- /dev/null +++ b/attocubeSup/attocube.proto @@ -0,0 +1,18 @@ +getIDN { + out "*IDN?"; + in "%\$1[^\r\n]"; + ExtraInput = Ignore; +} + +cmd { + out "\$1"; +} + +setD { + out "\$1 %d"; +} +getD { + out "\$1?"; + in "%d"; + ExtraInput = Ignore; +} diff --git a/configure/CONFIG b/configure/CONFIG new file mode 100644 index 0000000..c1a4703 --- /dev/null +++ b/configure/CONFIG @@ -0,0 +1,29 @@ +# CONFIG - Load build configuration data +# +# Do not make changes to this file! + +# Allow user to override where the build rules come from +RULES = $(EPICS_BASE) + +# RELEASE files point to other application tops +include $(TOP)/configure/RELEASE +-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common +ifdef T_A +-include $(TOP)/configure/RELEASE.Common.$(T_A) +-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) +endif + +CONFIG = $(RULES)/configure +include $(CONFIG)/CONFIG + +# Override the Base definition: +INSTALL_LOCATION = $(TOP) + +# CONFIG_SITE files contain other build configuration settings +include $(TOP)/configure/CONFIG_SITE +-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common +ifdef T_A + -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A) + -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) +endif + diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE new file mode 100644 index 0000000..212485e --- /dev/null +++ b/configure/CONFIG_SITE @@ -0,0 +1,43 @@ +# CONFIG_SITE + +# Make any application-specific changes to the EPICS build +# configuration variables in this file. +# +# Host/target specific settings can be specified in files named +# CONFIG_SITE.$(EPICS_HOST_ARCH).Common +# CONFIG_SITE.Common.$(T_A) +# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) + +# CHECK_RELEASE controls the consistency checking of the support +# applications pointed to by the RELEASE* files. +# Normally CHECK_RELEASE should be set to YES. +# Set CHECK_RELEASE to NO to disable checking completely. +# Set CHECK_RELEASE to WARN to perform consistency checking but +# continue building even if conflicts are found. +CHECK_RELEASE = YES + +# Set this when you only want to compile this application +# for a subset of the cross-compiled target architectures +# that Base is built for. +#CROSS_COMPILER_TARGET_ARCHS = vxWorks-ppc32 + +# To install files into a location other than $(TOP) define +# INSTALL_LOCATION here. +#INSTALL_LOCATION= + +# Set this when the IOC and build host use different paths +# to the install location. This may be needed to boot from +# a Microsoft FTP server say, or on some NFS configurations. +#IOCS_APPL_TOP = + +# For application debugging purposes, override the HOST_OPT and/ +# or CROSS_OPT settings from base/configure/CONFIG_SITE +#HOST_OPT = NO +#CROSS_OPT = NO + +# These allow developers to override the CONFIG_SITE variable +# settings without having to modify the configure/CONFIG_SITE +# file itself. +-include $(TOP)/../CONFIG_SITE.local +-include $(TOP)/configure/CONFIG_SITE.local + diff --git a/configure/Makefile b/configure/Makefile new file mode 100644 index 0000000..9254309 --- /dev/null +++ b/configure/Makefile @@ -0,0 +1,8 @@ +TOP=.. + +include $(TOP)/configure/CONFIG + +TARGETS = $(CONFIG_TARGETS) +CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) + +include $(TOP)/configure/RULES diff --git a/configure/RELEASE b/configure/RELEASE new file mode 100644 index 0000000..7fc8365 --- /dev/null +++ b/configure/RELEASE @@ -0,0 +1,5 @@ +# optional extra local definitions here +-include $(TOP)/configure/RELEASE.private + +include $(TOP)/../../../ISIS_CONFIG +-include $(TOP)/../../../ISIS_CONFIG.$(EPICS_HOST_ARCH) diff --git a/configure/RULES b/configure/RULES new file mode 100644 index 0000000..6d56e14 --- /dev/null +++ b/configure/RULES @@ -0,0 +1,6 @@ +# RULES + +include $(CONFIG)/RULES + +# Library should be rebuilt because LIBOBJS may have changed. +$(LIBNAME): ../Makefile diff --git a/configure/RULES_DIRS b/configure/RULES_DIRS new file mode 100644 index 0000000..3ba269d --- /dev/null +++ b/configure/RULES_DIRS @@ -0,0 +1,2 @@ +#RULES_DIRS +include $(CONFIG)/RULES_DIRS diff --git a/configure/RULES_TOP b/configure/RULES_TOP new file mode 100644 index 0000000..d09d668 --- /dev/null +++ b/configure/RULES_TOP @@ -0,0 +1,3 @@ +#RULES_TOP +include $(CONFIG)/RULES_TOP + diff --git a/documentation/devattocube.html b/documentation/devattocube.html new file mode 100644 index 0000000..0dc95a3 --- /dev/null +++ b/documentation/devattocube.html @@ -0,0 +1,78 @@ + + + + + + attocube Instrument Support + + + + +

Using attocube instrument support in an application

+ +

Several files need minor modifications to use attocube instrument support in +an application.

+
    +
  1. Add the full path to the attocube support directory to the + application configure/RELEASE file:
    + attocube=xxxx/modules/instrument/attocube/<release>
    +Where <release> is the release number of of the attocube support.
  2. +
  3. Add stream and asyn support to application database definition file
    + The application database definition file must include the database + definition files for the stream package and for any needed ASYN + drivers. There are two ways that this can be done: +
      +
    • If you are building your application database definition file from + an xxxInclude.dbd file you include the additional database + definitions in that file:
      + include "base.dbd"
      + include "stream.dbd"
      + include "drvAsynIPPort.dbd"
    • +
    • If you are building your application database definition file from + the application Makefile you specify the aditional database + definitions there:
      + xxx_DBD += base.dbd
      + xxx_DBD += stream.dbd
      + xxx_DBD += drvAsynIPPort.dbd
    • +
    +
  4. +
  5. Add the stream and asyn support libraries to the application
    + You must link the stream support library and the ASYN support library + with the application. Add the following lines:
    + xxx_LIBS += stream
    + xxx_LIBS += asyn
    + before the
    + xxx_LIBS += $(EPICS_BASE_IOC_LIBS)
    + in the application Makefile.
  6. +
  7. Load the attocube support database records in the application startup script:
    + cd $(attocube)      (cd attocube if using the vxWorks shell)
    + dbLoadRecords("db/devattocube.db,"P=<P>,R=<R>,PORT=<PORT>,A=<A>")
    + You'll have to provide appropriate values for the PV name prefixes + (<P> and <R>), the port name (<PORT>) and the device address + (<A>). The port name must match the value specified in + an ASYN drvxxxxxConfigure command. +
  8. +
+

Installation and Building

+After obtaining a copy of the distribution, it must be installed and built +for use at your site. +
    +
  1. Create an installation directory for the module. The path name + of this directory should end with modules/instrument/attocube.
  2. +
  3. Place the distribution file into this directory.
  4. +
  5. Execute the following commands:
    + cd modules/instrument/attocube
    + gunzip attocube<release>.tar.gz
    + tar xvf attocube<release>.tar
    + cd <release>
    +Where <release> is the release number of of the attocube support. +
  6. +
  7. Edit the configure/RELEASE file and set the paths to your + installation of EPICS base, stream and ASYN support modules.
  8. +
  9. Execute make in the top level directory.
  10. +
+ + + + From 7f7810bb48f5a2448a960f6b08246d63c1e91569 Mon Sep 17 00:00:00 2001 From: George Ryall Date: Thu, 16 May 2024 16:35:29 +0100 Subject: [PATCH 02/13] Add device to test framework --- system_tests/run_tests.bat | 13 +++++++++++++ system_tests/tests/__init__.py | 0 system_tests/tests/attocube.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 system_tests/run_tests.bat create mode 100644 system_tests/tests/__init__.py create mode 100644 system_tests/tests/attocube.py diff --git a/system_tests/run_tests.bat b/system_tests/run_tests.bat new file mode 100644 index 0000000..56f9c25 --- /dev/null +++ b/system_tests/run_tests.bat @@ -0,0 +1,13 @@ +@echo off +REM Run this directory's tests using the IOC Testing Framework + +SET CurrentDir=%~dp0 + +call "%~dp0..\..\..\..\config_env.bat" + +set "PYTHONUNBUFFERED=1" + +REM Command line arguments always passed to the test script +SET ARGS=--test_and_emulator %~dp0 +call %PYTHON3% "%EPICS_KIT_ROOT%\support\IocTestFramework\master\run_tests.py" %ARGS% %* +IF %ERRORLEVEL% NEQ 0 EXIT /b %errorlevel% diff --git a/system_tests/tests/__init__.py b/system_tests/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/system_tests/tests/attocube.py b/system_tests/tests/attocube.py new file mode 100644 index 0000000..2eea54b --- /dev/null +++ b/system_tests/tests/attocube.py @@ -0,0 +1,34 @@ +import unittest + +from utils.channel_access import ChannelAccess +from utils.ioc_launcher import get_default_ioc_dir +from utils.test_modes import TestModes +from utils.testing import get_running_lewis_and_ioc, skip_if_recsim + + +DEVICE_PREFIX = "ATTOCUBE_01" + + +IOCS = [ + { + "name": DEVICE_PREFIX, + "directory": get_default_ioc_dir("ATTOCUBE"), + "macros": {}, + "emulator": "Attocube", + }, +] + + +TEST_MODES = [TestModes.RECSIM, TestModes.DEVSIM] + + +class AttocubeTests(unittest.TestCase): + """ + Tests for the Attocube IOC. + """ + def setUp(self): + self._lewis, self._ioc = get_running_lewis_and_ioc("Attocube", DEVICE_PREFIX) + self.ca = ChannelAccess(device_prefix=DEVICE_PREFIX) + + def test_that_fails(self): + self.fail("You haven't implemented any tests!") From a664bbc205615e282312e9f2f02c6ce06a62fe27 Mon Sep 17 00:00:00 2001 From: George Ryall Date: Thu, 16 May 2024 16:35:40 +0100 Subject: [PATCH 03/13] Add template emulator --- .../lewis_emulators/Attocube/__init__.py | 5 +++ .../lewis_emulators/Attocube/device.py | 25 +++++++++++++++ .../Attocube/interfaces/__init__.py | 3 ++ .../Attocube/interfaces/stream_interface.py | 32 +++++++++++++++++++ .../lewis_emulators/Attocube/states.py | 5 +++ system_tests/lewis_emulators/__init__.py | 0 .../lewis_emulators/lewis_versions.py | 2 ++ 7 files changed, 72 insertions(+) create mode 100644 system_tests/lewis_emulators/Attocube/__init__.py create mode 100644 system_tests/lewis_emulators/Attocube/device.py create mode 100644 system_tests/lewis_emulators/Attocube/interfaces/__init__.py create mode 100644 system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py create mode 100644 system_tests/lewis_emulators/Attocube/states.py create mode 100644 system_tests/lewis_emulators/__init__.py create mode 100644 system_tests/lewis_emulators/lewis_versions.py diff --git a/system_tests/lewis_emulators/Attocube/__init__.py b/system_tests/lewis_emulators/Attocube/__init__.py new file mode 100644 index 0000000..08cc450 --- /dev/null +++ b/system_tests/lewis_emulators/Attocube/__init__.py @@ -0,0 +1,5 @@ +from .device import SimulatedAttocube +from ..lewis_versions import LEWIS_LATEST + +framework_version = LEWIS_LATEST +__all__ = ['SimulatedAttocube'] diff --git a/system_tests/lewis_emulators/Attocube/device.py b/system_tests/lewis_emulators/Attocube/device.py new file mode 100644 index 0000000..4432b72 --- /dev/null +++ b/system_tests/lewis_emulators/Attocube/device.py @@ -0,0 +1,25 @@ +from collections import OrderedDict +from .states import DefaultState +from lewis.devices import StateMachineDevice + + +class SimulatedAttocube(StateMachineDevice): + + def _initialize_data(self): + """ + Initialize all of the device's attributes. + """ + pass + + def _get_state_handlers(self): + return { + 'default': DefaultState(), + } + + def _get_initial_state(self): + return 'default' + + def _get_transition_handlers(self): + return OrderedDict([ + ]) + diff --git a/system_tests/lewis_emulators/Attocube/interfaces/__init__.py b/system_tests/lewis_emulators/Attocube/interfaces/__init__.py new file mode 100644 index 0000000..8e748bf --- /dev/null +++ b/system_tests/lewis_emulators/Attocube/interfaces/__init__.py @@ -0,0 +1,3 @@ +from .stream_interface import AttocubeStreamInterface + +__all__ = ['AttocubeStreamInterface'] diff --git a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py new file mode 100644 index 0000000..3ee7fa1 --- /dev/null +++ b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py @@ -0,0 +1,32 @@ +from lewis.adapters.stream import StreamInterface, Cmd +from lewis.utils.command_builder import CmdBuilder +from lewis.core.logging import has_log +from lewis.utils.replies import conditional_reply + + +@has_log +class AttocubeStreamInterface(StreamInterface): + + in_terminator = "\r\n" + out_terminator = "\r\n" + + def __init__(self): + super(AttocubeStreamInterface, self).__init__() + # Commands that we expect via serial during normal operation + self.commands = { + CmdBuilder(self.catch_all).arg("^#9.*$").build() # Catch-all command for debugging + } + + def handle_error(self, request, error): + """ + If command is not recognised print and error + + Args: + request: requested string + error: problem + + """ + self.log.error("An error occurred at request " + repr(request) + ": " + repr(error)) + + def catch_all(self, command): + pass diff --git a/system_tests/lewis_emulators/Attocube/states.py b/system_tests/lewis_emulators/Attocube/states.py new file mode 100644 index 0000000..e4ca48e --- /dev/null +++ b/system_tests/lewis_emulators/Attocube/states.py @@ -0,0 +1,5 @@ +from lewis.core.statemachine import State + + +class DefaultState(State): + pass diff --git a/system_tests/lewis_emulators/__init__.py b/system_tests/lewis_emulators/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/system_tests/lewis_emulators/lewis_versions.py b/system_tests/lewis_emulators/lewis_versions.py new file mode 100644 index 0000000..0f6c45a --- /dev/null +++ b/system_tests/lewis_emulators/lewis_versions.py @@ -0,0 +1,2 @@ +LEWIS_1_3_0 = "1.3.0" +LEWIS_LATEST = LEWIS_1_3_0 From 847de3f5abe211b24a6cfe5956449316032e824b Mon Sep 17 00:00:00 2001 From: George Ryall Date: Fri, 26 Jul 2024 10:05:55 +0100 Subject: [PATCH 04/13] First run at IOC for Attocube --- .github/workflows/linters.yml | 7 ++ attocubeSup/attocube.db | 94 +++++++++++++++++++ attocubeSup/attocube.proto | 49 +++++++--- .../lewis_emulators/Attocube/device.py | 23 ++++- .../Attocube/interfaces/stream_interface.py | 62 +++++++++++- system_tests/tests/attocube.py | 62 +++++++++++- 6 files changed, 276 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/linters.yml diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml new file mode 100644 index 0000000..82ad245 --- /dev/null +++ b/.github/workflows/linters.yml @@ -0,0 +1,7 @@ +name: Linter +on: [pull_request] +jobs: + call-workflow: + uses: ISISComputingGroup/reusable-workflows/.github/workflows/linters.yml@main + with: + compare-branch: origin/master diff --git a/attocubeSup/attocube.db b/attocubeSup/attocube.db index 4c7f7b3..d8dcbbd 100644 --- a/attocubeSup/attocube.db +++ b/attocubeSup/attocube.db @@ -17,3 +17,97 @@ record(bo, "$(P)DISABLE") field(ZNAM, "COMMS ENABLED") field(ONAM, "COMMS DISABLED") } + +record(ai, "$(P)ANGLE") +{ + field(DESC, "Angle") + field(EGU, "deg") + field(PREC, "2") + field(SCAN, "1 second") + field(DTYP, "stream") + field(INP, "@attocube.proto get_angle $(PORT)") + field(SIML, "$(P)SIM") + field(SIOL, "$(P)SIM:ANGLE") +} + +record(ao, "$(P)ANGLE:SP") +{ + field(DESC, "Angle Set Point") + field(EGU, "deg") + field(PREC, "2") + field(DRVH, "170") + field(DRVL, "-170") + field(DTYP, "stream") + field(OUT, "@attocube.proto set_angle $(PORT)") + field(SIML, "$(P)SIM") + field(SIOL, "$(P)SIM:ANGLE:SP") +} + +record(ai, "$(P)ARK") +{ + field(DESC, "Ark") + field(EGU, "deg") + field(PREC, "2") + field(SCAN, "1 second") + field(DTYP, "stream") + field(INP, "@attocube.proto get_ark $(PORT)") + field(SIML, "$(P)SIM") + field(SIOL, "$(P)SIM:ARK") +} + +record(ao, "$(P)ARK:SP") +{ + field(DESC, "Ark Set Point") + field(EGU, "deg") + field(PREC, "2") + field(DRVH, "19") + field(DRVL, "-19") + field(DTYP, "stream") + field(OUT, "@attocube.proto set_ark $(PORT)") + field(SIML, "$(P)SIM") + field(SIOL, "$(P)SIM:ARK:SP") +} + +record(bo, "$(P)INIT") +{ + field(DESC, "Initialisation commands") + field(PINI, "YES") + field(SCAN, "Passive") + field(DTYP, "stream") + field(OUT, "@attocube.proto initialise $(PORT)") +} + +record(bo, "$(P)STOP") +{ + field(DESC, "Stop commands") + field(PINI, "NO") + field(SCAN, "Passive") + field(DTYP, "stream") + field(OUT, "@attocube.proto stop $(PORT)") +} + +record(bi, "$(P)BUSY") +{ + field(DESC, "Busy") + field(SCAN, "1 second") + field(DTYP, "stream") + field(INP, "@attocube.proto get_busy $(PORT)") + field(ZNAM, "NOT BUSY") + field(ONAM, "BUSY") +} + +record(ao, "$(P)SIM:ANGLE") +{ + field(SCAN, "Passive") + field(DTYP, "Soft Channel") +} + +alias("$(P)SIM:ANGLE","$(P)SIM:ANGLE:SP") + +record(ao, "$(P)SIM:ARK") +{ + field(SCAN, "Passive") + field(DTYP, "Soft Channel") +} + +alias("$(P)SIM:ARK","$(P)SIM:ARK:SP") diff --git a/attocubeSup/attocube.proto b/attocubeSup/attocube.proto index 3326465..1cf514a 100644 --- a/attocubeSup/attocube.proto +++ b/attocubeSup/attocube.proto @@ -1,18 +1,45 @@ -getIDN { - out "*IDN?"; - in "%\$1[^\r\n]"; - ExtraInput = Ignore; +ReadTimeout = 100; +OutTerminator = "\r\n"; +InTerminator = "\r\n:"; + +get_angle { + out "ATANGLE="; + in "%f"; +} + +set_angle { + out "ATTO=%f"; + out "ATGO=1"; +} + +get_ark { + out "Y="; + in "%f"; +} + +set_ark { + out "ARK=%f"; + out "ARGO=1"; } -cmd { - out "\$1"; +initialise { + out "VERBOSE=0"; + out "AB0"; + out "MO"; + out "XQ #SXD"; + out "MG \"stop 1\" {P2}"; } -setD { - out "\$1 %d"; +stop { + out "AB0"; + out "MO"; + out "XQ #SXD"; + out "MG \"stop 1\" {P2}"; } -getD { - out "\$1?"; - in "%d"; + +get_busy { ExtraInput = Ignore; + out "BUSY="; + in "%1i"; } + diff --git a/system_tests/lewis_emulators/Attocube/device.py b/system_tests/lewis_emulators/Attocube/device.py index 4432b72..6514381 100644 --- a/system_tests/lewis_emulators/Attocube/device.py +++ b/system_tests/lewis_emulators/Attocube/device.py @@ -6,10 +6,25 @@ class SimulatedAttocube(StateMachineDevice): def _initialize_data(self): - """ - Initialize all of the device's attributes. - """ - pass + #Ark + self.angle = 0 + #Angle as recieved by emulator, angle only set when go comand recieved + self._angle_val = None + + #Angle + self.ark = 0 + #Ark as recieved by emulator, ark only set when go comand recieved + self._ark_val = None + + #Initialisation and Stop comand recieved counters + self.verbose_count = 0 + self.ab0_count = 0 + self.mo_count = 0 + self.xq_count = 0 + self.mg_count = 0 + + #Busy + self.busy = False def _get_state_handlers(self): return { diff --git a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py index 3ee7fa1..6e0051a 100644 --- a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py +++ b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py @@ -8,13 +8,25 @@ class AttocubeStreamInterface(StreamInterface): in_terminator = "\r\n" - out_terminator = "\r\n" + out_terminator = "\r\n:" + readtimeout = 100 def __init__(self): super(AttocubeStreamInterface, self).__init__() # Commands that we expect via serial during normal operation self.commands = { - CmdBuilder(self.catch_all).arg("^#9.*$").build() # Catch-all command for debugging + CmdBuilder(self.get_angle).escape("ATANGLE=").eos().build(), + CmdBuilder(self.set_angle_val).escape("ATTO=").float().eos().build(), + CmdBuilder(self.set_angle_go).escape("ATGO=1").eos().build(), + CmdBuilder(self.get_ark).escape("Y=").eos().build(), + CmdBuilder(self.set_ark_val).escape("ARK=").float().eos().build(), + CmdBuilder(self.set_ark_go).escape("ARGO=1").eos().build(), + CmdBuilder(self.count_verbose).escape("VERBOSE=0").eos().build(), + CmdBuilder(self.count_ab0).escape("AB0").eos().build(), + CmdBuilder(self.count_mo).escape("MO").eos().build(), + CmdBuilder(self.count_xq).escape("XQ #SXD").eos().build(), + CmdBuilder(self.count_mg).escape("MG \"stop 1\" {P2}").eos().build(), + CmdBuilder(self.get_busy).escape("BUSY=").eos().build() } def handle_error(self, request, error): @@ -28,5 +40,47 @@ def handle_error(self, request, error): """ self.log.error("An error occurred at request " + repr(request) + ": " + repr(error)) - def catch_all(self, command): - pass + def get_angle(self): + return f"{self.device.angle}" + + def set_angle_val(self, angle_sp): + self.device._angle_val = angle_sp + + def set_angle_go(self): + if self.device._angle_val is None: + self.log.error("ATGO was recieved before angle had been set") + else: self.device.angle = self.device._angle_val + + def get_ark(self): + return f"{self.device.ark}" + + def set_ark_val(self, ark_sp): + self.device._ark_val = ark_sp + + def set_ark_go(self): + if self.device._ark_val is None: + self.log.error("ARGO was recieved before ark had been set") + else: self.device.ark = self.device._ark_val + + def count_verbose(self) : + self.device.verbose_count += 1 + + def count_ab0(self) : + self.device.ab0_count += 1 + + def count_mo(self) : + self.device.mo_count += 1 + + def count_xq(self) : + self.device.xq_count += 1 + + def count_mg(self) : + self.device.mg_count += 1 + + def get_busy(self): + if self.device.busy: + return "1.0000" + else: + return "0.0000" + + diff --git a/system_tests/tests/attocube.py b/system_tests/tests/attocube.py index 2eea54b..421e6b8 100644 --- a/system_tests/tests/attocube.py +++ b/system_tests/tests/attocube.py @@ -30,5 +30,63 @@ def setUp(self): self._lewis, self._ioc = get_running_lewis_and_ioc("Attocube", DEVICE_PREFIX) self.ca = ChannelAccess(device_prefix=DEVICE_PREFIX) - def test_that_fails(self): - self.fail("You haven't implemented any tests!") + def test_that_dissable_pv_IS_ENABLED(self): + self.ca.assert_that_pv_is("DISABLE", "COMMS ENABLED") + + def test_WHEN_angle_set_THEN_ioc_reads_angle(self): + self.ca.assert_setting_setpoint_sets_readback(-20, "ANGLE") + + def test_WHEN_ark_set_THEN_ioc_reads_ark(self): + self.ca.assert_setting_setpoint_sets_readback(-15, "ARK") + + @skip_if_recsim("makes use of backdoor") + def test_WHEN_initialised_THEN_correct_comands_sent(self): + #Given current count of comands recieved by emulator + self.verbose_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("verbose_count")) + self.ab0_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("ab0_count")) + self.mo_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("mo_count")) + self.xq_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("xq_count")) + self.mg_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("mg_count")) + + #When initialisation PV processed + self.ca.process_pv("INIT") + + #Then assert count of comands recieved by emulator increases by 1 + self._lewis.assert_that_emulator_value_is("verbose_count",str(self.verbose_count_backdoor_pre+1)) + self._lewis.assert_that_emulator_value_is("ab0_count",str(self.ab0_count_backdoor_pre+1)) + self._lewis.assert_that_emulator_value_is("mo_count",str(self.mo_count_backdoor_pre+1)) + self._lewis.assert_that_emulator_value_is("xq_count",str(self.xq_count_backdoor_pre+1)) + self._lewis.assert_that_emulator_value_is("mg_count",str(self.mg_count_backdoor_pre+1)) + + @skip_if_recsim("makes use of backdoor") + def test_WHEN_stopped_THEN_correct_comands_sent(self): + #Given current count of comands recieved by emulator + self.ab0_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("ab0_count")) + self.mo_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("mo_count")) + self.xq_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("xq_count")) + self.mg_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("mg_count")) + + #When initialisation PV processed + self.ca.process_pv("STOP") + + #Then assert count of comands recieved by emulator increases by 1 + self._lewis.assert_that_emulator_value_is("ab0_count",str(self.ab0_count_backdoor_pre+1)) + self._lewis.assert_that_emulator_value_is("mo_count",str(self.mo_count_backdoor_pre+1)) + self._lewis.assert_that_emulator_value_is("xq_count",str(self.xq_count_backdoor_pre+1)) + self._lewis.assert_that_emulator_value_is("mg_count",str(self.mg_count_backdoor_pre+1)) + + @skip_if_recsim("makes use of backdoor") + def test_WHEN_busy_THEN_reports_busy(self): + #Given emulator set to be busy + self._lewis.backdoor_set_on_device("busy", True) + + #Then BUSY PV reports busy + self.ca.assert_that_pv_is("BUSY", "BUSY") + + @skip_if_recsim("makes use of backdoor") + def test_WHEN_not_busy_THEN_not_busy(self): + #Given emulator set to be not busy + self._lewis.backdoor_set_on_device("busy", False) + + #Then BUSY PV reports not busy + self.ca.assert_that_pv_is("BUSY", "NOT BUSY") From 8dad6b5ec901bce28616cd30c0789c9cb4cc961d Mon Sep 17 00:00:00 2001 From: George Ryall Date: Wed, 16 Oct 2024 14:02:23 +0100 Subject: [PATCH 05/13] IOC polishing As per https://github.com/ISISComputingGroup/ibex_developers_manual/wiki/IOC-Finishing-Touches --- attocubeSup/attocube.db | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/attocubeSup/attocube.db b/attocubeSup/attocube.db index d8dcbbd..df70749 100644 --- a/attocubeSup/attocube.db +++ b/attocubeSup/attocube.db @@ -28,6 +28,11 @@ record(ai, "$(P)ANGLE") field(INP, "@attocube.proto get_angle $(PORT)") field(SIML, "$(P)SIM") field(SIOL, "$(P)SIM:ANGLE") + field(SDIS, "$(P)DISABLE") + info(INTEREST, "HIGH") + info(archive, "VAL") + info(alarm, "ATTOCUBE") + } record(ao, "$(P)ANGLE:SP") @@ -41,6 +46,9 @@ record(ao, "$(P)ANGLE:SP") field(OUT, "@attocube.proto set_angle $(PORT)") field(SIML, "$(P)SIM") field(SIOL, "$(P)SIM:ANGLE:SP") + field(SDIS, "$(P)DISABLE") + field(UDFS, "NO_ALARM") + info(archive, "VAL") } record(ai, "$(P)ARK") @@ -53,6 +61,10 @@ record(ai, "$(P)ARK") field(INP, "@attocube.proto get_ark $(PORT)") field(SIML, "$(P)SIM") field(SIOL, "$(P)SIM:ARK") + field(SDIS, "$(P)DISABLE") + info(INTEREST, "HIGH") + info(archive, "VAL") + info(alarm, "ATTOCUBE") } record(ao, "$(P)ARK:SP") @@ -66,6 +78,9 @@ record(ao, "$(P)ARK:SP") field(OUT, "@attocube.proto set_ark $(PORT)") field(SIML, "$(P)SIM") field(SIOL, "$(P)SIM:ARK:SP") + field(SDIS, "$(P)DISABLE") + field(UDFS, "NO_ALARM") + info(archive, "VAL") } record(bo, "$(P)INIT") @@ -75,6 +90,7 @@ record(bo, "$(P)INIT") field(SCAN, "Passive") field(DTYP, "stream") field(OUT, "@attocube.proto initialise $(PORT)") + field(SDIS, "$(P)DISABLE") } record(bo, "$(P)STOP") @@ -84,6 +100,7 @@ record(bo, "$(P)STOP") field(SCAN, "Passive") field(DTYP, "stream") field(OUT, "@attocube.proto stop $(PORT)") + field(SDIS, "$(P)DISABLE") } record(bi, "$(P)BUSY") @@ -94,6 +111,7 @@ record(bi, "$(P)BUSY") field(INP, "@attocube.proto get_busy $(PORT)") field(ZNAM, "NOT BUSY") field(ONAM, "BUSY") + field(SDIS, "$(P)DISABLE") } record(ao, "$(P)SIM:ANGLE") From f98fa8e4fb41e6ff118927a64cb11bb8d6f079e9 Mon Sep 17 00:00:00 2001 From: George Ryall Date: Wed, 16 Oct 2024 14:16:49 +0100 Subject: [PATCH 06/13] Update linters.yml primary branch is main not master --- .github/workflows/linters.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 82ad245..72d9425 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -4,4 +4,4 @@ jobs: call-workflow: uses: ISISComputingGroup/reusable-workflows/.github/workflows/linters.yml@main with: - compare-branch: origin/master + compare-branch: origin/main From d08d38f1791bf41381025559b45bcb769ef870e1 Mon Sep 17 00:00:00 2001 From: George Ryall Date: Wed, 16 Oct 2024 14:49:27 +0100 Subject: [PATCH 07/13] ruff format fixes --- .../lewis_emulators/Attocube/__init__.py | 4 +- .../lewis_emulators/Attocube/device.py | 31 +++++----- .../Attocube/interfaces/__init__.py | 2 +- .../Attocube/interfaces/stream_interface.py | 40 ++++++------ system_tests/tests/attocube.py | 62 ++++++++++--------- 5 files changed, 69 insertions(+), 70 deletions(-) diff --git a/system_tests/lewis_emulators/Attocube/__init__.py b/system_tests/lewis_emulators/Attocube/__init__.py index 08cc450..a2380f9 100644 --- a/system_tests/lewis_emulators/Attocube/__init__.py +++ b/system_tests/lewis_emulators/Attocube/__init__.py @@ -1,5 +1,5 @@ -from .device import SimulatedAttocube from ..lewis_versions import LEWIS_LATEST +from .device import SimulatedAttocube framework_version = LEWIS_LATEST -__all__ = ['SimulatedAttocube'] +__all__ = ["SimulatedAttocube"] diff --git a/system_tests/lewis_emulators/Attocube/device.py b/system_tests/lewis_emulators/Attocube/device.py index 6514381..6d95135 100644 --- a/system_tests/lewis_emulators/Attocube/device.py +++ b/system_tests/lewis_emulators/Attocube/device.py @@ -1,40 +1,39 @@ from collections import OrderedDict -from .states import DefaultState + from lewis.devices import StateMachineDevice +from .states import DefaultState -class SimulatedAttocube(StateMachineDevice): +class SimulatedAttocube(StateMachineDevice): def _initialize_data(self): - #Ark + # Ark self.angle = 0 - #Angle as recieved by emulator, angle only set when go comand recieved + # Angle as recieved by emulator, angle only set when go comand recieved self._angle_val = None - - #Angle + + # Angle self.ark = 0 - #Ark as recieved by emulator, ark only set when go comand recieved + # Ark as recieved by emulator, ark only set when go comand recieved self._ark_val = None - - #Initialisation and Stop comand recieved counters + + # Initialisation and Stop comand recieved counters self.verbose_count = 0 self.ab0_count = 0 self.mo_count = 0 self.xq_count = 0 self.mg_count = 0 - - #Busy + + # Busy self.busy = False def _get_state_handlers(self): return { - 'default': DefaultState(), + "default": DefaultState(), } def _get_initial_state(self): - return 'default' + return "default" def _get_transition_handlers(self): - return OrderedDict([ - ]) - + return OrderedDict([]) diff --git a/system_tests/lewis_emulators/Attocube/interfaces/__init__.py b/system_tests/lewis_emulators/Attocube/interfaces/__init__.py index 8e748bf..3d96927 100644 --- a/system_tests/lewis_emulators/Attocube/interfaces/__init__.py +++ b/system_tests/lewis_emulators/Attocube/interfaces/__init__.py @@ -1,3 +1,3 @@ from .stream_interface import AttocubeStreamInterface -__all__ = ['AttocubeStreamInterface'] +__all__ = ["AttocubeStreamInterface"] diff --git a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py index 6e0051a..3b481d9 100644 --- a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py +++ b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py @@ -1,12 +1,10 @@ -from lewis.adapters.stream import StreamInterface, Cmd -from lewis.utils.command_builder import CmdBuilder +from lewis.adapters.stream import StreamInterface from lewis.core.logging import has_log -from lewis.utils.replies import conditional_reply +from lewis.utils.command_builder import CmdBuilder @has_log class AttocubeStreamInterface(StreamInterface): - in_terminator = "\r\n" out_terminator = "\r\n:" readtimeout = 100 @@ -25,8 +23,8 @@ def __init__(self): CmdBuilder(self.count_ab0).escape("AB0").eos().build(), CmdBuilder(self.count_mo).escape("MO").eos().build(), CmdBuilder(self.count_xq).escape("XQ #SXD").eos().build(), - CmdBuilder(self.count_mg).escape("MG \"stop 1\" {P2}").eos().build(), - CmdBuilder(self.get_busy).escape("BUSY=").eos().build() + CmdBuilder(self.count_mg).escape('MG "stop 1" {P2}').eos().build(), + CmdBuilder(self.get_busy).escape("BUSY=").eos().build(), } def handle_error(self, request, error): @@ -42,45 +40,45 @@ def handle_error(self, request, error): def get_angle(self): return f"{self.device.angle}" - + def set_angle_val(self, angle_sp): self.device._angle_val = angle_sp - + def set_angle_go(self): if self.device._angle_val is None: self.log.error("ATGO was recieved before angle had been set") - else: self.device.angle = self.device._angle_val + else: + self.device.angle = self.device._angle_val def get_ark(self): return f"{self.device.ark}" - + def set_ark_val(self, ark_sp): self.device._ark_val = ark_sp - + def set_ark_go(self): if self.device._ark_val is None: self.log.error("ARGO was recieved before ark had been set") - else: self.device.ark = self.device._ark_val + else: + self.device.ark = self.device._ark_val - def count_verbose(self) : + def count_verbose(self): self.device.verbose_count += 1 - def count_ab0(self) : + def count_ab0(self): self.device.ab0_count += 1 - def count_mo(self) : + def count_mo(self): self.device.mo_count += 1 - - def count_xq(self) : + + def count_xq(self): self.device.xq_count += 1 - def count_mg(self) : + def count_mg(self): self.device.mg_count += 1 - + def get_busy(self): if self.device.busy: return "1.0000" else: return "0.0000" - - diff --git a/system_tests/tests/attocube.py b/system_tests/tests/attocube.py index 421e6b8..6ce6f7d 100644 --- a/system_tests/tests/attocube.py +++ b/system_tests/tests/attocube.py @@ -5,7 +5,6 @@ from utils.test_modes import TestModes from utils.testing import get_running_lewis_and_ioc, skip_if_recsim - DEVICE_PREFIX = "ATTOCUBE_01" @@ -26,67 +25,70 @@ class AttocubeTests(unittest.TestCase): """ Tests for the Attocube IOC. """ + def setUp(self): self._lewis, self._ioc = get_running_lewis_and_ioc("Attocube", DEVICE_PREFIX) self.ca = ChannelAccess(device_prefix=DEVICE_PREFIX) def test_that_dissable_pv_IS_ENABLED(self): self.ca.assert_that_pv_is("DISABLE", "COMMS ENABLED") - + def test_WHEN_angle_set_THEN_ioc_reads_angle(self): - self.ca.assert_setting_setpoint_sets_readback(-20, "ANGLE") - + self.ca.assert_setting_setpoint_sets_readback(-20, "ANGLE") + def test_WHEN_ark_set_THEN_ioc_reads_ark(self): - self.ca.assert_setting_setpoint_sets_readback(-15, "ARK") + self.ca.assert_setting_setpoint_sets_readback(-15, "ARK") @skip_if_recsim("makes use of backdoor") def test_WHEN_initialised_THEN_correct_comands_sent(self): - #Given current count of comands recieved by emulator + # Given current count of comands recieved by emulator self.verbose_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("verbose_count")) self.ab0_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("ab0_count")) self.mo_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("mo_count")) self.xq_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("xq_count")) self.mg_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("mg_count")) - #When initialisation PV processed + # When initialisation PV processed self.ca.process_pv("INIT") - - #Then assert count of comands recieved by emulator increases by 1 - self._lewis.assert_that_emulator_value_is("verbose_count",str(self.verbose_count_backdoor_pre+1)) - self._lewis.assert_that_emulator_value_is("ab0_count",str(self.ab0_count_backdoor_pre+1)) - self._lewis.assert_that_emulator_value_is("mo_count",str(self.mo_count_backdoor_pre+1)) - self._lewis.assert_that_emulator_value_is("xq_count",str(self.xq_count_backdoor_pre+1)) - self._lewis.assert_that_emulator_value_is("mg_count",str(self.mg_count_backdoor_pre+1)) + + # Then assert count of comands recieved by emulator increases by 1 + self._lewis.assert_that_emulator_value_is( + "verbose_count", str(self.verbose_count_backdoor_pre + 1) + ) + self._lewis.assert_that_emulator_value_is("ab0_count", str(self.ab0_count_backdoor_pre + 1)) + self._lewis.assert_that_emulator_value_is("mo_count", str(self.mo_count_backdoor_pre + 1)) + self._lewis.assert_that_emulator_value_is("xq_count", str(self.xq_count_backdoor_pre + 1)) + self._lewis.assert_that_emulator_value_is("mg_count", str(self.mg_count_backdoor_pre + 1)) @skip_if_recsim("makes use of backdoor") def test_WHEN_stopped_THEN_correct_comands_sent(self): - #Given current count of comands recieved by emulator + # Given current count of comands recieved by emulator self.ab0_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("ab0_count")) self.mo_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("mo_count")) self.xq_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("xq_count")) self.mg_count_backdoor_pre = int(self._lewis.backdoor_get_from_device("mg_count")) - #When initialisation PV processed + # When initialisation PV processed self.ca.process_pv("STOP") - - #Then assert count of comands recieved by emulator increases by 1 - self._lewis.assert_that_emulator_value_is("ab0_count",str(self.ab0_count_backdoor_pre+1)) - self._lewis.assert_that_emulator_value_is("mo_count",str(self.mo_count_backdoor_pre+1)) - self._lewis.assert_that_emulator_value_is("xq_count",str(self.xq_count_backdoor_pre+1)) - self._lewis.assert_that_emulator_value_is("mg_count",str(self.mg_count_backdoor_pre+1)) - + + # Then assert count of comands recieved by emulator increases by 1 + self._lewis.assert_that_emulator_value_is("ab0_count", str(self.ab0_count_backdoor_pre + 1)) + self._lewis.assert_that_emulator_value_is("mo_count", str(self.mo_count_backdoor_pre + 1)) + self._lewis.assert_that_emulator_value_is("xq_count", str(self.xq_count_backdoor_pre + 1)) + self._lewis.assert_that_emulator_value_is("mg_count", str(self.mg_count_backdoor_pre + 1)) + @skip_if_recsim("makes use of backdoor") def test_WHEN_busy_THEN_reports_busy(self): - #Given emulator set to be busy + # Given emulator set to be busy self._lewis.backdoor_set_on_device("busy", True) - - #Then BUSY PV reports busy + + # Then BUSY PV reports busy self.ca.assert_that_pv_is("BUSY", "BUSY") - + @skip_if_recsim("makes use of backdoor") def test_WHEN_not_busy_THEN_not_busy(self): - #Given emulator set to be not busy + # Given emulator set to be not busy self._lewis.backdoor_set_on_device("busy", False) - - #Then BUSY PV reports not busy + + # Then BUSY PV reports not busy self.ca.assert_that_pv_is("BUSY", "NOT BUSY") From 390de7a5141a105ab62d29495f5087c43a5f5de1 Mon Sep 17 00:00:00 2001 From: George Ryall Date: Wed, 16 Oct 2024 15:45:56 +0100 Subject: [PATCH 08/13] Type Ignores for pyright Otherwise pyright complains the imports can't be resolved. I am told there is a ticket to fix this, but for now some ugly ornamatation. --- system_tests/lewis_emulators/Attocube/device.py | 2 +- .../Attocube/interfaces/stream_interface.py | 6 +++--- system_tests/tests/attocube.py | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/system_tests/lewis_emulators/Attocube/device.py b/system_tests/lewis_emulators/Attocube/device.py index 6d95135..a891d0b 100644 --- a/system_tests/lewis_emulators/Attocube/device.py +++ b/system_tests/lewis_emulators/Attocube/device.py @@ -1,6 +1,6 @@ from collections import OrderedDict -from lewis.devices import StateMachineDevice +from lewis.devices import StateMachineDevice # type: ignore from .states import DefaultState diff --git a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py index 3b481d9..734be23 100644 --- a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py +++ b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py @@ -1,6 +1,6 @@ -from lewis.adapters.stream import StreamInterface -from lewis.core.logging import has_log -from lewis.utils.command_builder import CmdBuilder +from lewis.adapters.stream import StreamInterface # type: ignore +from lewis.core.logging import has_log # type: ignore +from lewis.utils.command_builder import CmdBuilder # type: ignore @has_log diff --git a/system_tests/tests/attocube.py b/system_tests/tests/attocube.py index 6ce6f7d..def34ff 100644 --- a/system_tests/tests/attocube.py +++ b/system_tests/tests/attocube.py @@ -1,9 +1,9 @@ import unittest -from utils.channel_access import ChannelAccess -from utils.ioc_launcher import get_default_ioc_dir -from utils.test_modes import TestModes -from utils.testing import get_running_lewis_and_ioc, skip_if_recsim +from utils.channel_access import ChannelAccess # type: ignore +from utils.ioc_launcher import get_default_ioc_dir # type: ignore +from utils.test_modes import TestModes # type: ignore +from utils.testing import get_running_lewis_and_ioc, skip_if_recsim # type: ignore DEVICE_PREFIX = "ATTOCUBE_01" From d7b3ee37e5b19f7f495a1f7ec126d03d9508d2df Mon Sep 17 00:00:00 2001 From: George Ryall Date: Wed, 16 Oct 2024 16:25:56 +0100 Subject: [PATCH 09/13] Ruff - return type annotations --- .../lewis_emulators/Attocube/device.py | 8 +++--- .../Attocube/interfaces/stream_interface.py | 28 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/system_tests/lewis_emulators/Attocube/device.py b/system_tests/lewis_emulators/Attocube/device.py index a891d0b..bd6a2c5 100644 --- a/system_tests/lewis_emulators/Attocube/device.py +++ b/system_tests/lewis_emulators/Attocube/device.py @@ -6,7 +6,7 @@ class SimulatedAttocube(StateMachineDevice): - def _initialize_data(self): + def _initialize_data(self) -> None: # Ark self.angle = 0 # Angle as recieved by emulator, angle only set when go comand recieved @@ -27,13 +27,13 @@ def _initialize_data(self): # Busy self.busy = False - def _get_state_handlers(self): + def _get_state_handlers(self) -> dict: return { "default": DefaultState(), } - def _get_initial_state(self): + def _get_initial_state(self) -> str: return "default" - def _get_transition_handlers(self): + def _get_transition_handlers(self) -> dict: return OrderedDict([]) diff --git a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py index 734be23..0a81f8a 100644 --- a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py +++ b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py @@ -9,7 +9,7 @@ class AttocubeStreamInterface(StreamInterface): out_terminator = "\r\n:" readtimeout = 100 - def __init__(self): + def __init__(self) -> None: super(AttocubeStreamInterface, self).__init__() # Commands that we expect via serial during normal operation self.commands = { @@ -27,7 +27,7 @@ def __init__(self): CmdBuilder(self.get_busy).escape("BUSY=").eos().build(), } - def handle_error(self, request, error): + def handle_error(self, request, error) -> None: """ If command is not recognised print and error @@ -38,46 +38,46 @@ def handle_error(self, request, error): """ self.log.error("An error occurred at request " + repr(request) + ": " + repr(error)) - def get_angle(self): + def get_angle(self) -> str: return f"{self.device.angle}" - def set_angle_val(self, angle_sp): + def set_angle_val(self, angle_sp) -> None: self.device._angle_val = angle_sp - def set_angle_go(self): + def set_angle_go(self) -> None: if self.device._angle_val is None: self.log.error("ATGO was recieved before angle had been set") else: self.device.angle = self.device._angle_val - def get_ark(self): + def get_ark(self) -> str: return f"{self.device.ark}" - def set_ark_val(self, ark_sp): + def set_ark_val(self, ark_sp) -> None: self.device._ark_val = ark_sp - def set_ark_go(self): + def set_ark_go(self) -> None: if self.device._ark_val is None: self.log.error("ARGO was recieved before ark had been set") else: self.device.ark = self.device._ark_val - def count_verbose(self): + def count_verbose(self) -> None: self.device.verbose_count += 1 - def count_ab0(self): + def count_ab0(self) -> None: self.device.ab0_count += 1 - def count_mo(self): + def count_mo(self) -> None: self.device.mo_count += 1 - def count_xq(self): + def count_xq(self) -> None: self.device.xq_count += 1 - def count_mg(self): + def count_mg(self) -> None: self.device.mg_count += 1 - def get_busy(self): + def get_busy(self) -> str: if self.device.busy: return "1.0000" else: From 7c459d37606fabf4977dde3ea39e2dae2abe7178 Mon Sep 17 00:00:00 2001 From: George Ryall Date: Wed, 16 Oct 2024 16:32:34 +0100 Subject: [PATCH 10/13] Ruff -type annotations on args in my code --- .../lewis_emulators/Attocube/interfaces/stream_interface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py index 0a81f8a..191cd5b 100644 --- a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py +++ b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py @@ -41,7 +41,7 @@ def handle_error(self, request, error) -> None: def get_angle(self) -> str: return f"{self.device.angle}" - def set_angle_val(self, angle_sp) -> None: + def set_angle_val(self, angle_sp: float) -> None: self.device._angle_val = angle_sp def set_angle_go(self) -> None: @@ -53,7 +53,7 @@ def set_angle_go(self) -> None: def get_ark(self) -> str: return f"{self.device.ark}" - def set_ark_val(self, ark_sp) -> None: + def set_ark_val(self, ark_sp: float) -> None: self.device._ark_val = ark_sp def set_ark_go(self) -> None: From c1b5a66636bacc1af98a4510f75fa0462ada67c5 Mon Sep 17 00:00:00 2001 From: George Ryall Date: Wed, 16 Oct 2024 17:08:04 +0100 Subject: [PATCH 11/13] Ruff -type annotations on args in boiler plate --- .../lewis_emulators/Attocube/interfaces/stream_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py index 191cd5b..3d8798f 100644 --- a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py +++ b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py @@ -27,7 +27,7 @@ def __init__(self) -> None: CmdBuilder(self.get_busy).escape("BUSY=").eos().build(), } - def handle_error(self, request, error) -> None: + def handle_error(self, request: str, error: str | Exception) -> None: """ If command is not recognised print and error From ba446593e59ae4c528ea253c4b12b1d37f0a1a5d Mon Sep 17 00:00:00 2001 From: George Ryall Date: Wed, 16 Oct 2024 17:21:22 +0100 Subject: [PATCH 12/13] PyRight (local) --- .../lewis_emulators/Attocube/device.py | 8 ++++---- .../Attocube/interfaces/stream_interface.py | 20 +++++++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/system_tests/lewis_emulators/Attocube/device.py b/system_tests/lewis_emulators/Attocube/device.py index bd6a2c5..95753ae 100644 --- a/system_tests/lewis_emulators/Attocube/device.py +++ b/system_tests/lewis_emulators/Attocube/device.py @@ -8,14 +8,14 @@ class SimulatedAttocube(StateMachineDevice): def _initialize_data(self) -> None: # Ark - self.angle = 0 + self.angle: float = 0 # Angle as recieved by emulator, angle only set when go comand recieved - self._angle_val = None + self.angle_val: float | None = None # Angle - self.ark = 0 + self.ark: float = 0 # Ark as recieved by emulator, ark only set when go comand recieved - self._ark_val = None + self.ark_val: float | None = None # Initialisation and Stop comand recieved counters self.verbose_count = 0 diff --git a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py index 3d8798f..8169632 100644 --- a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py +++ b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py @@ -1,7 +1,13 @@ +import typing +import logging + from lewis.adapters.stream import StreamInterface # type: ignore from lewis.core.logging import has_log # type: ignore from lewis.utils.command_builder import CmdBuilder # type: ignore +if typing.TYPE_CHECKING: + from lewis_emulators.Attocube.device import SimulatedAttocube + @has_log class AttocubeStreamInterface(StreamInterface): @@ -11,6 +17,8 @@ class AttocubeStreamInterface(StreamInterface): def __init__(self) -> None: super(AttocubeStreamInterface, self).__init__() + self.device: "SimulatedAttocube" + self.log: "logging.Logger" # Commands that we expect via serial during normal operation self.commands = { CmdBuilder(self.get_angle).escape("ATANGLE=").eos().build(), @@ -42,25 +50,25 @@ def get_angle(self) -> str: return f"{self.device.angle}" def set_angle_val(self, angle_sp: float) -> None: - self.device._angle_val = angle_sp + self.device.angle_val = angle_sp def set_angle_go(self) -> None: - if self.device._angle_val is None: + if self.device.angle_val is None: self.log.error("ATGO was recieved before angle had been set") else: - self.device.angle = self.device._angle_val + self.device.angle = self.device.angle_val def get_ark(self) -> str: return f"{self.device.ark}" def set_ark_val(self, ark_sp: float) -> None: - self.device._ark_val = ark_sp + self.device.ark_val = ark_sp def set_ark_go(self) -> None: - if self.device._ark_val is None: + if self.device.ark_val is None: self.log.error("ARGO was recieved before ark had been set") else: - self.device.ark = self.device._ark_val + self.device.ark = self.device.ark_val def count_verbose(self) -> None: self.device.verbose_count += 1 From 034ece6ff622f568c208109129c8d3f165c3ecdd Mon Sep 17 00:00:00 2001 From: George Ryall Date: Wed, 16 Oct 2024 17:22:50 +0100 Subject: [PATCH 13/13] Ruff --- .../lewis_emulators/Attocube/interfaces/stream_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py index 8169632..4c6741a 100644 --- a/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py +++ b/system_tests/lewis_emulators/Attocube/interfaces/stream_interface.py @@ -1,5 +1,5 @@ -import typing import logging +import typing from lewis.adapters.stream import StreamInterface # type: ignore from lewis.core.logging import has_log # type: ignore