Skip to content

Commit 01b6e3e

Browse files
Merge pull request #9 from nextroundwinner/logging
Logging
2 parents 6815373 + 10efeff commit 01b6e3e

57 files changed

Lines changed: 300 additions & 306 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"type": "debugpy",
1010
"request": "launch",
1111
// "program": "__main__.py",
12-
"module": "examples.mid_level.example_mid_level",
12+
"module": "examples.dyscom.example_dyscom_write_csv",
1313
"justMyCode": false,
1414
// "args": ["COM3"],
1515
"console": "integratedTerminal"

HINTS.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ This page describes implementation details.
2424
- The acknowledge needs to handled manually by using _PacketBuffer_ object from device
2525
- _PacketBuffer_ reads data from connection and separates packets from data stream
2626

27+
## Logging
28+
- Library creates a custom logger, see class _Logger_
29+
- By default some information is logged to console
30+
- Set log level to DEBUG to get more detailed information
31+
- For more performance, disable logger
32+
2733
## General layer
2834
- Contains functions to get common information like device serial or firmware version
2935

@@ -57,6 +63,7 @@ This page describes implementation details.
5763
- Device sends now _DlSendLiveData_ packets with measurement data
5864
- Call _stop()_ to end measurement
5965
- Call _power_module()_ to power off measurement module
66+
- Important: all storage related functions are untested
6067

6168
# Deviation from Instruction for Use
6269

@@ -66,8 +73,12 @@ This page describes implementation details.
6673
- Strings are 1 byte less long (null termination is not an extra byte) in acknowledge packets
6774
- Datetime parameters have a different order
6875

76+
### DL_init
77+
- Init state seems always be UNUSED
78+
- Output data rate depends on init params filter property
79+
6980
### DL_get_ack for type file by name
70-
- Addition parameter mode (1 byte)
81+
- Additional parameter mode (1 byte)
7182
- Undefined = 0
7283
- Multiblock = 1
7384
- Singleblock = 2

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ Python 3.11 or higher
4848
- Located in folder `examples`
4949
- Run examples with `python -m examples.<layer>.<example>`
5050
- Example: `python -m examples.dyscom.example_dyscom_fastplotlib`
51+
- All examples try to find the serial port that a science mode device is connected to automatically
52+
- If that fails, provide serial port name as parameter, e.g. `python -m examples.<layer>.<example> COM3`
5153
- Examples have own dependencies, see [Dependencies for examples](#dependencies-for-examples)
5254
- General layer
5355
- `example_general.py`

__main__.py

Lines changed: 9 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -4,139 +4,27 @@
44
import sys
55
import asyncio
66

7-
import matplotlib.pyplot as plt
8-
import numpy as np
7+
from science_mode_4.device_p24 import DeviceP24
8+
from science_mode_4.utils.serial_port_connection import SerialPortConnection
99

10-
from src.science_mode_4 import LayerDyscom, LayerLowLevel,\
11-
Commands, Connector, Channel, ChannelPoint,\
12-
SerialPortConnection,\
13-
DeviceI24,\
14-
Ads129xOutputDataRate, Ads129xPowerMode,\
15-
PacketDyscomGetAckOperationMode, PacketDyscomSendLiveData,\
16-
DyscomInitParams, DyscomPowerModulePowerType, DyscomPowerModuleType, DyscomSignalType
1710

1811

19-
20-
# print(science_mode_4.__version__)
21-
22-
def send_channel_config(low_level_layer: LayerLowLevel, connector: Connector):
23-
"""Sends channel update"""
24-
# device can store up to 10 channel config commands
25-
for channel in Channel:
26-
# send_channel_config does not wait for an acknowledge
27-
low_level_layer.send_channel_config(True, channel, connector,
28-
[ChannelPoint(4000, 20), ChannelPoint(4000, -20),
29-
ChannelPoint(4000, 0)])
30-
3112
async def main() -> int:
3213
"""Main function"""
3314

34-
connection = SerialPortConnection("COM6")
15+
devices = SerialPortConnection.list_science_mode_device_ports()
16+
connection = SerialPortConnection(devices[0].device)
3517
# devices = UsbConnection.list_science_mode_devices()
3618
# connection = UsbConnection(devices[0])
3719
# connection = NullConnection()
3820
connection.open()
3921

40-
device = DeviceI24(connection)
22+
device = DeviceP24(connection)
4123
await device.initialize()
42-
# general = device.get_layer_general()
43-
# print(f"device id: {general.device_id}")
44-
# print(f"firmware version: {general.firmware_version}")
45-
# print(f"science mode version: {general.science_mode_version}")
46-
47-
dyscom: LayerDyscom = device.get_layer_dyscom()
48-
# fss: DyscomGetFileSystemStatusResult = await dyscom.get_file_system_status()
49-
# print(f"Ready {fss.file_system_ready}, used size {fss.used_size}, free size {fss.free_size}")
50-
# fbn: DyscomGetFileByNameResult = await dyscom.get_file_by_name()
51-
# print(f"Filename {fbn.filename}, block offset {fbn.block_offset}, filesize {fbn.filesize}, nr of blocks {fbn.number_of_blocks}")
52-
# fv: str = await dyscom.get_firmware_version()
53-
# print(f"Firmware version {fv}")
54-
# nrof = await dyscom.get_list_of_measurement_meta_info()
55-
# print(f"Number of measurement meta info {nrof}")
56-
# did = await dyscom.get_device_id()
57-
# print(f"Device ID {did}")
58-
# fi = await dyscom.get_file_info()
59-
# print(f"File info {fi.filename} {fi.filesize} {fi.checksum}")
60-
# b = await dyscom.get_battery()
61-
# print(f"Battery {b.voltage} {b.current} {b.percentage} {b.temperature} {b.energy_state}")
62-
63-
# sys_ack: DyscomSysResult = await dyscom.sys(DyscomSysType.DEVICE_STORAGE)
64-
# print(f"Sys {sys_ack.sys_type} {sys_ack.state} {sys_ack.filename}")
65-
66-
await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_ON)
67-
init_params = DyscomInitParams()
68-
init_params.register_map_ads129x.config_register_1.output_data_rate = Ads129xOutputDataRate.HR_MODE_500_SPS__LP_MODE_250_SPS
69-
init_params.register_map_ads129x.config_register_1.power_mode = Ads129xPowerMode.LOW_POWER
70-
await dyscom.init(init_params)
71-
72-
fig, ax = plt.subplots()
73-
ax.set(xlabel="Sample Time (µs)", ylabel="Current (mA)",
74-
title="Current measurement")
75-
ax.grid()
76-
plt.ion()
77-
plt.show()
78-
79-
def update_ylim(data: list[float]):
80-
if len(data) == 0:
81-
return
82-
83-
new_min = data[0]
84-
new_max = data[0]
85-
for x in data:
86-
new_min = min(new_min, x)
87-
new_max = max(new_max, x)
88-
89-
offset = (new_max - new_min) * 0.1
90-
plt.ylim(new_min - offset, new_max + offset)
91-
92-
plot_buffer: list[float] = []
93-
plot_data, = ax.plot(np.linspace(0, 100, len(plot_buffer)), plot_buffer)
94-
update_ylim(plot_buffer)
95-
used_signals: set[DyscomSignalType] = set()
96-
await dyscom.start()
97-
98-
for x in range(1000):
99-
if x % 100 == 0:
100-
dyscom.send_get_operation_mode()
101-
102-
while True:
103-
ack = dyscom.packet_buffer.get_packet_from_buffer()
104-
if ack:
105-
if ack.command == Commands.DlGetAck:
106-
om_ack: PacketDyscomGetAckOperationMode = ack
107-
print(f"Operation mode {om_ack.operation_mode}")
108-
elif ack.command == Commands.DlSendLiveData:
109-
sld: PacketDyscomSendLiveData = ack
110-
if sld.status_error:
111-
print(f"SendLiveData status error {sld.samples}")
112-
break
113-
if sld.number % 50 == 0:
114-
# print(f"Append {sld.value} {sld.signal_type}")
115-
for s in sld.samples:
116-
used_signals.add(s.signal_type)
117-
if len(plot_buffer) > 250:
118-
plot_buffer.pop(0)
119-
plot_buffer.append(sld.time_offset) # samples[1].value
120-
plot_data.remove()
121-
plot_data, = ax.plot(np.linspace(0, len(plot_buffer), len(plot_buffer)), plot_buffer, color = "b")
122-
# plot_data.set_xdata(np.linspace(0, 100, len(plot_buffer)))
123-
# plot_data.set_ydata(plot_buffer)
124-
# update_ylim(plot_buffer)
125-
fig.canvas.draw()
126-
fig.canvas.flush_events()
127-
128-
else:
129-
break
130-
131-
await asyncio.sleep(0.01)
132-
133-
# wait until all acknowledges are received
134-
await asyncio.sleep(0.5)
135-
136-
await dyscom.stop()
137-
await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_OFF)
138-
139-
print(used_signals)
24+
general = device.get_layer_general()
25+
print(f"Device id: {general.device_id}")
26+
print(f"Firmware version: {general.firmware_version}")
27+
print(f"Science mode version: {general.science_mode_version}")
14028

14129
connection.close()
14230

examples/dyscom/example_dyscom_fastplotlib.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,13 @@ async def device_communication() -> int:
7979
if ack:
8080
# because there are multiple get commands, we need to additionally check kind,
8181
# which is always associated DyscomGetType
82-
if ack.command == Commands.DlGetAck and ack.kind == DyscomGetType.OPERATION_MODE:
82+
if ack.command == Commands.DL_GET_ACK and ack.kind == DyscomGetType.OPERATION_MODE:
8383
om_ack: PacketDyscomGetAckOperationMode = ack
84-
print(f"Operation mode {om_ack.operation_mode}")
84+
print(f"Operation mode {om_ack.operation_mode.name}")
8585
# check if measurement is still active
8686
if om_ack.result_error != ResultAndError.NO_ERROR:
8787
break
88-
elif ack.command == Commands.DlSendLiveData:
88+
elif ack.command == Commands.DL_SEND_LIVE_DATA:
8989
live_data_counter += 1
9090

9191
sld: PacketDyscomSendLiveData = ack
@@ -111,9 +111,6 @@ async def device_communication() -> int:
111111

112112
await asyncio.sleep(0.01)
113113

114-
# wait until all acknowledges are received
115-
await asyncio.sleep(0.5)
116-
117114
# stop measurement
118115
await dyscom.stop()
119116
# turn power module off

examples/dyscom/example_dyscom_pyplot.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from science_mode_4.dyscom.dyscom_get_operation_mode import PacketDyscomGetAckOperationMode
1010
from science_mode_4.dyscom.dyscom_send_live_data import PacketDyscomSendLiveData
1111
from science_mode_4.dyscom.dyscom_types import DyscomInitParams, DyscomPowerModulePowerType, DyscomPowerModuleType, DyscomSignalType
12+
from science_mode_4.utils.logger import logger
1213
from examples.utils.example_utils import ExampleUtils
1314
from examples.utils.pyplot_utils import PyPlotHelper
1415

@@ -18,6 +19,9 @@ async def main() -> int:
1819

1920
plot_helper = PyPlotHelper({0: ["BI", "blue"]}, 250)
2021

22+
# disable logger to increase performance
23+
logger().disabled = True
24+
2125
# get comport from command line argument
2226
com_port = ExampleUtils.get_comport_from_commandline_argument()
2327
# create serial port connection
@@ -55,10 +59,10 @@ async def main() -> int:
5559
while True:
5660
ack = dyscom.packet_buffer.get_packet_from_buffer(live_data_counter == 0)
5761
if ack:
58-
if ack.command == Commands.DlGetAck:
62+
if ack.command == Commands.DL_GET_ACK:
5963
om_ack: PacketDyscomGetAckOperationMode = ack
60-
print(f"Operation mode {om_ack.operation_mode}")
61-
elif ack.command == Commands.DlSendLiveData:
64+
print(f"Operation mode {om_ack.operation_mode.name}")
65+
elif ack.command == Commands.DL_SEND_LIVE_DATA:
6266
live_data_counter += 1
6367

6468
sld: PacketDyscomSendLiveData = ack
@@ -77,9 +81,6 @@ async def main() -> int:
7781

7882
await asyncio.sleep(0.01)
7983

80-
# wait until all acknowledges are received
81-
await asyncio.sleep(0.5)
82-
8384
# stop measurement
8485
await dyscom.stop()
8586
# turn power module off

examples/dyscom/example_dyscom_write_csv.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
from science_mode_4.dyscom.ads129x.ads129x_config_register_1 import Ads129xOutputDataRate, Ads129xPowerMode
1212
from science_mode_4.dyscom.dyscom_get_operation_mode import PacketDyscomGetAckOperationMode
1313
from science_mode_4.dyscom.dyscom_send_live_data import PacketDyscomSendLiveData
14-
from science_mode_4.dyscom.dyscom_types import DyscomGetType, DyscomInitParams, DyscomPowerModulePowerType,\
14+
from science_mode_4.dyscom.dyscom_types import DyscomFilterType, DyscomGetType, DyscomInitParams, DyscomPowerModulePowerType,\
1515
DyscomPowerModuleType, DyscomSignalType
1616
from science_mode_4.protocol.types import ResultAndError
17+
from science_mode_4.utils.logger import logger
1718
from examples.utils.example_utils import ExampleUtils
1819
from examples.utils.csv_utils import CsvHelper
1920

@@ -27,6 +28,9 @@ def main():
2728
async def device_communication() -> int:
2829
"""Communication with science mode device"""
2930

31+
# disable logger to increase performance
32+
logger().disabled = True
33+
3034
# get comport from command line argument
3135
com_port = ExampleUtils.get_comport_from_commandline_argument()
3236
# create serial port connection
@@ -47,6 +51,7 @@ async def device_communication() -> int:
4751
await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_ON)
4852
# call init with 4k sample rate and enable signal types
4953
init_params = DyscomInitParams()
54+
init_params.filter = DyscomFilterType.PREDEFINED_FILTER_3
5055
init_params.signal_type = [DyscomSignalType.BI, DyscomSignalType.EMG_1,\
5156
DyscomSignalType.EMG_2, DyscomSignalType.BREATHING, DyscomSignalType.TEMPERATURE]
5257
init_params.register_map_ads129x.config_register_1.output_data_rate = Ads129xOutputDataRate.HR_MODE_4_KSPS__LP_MODE_2_KSPS
@@ -73,13 +78,13 @@ async def device_communication() -> int:
7378
if ack:
7479
# because there are multiple get commands, we need to additionally check kind,
7580
# which is always associated DyscomGetType
76-
if ack.command == Commands.DlGetAck and ack.kind == DyscomGetType.OPERATION_MODE:
81+
if ack.command == Commands.DL_GET_ACK and ack.kind == DyscomGetType.OPERATION_MODE:
7782
om_ack: PacketDyscomGetAckOperationMode = ack
78-
print(f"Operation mode {om_ack.operation_mode}")
83+
print(f"Operation mode {om_ack.operation_mode.name}")
7984
# check if measurement is still active
8085
if om_ack.result_error != ResultAndError.NO_ERROR:
8186
break
82-
elif ack.command == Commands.DlSendLiveData:
87+
elif ack.command == Commands.DL_SEND_LIVE_DATA:
8388
live_data_counter += 1
8489
total_count += 1
8590

@@ -90,7 +95,7 @@ async def device_communication() -> int:
9095

9196
csv_helper.append_values(ack.number, [sld.samples[0].value, sld.samples[1].value,\
9297
sld.samples[2].value, sld.samples[3].value,\
93-
sld.samples[4].value], sld.time_offset)
98+
sld.samples[4].value], sld.time_offset)
9499

95100
else:
96101
# print(f"Live data acknowledges per iteration {live_data_counter}")
@@ -101,8 +106,6 @@ async def device_communication() -> int:
101106
# print stats
102107
end_time = timer()
103108
print(f"Samples: {total_count}, duration: {end_time - start_time}, sample rate: {total_count / (end_time - start_time)}")
104-
# wait until all acknowledges are received
105-
await asyncio.sleep(0.5)
106109

107110
# stop measurement
108111
await dyscom.stop()

examples/low_level/example_low_level_plot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ async def main() -> int:
5050
# get new data from connection
5151
ack = low_level_layer.packet_buffer.get_packet_from_buffer()
5252
if ack:
53-
if ack.command == Commands.LowLevelChannelConfigAck:
53+
if ack.command == Commands.LOW_LEVEL_CHANNEL_CONFIG_ACK:
5454
ll_config_ack: PacketLowLevelChannelConfigAck = ack
5555
measurement_sample_time = ll_config_ack.sampling_time_in_microseconds
5656
measurement_samples.extend(ll_config_ack.measurement_samples)

examples/utils/fastplotlib_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def __init__(self, sub_plot, max_value_count: int, color: str):
1919
self._sub_plot = sub_plot
2020

2121
# we use an array with zero as start data
22-
y_data = np.array([0] * max_value_count)
22+
y_data = np.array([0.0] * max_value_count, dtype=np.float32)
2323
self._line = sub_plot.add_line(y_data, name="values", colors=color)
2424

2525
# this queue is used to synchronize data between background and main thread
@@ -88,7 +88,7 @@ def __init__(self, channels: dict[int, tuple[str, str]], max_value_count: int):
8888

8989
sub_plot_counter += 1
9090

91-
# set animation function that is called regulary to update plots
91+
# set animation function that is called regularly to update plots
9292
self._figure.add_animations(self._animation)
9393
# show figure
9494
self._figure.show(maintain_aspect=False)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "science_mode_4"
7-
version = "0.0.9"
7+
version = "0.0.10"
88
authors = [
99
{ name="Marc Hofmann", email="marc-hofmann@gmx.de" },
1010
]

0 commit comments

Comments
 (0)