Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drv scpi #82

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion code/drv_scpi/src/scpi_sniffer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
'''
from .drv_scpi_iface import DrvScpiHandlerC, DrvScpiErrorC
from .drv_scpi_cmd import DrvScpiCmdTypeE, DrvScpiCmdDataC, DrvScpiSerialConfC
from .drv_scpi_node import DrvScpiNodeC, TX_NAME_CHAN, SCPI_MAX_MESSAGE_SIZE, SCPI_MAX_MSG
from .drv_scpi_node import DrvScpiNodeC

__all__ = [
'DrvScpiHandlerC',
Expand Down
43 changes: 43 additions & 0 deletions code/drv_scpi/src/scpi_sniffer/context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/python3
'''
This module manages the constants variables.
Those variables are used in the scripts inside the module and can be modified
in a config yaml file specified in the environment variable with name declared
in system_config_tool.
'''

####################### MANDATORY IMPORTS #######################
from __future__ import annotations
####################### GENERIC IMPORTS #######################

####################### SYSTEM ABSTRACTION IMPORTS #######################
from system_logger_tool import Logger, sys_log_logger_get_module_logger
log: Logger = sys_log_logger_get_module_logger(__name__)

####################### THIRD PARTY IMPORTS #######################

####################### PROJECT IMPORTS #######################
from system_config_tool import sys_conf_update_config_params

####################### MODULE IMPORTS #######################

###################### CONSTANTS ######################
# For further information check out README.md

DEFAULT_TIMEOUT_SEND_MSG: float = 0.1
DEFAULT_TIMEOUT_RX_MSG: float = 0.02
DEFAULT_NODE_PERIOD: int = 40
DEFAULT_NODE_NAME: str = 'SCPI'

DEFAULT_CHAN_NUM_MSG : int = 200 # Max number of allowed message per chan
DEFAULT_MAX_MSG_SIZE : int = 350 # Size of message sent through IPC message queue
DEFAULT_TX_CHAN : str = 'TX_SCPI' # Name of the TX channel in CAN
DEFAULT_RX_CHAN: str = 'RX_SCPI_' #Name of the RX channel for epc
DEFAULT_NUM_ATTEMPTS: int = 10 # Max number of reads to get data


CONSTANTS_NAMES = ('DEFAULT_TIMEOUT_SEND_MSG', 'DEFAULT_TIMEOUT_RX_MSG', 'DEFAULT_NODE_PERIOD',
'DEFAULT_NODE_NAME', 'DEFAULT_CHAN_NUM_MSG', 'DEFAULT_MAX_MSG_SIZE',
'DEFAULT_TX_CHAN', 'DEFAULT_RX_CHAN', 'DEFAULT_NUM_ATTEMPTS')
sys_conf_update_config_params(context=globals(),
constants_names=CONSTANTS_NAMES)
11 changes: 7 additions & 4 deletions code/drv_scpi/src/scpi_sniffer/drv_scpi_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@ def __init__(self, data_type: DrvScpiCmdTypeE, port: str, **kwargs:any):
self.port: str = port
self.__dict__.update(kwargs)

## If deleting device only need the port
if self.data_type != DrvScpiCmdTypeE.DEL_DEV and not hasattr(self, 'payload'):
log.error("No exist payload")
raise TypeError("No exist payload")
else:
if self.data_type == DrvScpiCmdTypeE.ADD_DEV:
## When adding deving the payload must be serial and a rx chan name must be provided
if not isinstance(self.payload, DrvScpiSerialConfC):
log.error("Payload must be a DrvScpiSerialConfC object")
raise TypeError("Payload must be a DrvScpiSerialConfC object")
Expand All @@ -64,12 +66,13 @@ def __init__(self, data_type: DrvScpiCmdTypeE, port: str, **kwargs:any):
elif not isinstance(self.rx_chan_name, str):
log.error("rx_chan_name must be a string")
raise TypeError("rx_chan_name must be a string")

elif (self.data_type in (DrvScpiCmdTypeE.WRITE, DrvScpiCmdTypeE.WRITE_READ,)) and \
not isinstance(self.payload, str):
## When writing and reading the payload must be a string
elif ((self.data_type in (DrvScpiCmdTypeE.WRITE, DrvScpiCmdTypeE.WRITE_READ,)) and
not isinstance(self.payload, str)):
log.error("Payload must be a string")
raise TypeError("Payload must be a string")

## When responding the payload must be a list and a status must be provided,
## this message is the answer of the device.
elif self.data_type == DrvScpiCmdTypeE.RESP:
if not isinstance(self.payload, list):
log.error("Payload must be a list")
Expand Down
48 changes: 25 additions & 23 deletions code/drv_scpi/src/scpi_sniffer/drv_scpi_iface.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def decode_and_split(self, data: bytes) -> List[str]:
- None.
"""
data_dec = data.decode("utf-8")
msg_decode = data_dec.split(f"{self.__separator}")
msg_decode = data_dec.split(f"{self.__separator}")[0]
return msg_decode


Expand All @@ -119,32 +119,34 @@ def read(self) -> None:
Raises:
- None.
'''
msg_read = self.__serial.readline()
log.warning(f"Port: {self.__rx_chan_name} \t Message read RX: {msg_read}")

resp_msg = DrvScpiCmdDataC(port = self.__serial.port, data_type = DrvScpiCmdTypeE.RESP,
payload = [], status = SysShdNodeStatusE.OK)
if len(msg_read) > 0:
msg_read_decoded = self.decode_and_split(msg_read)
self.wait_4_response = False
self.num_attempts_read = 0
self.status = SysShdNodeStatusE.OK

# Update message
resp_msg.payload = msg_read_decoded
resp_msg.status = self.status
log.info(f"Rx chan: {self.__rx_chan_name}. Message send: {resp_msg.payload}")
self.__rx_chan.send_data(resp_msg)
else:
self.num_attempts_read += 1
if self.num_attempts_read >= NUM_ATTEMPTS:
log.critical(f"rx chan: {self.__rx_chan_name}. No response from device") # pylint: disable=logging-fstring-interpolation
self.status = SysShdNodeStatusE.COMM_ERROR
if self.__serial.readable() and self.__serial.in_waiting > 0:
msg_read = self.__serial.readlines()
log.warning(f"Port: {self.__rx_chan_name} \t Message read RX: {msg_read}")

resp_msg = DrvScpiCmdDataC(port = self.__serial.port, data_type = DrvScpiCmdTypeE.RESP,
payload = [], status = SysShdNodeStatusE.OK)
if len(msg_read) > 0:
msg_read_decoded = [self.decode_and_split(
msg_read_partially) for msg_read_partially in msg_read]
self.wait_4_response = False
self.num_attempts_read = 0
self.status = SysShdNodeStatusE.OK

# Update message
resp_msg.payload = []
resp_msg.payload = msg_read_decoded
resp_msg.status = self.status
log.info(f"Rx chan: {self.__rx_chan_name}. Message send: {resp_msg.payload}")
self.__rx_chan.send_data(resp_msg)
else:
self.num_attempts_read += 1
if self.num_attempts_read >= NUM_ATTEMPTS and self.wait_4_response:
log.critical(f"rx chan: {self.__rx_chan_name}. No response from device") # pylint: disable=logging-fstring-interpolation
self.status = SysShdNodeStatusE.COMM_ERROR

# Update message
resp_msg.payload = []
resp_msg.status = self.status
self.__rx_chan.send_data(resp_msg)

def close(self) -> None:
''' Close the serial connection.
Expand Down
35 changes: 19 additions & 16 deletions code/drv_scpi/src/scpi_sniffer/drv_scpi_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@
from .drv_scpi_cmd import DrvScpiCmdDataC, DrvScpiCmdTypeE # pylint: disable=wrong-import-position

####################### ENUMS #######################
SCPI_MAX_MSG = 300 # messages per queue
SCPI_MAX_MESSAGE_SIZE = 400 # bytes per msg
TX_NAME_CHAN = 'tx_scpi'
_NODE_NAME = 'scpi_sniffer'

###################### CONSTANTS ######################
from .context import (DEFAULT_CHAN_NUM_MSG, DEFAULT_MAX_MSG_SIZE, DEFAULT_TX_CHAN,
DEFAULT_NODE_NAME, DEFAULT_NODE_PERIOD)

####################### CLASSES #######################
class DrvScpiNodeC(SysShdNodeC):
"Returns a removable version of the DRV command."
def __init__(self, working_flag: Event, cycle_period: int):
def __init__(self, working_flag: Event, cycle_period: int= DEFAULT_NODE_PERIOD,
name: str = DEFAULT_NODE_NAME):
'''
Args:
- working_flag (Event): Flag to know if the SCPI is working.
Expand All @@ -44,11 +45,11 @@ def __init__(self, working_flag: Event, cycle_period: int):
- None.
'''
self.__used_dev: Dict(str, DrvScpiHandlerC) = {}
self.tx_scpi: SysShdIpcChanC = SysShdIpcChanC(name = TX_NAME_CHAN,
max_msg= SCPI_MAX_MSG,
max_message_size= SCPI_MAX_MESSAGE_SIZE)
self.tx_scpi: SysShdIpcChanC = SysShdIpcChanC(name = DEFAULT_TX_CHAN,
max_msg= DEFAULT_CHAN_NUM_MSG,
max_message_size= DEFAULT_MAX_MSG_SIZE)
self.tx_scpi.delete_until_last()
super().__init__(name = _NODE_NAME, cycle_period = cycle_period,\
super().__init__(name = name, cycle_period = cycle_period,\
working_flag = working_flag)
signal(SIGINT, self.signal_handler)

Expand All @@ -65,12 +66,13 @@ def __apply_command(self, cmd: DrvScpiCmdDataC) -> None:
# Add device
if cmd.data_type == DrvScpiCmdTypeE.ADD_DEV:
log.info("Adding device...")
if cmd.port in self.__used_dev:
if cmd.port in self.__used_dev.keys(): #pylint: disable= consider-iterating-dictionary
log.warning("Device already exist")
else:
self.__used_dev[cmd.port] = DrvScpiHandlerC(serial_conf = cmd.payload,\
self.__used_dev[cmd.port] = DrvScpiHandlerC(serial_conf = cmd.payload,
rx_chan_name = cmd.rx_chan_name)
log.info("Device added")
log.info(f"List of devices added: {self.__used_dev.keys()}")

# Delete device
elif cmd.data_type == DrvScpiCmdTypeE.DEL_DEV:
Expand Down Expand Up @@ -102,9 +104,10 @@ def __receive_response(self) -> None:
'''
for handler in self.__used_dev.values():
handler: DrvScpiHandlerC
if handler.wait_4_response:
handler.read()

handler.read()
# if handler.wait_4_response:
# handler.read()
# handler.read()

def process_iteration(self) -> None:
''' Read the chan.
Expand All @@ -118,8 +121,8 @@ def process_iteration(self) -> None:
if not self.tx_scpi.is_empty():
command : DrvScpiCmdDataC = self.tx_scpi.receive_data() # type: ignore
if (command.data_type is DrvScpiCmdTypeE.ADD_DEV) or (command.port in self.__used_dev):
log.info(f"Port: {command.port.split('/')[-1]}. "+\
f"Command to apply: {command.data_type.name}") # pylint: disable=logging-fstring-interpolation
log.info((f"Port: {command.port.split('/')[-1]}. "+
f"Command to apply: {command.data_type.name}"))
self.__apply_command(command)
else:
log.error("First add device.")
Expand Down
53 changes: 30 additions & 23 deletions code/drv_scpi/tests/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

####################### GENERIC IMPORTS #######################
import os
from sys import path
import sys
from threading import Event
from time import sleep
from signal import signal, SIGINT
Expand All @@ -23,17 +23,22 @@
from system_shared_tool import SysShdIpcChanC

####################### PROJECT IMPORTS #######################
path.append(os.getcwd())
from drv_scpi.src.scpi_sniffer import DrvScpiSerialConfC, DrvScpiCmdDataC,\
DrvScpiCmdTypeE, TX_NAME_CHAN, SCPI_MAX_MSG, SCPI_MAX_MESSAGE_SIZE
sys.path.append(os.getcwd())
from drv_scpi.src.scpi_sniffer import DrvScpiSerialConfC, DrvScpiCmdDataC, DrvScpiCmdTypeE

####################### MODULE IMPORTS #######################

####################### ENUMS #######################

####################### CLASSES #######################

__SERIAL_PORT = '/dev/ttyACM0'
__RX_CHAN_NAME = 'rx_scpi_source' #'rx_scpi_flow'
__RX_CHAN_NAME = 'rx_scpi_device' #'rx_scpi_flow'
__FLOW_MAX_MSG = 100
__FLOW_MSG_SIZE = 250
__SOURCE_MAX_MSG = 100
__SOURCE_MSG_SIZE = 250
__TX_NAME_CHAN = 'TX_SCPI'

def example_with_flowmeter():
'''
Expand All @@ -49,16 +54,16 @@ def example_with_flowmeter():
write_timeout = None,
inter_byte_timeout = None)

rx_chan = SysShdIpcChanC(name=__RX_CHAN_NAME, max_msg=SCPI_MAX_MSG,\
max_message_size= SCPI_MAX_MESSAGE_SIZE)
rx_chan = SysShdIpcChanC(name=__RX_CHAN_NAME, max_msg=__FLOW_MAX_MSG, #pylint: disable= redefined-outer-name
max_message_size= __FLOW_MSG_SIZE)

cmd = DrvScpiCmdDataC(DrvScpiCmdTypeE.ADD_DEV, port=__SERIAL_PORT,\
payload = flow_conf_scpi, rx_chan_name=__RX_CHAN_NAME)
tx_chan.send_data(cmd)
tx_chan.send_data(cmd) #pylint: disable= used-before-assignment

req_meas = ':MEASure:FLOW?'
cmd_req_meas = DrvScpiCmdDataC(DrvScpiCmdTypeE.WRITE_READ, port=__SERIAL_PORT, payload=req_meas)
while working_flag.isSet():
while working_flag.is_set(): #pylint: disable= used-before-assignment
tx_chan.send_data(cmd_req_meas)
sleep(0.1)
recv = False
Expand All @@ -83,9 +88,6 @@ def example_with_source_ea():
write_timeout = None,
inter_byte_timeout = None)

rx_chan = SysShdIpcChanC(name=__RX_CHAN_NAME, max_msg=SCPI_MAX_MSG,\
max_message_size= SCPI_MAX_MESSAGE_SIZE)

msg1 = DrvScpiCmdDataC(data_type = DrvScpiCmdTypeE.ADD_DEV, port = __SERIAL_PORT,\
payload = source_conf_scpi, rx_chan_name=__RX_CHAN_NAME)

Expand All @@ -97,22 +99,23 @@ def example_with_source_ea():

msg4 = DrvScpiCmdDataC(data_type = DrvScpiCmdTypeE.WRITE_READ, port = __SERIAL_PORT, \
payload = 'MEASure:VOLTage?')
msg5 = DrvScpiCmdDataC(data_type = DrvScpiCmdTypeE.WRITE, port = __SERIAL_PORT, \
payload = 'OUTPut: OFF')

for msg in [msg1, msg2, msg3, msg4]:
for msg in [msg1, msg2, msg3, msg4, msg5]:
tx_chan.send_data(msg)
sleep(0.1)

recv = False
while (working_flag.isSet() and recv is False):
while (working_flag.is_set() and not recv):
sleep(0.1)
while not recv:
if not rx_chan.is_empty():
resp = rx_chan.receive_data(timeout = 1.0)
log.info(f"Meas received: {resp}, {resp.payload}")
recv = True
if not rx_chan.is_empty(): #pylint: disable= used-before-assignment
resp = rx_chan.receive_data(timeout = 1.0)
log.info(f"Meas received: {resp}, {resp.payload}")
recv = True

msg5 = DrvScpiCmdDataC(data_type = DrvScpiCmdTypeE.DEL_DEV, port = __SERIAL_PORT)
tx_chan.send_data(msg5)
msg6 = DrvScpiCmdDataC(data_type = DrvScpiCmdTypeE.DEL_DEV, port = __SERIAL_PORT)
tx_chan.send_data(msg6)

def raw_example_flow() -> None:
'''
Expand Down Expand Up @@ -163,11 +166,15 @@ def signal_handler(sig, frame) -> None: # pylint: disable=unused-argument
cls_msg = DrvScpiCmdDataC(DrvScpiCmdTypeE.DEL_DEV, port=__SERIAL_PORT)
working_flag.clear()
tx_chan.send_data(cls_msg)
rx_chan.terminate()
tx_chan.terminate()
sys.exit(0)


if __name__ == '__main__':
tx_chan = SysShdIpcChanC(name = TX_NAME_CHAN, max_msg= SCPI_MAX_MSG,\
max_message_size= SCPI_MAX_MESSAGE_SIZE)
tx_chan = SysShdIpcChanC(name = __TX_NAME_CHAN)
rx_chan = SysShdIpcChanC(name=__RX_CHAN_NAME, max_msg=__SOURCE_MAX_MSG,
max_message_size= __SOURCE_MSG_SIZE)
working_flag = Event()
working_flag.set()
signal(SIGINT, signal_handler)
Expand Down
2 changes: 1 addition & 1 deletion code/drv_scpi/tests/run_scpi_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@
_working_scpi = Event()
_working_scpi.set()
#Create the thread for SCPI
scpi_node = DrvScpiNodeC(working_flag = _working_scpi, cycle_period = 100)
scpi_node = DrvScpiNodeC(working_flag = _working_scpi)
scpi_node.run()