From fddb9a0ec56ba22370b1a2ee9aaf1b7b06510381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Ahlstr=C3=B6m?= Date: Fri, 9 Dec 2022 12:31:42 +0100 Subject: [PATCH 1/7] Ability to connect devices in yaml --- pyvisa_sim/component.py | 57 +++++++++++++ pyvisa_sim/default.yaml | 141 +++++++++++++++++++++++++++++++ pyvisa_sim/parser.py | 28 +++++- pyvisa_sim/testsuite/test_all.py | 13 +++ 4 files changed, 235 insertions(+), 4 deletions(-) diff --git a/pyvisa_sim/component.py b/pyvisa_sim/component.py index 279bc92..a7f02bf 100644 --- a/pyvisa_sim/component.py +++ b/pyvisa_sim/component.py @@ -180,6 +180,21 @@ def validate_value(self, string_value: str) -> T: return value # --- Private API + +class Connection(object): + """A device connection""" + + def __init__(self, q, source_list, function): + """ + :param q: query to use + :param source: resource to connect to + :param source_parameter: resource output parameter + :param function: function to apply + """ + + self.q = q + self.source_list = source_list + self.function = function #: Current value of the property. _value: Optional[T] @@ -193,6 +208,7 @@ def __init__(self) -> None: self._properties = {} self._getters = {} self._setters = [] + self.devices = [] def add_dialogue(self, query: str, response: str) -> None: """Add dialogue to device. @@ -356,3 +372,44 @@ def _match_setters(self, query: bytes) -> Optional[OptionalBytes]: return error_response return None + + def _match_connection(self, query, connections=None): + """Tries to match in connections + + :param query: message tuple + :type query: Tuple[bytes] + :return: response if found or None + :rtype: Tuple[bytes] | None + """ + + if connections is None: + connections = self._connections + + # Check every connection for query match + for connection in connections: + if (connections[connection].q).encode("utf-8") == query: + + # function as written in yaml + func = connections[connection].function + + for source in connections[connection].source_list: + # Find source for connection + if source["source_name"] == "self": + value = self._properties[source["source_parameter"]]._value + func = func.replace('%' + source["source_parameter"] + '%', str(value)) + else: + + for device in self.devices._internal: + if device == source["source_name"]: + # Find correct property + for property in self.devices._internal[device]._properties: + if property == source["source_parameter"]: + # Replace parameter in func with value of property + value = self.devices._internal[device]._properties[property]._value + func = func.replace('%' + property + '%', str(value)) + break + break + # Run function + response = str(eval(func)) + logger.debug("Found response in queries: %s" % response) + return response.encode("utf-8") diff --git a/pyvisa_sim/default.yaml b/pyvisa_sim/default.yaml index ecf6e59..1f45a8a 100644 --- a/pyvisa_sim/default.yaml +++ b/pyvisa_sim/default.yaml @@ -241,6 +241,143 @@ devices: min: 1 max: 6 type: float + device 5: + eom: + ASRL INSTR: + q: "\r\n" + r: "\n" + USB INSTR: + q: "\r\n" + r: "\n" + TCPIP INSTR: + q: "\r\n" + r: "\n" + TCPIP SOCKET: + q: "\r\n" + r: "\n" + GPIB INSTR: + q: "\r\n" + r: "\n" + error: ERROR + dialogues: + - q: "*IDN?" + r: "HEWLETT-PACKARD, E3632A,0,1.2-5.0-1.0" + - q: "SYST:ERR?" + r: "NO ERROR" + - q: "SOUR1:POW?" + r: "2" + - q: "RUN:FUNCTION" + r: "hello {:}" + properties: + frequency: + default: 1.0 + getter: + q: "SENS1:FREQ?" + r: "{:.2f}" + setter: + q: "SENS1:FREQ {:.2f} MHZ" + r: "" + e: "FREQ ERROR" + specs: + min: 0.0 + max: 27.0 + type: float + current limit: + default: 1.0 + getter: + q: "MEAS:CURR?" + r: "{:.2f}" + setter: + q: "CURR:LIM {:}" + r: "" + e: 'CURRENT_ERROR' + specs: + min: 0.0 + max: 7.0 + type: float + voltage: + default: 1.0 + getter: + q: "MEAS:VOLT?" + r: "{:.2f}" + setter: + q: "VOLT {:.2f}" + r: "" + specs: + min: 0 + max: 30 + type: float + output_enabled: + default: '0' + getter: + q: "OUTPUT:STATE?" + r: "{:s}" + setter: + q: "OUTPUT:STATE {:s}" + r: "" + specs: + valid: ['0', '1', 'ON', 'OFF'] + type: str + connections: + power: + q: "READ1" + source_list: [ + {source_name: "GPIB0::2::65535::INSTR", source_parameter: amplitude}, + {source_name: "self", source_parameter: frequency} + ] + function: "0.23 * (%amplitude% ** 2) - %frequency%" + device 6: + eom: + ASRL INSTR: + q: "\r\n" + r: "\n" + USB INSTR: + q: "\r\n" + r: "\n" + TCPIP INSTR: + q: "\r\n" + r: "\n" + TCPIP SOCKET: + q: "\r\n" + r: "\n" + GPIB INSTR: + q: "\r\n" + r: "\n" + error: ERROR + dialogues: + - q: "*IDN?" + r: "Hewlett-Packard, ESG-D4000B, GB40050924, B.03.86" + - q: "*OPC?" + r: "Hewlett-Packard, ESG-D4000B, GB40050924, B.03.86" + - q: "SYST:ERR?" + r: "NO ERROR" + properties: + frequency: + default: 1.0 + getter: + q: "FREQ?" + r: "{:.2f}" + setter: + q: "FREQ {:.2f}MHZ" + r: "" + e: "FREQ ERROR" + specs: + min: 0.0 + max: 6.0 + type: float + amplitude: + default: 0.0 + getter: + q: "POW:LEV?" + r: "{:.2f}" + setter: + q: "POW:LEV {:.2f}" + r: "" + e: "POW ERROR" + specs: + min: -100.0 + max: 20.0 + type: float resources: ASRL1::INSTR: @@ -279,3 +416,7 @@ resources: device: device 4 USB::0x1111::0x2222::0x4445::RAW: device: device 1 + GPIB0::22::INSTR: + device: device 5 + GPIB0::2::INSTR: + device: device 6 diff --git a/pyvisa_sim/parser.py b/pyvisa_sim/parser.py index 94ae7da..860fa57 100644 --- a/pyvisa_sim/parser.py +++ b/pyvisa_sim/parser.py @@ -122,7 +122,7 @@ def parse_file(fullpath: Union[str, pathlib.Path]) -> Dict[str, Any]: def update_component( - name: str, comp: Component, component_dict: Dict[str, Any] + name: str, comp: Component, component_dict: Dict[str, Any], devices: Dict[str, Device] ) -> None: """Get a component from a component dict.""" for dia in component_dict.get("dialogues", ()): @@ -149,6 +149,24 @@ def update_component( msg = "In device %s, malformed property %s\n%r" raise type(e)(msg % (name, prop_name, format_exc())) + for conn_name, conn_dict in component_dict.get("connections", {}).items(): + try: + comp.add_connection( + conn_name, + conn_dict["q"], + conn_dict["source_list"], + conn_dict["function"], + ) + except Exception as e: + msg = "In device %s, malformed connection %s\n%r" + raise type(e)(msg % (name, conn_name, format_exc())) + + try: + comp.add_devices(devices) + except Exception as e: + msg = "In device %s, malformed devices %s\n%r" + raise Exception(msg % (name, devices, e)) + def get_bases(definition_dict: Dict[str, Any], loader: "Loader") -> Dict[str, Any]: """Collect inherited behaviors.""" @@ -171,6 +189,7 @@ def get_channel( channel_dict: Dict[str, Any], loader: "Loader", resource_dict: Dict[str, Any], + devices: Dict[str, Device] ) -> Channels: """Get a channels from a channels dictionary. @@ -201,7 +220,7 @@ def get_channel( can_select = False if channel_dict.get("can_select") == "False" else True channels = Channels(device, ids, can_select) - update_component(ch_name, channels, cd) + update_component(ch_name, channels, channel_dict, devices) return channels @@ -211,6 +230,7 @@ def get_device( device_dict: Dict[str, Any], loader: "Loader", resource_dict: Dict[str, str], + devices: Dict[str, Device] ) -> Device: """Get a device from a device dictionary. @@ -241,7 +261,7 @@ def get_device( for itype, eom_dict in device_dict.get("eom", {}).items(): device.add_eom(itype, *_get_pair(eom_dict)) - update_component(name, device, device_dict) + update_component(name, device, device_dict, devices) for ch_name, ch_dict in device_dict.get("channels", {}).items(): device.add_channels( @@ -410,7 +430,7 @@ def get_devices(filename: Union[str, pathlib.Path], bundled: bool) -> Devices: ) devices.add_device( - resource_name, get_device(device_name, dd, loader, resource_dict) + resource_name, get_device(device_name, dd, loader, resource_dict, devices) ) return devices diff --git a/pyvisa_sim/testsuite/test_all.py b/pyvisa_sim/testsuite/test_all.py index 5fb9b89..d6c9307 100644 --- a/pyvisa_sim/testsuite/test_all.py +++ b/pyvisa_sim/testsuite/test_all.py @@ -24,10 +24,12 @@ def test_list(resource_manager): "USB0::0x1111::0x2222::0x3692::0::INSTR", "USB0::0x1111::0x2222::0x4444::0::INSTR", "USB0::0x1111::0x2222::0x4445::0::RAW", + "GPIB0::2::INSTR", "GPIB0::4::INSTR", "GPIB0::8::INSTR", "GPIB0::9::INSTR", "GPIB0::10::INSTR", + "GPIB0::22::INSTR", } @@ -179,3 +181,14 @@ def test_instrument_for_error_state(resource, resource_manager): inst.write(":VOLT:IMM:AMPL 0") assert_instrument_response(inst, ":SYST:ERR?", "1, Command error") + +def test_connections(resource_manager): + inst1 = resource_manager.open_resource('GPIB0::22::INSTR', read_termination='\n') + inst2 = resource_manager.open_resource('GPIB0::2::INSTR', read_termination='\n') + inst2.query("POW:LEV 8.5") + response = inst1.query("READ1") + assert(response == '15.6175') + inst2.query("POW:LEV 1.0") + response = inst1.query("READ1") + assert(response == '-0.77') + \ No newline at end of file From 402623fc2370e5ecadc2af2eb827d5067cff5e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Ahlstr=C3=B6m?= Date: Fri, 9 Dec 2022 13:21:26 +0100 Subject: [PATCH 2/7] Update parser.py Missing devices in device.add_channels(...) --- pyvisa_sim/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyvisa_sim/parser.py b/pyvisa_sim/parser.py index 860fa57..d2d01f1 100644 --- a/pyvisa_sim/parser.py +++ b/pyvisa_sim/parser.py @@ -265,7 +265,7 @@ def get_device( for ch_name, ch_dict in device_dict.get("channels", {}).items(): device.add_channels( - ch_name, get_channel(device, ch_name, ch_dict, loader, resource_dict) + ch_name, get_channel(device, ch_name, ch_dict, loader, resource_dict, devices) ) return device From 3c2b41dddc0655173f7b2e1cdd389b4740a60750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Ahlstr=C3=B6m?= Date: Fri, 9 Dec 2022 14:57:27 +0100 Subject: [PATCH 3/7] Minor fixes to better comply with project --- pyvisa_sim/component.py | 42 +++++++++++++++++++---------------------- pyvisa_sim/default.yaml | 4 ++-- pyvisa_sim/parser.py | 2 +- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/pyvisa_sim/component.py b/pyvisa_sim/component.py index a7f02bf..c67c8c2 100644 --- a/pyvisa_sim/component.py +++ b/pyvisa_sim/component.py @@ -187,8 +187,7 @@ class Connection(object): def __init__(self, q, source_list, function): """ :param q: query to use - :param source: resource to connect to - :param source_parameter: resource output parameter + :param source_list: list of sources to connect with :param function: function to apply """ @@ -386,30 +385,27 @@ def _match_connection(self, query, connections=None): connections = self._connections # Check every connection for query match - for connection in connections: - if (connections[connection].q).encode("utf-8") == query: + for connection in connections.values(): + if (connection.q).encode("utf-8") == query: - # function as written in yaml - func = connections[connection].function + prop_dict = {} - for source in connections[connection].source_list: + for source in connection.source_list: # Find source for connection - if source["source_name"] == "self": - value = self._properties[source["source_parameter"]]._value - func = func.replace('%' + source["source_parameter"] + '%', str(value)) - else: - - for device in self.devices._internal: - if device == source["source_name"]: - # Find correct property - for property in self.devices._internal[device]._properties: - if property == source["source_parameter"]: - # Replace parameter in func with value of property - value = self.devices._internal[device]._properties[property]._value - func = func.replace('%' + property + '%', str(value)) - break - break - # Run function + + for device in self.devices._internal: + if device == source["source_name"]: + # Find correct property + for property in self.devices._internal[device]._properties: + if property == source["source_parameter"]: + # Add property and value to prop_dict + value = self.devices._internal[device]._properties[property]._value + prop_dict[property] = value + break + break + + # Populate and run function + func = connection.function.format(**prop_dict) response = str(eval(func)) logger.debug("Found response in queries: %s" % response) return response.encode("utf-8") diff --git a/pyvisa_sim/default.yaml b/pyvisa_sim/default.yaml index 1f45a8a..c30403f 100644 --- a/pyvisa_sim/default.yaml +++ b/pyvisa_sim/default.yaml @@ -323,9 +323,9 @@ devices: q: "READ1" source_list: [ {source_name: "GPIB0::2::65535::INSTR", source_parameter: amplitude}, - {source_name: "self", source_parameter: frequency} + {source_name: "GPIB0::22::65535::INSTR", source_parameter: frequency} ] - function: "0.23 * (%amplitude% ** 2) - %frequency%" + function: "0.23 * ({amplitude} ** 2) - {frequency}" device 6: eom: ASRL INSTR: diff --git a/pyvisa_sim/parser.py b/pyvisa_sim/parser.py index d2d01f1..64eff26 100644 --- a/pyvisa_sim/parser.py +++ b/pyvisa_sim/parser.py @@ -162,7 +162,7 @@ def update_component( raise type(e)(msg % (name, conn_name, format_exc())) try: - comp.add_devices(devices) + comp.set_devices(devices) except Exception as e: msg = "In device %s, malformed devices %s\n%r" raise Exception(msg % (name, devices, e)) From e4e31bd6c5eee7998b6b2d68c22ab85e5680ac42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Ahlstr=C3=B6m?= Date: Mon, 12 Dec 2022 13:43:26 +0100 Subject: [PATCH 4/7] Made connection part of dialogues Removed connection class. Dialogues no longer use _get_pair(), they use get_dialogue(). A dialogue with a "sources" key expects response to be a function. --- pyvisa_sim/component.py | 103 ++++++++++++++++------------------------ pyvisa_sim/default.yaml | 16 +++---- pyvisa_sim/parser.py | 28 ++++++----- 3 files changed, 61 insertions(+), 86 deletions(-) diff --git a/pyvisa_sim/component.py b/pyvisa_sim/component.py index c67c8c2..1f47db9 100644 --- a/pyvisa_sim/component.py +++ b/pyvisa_sim/component.py @@ -179,21 +179,6 @@ def validate_value(self, string_value: str) -> T: ) return value - # --- Private API - -class Connection(object): - """A device connection""" - - def __init__(self, q, source_list, function): - """ - :param q: query to use - :param source_list: list of sources to connect with - :param function: function to apply - """ - - self.q = q - self.source_list = source_list - self.function = function #: Current value of the property. _value: Optional[T] @@ -208,19 +193,23 @@ def __init__(self) -> None: self._getters = {} self._setters = [] self.devices = [] + self._devices = {} - def add_dialogue(self, query: str, response: str) -> None: + def add_dialogue(self, query, response, sources = None): """Add dialogue to device. - Parameters - ---------- - query : str - Query to which the dialog answers to. - response : str - Response to the dialog query. - + :param query: query string + :param response: response string + :param sources: connected sources """ - self._dialogues[to_bytes(query)] = to_bytes(response) + if sources: + dialogue = { + "func": response, + "sources": (sources) + } + self._dialogues[to_bytes(query)] = dialogue + + else: self._dialogues[to_bytes(query)] = to_bytes(response) def add_property( self, @@ -303,7 +292,33 @@ def _match_dialog( # Try to match in the queries if query in dialogues: - response = dialogues[query] + # if connection + if type(dialogues[query]) == dict: + dialogue = dialogues[query] + function = dialogue["func"] + sources = dialogue["sources"] + prop_dict = {} + + for source in sources: + # Find source for connection + for device in self.devices._internal: + if device == source["source_name"]: + # Find correct property + for property in self.devices._internal[device]._properties: + if property == source["source_parameter"]: + # Add property and value to prop_dict + value = self.devices._internal[device]._properties[property]._value + prop_dict[property] = value + break + break + + # Populate and run function + func = function.format(**prop_dict) + response = to_bytes(str(eval(func))) + + else: + response = dialogues[query] + logger.debug("Found response in queries: %s" % repr(response)) return response @@ -371,41 +386,3 @@ def _match_setters(self, query: bytes) -> Optional[OptionalBytes]: return error_response return None - - def _match_connection(self, query, connections=None): - """Tries to match in connections - - :param query: message tuple - :type query: Tuple[bytes] - :return: response if found or None - :rtype: Tuple[bytes] | None - """ - - if connections is None: - connections = self._connections - - # Check every connection for query match - for connection in connections.values(): - if (connection.q).encode("utf-8") == query: - - prop_dict = {} - - for source in connection.source_list: - # Find source for connection - - for device in self.devices._internal: - if device == source["source_name"]: - # Find correct property - for property in self.devices._internal[device]._properties: - if property == source["source_parameter"]: - # Add property and value to prop_dict - value = self.devices._internal[device]._properties[property]._value - prop_dict[property] = value - break - break - - # Populate and run function - func = connection.function.format(**prop_dict) - response = str(eval(func)) - logger.debug("Found response in queries: %s" % response) - return response.encode("utf-8") diff --git a/pyvisa_sim/default.yaml b/pyvisa_sim/default.yaml index c30403f..bff6f66 100644 --- a/pyvisa_sim/default.yaml +++ b/pyvisa_sim/default.yaml @@ -266,8 +266,12 @@ devices: r: "NO ERROR" - q: "SOUR1:POW?" r: "2" - - q: "RUN:FUNCTION" - r: "hello {:}" + - q: "READ1" + r: "0.23 * ({amplitude} ** 2) - {frequency}" + sources: [ + {source_name: "GPIB0::2::65535::INSTR", source_parameter: amplitude}, + {source_name: "GPIB0::22::65535::INSTR", source_parameter: frequency} + ] properties: frequency: default: 1.0 @@ -318,14 +322,6 @@ devices: specs: valid: ['0', '1', 'ON', 'OFF'] type: str - connections: - power: - q: "READ1" - source_list: [ - {source_name: "GPIB0::2::65535::INSTR", source_parameter: amplitude}, - {source_name: "GPIB0::22::65535::INSTR", source_parameter: frequency} - ] - function: "0.23 * ({amplitude} ** 2) - {frequency}" device 6: eom: ASRL INSTR: diff --git a/pyvisa_sim/parser.py b/pyvisa_sim/parser.py index 64eff26..ff8f9ce 100644 --- a/pyvisa_sim/parser.py +++ b/pyvisa_sim/parser.py @@ -76,6 +76,20 @@ def _get_triplet( dd["e"].strip(" ") if "e" in dd else NoResponse, ) +def _get_dialogue(dd): + """Return a dialogue from a dialogue dictionary. + + :param dd: Dialogue dictionary. + :type dd: Dict[str, str] or Dict[str, str, str] + :return: (query, response, sources) + :rtype: (str, str, str) + """ + if "sources" in dd.keys(): + sources = dd["sources"] + else: + sources = None + return _s(dd["q"]), _s(dd.get("r", NoResponse)), sources + def _load(content_or_fp: Union[str, bytes, TextIO, BinaryIO]) -> Dict[str, Any]: """YAML Parse a file or str and check version.""" @@ -127,7 +141,7 @@ def update_component( """Get a component from a component dict.""" for dia in component_dict.get("dialogues", ()): try: - comp.add_dialogue(*_get_pair(dia)) + comp.add_dialogue(*_get_dialogue(dia)) except Exception as e: msg = "In device %s, malformed dialogue %s\n%r" raise Exception(msg % (name, dia, e)) @@ -149,18 +163,6 @@ def update_component( msg = "In device %s, malformed property %s\n%r" raise type(e)(msg % (name, prop_name, format_exc())) - for conn_name, conn_dict in component_dict.get("connections", {}).items(): - try: - comp.add_connection( - conn_name, - conn_dict["q"], - conn_dict["source_list"], - conn_dict["function"], - ) - except Exception as e: - msg = "In device %s, malformed connection %s\n%r" - raise type(e)(msg % (name, conn_name, format_exc())) - try: comp.set_devices(devices) except Exception as e: From d2ba58926cc30b5b66b3821b259af93a3f01b204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Ahlstr=C3=B6m?= Date: Wed, 14 Dec 2022 14:54:06 +0100 Subject: [PATCH 5/7] Updated to latest dev version Updated to latest dev version + some optimizations when finding connections --- pyvisa_sim/component.py | 42 +++++++++++++++++++------------- pyvisa_sim/default.yaml | 5 ++-- pyvisa_sim/parser.py | 30 +++++++++++------------ pyvisa_sim/testsuite/test_all.py | 5 ++-- 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/pyvisa_sim/component.py b/pyvisa_sim/component.py index 1f47db9..843f899 100644 --- a/pyvisa_sim/component.py +++ b/pyvisa_sim/component.py @@ -179,6 +179,7 @@ def validate_value(self, string_value: str) -> T: ) return value + # --- Private API #: Current value of the property. _value: Optional[T] @@ -192,15 +193,18 @@ def __init__(self) -> None: self._properties = {} self._getters = {} self._setters = [] - self.devices = [] - self._devices = {} + self.devices = {} - def add_dialogue(self, query, response, sources = None): + def add_dialogue(self, query: str, response: str, sources: dict = None) -> None: """Add dialogue to device. - :param query: query string - :param response: response string - :param sources: connected sources + Parameters + ---------- + query : str + Query to which the dialog answers to. + response : str + Response to the dialog query. + """ if sources: dialogue = { @@ -248,6 +252,13 @@ def add_property( (name, stringparser.Parser(query), to_bytes(response_), to_bytes(error)) ) + def set_devices(self, devices:dict) -> None: + """"Add all initialized devices + + :param devices: storage for devices + """ + self.devices = devices + def match(self, query: bytes) -> Optional[OptionalBytes]: """Try to find a match for a query in the instrument commands.""" raise NotImplementedError() @@ -301,16 +312,13 @@ def _match_dialog( for source in sources: # Find source for connection - for device in self.devices._internal: - if device == source["source_name"]: - # Find correct property - for property in self.devices._internal[device]._properties: - if property == source["source_parameter"]: - # Add property and value to prop_dict - value = self.devices._internal[device]._properties[property]._value - prop_dict[property] = value - break - break + if source["name"] in self.devices._internal.keys(): + device = self.devices._internal[source["name"]] + # Find correct property + property = device._properties[source["parameter"]] + # Add property and value to prop_dict + value = property._value + prop_dict[property.name] = value # Populate and run function func = function.format(**prop_dict) @@ -318,7 +326,7 @@ def _match_dialog( else: response = dialogues[query] - + logger.debug("Found response in queries: %s" % repr(response)) return response diff --git a/pyvisa_sim/default.yaml b/pyvisa_sim/default.yaml index bff6f66..9985771 100644 --- a/pyvisa_sim/default.yaml +++ b/pyvisa_sim/default.yaml @@ -241,6 +241,7 @@ devices: min: 1 max: 6 type: float + device 5: eom: ASRL INSTR: @@ -269,8 +270,8 @@ devices: - q: "READ1" r: "0.23 * ({amplitude} ** 2) - {frequency}" sources: [ - {source_name: "GPIB0::2::65535::INSTR", source_parameter: amplitude}, - {source_name: "GPIB0::22::65535::INSTR", source_parameter: frequency} + {name: "GPIB0::2::INSTR", parameter: amplitude}, + {name: "GPIB0::22::INSTR", parameter: frequency} ] properties: frequency: diff --git a/pyvisa_sim/parser.py b/pyvisa_sim/parser.py index ff8f9ce..fdd586a 100644 --- a/pyvisa_sim/parser.py +++ b/pyvisa_sim/parser.py @@ -60,6 +60,20 @@ def __getitem__(self, key: K) -> V: pass raise KeyError(key) +def _get_dialogue(dd: Dict[str, str]) -> Tuple[str, str, Dict[str, str]]: + """Return a dialogue from a dialogue dictionary. + + :param dd: Dialogue dictionary. + :type dd: Dict[str, str] or Dict[str, str, str] + :return: (query, response, sources) + :rtype: (str, str, str) + """ + if "sources" in dd.keys(): + sources = dd["sources"] + else: + sources = None + + return dd["q"].strip(" "), dd["r"].strip(" "), sources def _get_pair(dd: Dict[str, str]) -> Tuple[str, str]: """Return a pair from a dialogue dictionary.""" @@ -76,20 +90,6 @@ def _get_triplet( dd["e"].strip(" ") if "e" in dd else NoResponse, ) -def _get_dialogue(dd): - """Return a dialogue from a dialogue dictionary. - - :param dd: Dialogue dictionary. - :type dd: Dict[str, str] or Dict[str, str, str] - :return: (query, response, sources) - :rtype: (str, str, str) - """ - if "sources" in dd.keys(): - sources = dd["sources"] - else: - sources = None - return _s(dd["q"]), _s(dd.get("r", NoResponse)), sources - def _load(content_or_fp: Union[str, bytes, TextIO, BinaryIO]) -> Dict[str, Any]: """YAML Parse a file or str and check version.""" @@ -222,7 +222,7 @@ def get_channel( can_select = False if channel_dict.get("can_select") == "False" else True channels = Channels(device, ids, can_select) - update_component(ch_name, channels, channel_dict, devices) + update_component(ch_name, channels, cd, devices) return channels diff --git a/pyvisa_sim/testsuite/test_all.py b/pyvisa_sim/testsuite/test_all.py index d6c9307..7ffe835 100644 --- a/pyvisa_sim/testsuite/test_all.py +++ b/pyvisa_sim/testsuite/test_all.py @@ -29,7 +29,7 @@ def test_list(resource_manager): "GPIB0::8::INSTR", "GPIB0::9::INSTR", "GPIB0::10::INSTR", - "GPIB0::22::INSTR", + "GPIB0::22::INSTR" } @@ -182,7 +182,7 @@ def test_instrument_for_error_state(resource, resource_manager): inst.write(":VOLT:IMM:AMPL 0") assert_instrument_response(inst, ":SYST:ERR?", "1, Command error") -def test_connections(resource_manager): +def test_dialogue_connection(resource_manager): inst1 = resource_manager.open_resource('GPIB0::22::INSTR', read_termination='\n') inst2 = resource_manager.open_resource('GPIB0::2::INSTR', read_termination='\n') inst2.query("POW:LEV 8.5") @@ -191,4 +191,3 @@ def test_connections(resource_manager): inst2.query("POW:LEV 1.0") response = inst1.query("READ1") assert(response == '-0.77') - \ No newline at end of file From 63e30e4fa845e183c244bdaeacf723a2c3984797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Ahlstr=C3=B6m?= Date: Wed, 21 Dec 2022 09:45:47 +0100 Subject: [PATCH 6/7] Fixed Black/Flake8 formatting --- pyvisa_sim/component.py | 20 +++++++++----------- pyvisa_sim/parser.py | 14 ++++++++++---- pyvisa_sim/testsuite/test_all.py | 11 ++++++----- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/pyvisa_sim/component.py b/pyvisa_sim/component.py index 843f899..552c3f4 100644 --- a/pyvisa_sim/component.py +++ b/pyvisa_sim/component.py @@ -207,13 +207,11 @@ def add_dialogue(self, query: str, response: str, sources: dict = None) -> None: """ if sources: - dialogue = { - "func": response, - "sources": (sources) - } + dialogue = {"func": response, "sources": (sources)} self._dialogues[to_bytes(query)] = dialogue - else: self._dialogues[to_bytes(query)] = to_bytes(response) + else: + self._dialogues[to_bytes(query)] = to_bytes(response) def add_property( self, @@ -252,13 +250,13 @@ def add_property( (name, stringparser.Parser(query), to_bytes(response_), to_bytes(error)) ) - def set_devices(self, devices:dict) -> None: - """"Add all initialized devices + def set_devices(self, devices: dict) -> None: + """ "Add all initialized devices :param devices: storage for devices """ self.devices = devices - + def match(self, query: bytes) -> Optional[OptionalBytes]: """Try to find a match for a query in the instrument commands.""" raise NotImplementedError() @@ -319,14 +317,14 @@ def _match_dialog( # Add property and value to prop_dict value = property._value prop_dict[property.name] = value - + # Populate and run function func = function.format(**prop_dict) response = to_bytes(str(eval(func))) - else: + else: response = dialogues[query] - + logger.debug("Found response in queries: %s" % repr(response)) return response diff --git a/pyvisa_sim/parser.py b/pyvisa_sim/parser.py index fdd586a..aa7dcd0 100644 --- a/pyvisa_sim/parser.py +++ b/pyvisa_sim/parser.py @@ -60,6 +60,7 @@ def __getitem__(self, key: K) -> V: pass raise KeyError(key) + def _get_dialogue(dd: Dict[str, str]) -> Tuple[str, str, Dict[str, str]]: """Return a dialogue from a dialogue dictionary. @@ -75,6 +76,7 @@ def _get_dialogue(dd: Dict[str, str]) -> Tuple[str, str, Dict[str, str]]: return dd["q"].strip(" "), dd["r"].strip(" "), sources + def _get_pair(dd: Dict[str, str]) -> Tuple[str, str]: """Return a pair from a dialogue dictionary.""" return dd["q"].strip(" "), dd["r"].strip(" ") @@ -136,7 +138,10 @@ def parse_file(fullpath: Union[str, pathlib.Path]) -> Dict[str, Any]: def update_component( - name: str, comp: Component, component_dict: Dict[str, Any], devices: Dict[str, Device] + name: str, + comp: Component, + component_dict: Dict[str, Any], + devices: Dict[str, Device], ) -> None: """Get a component from a component dict.""" for dia in component_dict.get("dialogues", ()): @@ -191,7 +196,7 @@ def get_channel( channel_dict: Dict[str, Any], loader: "Loader", resource_dict: Dict[str, Any], - devices: Dict[str, Device] + devices: Dict[str, Device], ) -> Channels: """Get a channels from a channels dictionary. @@ -232,7 +237,7 @@ def get_device( device_dict: Dict[str, Any], loader: "Loader", resource_dict: Dict[str, str], - devices: Dict[str, Device] + devices: Dict[str, Device], ) -> Device: """Get a device from a device dictionary. @@ -267,7 +272,8 @@ def get_device( for ch_name, ch_dict in device_dict.get("channels", {}).items(): device.add_channels( - ch_name, get_channel(device, ch_name, ch_dict, loader, resource_dict, devices) + ch_name, + get_channel(device, ch_name, ch_dict, loader, resource_dict, devices), ) return device diff --git a/pyvisa_sim/testsuite/test_all.py b/pyvisa_sim/testsuite/test_all.py index 7ffe835..862e336 100644 --- a/pyvisa_sim/testsuite/test_all.py +++ b/pyvisa_sim/testsuite/test_all.py @@ -29,7 +29,7 @@ def test_list(resource_manager): "GPIB0::8::INSTR", "GPIB0::9::INSTR", "GPIB0::10::INSTR", - "GPIB0::22::INSTR" + "GPIB0::22::INSTR", } @@ -182,12 +182,13 @@ def test_instrument_for_error_state(resource, resource_manager): inst.write(":VOLT:IMM:AMPL 0") assert_instrument_response(inst, ":SYST:ERR?", "1, Command error") + def test_dialogue_connection(resource_manager): - inst1 = resource_manager.open_resource('GPIB0::22::INSTR', read_termination='\n') - inst2 = resource_manager.open_resource('GPIB0::2::INSTR', read_termination='\n') + inst1 = resource_manager.open_resource("GPIB0::22::INSTR", read_termination="\n") + inst2 = resource_manager.open_resource("GPIB0::2::INSTR", read_termination="\n") inst2.query("POW:LEV 8.5") response = inst1.query("READ1") - assert(response == '15.6175') + assert response == "15.6175" inst2.query("POW:LEV 1.0") response = inst1.query("READ1") - assert(response == '-0.77') + assert response == "-0.77" From 3c480d9094f237b8a095623c4a8496fcdfd05083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Ahlstr=C3=B6m?= Date: Wed, 21 Dec 2022 10:32:54 +0100 Subject: [PATCH 7/7] Fixed add_dialogue() in channels.py Fixed: Incompatible default for argument "sources" (default has type "None", argument has type "Dict[Any, Any]") [assignment] --- pyvisa_sim/channels.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pyvisa_sim/channels.py b/pyvisa_sim/channels.py index d204dca..451f260 100644 --- a/pyvisa_sim/channels.py +++ b/pyvisa_sim/channels.py @@ -80,7 +80,7 @@ def __init__(self, device: "Device", ids: List[str], can_select: bool): self._getters = ChDict(__default__={}) self._dialogues = ChDict(__default__={}) - def add_dialogue(self, query: str, response: str) -> None: + def add_dialogue(self, query: str, response: str, sources: dict = {}) -> None: """Add dialogue to channel. Parameters @@ -91,7 +91,12 @@ def add_dialogue(self, query: str, response: str) -> None: Response sent in response to a query. """ - self._dialogues["__default__"][to_bytes(query)] = to_bytes(response) + if sources: + dialogue = {"func": response, "sources": (sources)} + self._dialogues["__default__"][to_bytes(query)] = dialogue + + else: + self._dialogues["__default__"][to_bytes(query)] = to_bytes(response) def add_property( self,