Skip to content

Commit 8a50171

Browse files
committed
rework and linting
1 parent 2fbf5bb commit 8a50171

7 files changed

Lines changed: 139 additions & 36 deletions

File tree

sciopy/EIT_16_32_64_128.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ def connect_device_HS(self, url: str = "ftdi://ftdi:232h/1", baudrate: int = 900
7979
Any exceptions raised by the FTDI library during device initialization or configuration.
8080
"""
8181
if hasattr(self, "serial_protocol"):
82-
print(f"Replacing existing {self.serial_protocol} serial connection with HS.")
82+
print(
83+
f"Replacing existing {self.serial_protocol} serial connection with HS."
84+
)
8385
self.serial_protocol = "HS"
8486

8587
serial = Ftdi().create_from_url(url=url)
@@ -109,7 +111,9 @@ def connect_device_FS(self, port: str, baudrate: int = 9600, timeout: int = 1):
109111
- Prints a confirmation message upon successful connection.
110112
"""
111113
if hasattr(self, "serial_protocol"):
112-
print(f"Replacing existing {self.serial_protocol} serial connection with FS.")
114+
print(
115+
f"Replacing existing {self.serial_protocol} serial connection with FS."
116+
)
113117
self.serial_protocol = "FS"
114118
self.device = serial.Serial(
115119
port=port,

sciopy/ISX_3.py

Lines changed: 95 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ def _choice(value, choices, name):
125125
try:
126126
return choices[value.lower()]
127127
except KeyError as error:
128-
raise ValueError(f"Unknown {name} {value!r}; choose from {tuple(choices)}") from error
128+
raise ValueError(
129+
f"Unknown {name} {value!r}; choose from {tuple(choices)}"
130+
) from error
129131
if isinstance(value, int) and value in choices.values():
130132
return value
131133
raise ValueError(f"Invalid {name}: {value!r}")
@@ -323,7 +325,11 @@ def decode_frame(self, frame):
323325
"""Decode one validated protocol frame into a typed or dictionary value."""
324326
tag, payload = frame[0], bytes(frame[2:-1])
325327
if tag == 0x18:
326-
return {"command": tag, "status": payload[0], "message": SYSTEM_MESSAGES.get(payload[0])}
328+
return {
329+
"command": tag,
330+
"status": payload[0],
331+
"message": SYSTEM_MESSAGES.get(payload[0]),
332+
}
327333
if tag == 0xB8:
328334
return self._decode_measurement(payload)
329335
if tag == 0xB1 and len(payload) >= 4:
@@ -334,7 +340,9 @@ def decode_frame(self, frame):
334340
"voltage_range": payload[3],
335341
}
336342
if tag == 0xB3 and len(payload) == 4:
337-
return dict(zip(("counter", "reference", "working_sense", "working"), payload))
343+
return dict(
344+
zip(("counter", "reference", "working_sense", "working"), payload)
345+
)
338346
if tag == 0xB5:
339347
return self._decode_modules(payload)
340348
if tag == 0x98:
@@ -349,8 +357,12 @@ def decode_frame(self, frame):
349357
developer_length = 2 if tag == 0xD0 else 5
350358
return {
351359
"developer_information": payload[:developer_length].hex(),
352-
"revision": int.from_bytes(payload[developer_length : developer_length + 2], "big"),
353-
"build": int.from_bytes(payload[developer_length + 2 : developer_length + 4], "big"),
360+
"revision": int.from_bytes(
361+
payload[developer_length : developer_length + 2], "big"
362+
),
363+
"build": int.from_bytes(
364+
payload[developer_length + 2 : developer_length + 4], "big"
365+
),
354366
}
355367
if tag == 0xD1 and len(payload) >= 7:
356368
return {
@@ -380,7 +392,9 @@ def _decode_measurement(self, payload):
380392
if self.current_range_output:
381393
current_range, index = payload[index], index + 1
382394
if len(payload) - index != 8:
383-
raise ValueError("Measurement layout does not match configured output options")
395+
raise ValueError(
396+
"Measurement layout does not match configured output options"
397+
)
384398
real, imaginary = struct.unpack(">ff", payload[index : index + 8])
385399
return ISXMeasurement(
386400
frequency_id=int.from_bytes(payload[:2], "big"),
@@ -400,14 +414,18 @@ def _decode_modules(payload):
400414
if payload[0] == 0x09:
401415
if len(payload) < 4:
402416
raise ValueError("Incomplete external module channel count")
403-
result["extension_channel_count"] = int.from_bytes(payload[index : index + 2], "big")
417+
result["extension_channel_count"] = int.from_bytes(
418+
payload[index : index + 2], "big"
419+
)
404420
index += 2
405421
if index >= len(payload):
406422
raise ValueError("Missing internal module identifier")
407423
result["internal_module"] = INTERNAL_MODULES.get(payload[index], payload[index])
408424
index += 1
409425
if payload[index - 1] == 0x09 and len(payload) >= index + 2:
410-
result["internal_channel_count"] = int.from_bytes(payload[index : index + 2], "big")
426+
result["internal_channel_count"] = int.from_bytes(
427+
payload[index : index + 2], "big"
428+
)
411429
return result
412430

413431
@staticmethod
@@ -418,7 +436,11 @@ def _decode_option(payload):
418436
option, data = payload[0], payload[1:]
419437
if option == 0x03 and len(data) == 8:
420438
minimum, maximum = struct.unpack(">ff", data)
421-
return {"option": "frequency_range", "minimum_hz": minimum, "maximum_hz": maximum}
439+
return {
440+
"option": "frequency_range",
441+
"minimum_hz": minimum,
442+
"maximum_hz": maximum,
443+
}
422444
return {"option": option, "value": data[0] if len(data) == 1 else list(data)}
423445

424446
@staticmethod
@@ -430,7 +452,12 @@ def _decode_setup(payload):
430452
if option == 0x01 and len(data) == 2:
431453
return {"frequency_count": int.from_bytes(data, "big")}
432454
if option == 0x04 and len(data) % 4 == 0:
433-
return {"frequencies_hz": [struct.unpack(">f", data[i : i + 4])[0] for i in range(0, len(data), 4)]}
455+
return {
456+
"frequencies_hz": [
457+
struct.unpack(">f", data[i : i + 4])[0]
458+
for i in range(0, len(data), 4)
459+
]
460+
}
434461
if option == 0x02 and len(data) >= 12:
435462
result = {
436463
"frequency_hz": struct.unpack(">f", data[0:4])[0],
@@ -443,8 +470,14 @@ def _decode_setup(payload):
443470
if index + 5 > len(data):
444471
raise ValueError("Incomplete extended frequency option")
445472
value = int.from_bytes(data[index + 1 : index + 5], "big")
446-
names = {0x01: "point_delay_us", 0x02: "phase_sync", 0x03: "excitation_type"}
447-
result[names.get(extended_option, f"option_0x{extended_option:02x}")] = value
473+
names = {
474+
0x01: "point_delay_us",
475+
0x02: "phase_sync",
476+
0x03: "excitation_type",
477+
}
478+
result[
479+
names.get(extended_option, f"option_0x{extended_option:02x}")
480+
] = value
448481
index += 5
449482
return result
450483
if option == 0x33 and len(data) == 4:
@@ -505,10 +538,14 @@ def SetOptions(self, option, enabled=True):
505538

506539
def GetOptions(self, option):
507540
"""Read a timestamp, current-range, or available-frequency option."""
508-
return self.send_command(0x98, [_choice(option, OPTION_CODES | {"frequency_range": 0x03}, "option")])
541+
return self.send_command(
542+
0x98, [_choice(option, OPTION_CODES | {"frequency_range": 0x03}, "option")]
543+
)
509544

510545
# Frontend and extension-port commands ---------------------------------------
511-
def SetFE_Settings(self, measurement_mode, measurement_channel, current_range, voltage_range="1v"):
546+
def SetFE_Settings(
547+
self, measurement_mode, measurement_channel, current_range, voltage_range="1v"
548+
):
512549
"""Append one frontend configuration to the device stack.
513550
514551
Parameters accept either documented numeric codes or symbolic names
@@ -557,11 +594,25 @@ def _extended_options(point_delay=None, phase_sync=None, excitation_type=None):
557594
if phase_sync is not None:
558595
data.extend([0x02, *_uint_bytes(int(bool(phase_sync)), 4)])
559596
if excitation_type is not None:
560-
data.extend([0x03, *_uint_bytes(_choice(excitation_type, EXCITATION_TYPES, "excitation type"), 4)])
597+
data.extend(
598+
[
599+
0x03,
600+
*_uint_bytes(
601+
_choice(excitation_type, EXCITATION_TYPES, "excitation type"), 4
602+
),
603+
]
604+
)
561605
return data
562606

563607
def AddFrequencyPoint(
564-
self, frequency, precision, amplitude, *, point_delay=None, phase_sync=None, excitation_type=None
608+
self,
609+
frequency,
610+
precision,
611+
amplitude,
612+
*,
613+
point_delay=None,
614+
phase_sync=None,
615+
excitation_type=None,
565616
):
566617
"""Append one frequency point to the active setup.
567618
@@ -581,7 +632,9 @@ def AddFrequencyPoint(
581632
Unit and excitation mode used by ``amplitude``.
582633
"""
583634
data = bytearray([0x02])
584-
data.extend(_float_bytes(frequency) + _float_bytes(precision) + _float_bytes(amplitude))
635+
data.extend(
636+
_float_bytes(frequency) + _float_bytes(precision) + _float_bytes(amplitude)
637+
)
585638
data.extend(self._extended_options(point_delay, phase_sync, excitation_type))
586639
return self.send_command(0xB6, data)
587640

@@ -641,7 +694,9 @@ def AcknowledgeCompensation(self, accept=True):
641694
def SetCompensationLoad(self, value, load_type="resistance"):
642695
"""Set the known compensation load in ohms or farads."""
643696
types = {"resistance": 0x01, "capacitor": 0x02}
644-
return self.send_command(0xB6, [0x16, _choice(load_type, types, "load type"), *_float_bytes(value)])
697+
return self.send_command(
698+
0xB6, [0x16, _choice(load_type, types, "load type"), *_float_bytes(value)]
699+
)
645700

646701
def ResetCompensationData(self):
647702
"""Delete compensation data associated with the active setup."""
@@ -699,12 +754,22 @@ def GetSetup(self, option, data=()):
699754
data : iterable of int, optional
700755
Additional command data, such as a two-byte frequency row.
701756
"""
702-
return self.send_command(0xB7, [_choice(option, {
703-
"frequency_count": 0x01,
704-
"frequency_point": 0x02,
705-
"frequency_list": 0x04,
706-
"dc_bias": 0x33,
707-
}, "setup option"), *data])
757+
return self.send_command(
758+
0xB7,
759+
[
760+
_choice(
761+
option,
762+
{
763+
"frequency_count": 0x01,
764+
"frequency_point": 0x02,
765+
"frequency_list": 0x04,
766+
"dc_bias": 0x33,
767+
},
768+
"setup option",
769+
),
770+
*data,
771+
],
772+
)
708773

709774
def GetFrequencyCount(self):
710775
"""Read the total number of configured frequency points."""
@@ -770,7 +835,9 @@ def StartMeasure(self, repeat=1, timeout=None):
770835
"""
771836
self.measurements = []
772837
self.send_message(self.build_frame(0xB8, [0x01, *_uint_bytes(repeat, 2)]))
773-
self._pending_command = list(self.build_frame(0xB8, [0x01, *_uint_bytes(repeat, 2)]))
838+
self._pending_command = list(
839+
self.build_frame(0xB8, [0x01, *_uint_bytes(repeat, 2)])
840+
)
774841
self.SystemMessageCallback(timeout=timeout)
775842
return list(self.measurements)
776843

@@ -823,7 +890,9 @@ def SetEthernetConfiguration(self, *, ip_address=None, dhcp=None):
823890
if (ip_address is None) == (dhcp is None):
824891
raise ValueError("provide exactly one of ip_address or dhcp")
825892
if ip_address is not None:
826-
return self.send_command(0xBD, [0x01, *ipaddress.IPv4Address(ip_address).packed])
893+
return self.send_command(
894+
0xBD, [0x01, *ipaddress.IPv4Address(ip_address).packed]
895+
)
827896
return self.send_command(0xBD, [0x03, int(bool(dhcp))])
828897

829898
def GetEthernetConfiguration(self, option):

sciopy/com_util.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
import struct
1111
import sys
1212
from glob import glob
13-
from .datatype_conversion import bytesarray_to_float, bytesarray_to_int, single_hex_to_int
13+
from .datatype_conversion import (
14+
bytesarray_to_float,
15+
bytesarray_to_int,
16+
single_hex_to_int,
17+
)
1418

1519

1620
def available_serial_ports() -> list:

sciopy/datatype_conversion.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,9 @@ def bytelist_to_int(bytelist):
206206

207207

208208
def _hex_array_to_bytes(values, expected_length=None):
209-
result = bytes(int(value, 16) if isinstance(value, str) else int(value) for value in values)
209+
result = bytes(
210+
int(value, 16) if isinstance(value, str) else int(value) for value in values
211+
)
210212
if expected_length is not None and len(result) != expected_length:
211213
raise ValueError(f"Expected exactly {expected_length} bytes.")
212214
return result

sciopy/usb_message_parser.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ def describe_message(message):
7878
description = f"{command_name} (0x{command_tag:02X})"
7979

8080
if command_tag == 0x18:
81-
status = msg_dict.get(hex(message[2]), f"Unknown system status 0x{message[2]:02X}")
81+
status = msg_dict.get(
82+
hex(message[2]), f"Unknown system status 0x{message[2]:02X}"
83+
)
8284
return f"{description}: {status}"
8385

8486
if command_tag in {0xB0, 0xB1}:
@@ -208,7 +210,9 @@ def reset_new_data_frame(self):
208210
Resets the Current EITFrame.
209211
"""
210212
if self.setup is None:
211-
raise RuntimeError("A measurement setup is required to create an EIT frame.")
213+
raise RuntimeError(
214+
"A measurement setup is required to create an EIT frame."
215+
)
212216
self.iInjIndex = 0
213217
self.iSaveCounter = 0
214218
self.CurrentFrame = EITFrame(

setup.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
1-
from setuptools import setup, find_packages
1+
from pathlib import Path
2+
3+
from setuptools import find_packages, setup
4+
5+
ROOT = Path(__file__).parent
26

37
setup(
48
name="sciopy",
5-
version="0.9.1",
9+
version="0.9.2",
610
packages=find_packages(),
711
author="Jacob P. Thönes",
812
author_email="jacob.thoenes@uni-rostock.de",
913
description="Python based interface module for communication with Sciospec devices.",
10-
long_description=open("README.md").read(),
14+
long_description=(ROOT / "README.md").read_text(encoding="utf-8"),
1115
long_description_content_type="text/markdown",
1216
keywords="Sciospec EIT EIS".split(),
1317
platforms="any",
18+
python_requires=">=3.9",
19+
install_requires=[
20+
"numpy>=1.23",
21+
"pyftdi>=0.54",
22+
"pyserial>=3.5",
23+
],
24+
extras_require={
25+
"plot": ["matplotlib>=3.6", "pyeit>=1.2"],
26+
"test": ["pytest>=7", "ruff>=0.11"],
27+
},
1428
classifiers=[
1529
"Programming Language :: Python :: 3",
1630
"License :: OSI Approved :: MIT License",

tests/test_usb_message_parser.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ def test_parser_rejects_mismatched_message_tags():
4343

4444
def test_status_read_does_not_require_measurement_setup():
4545
device = Mock()
46-
device.read.side_effect = [bytes([0x18]), bytes([0x01]), bytes([0x83]), bytes([0x18]), b""]
46+
device.read.side_effect = [
47+
bytes([0x18]),
48+
bytes([0x01]),
49+
bytes([0x83]),
50+
bytes([0x18]),
51+
b"",
52+
]
4753
parser = MessageParser(device, devicetype="FS")
4854

4955
assert parser.read_usb_till_timeout() == []

0 commit comments

Comments
 (0)