Files
MKS_ASCII_Protocol/mks_ascii_protocol/MKS_ASCII_Protocol.py
Georg Schlisio 16912b627f cleanup, comments
2022-01-20 11:20:09 +01:00

443 lines
34 KiB
Python

"""
Implementation of the MKS ASCII Protocol
as specified in "ASCII Protocol V1.8.pdf"
The
"""
from __future__ import absolute_import
__author__ = "Georg Schlisio <georg.schlisio@ipp.mpg.de"
__version__ = 0.1
__protocol_version__ = 1.8
#### imports
import time
import sys
import os
#import numpy as np
import logging
import threading
import queue
import socket
__softwarename__ = "MKS ASCII server on " + socket.gethostname()
from message import ParseMessage, ComposeMessage
## basic config
logging.basicConfig(level=logging.INFO)
assert (sys.version_info[0] + sys.version_info[1]*0.1 ) >= 3.7, "Minimum python requirement not met: "+str(sys.version_info[0] + sys.version_info[1]*0.1)+". We need 3.7 or above for fstrings and queue features."
####
composer = ComposeMessage()
class MKSListener():
def __init__(self, ip, up_queue, down_queue, port=10014, local_port=10000, target_state="connected"):
"""
Talk to an MKS mass spectrometer over the ethernet interface.
:param ip: IPv4 address of RGA device as string
:param up_queue: queue of messages towards the RGA device (type: queue.SimpleQueue)
:param down_queue: queue of messages from the RGA device (type: queue.SimpleQueue)
:param port: port on the RGA device (default: 10014)
:param local_port: local port (default 10000)
:param target_state:
"""
self.up_queue = up_queue # queue of messages towards the RGA device
self.down_queue = down_queue # queue of messages from the RGA device
self._target_state = target_state
# networking
self._remote_ip = ip #
self._remote_port = port
self._local_port = local_port
self._s = None # socket object
# connection state
self._connected = False # connection state
self._target_state = "disconnected" # target connection state
self.device_sn = None # device SN currently connected to
# protocol versioning and formatting
self._remote_min_protocol_version = None # remote requirement of min protocol, retrieved from remote device
self._min_protocol_version = 1.6 # assumed minimum protocol version
self._protocol_version = __protocol_version__
self.format_with_tab = False # whether to request FormatWithTab (reduces networkload slighlty)
# program flow control
self.loop_min_time = 0.01 # minimum duration of a listener loop in seconds
# Mass spectrometer properties
self.filament = None # number of filament in use
def turn_on(self):
self._target_state = "connected"
def turn_off(self):
self._target_state = "disconnected"
def run(self):
"""
Main loop.
:return:
"""
while True:
logging.info(f"starting new loop")
start_time = time.time()
if self._target_state == "connected" and not self._connected:
self._connect()
if self._connected:
self._check_for_messages()
if self._target_state == "disconnected":
self._disconnect()
break
if self._target_state == "exit":
self._disconnect()
exit(0)
end_time = time.time()
if end_time - start_time < self.loop_min_time:
sleeptime = self.loop_min_time - (end_time - start_time)
logging.info(f"sleep for {sleeptime}s")
time.sleep(sleeptime)
def _send(self, msg, args=None):
"""
Wrapper for sending messages
:param msg:
:return:
"""
self._s.send(composer(msg, args=args))
def _connect(self):
"""
Initiate connection
:return:
"""
assert not self._connected, "Cannot connect "
self._s = socket.socket(family=socket.AF_INET)
self._host = socket.gethostname()
self._s.bind((self._host, self._local_port))
self._s.connect((self._remote_ip, self._remote_port))
self.connected = True
self._protocol_version_check()
# control message formatting
if self.format_with_tab:
self._send("FormatWithTab True")
# negotiate protocol version
self._send("AcceptProtocol 1.6") # TODO check if actual protocol needs to be signaled
def _protocol_version_check(self, tries = 10):
"""
Wait for first message and evaluate protocol compatibility
:return:
"""
while True:
self._check_for_messages()
if self.down_queue.qsize() > 0:
break
time.sleep(1)
tries -= 1
if tries == 0:
logging.error(f"Connection not successfully established: received no greeting. Exiting.")
exit(201)
message = self.down_queue.pop(0)
assert message.type == "MKSRGA", f"Unexpected message type: {message.type}"
assert message.parms[message.type] in ("Single", "Multi"), f"Unexpected connection type: {message.parms[message.type]}"
assert message.parms[message.type] == "Single", f"Connection to QMS Servers of type 'Multi' is not supported." # To support this, implement the commands "Sensors" and "Select"
assert "Protocol_Revision" in message.parms
assert "Min_Compatibility" in message.parms
self._remote_protocol_version = float(message.parms["Protocol_Revision"])
self._remote_min_protocol_version = float(message.parms["Min_Compatibility"])
if self._remote_min_protocol_version > self._protocol_version:
logging.error(f"Version mismatches: remote requires V{self._remote_min_protocol_version} but we have only V{self._protocol_version}, exiting")
exit(202)
def _disconnect(self):
"""
Terminate connection
:return:
"""
# send message "Release"
self._s.send(composer(mtype="Release"))
# TODO: replace sleep with actual check for "Release Ok"
time.sleep(2)
self._s.close()
self._connected = False
def _check_for_messages(self):
"""
Read socket buffer, parse messages and enqueue them into the message queue
:return:
"""
rawinput = self._s.recv(4096).decode("ASCII").split("\r\r")
if len(rawinput) > 1:
logging.info(f"found {len(rawinput) - 1} messages")
for i in range(len(rawinput) - 1):
self.down_queue.put(ParseMessage(rawinput[i]))
class MKSOperator():
_commands = {
# controlling message formatting
"FormatWithTab": {"type": "Controlling message formatting", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"AcceptProtocol": {"type": "Controlling message formatting", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.5},
# sensor information commands
"Sensors": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"Select": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"SensorState": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"Info": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"EGains": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"InletInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"RFInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MultiplierInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"SourceInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"DetectorInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"FilamentInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"TotalPressureInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"AnalogInputInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"AnalogOutputInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"DigitalInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"RolloverInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"CirrusInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"PECal_Info": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"Sensor": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"SourceAlignmentInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"SourceResolutionInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"SourceTuningInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"DiagnosticInputInfo": {"type": "Sensor Information Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"Control": {"type": "Gaining control of a sensor", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"Release": {"type": "Gaining control of a sensor", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
# sensor control commands
"FilamentControl": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"FilamentSelect": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"FilamentOnTime": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"AddAnalog": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"AddBarchart": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"AddPeakJump": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"AddSinglePeak": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementAccuracy": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementAddMass": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementChangeMass": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementDetectorIndex": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementEGainIndex": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementFilterMode": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementMass": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementPointsPerPeak": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementRemoveMass": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementSourceIndex": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementRolloverCorrection": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementZeroBeamOff": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementZeroBufferDepth": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementZeroBufferMode": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementZeroReTrigger": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementZeroMass": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MultiplierProtect": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"RunDiagnostics": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"SetTotalPressure": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"TotalPressureCalFactor": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"TotalPressureCalDate": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"CalibrationOptions": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"DetectorFactor": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"DetectorCalDate": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"DetectorVoltage": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"InletFactor": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"ScanAdd": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"ScanStart": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"ScanStop": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"ScanResume": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"ScanRestart": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.3},
"MeasurementSelect": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementStartMass": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementEndMass": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementMultSkipAutoProtect": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.4},
"MeasurementMultSkipMassAdd": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.4},
"MeasurementMultSkipMassRemove": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.4},
"MeasurementMultSkipMassRemoveAll": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.4},
"MeasurementRemoveAll": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MeasurementRemove": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"SourceIonEnergy": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"SourceEmission": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"SourceExtract": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"SourceElectronEnergy": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"SourceLowMassResolution": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"SourceLowMassAlignment": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"SourceHighMassAlignment": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"SourceHighMassResolution": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"AnalogInputAverageCount": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"AnalogInputEnable": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"AnalogInputInterval": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"AnalogOutput": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"AudioFrequency": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"AudioMode": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"CirrusCapillaryHeater": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"CirrusHeater": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"CirrusPump": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"CirrusValvePosition": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"DigitalMaxPB67OnTime": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"DigitalOutput": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"PECal_DateMsg": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"PECal_Flush": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"PECal_Inlet": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"PECal_MassMethodContribution": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"PECal_Pressures": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"PECal_Select": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RolloverScaleFactor": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RolloverVariables": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCAlarm": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCCloseAllValves": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCHeater": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCPump": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCValveControl": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCValveMode": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"SaveChanges": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"StartDegas": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"StopDegas": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"SourceAlignmentCopyToAll": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"SourceAlignmentUpdate": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"SourceAlignmentRemove": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"SourceResolutionCopyToAll": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"SourceResolutionUpdate": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"SourceResolutiontRemove": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"SourcePoleBias": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"DiagnosticInputAverageCount": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"DiagnosticInputEnable": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"DiagnosticInputInterval": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"InletSelect": {"type": "Sensor Control Commands", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.7},
# asynchronous sensor notifications
"MKSRGA": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"FilamentStatus": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"FilamentTimeRemaining": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"StartingScan": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"StartingMeasurement": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"ZeroReading": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"MassReading": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.4},
"MultAutoSkip": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.4},
"MultiplierStatus": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"RFTripState": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1},
"InletChange": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"AnalogInput": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"TotalPressure": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
"DigitalPortChange": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCPumpStatus": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCHeaterStatus": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCValveStatus": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCInterlocks": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCStatus": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"RVCDigitalInput": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"LinkDown": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"VSCEvent": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"DegasReading": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.2},
"DiagnosticInput": {"type": "Asynchronous Sensor Notifications", "description": "", "action": NotImplementedError, "expected_response": "", "from_version": 1.6},
}
def __init__(self, up_queue:queue.SimpleQueue, down_queue:queue.SimpleQueue, command_actions:dict={}, target_state="run"):
#raise NotImplementedError()
self._target_state = target_state
self._current_protocol_version = 1
self.up_queue = up_queue
self.down_queue = down_queue
# default pre-set actions
self.register_command_actions({
"FormatWithTab": self._message_tab_formatting,
"AcceptProtocol": self._current_protocol_version})
# update actions provided by user
self.register_command_actions(command_actions)
def run(self):
while self._target_state == "run":
message = self.down_queue.get(block=True, timeout=None)
self._digest(message)
def register_command_actions(self, actions:dict):
"""
Register actions for Protocol commands
:param actions: dict, where the key is the command name and value is the callable. The callable must accept one argument (the message)
:return: Nothing
"""
for command in actions:
if command not in self._commands:
logging.warning(f"Unknown command: {command}. Ignoring.")
continue
if self._commands[command]["from_version"] > self._current_protocol_version:
logging.warning(f"Unsupported command {command} for our version {self._current_protocol_version}. Ignoring.")
continue
assert hasattr(self._commands[command]['action'], "__call__"), f"{command} callable has not __call__"
self._commands[command]["action"] = actions[command]
def _digest_command(self, message):
"""
Receive Message and act upon it
:param message:
:return:
:raises:
"""
assert isinstance(message, ParseMessage)
if message.type not in self._commands:
logging.error(f"Received unknown message type: {message.type}")
raise NotImplementedError()
try:
self._commands[message.type].action(message)
except NotImplementedError as e:
logging.warning("Received unimplemented message {message.type} with content {message.body}")
except Exception as e:
logging.warning(f"Error during digestion of {message.type}: {e}")
def _set_protocol_version(self, message):
"""
:param message:
:return:
"""
assert message.type == "AcceptProtocol"
raise NotImplementedError()
def _message_tab_formatting(self, message):
# no action needed - all code is invariant to tabs vs spaces
# using spaces saves a few bits of bandwith due to less padding spaces, so use it anyways
pass
def _available_sensors(self, message):
raise NotImplementedError()
class ProtocolVersionError(Exception):
def __init__(self, version, message="Version mismatch: "):
self.message = message + str(version)
super().__init__(self.message)
def run(obj):
"""
Target wrapper for thread object invocation
"""
obj.run()
if __name__ == "__main__":
# communicating queues: up -> towards MKS instrument, down -> from MKS intrument
upq = queue.Queue()
downq = queue.Queue()
# create instances of listener and operator
l = MKSListener(ip="127.0.0.1", up_queue=upq, down_queue=downq)
o = MKSOperator(up_queue=upq, down_queue=downq)
# create thread for each
listenerThread = threading.Thread(target=run, args=(l,))
operatorThread = threading.Thread(target=run, args=(o,))
# start threads
operatorThread.start()
listenerThread.start()