Skip to content

Commit 0879d2c

Browse files
authored
Merge pull request #29 from aceglia/main
Trigno sdk interface
2 parents a594c77 + 1cf3d96 commit 0879d2c

38 files changed

Lines changed: 2869 additions & 211 deletions

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ __pycache__/
99
# bio extensions
1010
*.bio
1111

12+
# vs code settings
13+
*.env
14+
.vscode/
15+
16+
1217
# Distribution / packaging
1318
.idea
1419
.Python

biosiglive/__init__.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from .interfaces.param import Param, Device, MarkerSet
88

99

10-
from .file_io.save_and_load import load, save
10+
from .file_io.save_and_load import load, save, compress
1111

1212
from .processing.data_processing import RealTimeProcessing, OfflineProcessing, GenericProcessing
1313
from .processing.msk_functions import MskFunctions
@@ -16,7 +16,12 @@
1616
from .streaming.client import Client, Message
1717
from .streaming.server import Server
1818
from .streaming.stream_data import StreamData
19+
from .streaming.async_client import AsyncTCPClient
20+
from .streaming.async_server import AsyncTCPServer
1921

2022
from .enums import *
2123

22-
__version__ = "2.0.0"
24+
from .interfaces.trigno_sdk.sdk_client import TrignoSDKClient
25+
26+
27+
__version__ = "2.0.4"

biosiglive/enums.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""
22
Groups the different enums used in the program. it is a good place to start to check what's available.
33
"""
4+
45
from enum import Enum
56

67

@@ -22,6 +23,7 @@ class DeviceType(Enum):
2223

2324
Emg = "emg"
2425
Imu = "imu"
26+
DelsysGogniometer = "delsys_gogniometer"
2527
Generic = "generic"
2628
ForcePlate = "force_plate"
2729

Lines changed: 185 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,63 @@
1+
import os.path
2+
13
import pickle
2-
import numpy as np
34
from pathlib import Path
5+
from ..streaming.utils import dic_merger
6+
import pickletools
7+
import gzip
8+
from multiprocessing import Pool
9+
10+
11+
def _load_and_compress(origin_file, target_file=None):
12+
data = load(origin_file)
13+
if not target_file:
14+
target_file = origin_file.split(".")[:-1][0] + ".bio.gzip"
15+
if Path(target_file).suffix != ".gzip":
16+
target_file = target_file + ".gzip"
17+
f = gzip.open(target_file, "wb")
18+
data_pick = pickletools.optimize(pickle.dumps(data, pickle.HIGHEST_PROTOCOL))
19+
f.write(data_pick)
20+
f.close()
21+
22+
23+
def compress(origin_file, target_file=None, multi_proc=False):
24+
if target_file and len(target_file) != len(origin_file):
25+
raise ValueError("The number of target files must be equal to the number of origin files.")
26+
if multi_proc:
27+
pool = Pool(processes=os.cpu_count())
28+
pool.map(_load_and_compress, origin_file)
29+
else:
30+
for i in range(len(origin_file)):
31+
target_file_tmp = target_file[i] if target_file else None
32+
_load_and_compress(origin_file[i], target_file_tmp)
33+
34+
35+
def _safe_rename_file(file_name) -> str:
36+
"""This function checks if the file extension is an integer.
437
38+
Parameters
39+
----------
40+
file_name : str
41+
The name to the file.
42+
43+
Returns
44+
-------
45+
str
46+
new name of the file.
47+
"""
48+
i = 0
49+
while file_name[-(i + 1)].isdigit():
50+
i += 1
51+
old_value = int(file_name[-i:]) if i > 0 else None
52+
if old_value:
53+
if file_name[-(i + 1)] == "_" or file_name[-(i + 1)] == "-":
54+
i += 1
55+
return file_name[:-i] + "_" + str(old_value + 1)
56+
else:
57+
return file_name + "_1"
558

6-
def save(data_dict, data_path):
59+
60+
def save(data_dict, data_path, add_data=False, safe=True, compress=False):
761
"""This function adds data to a pickle file. It not open the file, but appends the data to the end, so it's fast.
862
963
Parameters
@@ -12,18 +66,44 @@ def save(data_dict, data_path):
1266
The data to be added to the file.
1367
data_path : str
1468
The path to the file. The file must exist.
69+
add_data : bool, optional
70+
If True, the data are added to the file. If False, the file is overwritten. The default is False.
71+
safe : bool, optional
72+
If True, the data are saved in a new file. The default is False.
73+
compress : bool, optional
74+
If True, the data are compressed. The default is False.
1575
"""
16-
if Path(data_path).suffix != ".bio":
17-
if Path(data_path).suffix == "":
76+
data_path_object = Path(data_path)
77+
if data_path_object.suffix != ".bio":
78+
if data_path_object.suffix == "":
1879
data_path += ".bio"
1980
else:
2081
raise ValueError("The file must be a .bio file.")
21-
with open(data_path, "ab") as outf:
22-
pickle.dump(data_dict, outf, pickle.HIGHEST_PROTOCOL)
82+
if not add_data:
83+
if safe and os.path.isfile(data_path):
84+
file_name = _safe_rename_file(data_path)
85+
data_path = data_path_object.parent / (file_name + data_path_object.suffix)
86+
print(
87+
f"The file {data_path_object.name} already exists. The data will be saved in {data_path.name}."
88+
f" To avoid this message, remove the safe option."
89+
)
90+
if not safe and os.path.isfile(data_path):
91+
os.remove(data_path)
92+
93+
if compress:
94+
if add_data:
95+
raise ValueError("The data cannot be added to a compressed file.")
96+
data_path = data_path + ".gzip"
97+
f = gzip.open(data_path, "wb")
98+
data_pick = pickletools.optimize(pickle.dumps(data_dict, pickle.HIGHEST_PROTOCOL))
99+
f.write(data_pick)
100+
f.close()
101+
else:
102+
with open(data_path, "ab") as outf:
103+
pickle.dump(data_dict, outf, pickle.HIGHEST_PROTOCOL)
23104

24105

25-
# TODO add dict merger
26-
def load(filename, number_of_line=None):
106+
def load_old(filename, number_of_line=None, merge=True):
27107
"""This function reads data from a pickle file to concatenate them into one dictionary.
28108
29109
Parameters
@@ -32,39 +112,115 @@ def load(filename, number_of_line=None):
32112
The path to the file.
33113
number_of_line : int
34114
The number of lines to read. If None, all lines are read.
115+
merge : bool
116+
If True, the data are merged into one dictionary. If False, the data are returned as a list of dictionaries.
35117
36118
Returns
37119
-------
38120
data : dict
39121
The data read from the file.
40122
41123
"""
42-
if Path(filename).suffix != ".bio":
43-
raise ValueError("The file must be a .bio file.")
44-
data = {}
124+
with_gzip = False
125+
126+
if Path(filename).suffix == ".bio":
127+
with_gzip = False
128+
elif Path(filename).suffix == ".gzip":
129+
with_gzip = True
130+
# else:
131+
# raise ValueError("The file must be a .bio or a .bio.gzip file.")
132+
data = None if merge else []
45133
limit = 2 if not number_of_line else number_of_line
46-
with open(filename, "rb") as file:
134+
if with_gzip:
135+
with gzip.open(filename, "rb") as file:
136+
count = 0
137+
while count < limit:
138+
try:
139+
data_tmp = pickle.load(file)
140+
if not merge:
141+
data.append(data_tmp)
142+
else:
143+
if not data:
144+
data = data_tmp
145+
else:
146+
data = dic_merger(data, data_tmp)
147+
if number_of_line:
148+
count += 1
149+
else:
150+
count = 1
151+
except EOFError:
152+
break
153+
else:
154+
with open(filename, "rb") as file:
155+
count = 0
156+
while count < limit:
157+
try:
158+
data_tmp = pickle.load(file)
159+
if not merge:
160+
data.append(data_tmp)
161+
else:
162+
if not data:
163+
data = data_tmp
164+
else:
165+
data = dic_merger(data, data_tmp)
166+
if number_of_line:
167+
count += 1
168+
else:
169+
count = 1
170+
except EOFError:
171+
break
172+
return data
173+
174+
175+
def load(filename, number_of_line=None, merge=True):
176+
"""This function reads data from a pickle file to concatenate them into one dictionary.
177+
178+
Parameters
179+
----------
180+
filename : str
181+
The path to the file.
182+
number_of_line : int
183+
The number of lines to read. If None, all lines are read.
184+
merge : bool
185+
If True, the data are merged into one dictionary. If False, the data are returned as a list of dictionaries.
186+
187+
Returns
188+
-------
189+
data : dict
190+
The data read from the file.
191+
192+
"""
193+
if not Path(filename).is_file():
194+
raise FileNotFoundError(f"The file {filename} does not exist.")
195+
196+
with_gzip = filename.endswith(".gzip")
197+
data = None if merge else []
198+
limit = number_of_line if number_of_line else float("inf")
199+
200+
try:
201+
return _read_all_lines(filename, limit, data, with_gzip, merge)
202+
except Exception as e:
203+
raise RuntimeError(f"An error occurred while loading the file: {e}")
204+
205+
206+
def _read_all_lines(filename, limit=float("inf"), data=(), with_gzip=False, merge=True):
207+
file_open = gzip.open if with_gzip else open
208+
with file_open(filename, "rb") as file:
47209
count = 0
48210
while count < limit:
49211
try:
50212
data_tmp = pickle.load(file)
51-
for key in data_tmp.keys():
52-
if key in data.keys():
53-
if isinstance(data[key], list) is True:
54-
data[key].append(data_tmp[key])
55-
else:
56-
data[key] = np.append(data[key], data_tmp[key], axis=len(data[key].shape) - 1)
57-
else:
58-
if isinstance(data_tmp[key], (int, float, str, dict)) is True:
59-
data[key] = [data_tmp[key]]
60-
elif isinstance(data_tmp[key], list) is True:
61-
data[key] = [data_tmp[key]]
62-
else:
63-
data[key] = data_tmp[key]
64-
if number_of_line:
65-
count += 1
213+
if not merge:
214+
data.append(data_tmp)
215+
else:
216+
data = dic_merger(data, data_tmp) if data else data_tmp
217+
count += 1
218+
except Exception as e:
219+
if str(e) == "Ran out of input":
220+
# print(f"{count} lines read from the file")
221+
pass
66222
else:
67-
count = 1
68-
except EOFError:
223+
print(f"An error occurred while reading the file: {e} at line {count}")
69224
break
225+
70226
return data

0 commit comments

Comments
 (0)