Skip to content

Commit 2cdf44d

Browse files
committed
Integration of USB Message Parser to EITclass
1 parent 8460792 commit 2cdf44d

File tree

2 files changed

+122
-108
lines changed

2 files changed

+122
-108
lines changed

sciopy/EIT_16_32_64_128.py

Lines changed: 120 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
except ImportError:
66
print("Could not import module: serial")
77

8-
98
from .com_util import (
109
clTbt_dp,
1110
clTbt_sp,
@@ -17,7 +16,6 @@
1716
import numpy as np
1817
from pyftdi.ftdi import Ftdi
1918

20-
2119
msg_dict = {
2220
"0x01": "No message inside the message buffer",
2321
"0x02": "Timeout: Communication-timeout (less data than expected)",
@@ -31,6 +29,7 @@
3129
}
3230

3331
from .sciopy_dataclasses import EitMeasurementSetup
32+
from sciopydev.sciopy.usb_message_parser import MessageParser, make_eitframes_hex, get_data_as_matrix
3433

3534

3635
class EIT_16_32_64_128:
@@ -51,6 +50,8 @@ def __init__(self, n_el: int) -> None:
5150
self.channel_group = self.init_channel_group()
5251
self.print_msg = True
5352
self.ret_hex_int = None
53+
self.cMessageParser = None
54+
self.setup = None
5455

5556
def init_channel_group(self):
5657
"""
@@ -107,6 +108,7 @@ def connect_device_HS(self, url: str = "ftdi://ftdi:232h/1", baudrate: int = 900
107108
serial.STOP_BIT_1
108109
serial.set_baudrate(baudrate)
109110
self.device = serial
111+
self.cMessageParser = MessageParser(self.device, devicetype="HS")
110112

111113
def connect_device_FS(self, port: str, baudrate: int = 9600, timeout: int = 1):
112114
"""
@@ -139,6 +141,7 @@ def connect_device_FS(self, port: str, baudrate: int = 9600, timeout: int = 1):
139141
)
140142

141143
print("Connection to", self.device.name, "is established.")
144+
self.cMessageParser = MessageParser(self.device, devicetype="FS")
142145

143146
def disconnect_device(self):
144147
"""
@@ -148,86 +151,58 @@ def disconnect_device(self):
148151
"""
149152
self.device.close()
150153

151-
def SystemMessageCallback_usb_fs(self):
152-
"""
153-
Reads data from a USB device, processes received messages, and returns the data in the specified format.
154-
155-
The method continuously reads from the device until no more data is received, then processes the received bytes.
156-
It converts the received data to hexadecimal format and attempts to identify a specific message index.
157-
Depending on the value of `self.ret_hex_int`, it returns the data as hexadecimal, integer, or both.
158154

159-
Prints diagnostic messages if `self.print_msg` is True.
155+
def send_message(self, message):
156+
"""
157+
Wrapper function to send a byte array to the device. Communication method is based on the defined serial
158+
protocol.
160159
161-
Returns:
162-
list[str]: List of received data in hexadecimal format if `self.ret_hex_int == "hex"`.
163-
list[int]: List of received data as integers if `self.ret_hex_int == "int"`.
164-
tuple: Both integer and hexadecimal lists if `self.ret_hex_int == "both"`.
165-
None: If `self.ret_hex_int` is None.
160+
Args:
161+
message (bytearray): A byte array to be sent to the device.
166162
"""
167-
timeout_count = 0
168-
received = []
169-
received_hex = []
170-
data_count = 0
163+
if self.serial_protocol == "HS":
164+
self.device.write_data(message)
165+
elif self.serial_protocol == "FS":
166+
self.device.write(message)
171167

172-
while True:
173-
buffer = self.device.read()
174-
if buffer:
175-
received.extend(buffer)
176-
data_count += len(buffer)
177-
timeout_count = 0
178-
continue
179-
timeout_count += 1
180-
if timeout_count >= 1:
181-
# Break if we haven't received any data
182-
break
183168

184-
received = "".join(str(received)) # If you need all the data
185-
received_hex = [hex(receive) for receive in received]
186-
try:
187-
msg_idx = received_hex.index("0x18")
188-
if self.print_msg:
189-
print(msg_dict[received_hex[msg_idx + 2]])
190-
except BaseException:
191-
if self.print_msg:
192-
print(msg_dict["0x01"])
193-
# self.print_msg = False
194-
if self.print_msg:
195-
print("message buffer:\n", received_hex)
196-
print("message length:\t", data_count)
169+
def read_message(self):
170+
"""
171+
Wrapper function to read single bytes from the device. Communication method is based on the defined serial
172+
protocol.
197173
198-
if self.ret_hex_int is None:
199-
return
200-
elif self.ret_hex_int == "hex":
201-
return received_hex
202-
elif self.ret_hex_int == "int":
203-
return received
204-
elif self.ret_hex_int == "both":
205-
return received, received_hex
174+
Return:
175+
A single byte read from the device.
176+
"""
177+
if self.serial_protocol == "HS":
178+
return self.device.read_data_bytes(size=1024, attempt=150)
179+
elif self.serial_protocol == "FS":
180+
return self.device.read()
206181

207-
def SystemMessageCallback_usb_hs(self):
182+
def SystemMessageCallback(self):
208183
"""
209-
Reads data from a USB high-speed device, processes received messages, and returns the data in various formats.
184+
Handles system messages based on the selected serial protocol.
185+
Reads data from a USB device, processes received messages, and returns the data in the specified format.
210186
211-
The method continuously reads data from the device until no more data is received. It converts the received bytes to hexadecimal format,
212-
searches for a specific message index, and prints corresponding messages if enabled. The returned data format depends on the value of
213-
`self.ret_hex_int` ("hex", "int", "both", or None).
187+
The method continuously reads from the device until no more data is received, then processes the received bytes.
188+
It converts the received data to hexadecimal format and attempts to identify a specific message index.
189+
Depending on the value of `self.ret_hex_int`, it returns the data as hexadecimal, integer, or both.
190+
191+
Prints diagnostic messages if `self.print_msg` is True.
214192
215193
Returns:
216194
list[str]: List of received data in hexadecimal format if `self.ret_hex_int == "hex"`.
217195
list[int]: List of received data as integers if `self.ret_hex_int == "int"`.
218-
tuple[list[int], list[str]]: Both integer and hexadecimal lists if `self.ret_hex_int == "both"`.
196+
tuple: Both integer and hexadecimal lists if `self.ret_hex_int == "both"`.
219197
None: If `self.ret_hex_int` is None.
220-
221-
Raises:
222-
BaseException: If message index is not found in the received data.
223198
"""
224199
timeout_count = 0
225200
received = []
226201
received_hex = []
227202
data_count = 0
228203

229204
while True:
230-
buffer = self.device.read_data_bytes(size=1024, attempt=150)
205+
buffer = self.read_message()
231206
if buffer:
232207
received.extend(buffer)
233208
data_count += len(buffer)
@@ -261,23 +236,6 @@ def SystemMessageCallback_usb_hs(self):
261236
elif self.ret_hex_int == "both":
262237
return received, received_hex
263238

264-
def SystemMessageCallback(self):
265-
"""
266-
Handles system messages based on the selected serial protocol.
267-
268-
Depending on the value of `self.serial_protocol`, this method delegates
269-
the handling of system messages to the appropriate callback:
270-
- If `self.serial_protocol` is "HS", calls `SystemMessageCallback_usb_hs()`.
271-
- If `self.serial_protocol` is "FS", calls `SystemMessageCallback_usb_fs()`.
272-
273-
Raises:
274-
AttributeError: If the required callback methods are not defined.
275-
"""
276-
if self.serial_protocol == "HS":
277-
self.SystemMessageCallback_usb_hs()
278-
elif self.serial_protocol == "FS":
279-
self.SystemMessageCallback_usb_fs()
280-
281239
def write_command_string(self, command):
282240
"""
283241
Sends a command string to the device using the appropriate serial protocol.
@@ -292,11 +250,9 @@ def write_command_string(self, command):
292250
Raises:
293251
AttributeError: If `self.device` does not have the required method for the selected protocol.
294252
"""
295-
if self.serial_protocol == "HS":
296-
self.device.write_data(command)
297-
elif self.serial_protocol == "FS":
298-
self.device.write(command)
299-
self.SystemMessageCallback()
253+
self.cMessageParser.bPrintMessages = self.print_msg
254+
self.send_message(command)
255+
self.cMessageParser.read_usb_till_timeout(bSaveData=False, bDeleteDataFrame=True)
300256

301257
# --- sciospec device commands
302258

@@ -325,6 +281,7 @@ def update_BurstCount(self, burst_count):
325281
326282
"""
327283
self.setup.burst_count = burst_count
284+
self.print_msg = True
328285
self.write_command_string(
329286
bytearray([0xB0, 0x03, 0x02, 0x00, self.setup.burst_count, 0xB0])
330287
)
@@ -410,6 +367,7 @@ def SetMeasurementSetup(self, setup: EitMeasurementSetup):
410367
"""
411368

412369
self.setup = setup
370+
self.cMessageParser.set_measurement_setup(self.setup)
413371
self.print_msg = False
414372
self.ResetMeasurementSetup()
415373

@@ -523,8 +481,18 @@ def ResetMeasurementSetup(self):
523481
self.write_command_string(bytearray([0xB0, 0x01, 0x01, 0xB0]))
524482
self.print_msg = False
525483

526-
def update_measurement_mode(self, meamode="skip4", boundary="external"):
484+
485+
def update_measurement_mode(self, meamode: str = "skip4", boundary: str = "external"):
486+
"""
487+
Updates the measurement modes out of the options "singleended", "skip0", "skip2" or "skip4"
488+
489+
Args:
490+
meamode (str): Measurement mode "singleended", "skip0", "skip2" or "skip4"
491+
boundary (str): Return if the boundary for skip patterns is internal of a channel group or external for all
492+
optional channels
493+
"""
527494
meamodeoptions = {"singleended": 0x01, "skip0": 0x02, "skip2": 0x03, "skip4": 0x04}
495+
self.print_msg = True
528496
try:
529497
cmd = meamodeoptions[meamode]
530498
except:
@@ -534,10 +502,10 @@ def update_measurement_mode(self, meamode="skip4", boundary="external"):
534502
bnd = "0x02"
535503
else:
536504
bnd = "0x01"
537-
self.write_command_string(bytearray([0xB0, 0x03, 0x08,cmd, bnd, 0xB0]))
505+
self.write_command_string(bytearray([0xB0, 0x03, 0x08, cmd, bnd, 0xB0]))
506+
self.print_msg = False
538507
#todo read out ACK messages
539508

540-
541509
def GetMeasurementSetup(self, setup_of: str):
542510
"""
543511
Retrieves and configures the measurement setup for the device based on the specified setup option.
@@ -573,7 +541,8 @@ def GetMeasurementSetup(self, setup_of: str):
573541
print("TBD: Translation")
574542
self.print_msg = False
575543

576-
def StartStopMeasurement(self, return_as="pot_mat"):
544+
#todo
545+
def StartStopMeasurementFast(self, return_as="pot_mat"):
577546
"""
578547
Starts and stops a measurement process using the configured serial protocol (HS or FS).
579548
Sends appropriate commands to the device to initiate and terminate measurement.
@@ -589,27 +558,18 @@ def StartStopMeasurement(self, return_as="pot_mat"):
589558
Returns:
590559
list or matrix: The measurement data in the format specified by `return_as`.
591560
"""
592-
if self.serial_protocol == "HS":
593-
self.device.write_data(bytearray([0xB4, 0x01, 0x01, 0xB4]))
594-
self.ret_hex_int = "hex"
595-
self.print_msg = False
596-
597-
data = self.SystemMessageCallback_usb_hs()
598-
599-
self.device.write_data(bytearray([0xB4, 0x01, 0x00, 0xB4]))
600-
self.ret_hex_int = None
601-
self.SystemMessageCallback()
602-
603-
elif self.serial_protocol == "FS":
604-
self.device.write(bytearray([0xB4, 0x01, 0x01, 0xB4]))
605-
self.ret_hex_int = "hex"
606-
self.print_msg = False
561+
if self.setup.burst_count == 0:
562+
print("Burst count for this setup needs to be >=1")
563+
return
564+
self.send_message(bytearray([0xB4, 0x01, 0x01, 0xB4]))
565+
self.ret_hex_int = "hex"
566+
self.print_msg = False
607567

608-
data = self.SystemMessageCallback_usb_fs()
568+
data = self.SystemMessageCallback()
609569

610-
self.device.write(bytearray([0xB4, 0x01, 0x00, 0xB4]))
611-
self.ret_hex_int = None
612-
self.SystemMessageCallback()
570+
self.send_message(bytearray([0xB4, 0x01, 0x00, 0xB4]))
571+
self.ret_hex_int = None
572+
self.SystemMessageCallback()
613573

614574
data = del_hex_in_list(data)
615575
data = reshape_full_message_in_bursts(data, self.setup)
@@ -619,7 +579,59 @@ def StartStopMeasurement(self, return_as="pot_mat"):
619579
if return_as == "hex":
620580
return self.data
621581
elif return_as == "pot_mat":
622-
return self.get_data_as_matrix()
582+
return self.get_data_as_matrix(self.data)
583+
584+
#todo check
585+
def StartStopMeasurementNew(self, timeout:int=0, return_as="pot_mat", bSaveData:bool=False, bDeleteData:bool=False,
586+
sSavepath:str="C/"):
587+
"""
588+
Starts and stops a measurement process using the configured serial protocol (HS or FS).
589+
Sends appropriate commands to the device to initiate and terminate measurement.
590+
If a timeout is specified, data is measrued for timeout seconds (burst_count==0). Else, a burst count needs to
591+
be specified, and all measured data is received.
592+
Processes the received data by removing hexadecimal values, reshaping messages into bursts,
593+
and splitting bursts into frames. Stores the processed data in NPZ format at sSavepath
594+
595+
Args:
596+
timeout (int): Specifies the timeout in seconds.
597+
return_as (str, optional): Specifies the format of the returned data.
598+
- "hex": Returns the processed data as a list of hexadecimal values.
599+
- "pot_mat": Returns the processed data as a matrix using `get_data_as_matrix()`.
600+
Default is "pot_mat".
601+
- else: data is only stored
602+
bSaveData (bool): Specifies if a the measured data is saved in NPZ format
603+
bDeleteData (bool): Specifies if a the measured data is deleted out of memory after each EITframe, with
604+
bSaveData=True, measured data is saved and then removed from RAM
605+
sSavepath (str): Specifies the sPath where the measured data is saved.
606+
607+
Returns:
608+
list or matrix: The measurement data in the format specified by `return_as`.
609+
"""
610+
611+
# Start measurement
612+
self.send_message(bytearray([0xB4, 0x01, 0x01, 0xB4]))
613+
self.cMessageParser.bPrintMessages = False
614+
if timeout != 0:
615+
data = self.cMessageParser.read_usb_for_seconds(timeout, bSaveData=bSaveData, bDeleteDataFrame=bDeleteData,
616+
sSavepath=sSavepath)
617+
else:
618+
if self.setup.burst_count ==0:
619+
print("Burst count for this setup needs to be >=1")
620+
return
621+
data = self.cMessageParser.read_usb_till_timeout(bSaveData=bSaveData, bDeleteDataFrame=bDeleteData,
622+
sSavepath=sSavepath)
623+
624+
# Stop measurement
625+
self.send_message(bytearray([0xB4, 0x01, 0x00, 0xB4]))
626+
data2 = self.cMessageParser.read_usb_till_timeout(bSaveData=bSaveData, bDeleteDataFrame=bDeleteData,
627+
sSavepath=sSavepath)
628+
data = data + data2
629+
if bDeleteData:
630+
return
631+
if return_as == "hex":
632+
return make_eitframes_hex(data)
633+
elif return_as == "pot_mat":
634+
return get_data_as_matrix(data)
623635

624636
def get_data_as_matrix(self):
625637
"""
@@ -650,7 +662,7 @@ def get_data_as_matrix(self):
650662
row += 1
651663
el_signs = list()
652664
for ch in range(16):
653-
el_signs.append(frame.__dict__[f"ch_{ch+1}"])
665+
el_signs.append(frame.__dict__[f"ch_{ch + 1}"])
654666

655667
el_signs = np.array(el_signs)
656668
start_idx = (curr_grp - 1) * 16

sciopy/sciopy_dataclasses.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class EitMeasurementSetup:
2828
inj_skip: Union[int, list]
2929
gain: int
3030
adc_range: int
31+
mea_mode: str = "singleended"
32+
mea_mode_boundary: str = "external"
3133
# TBD: lin/log/sweep
3234

3335

0 commit comments

Comments
 (0)