diff --git a/lewis_emulators/mk2_chopper/__init__.py b/lewis_emulators/mk2_chopper/__init__.py deleted file mode 100644 index 9b32df93..00000000 --- a/lewis_emulators/mk2_chopper/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .device import SimulatedMk2Chopper -from ..lewis_versions import LEWIS_LATEST - -framework_version = LEWIS_LATEST -__all__ = ['SimulatedMk2Chopper2400'] diff --git a/lewis_emulators/mk2_chopper/chopper_type.py b/lewis_emulators/mk2_chopper/chopper_type.py deleted file mode 100644 index da99cdbb..00000000 --- a/lewis_emulators/mk2_chopper/chopper_type.py +++ /dev/null @@ -1,41 +0,0 @@ -class ChopperType(object): - """ - A type of chopper that the system can represent. Typically MK2 choppers come in 50Hz and 100Hz varieties. - """ - - # An overspeed system state used to check the overspeed error flag - OVERSPEED = (999, 95) - - # A dictionary of states the system can be in {Maximum frequency: [(Actual frequency, Max phase delay), ...]} - VALID_SYSTEM_STATES = { - 50: [(5, 99995), (10,99995), (12.5, 79995), (16.67, 59995), (25, 39995), (50, 19995), OVERSPEED], - 100: [(12.5, 79995), (25, 39995), (50, 19995), (100, 9995), OVERSPEED] - } - - CORTINA = "cortina" - INDRAMAT = "indramat" - SPECTRAL = "spectral" - MANUFACTURERS = [CORTINA, INDRAMAT, SPECTRAL] - - def __init__(self, max_frequency, manufacturer): - possible_max_frequencies = ChopperType.VALID_SYSTEM_STATES.keys() - self._max_frequency = max_frequency if max_frequency in possible_max_frequencies\ - else min(possible_max_frequencies) - - manufacturer_low = manufacturer.lower() - self._manufacturer = manufacturer_low if manufacturer_low in ChopperType.MANUFACTURERS else ChopperType.INDRAMAT - - def get_closest_valid_frequency(self, frequency): - return self._get_frequency_and_phase_closest_to_frequency(frequency)[0] - - def get_max_phase_for_closest_frequency(self, frequency): - return self._get_frequency_and_phase_closest_to_frequency(frequency)[1] - - def _get_frequency_and_phase_closest_to_frequency(self, frequency): - return min(ChopperType.VALID_SYSTEM_STATES[self._max_frequency], key=lambda x: abs(x[0] - frequency)) - - def get_manufacturer(self): - return self._manufacturer - - def get_frequency(self): - return self._max_frequency diff --git a/lewis_emulators/mk2_chopper/device.py b/lewis_emulators/mk2_chopper/device.py deleted file mode 100644 index 178ac14a..00000000 --- a/lewis_emulators/mk2_chopper/device.py +++ /dev/null @@ -1,151 +0,0 @@ -from collections import OrderedDict -from .states import DefaultInitState, DefaultStoppedState, DefaultStartedState, MAX_TEMPERATURE -from lewis.devices import StateMachineDevice -from .chopper_type import ChopperType - - -class SimulatedMk2Chopper(StateMachineDevice): - - def _initialize_data(self): - """ - Initialize all of the device's attributes. - """ - self._type = ChopperType(50, ChopperType.INDRAMAT) - - self._demanded_frequency = self._type.get_frequency() - self._max_phase_delay = self._type.get_max_phase_for_closest_frequency(self._demanded_frequency) - self._true_frequency = 0 - - self._demanded_phase_delay = 0 - self._true_phase_delay = 0 - - self._demanded_phase_error_window = 1 - - self._started = False - self._fault = False - - self._phase_delay_error = False - - self._temperature = 0 - - # When initialisation is complete, this is set to true and the device will enter a running state - self.ready = True - - def _get_state_handlers(self): - return { - 'init': DefaultInitState(), - 'stopped': DefaultStoppedState(), - 'started': DefaultStartedState(), - } - - def _get_initial_state(self): - return 'init' - - def _get_transition_handlers(self): - return OrderedDict([ - (('init', 'stopped'), lambda: self.ready), - (('stopped', 'started'), lambda: self._started is True), - (('started', 'stopped'), lambda: self._started is False), - ]) - - def get_system_frequency(self): - return self._type.get_frequency() - - def get_manufacturer(self): - return self._type.get_manufacturer() - - def get_demanded_frequency(self): - return self._demanded_frequency - - def get_true_frequency(self): - return self._true_frequency - - def get_demanded_phase_delay(self): - return self._demanded_phase_delay - - def get_true_phase_delay(self): - return self._true_phase_delay - - def get_demanded_phase_error_window(self): - return self._demanded_phase_error_window - - def get_true_phase_error(self): - return abs(self._true_phase_delay - self._demanded_phase_delay) - - def get_temperature(self): - return self._temperature - - def inverter_ready(self): - return self._type.get_manufacturer() in [ChopperType.CORTINA] - - def motor_running(self): - return self._started - - def in_sync(self): - tolerance = 0.001*self._type.get_frequency() - return abs(self._true_frequency - self._demanded_frequency) < tolerance - - def reg_mode(self): - return False - - def external_fault(self): - return self._fault - - def clock_loss(self): - return False - - def bearing_1_overheat(self): - return self._overheat() - - def bearing_2_overheat(self): - return self._overheat() - - def motor_overheat(self): - return self._overheat() - - def _overheat(self): - return self._temperature > MAX_TEMPERATURE - - def chopper_overspeed(self): - return self._true_frequency > self._type.get_frequency() - - def phase_delay_error(self): - return self._phase_delay_error - - def phase_delay_correction_error(self): - tolerance = 0.001*self._demanded_phase_delay - return abs(self._true_phase_delay - self._demanded_phase_delay) > tolerance - - def phase_accuracy_window_error(self): - return abs(self._true_phase_delay - self._demanded_phase_delay) > self._demanded_phase_error_window - - def set_demanded_frequency(self, new_frequency_int): - self._demanded_frequency = self._type.get_closest_valid_frequency(new_frequency_int) - self._max_phase_delay = self._type.get_max_phase_for_closest_frequency(new_frequency_int) - - def set_demanded_phase_delay(self, new_phase_delay): - self._demanded_phase_delay = min(new_phase_delay, self._max_phase_delay) - self._phase_delay_error = self._demanded_phase_delay != new_phase_delay - - def set_demanded_phase_error_window(self, new_phase_window): - self._demanded_phase_error_window = new_phase_window - - def set_true_frequency(self, new_frequency): - self._true_frequency = new_frequency - - def set_true_phase_delay(self, new_delay): - self._true_phase_delay = new_delay - - def set_chopper_type(self, frequency, manufacturer): - self._type = ChopperType(frequency, manufacturer) - # Do this in case the current demanded frequency is invalid for the new type - self.set_demanded_frequency(self._demanded_frequency) - - def set_temperature(self, temperature): - self._temperature = temperature - - def start(self): - self._started = True - - def stop(self): - self._started = False diff --git a/lewis_emulators/mk2_chopper/interfaces/__init__.py b/lewis_emulators/mk2_chopper/interfaces/__init__.py deleted file mode 100644 index a4e1a1c3..00000000 --- a/lewis_emulators/mk2_chopper/interfaces/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .stream_interface import Mk2ChopperStreamInterface - -__all__ = ['Mk2ChopperStreamInterface'] diff --git a/lewis_emulators/mk2_chopper/interfaces/stream_interface.py b/lewis_emulators/mk2_chopper/interfaces/stream_interface.py deleted file mode 100644 index be6fca08..00000000 --- a/lewis_emulators/mk2_chopper/interfaces/stream_interface.py +++ /dev/null @@ -1,139 +0,0 @@ -from lewis.adapters.stream import StreamInterface, Cmd -from ..chopper_type import ChopperType - - -def filled_int(val, length): - """ - Takes a value and returns a zero padded representation of the integer component. - - :param val: The original value. - :param length: Minimum length of the returned string - :return: Zero-padded integer representation (if possible) of string. Original string used if integer conversion - fails - """ - try: - converted_val = int(val) - except ValueError: - converted_val = val - return str(converted_val).zfill(length) - - -class Mk2ChopperStreamInterface(StreamInterface): - - # Commands that we expect via serial during normal operation - commands = { - Cmd("get_true_frequency", "^RF$"), - Cmd("get_demanded_frequency", "^RG$"), - Cmd("get_true_phase_delay", "^RP$"), - Cmd("get_demanded_phase_delay", "^RQ$"), - Cmd("get_true_phase_error", "^RE$"), - Cmd("get_demanded_phase_error_window", "^RW$"), - Cmd("get_chopper_interlocks", "^RC$"), - Cmd("get_spectral_interlocks", "^RS$"), - Cmd("get_error_flags", "^RX$"), - Cmd("read_all", "^RA$"), - Cmd("set_chopper_started", "^WS([0-9]+)$"), - Cmd("set_demanded_frequency", "^WM([0-9]+)$"), - Cmd("set_demanded_phase_delay", "^WP([0-9]+)$"), - Cmd("set_demanded_phase_error_window", "^WR([0-9]+)$") - } - - in_terminator = "\r" - out_terminator = "\r" - - def handle_error(self, request, error): - print("An error occurred at request " + repr(request) + ": " + repr(error)) - return str(error) - - def get_demanded_frequency(self): - return "RG{0}".format(filled_int(self._device.get_demanded_frequency(), 3)) - - def get_true_frequency(self): - return "RF{0}".format(filled_int(self._device.get_true_frequency(), 3)) - - def get_demanded_phase_delay(self): - return "RQ{0}".format(filled_int(self._device.get_demanded_phase_delay(), 5)) - - def get_true_phase_delay(self): - return "RP{0}".format(filled_int(self._device.get_true_phase_delay(), 5)) - - def get_demanded_phase_error_window(self): - return "RW{0}".format(filled_int(self._device.get_demanded_phase_error_window(), 3)) - - def get_true_phase_error(self): - return "RE{0}".format(filled_int(self._device.get_true_phase_error(), 3)) - - def get_spectral_interlocks(self): - bits = [0]*8 - if self._device.get_manufacturer() == ChopperType.CORTINA: - bits[0] = 1 if self._device.inverter_ready() else 0 - bits[1] = 1 if self._device.motor_running() else 0 - bits[2] = 1 if self._device.in_sync() else 0 - elif self._device.get_manufacturer() == ChopperType.INDRAMAT: - bits[0] = 1 if self._device.motor_running() else 0 - bits[1] = 1 if self._device.reg_mode() else 0 - bits[2] = 1 if self._device.in_sync() else 0 - elif self._device.get_manufacturer() == ChopperType.SPECTRAL: - bits[2] = 1 if self._device.external_fault() else 0 - return "RS{0:8s}".format(Mk2ChopperStreamInterface._string_from_bits(bits)) - - def get_chopper_interlocks(self): - bits = [0]*8 - bits[0] = 1 if self._device.get_system_frequency() == 50 else 0 - bits[1] = 1 if self._device.clock_loss() else 0 - bits[2] = 1 if self._device.bearing_1_overheat() else 0 - bits[3] = 1 if self._device.bearing_2_overheat() else 0 - bits[4] = 1 if self._device.motor_overheat() else 0 - bits[5] = 1 if self._device.chopper_overspeed() else 0 - return "RC{0:8s}".format(Mk2ChopperStreamInterface._string_from_bits(bits)) - - def get_error_flags(self): - bits = [0]*8 - bits[0] = 1 if self._device.phase_delay_error() else 0 - bits[1] = 1 if self._device.phase_delay_correction_error() else 0 - bits[2] = 1 if self._device.phase_accuracy_window_error() else 0 - return "RX{0:8s}".format(Mk2ChopperStreamInterface._string_from_bits(bits)) - - def get_manufacturer(self): - return self._type.get_manufacturer() - - def set_chopper_started(self, start_flag_raw): - try: - start_flag = int(start_flag_raw) - except ValueError: - pass - else: - if start_flag == 1: - self._device.start() - elif start_flag == 2: - self._device.stop() - return - - def set_demanded_frequency(self, new_frequency_raw): - return Mk2ChopperStreamInterface._set(new_frequency_raw, self.get_demanded_frequency, - self._device.set_demanded_frequency) - - def set_demanded_phase_delay(self, new_phase_delay_raw): - return Mk2ChopperStreamInterface._set(new_phase_delay_raw, self.get_demanded_phase_delay, - self._device.set_demanded_phase_delay) - - def set_demanded_phase_error_window(self, new_phase_error_window_raw): - return Mk2ChopperStreamInterface._set(new_phase_error_window_raw, self.get_demanded_phase_error_window, - self._device.set_demanded_phase_error_window) - - def read_all(self): - return "RA:Don't use, it causes the driver to lock up" - - @staticmethod - def _set(raw, device_get, device_set): - try: - int_value = int(raw) - except ValueError: - pass - else: - device_set(int_value) - return device_get() - - @staticmethod - def _string_from_bits(bits): - return "".join(str(n) for n in reversed(bits)) diff --git a/lewis_emulators/mk2_chopper/states.py b/lewis_emulators/mk2_chopper/states.py deleted file mode 100644 index 7e693116..00000000 --- a/lewis_emulators/mk2_chopper/states.py +++ /dev/null @@ -1,42 +0,0 @@ -from lewis.core.statemachine import State -from lewis.core import approaches - -# Would rather this were in device but causes Lewis to fail -MAX_TEMPERATURE = 1 - - -def output_current_state(device, state_name): - print("{0}: Freq {1:.2f}, Phase {2:.2f}, Error {3:.2f}, Temperature {4:.2f}".format( - state_name.upper(), - device.get_true_frequency(), - device.get_true_phase_delay(), - device.get_true_phase_error(), - device.get_temperature(), - )) - - -class DefaultInitState(State): - pass - - -class DefaultStoppedState(State): - def in_state(self, dt): - device = self._context - output_current_state(self._context, "stopped") - device.set_true_frequency(approaches.linear(device.get_true_frequency(), 0, 1, dt)) - device.set_temperature(approaches.linear(device.get_temperature(), 0, 0.1, dt)) - device.set_true_phase_delay(approaches.linear(device.get_true_phase_delay(), 0, 1, dt)) - - -class DefaultStartedState(State): - def in_state(self, dt): - device = self._context - output_current_state(self._context, "started") - device.set_true_frequency(approaches.linear(device.get_true_frequency(), - device.get_demanded_frequency(), 1, dt)) - equilibrium_frequency_temperature = 2*MAX_TEMPERATURE*device.get_true_frequency()/device.get_system_frequency() - device.set_temperature(approaches.linear(device.get_temperature(), equilibrium_frequency_temperature, - device.get_true_frequency()*0.001, - dt)) - device.set_true_phase_delay(approaches.linear(device.get_true_phase_delay(), device.get_demanded_phase_delay(), - 1, dt))