ents 2.3.4__tar.gz → 2.3.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ents-2.3.4 → ents-2.3.5}/PKG-INFO +2 -2
- {ents-2.3.4 → ents-2.3.5}/pyproject.toml +2 -2
- {ents-2.3.4 → ents-2.3.5}/src/ents/__init__.py +4 -1
- {ents-2.3.4 → ents-2.3.5}/src/ents/cli.py +293 -5
- {ents-2.3.4 → ents-2.3.5}/src/ents/config/user_config.py +1 -4
- ents-2.3.5/src/ents/proto/sensor.py +290 -0
- ents-2.3.5/src/ents/proto/sensor_pb2.py +48 -0
- ents-2.3.5/src/ents/proto/soil_power_sensor_pb2.py +118 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/simulator/node.py +121 -0
- ents-2.3.5/tests/test_sensor.py +219 -0
- {ents-2.3.4 → ents-2.3.5}/tests/test_simulator.py +3 -0
- ents-2.3.4/src/ents/demo/demoPullRequests.py +0 -119
- ents-2.3.4/src/ents/proto/soil_power_sensor_pb2.py +0 -90
- {ents-2.3.4 → ents-2.3.5}/.gitignore +0 -0
- {ents-2.3.4 → ents-2.3.5}/LICENSE +0 -0
- {ents-2.3.4 → ents-2.3.5}/README.md +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/PingSMU.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/PingSPS.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/README.md +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/__init__.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/linear_regression.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/plots.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/recorder.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/config/README.md +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/config/__init__.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/config/adv_trace.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/proto/__init__.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/proto/decode.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/proto/encode.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/proto/esp32.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/src/ents/simulator/__init__.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/tests/__init__.py +0 -0
- {ents-2.3.4 → ents-2.3.5}/tests/test_generic.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ents
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.5
|
|
4
4
|
Summary: Python package for Environmental NeTworked Sensor (ENTS)
|
|
5
5
|
Project-URL: Homepage, https://github.com/jlab-sensing/soil-power-sensor-firmware
|
|
6
6
|
Project-URL: Issues, https://github.com/jlab-sensing/soil-power-sensor-firmware/issues
|
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: >=3.11
|
|
13
13
|
Requires-Dist: matplotlib
|
|
14
14
|
Requires-Dist: pandas
|
|
15
|
-
Requires-Dist: protobuf==6.
|
|
15
|
+
Requires-Dist: protobuf==6.33.2
|
|
16
16
|
Requires-Dist: pyserial
|
|
17
17
|
Requires-Dist: requests
|
|
18
18
|
Requires-Dist: scikit-learn
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ents"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.5"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="John Madden", email="jmadden173@pm.me" },
|
|
10
10
|
]
|
|
@@ -17,7 +17,7 @@ classifiers = [
|
|
|
17
17
|
"Operating System :: OS Independent",
|
|
18
18
|
]
|
|
19
19
|
dependencies = [
|
|
20
|
-
'protobuf==6.
|
|
20
|
+
'protobuf==6.33.2',
|
|
21
21
|
'matplotlib',
|
|
22
22
|
'pandas',
|
|
23
23
|
'pyserial',
|
|
@@ -3,10 +3,11 @@ from .proto.encode import (
|
|
|
3
3
|
encode_power_measurement,
|
|
4
4
|
encode_teros12_measurement,
|
|
5
5
|
encode_phytos31_measurement,
|
|
6
|
+
encode_user_configuration,
|
|
6
7
|
encode_bme280_measurement,
|
|
7
8
|
)
|
|
8
9
|
|
|
9
|
-
from .proto.decode import decode_response, decode_measurement
|
|
10
|
+
from .proto.decode import decode_response, decode_measurement, decode_user_configuration
|
|
10
11
|
|
|
11
12
|
from .proto.esp32 import encode_esp32command, decode_esp32command
|
|
12
13
|
|
|
@@ -20,4 +21,6 @@ __all__ = [
|
|
|
20
21
|
"decode_measurement",
|
|
21
22
|
"encode_esp32command",
|
|
22
23
|
"decode_esp32command",
|
|
24
|
+
"encode_user_configuration",
|
|
25
|
+
"decode_user_configuration",
|
|
23
26
|
]
|
|
@@ -29,6 +29,13 @@ from .proto.encode import (
|
|
|
29
29
|
from .proto.decode import decode_measurement, decode_response
|
|
30
30
|
from .proto.esp32 import encode_esp32command, decode_esp32command
|
|
31
31
|
|
|
32
|
+
from .proto.sensor import (
|
|
33
|
+
encode_repeated_sensor_measurements,
|
|
34
|
+
decode_repeated_sensor_measurements,
|
|
35
|
+
encode_sensor_response,
|
|
36
|
+
decode_sensor_response,
|
|
37
|
+
)
|
|
38
|
+
|
|
32
39
|
from .simulator.node import NodeSimulator
|
|
33
40
|
|
|
34
41
|
|
|
@@ -37,17 +44,68 @@ def entry():
|
|
|
37
44
|
|
|
38
45
|
parser = argparse.ArgumentParser()
|
|
39
46
|
|
|
40
|
-
subparsers = parser.add_subparsers(help="Ents Utilities")
|
|
47
|
+
subparsers = parser.add_subparsers(help="Ents Utilities", required=True)
|
|
41
48
|
|
|
42
49
|
create_encode_parser(subparsers)
|
|
50
|
+
create_encode_generic_parser(subparsers)
|
|
43
51
|
create_decode_parser(subparsers)
|
|
52
|
+
create_decode_generic_parser(subparsers)
|
|
44
53
|
create_calib_parser(subparsers)
|
|
45
54
|
create_sim_parser(subparsers)
|
|
55
|
+
create_sim_generic_parser(subparsers)
|
|
46
56
|
|
|
47
57
|
args = parser.parse_args()
|
|
48
58
|
args.func(args)
|
|
49
59
|
|
|
50
60
|
|
|
61
|
+
def create_sim_generic_parser(subparsers):
|
|
62
|
+
"""Creates the generic simulation subparser
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
subparsers: Reference to subparser group
|
|
66
|
+
Returns:
|
|
67
|
+
Reference to new subparser
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
sim_p = subparsers.add_parser("sim_generic", help="Simluate generic sensor uploads")
|
|
71
|
+
sim_p.add_argument(
|
|
72
|
+
"--url",
|
|
73
|
+
required=True,
|
|
74
|
+
type=str,
|
|
75
|
+
help="URL of the dirtviz instance (default: http://localhost:8000)",
|
|
76
|
+
)
|
|
77
|
+
sim_p.add_argument(
|
|
78
|
+
"--mode",
|
|
79
|
+
required=True,
|
|
80
|
+
choices=["batch", "stream"],
|
|
81
|
+
type=str,
|
|
82
|
+
help="Upload mode",
|
|
83
|
+
)
|
|
84
|
+
sim_p.add_argument(
|
|
85
|
+
"--sensor",
|
|
86
|
+
required=True,
|
|
87
|
+
type=str,
|
|
88
|
+
nargs="+",
|
|
89
|
+
help="Type of sensor to simulate",
|
|
90
|
+
)
|
|
91
|
+
sim_p.add_argument(
|
|
92
|
+
"--min", type=float, default=-1.0, help="Minimum sensor value (default: -1.0)"
|
|
93
|
+
)
|
|
94
|
+
sim_p.add_argument(
|
|
95
|
+
"--max", type=float, default=1.0, help="Maximum sensor value (default: 1.0)"
|
|
96
|
+
)
|
|
97
|
+
sim_p.add_argument("--cell", required=True, type=int, help="Cell Id")
|
|
98
|
+
sim_p.add_argument("--logger", required=True, type=int, help="Logger Id")
|
|
99
|
+
sim_p.add_argument("--start", type=str, help="Start date")
|
|
100
|
+
sim_p.add_argument("--end", type=str, help="End date")
|
|
101
|
+
sim_p.add_argument(
|
|
102
|
+
"--freq", default=10.0, type=float, help="Frequency of uploads (default: 10s)"
|
|
103
|
+
)
|
|
104
|
+
sim_p.set_defaults(func=simulate_generic)
|
|
105
|
+
|
|
106
|
+
return sim_p
|
|
107
|
+
|
|
108
|
+
|
|
51
109
|
def create_sim_parser(subparsers):
|
|
52
110
|
"""Creates the simulation subparser
|
|
53
111
|
|
|
@@ -90,6 +148,49 @@ def create_sim_parser(subparsers):
|
|
|
90
148
|
return sim_p
|
|
91
149
|
|
|
92
150
|
|
|
151
|
+
def simulate_generic(args):
|
|
152
|
+
simulation = NodeSimulator(
|
|
153
|
+
cell=args.cell,
|
|
154
|
+
logger=args.logger,
|
|
155
|
+
sensors=args.sensor,
|
|
156
|
+
_min=args.min,
|
|
157
|
+
_max=args.max,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
if args.mode == "batch":
|
|
161
|
+
if (args.start is None) or (args.end is None):
|
|
162
|
+
raise ValueError("Start and end date must be provided for batch mode.")
|
|
163
|
+
|
|
164
|
+
# format dates
|
|
165
|
+
curr_dt = datetime.fromisoformat(args.start)
|
|
166
|
+
end_dt = datetime.fromisoformat(args.end)
|
|
167
|
+
|
|
168
|
+
# create list of measurements
|
|
169
|
+
while curr_dt <= end_dt:
|
|
170
|
+
ts = int(curr_dt.timestamp())
|
|
171
|
+
simulation.measure(ts)
|
|
172
|
+
curr_dt += timedelta(seconds=args.freq)
|
|
173
|
+
|
|
174
|
+
# send measurements
|
|
175
|
+
while simulation.send_next(args.url):
|
|
176
|
+
print(simulation)
|
|
177
|
+
|
|
178
|
+
print("Done!")
|
|
179
|
+
|
|
180
|
+
elif args.mode == "stream":
|
|
181
|
+
print("Use CTRL+C to stop the simulation")
|
|
182
|
+
try:
|
|
183
|
+
while True:
|
|
184
|
+
dt = datetime.now()
|
|
185
|
+
ts = int(dt.timestamp())
|
|
186
|
+
simulation.measure(ts)
|
|
187
|
+
while simulation.send_next(args.url):
|
|
188
|
+
print(simulation)
|
|
189
|
+
time.sleep(args.freq)
|
|
190
|
+
except KeyboardInterrupt as _:
|
|
191
|
+
print("Stopping simulation")
|
|
192
|
+
|
|
193
|
+
|
|
93
194
|
def simulate(args):
|
|
94
195
|
simulation = NodeSimulator(
|
|
95
196
|
cell=args.cell,
|
|
@@ -169,6 +270,133 @@ def create_calib_parser(subparsers):
|
|
|
169
270
|
return calib_p
|
|
170
271
|
|
|
171
272
|
|
|
273
|
+
def create_encode_generic_parser(subparsers):
|
|
274
|
+
"""Create generic encode command subparser
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
subparsers: Reference to subparser group
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Reference to new subparser
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
encode_parser = subparsers.add_parser("encode_generic", help="Encode generic data")
|
|
284
|
+
|
|
285
|
+
print_format = encode_parser.add_mutually_exclusive_group()
|
|
286
|
+
print_format.add_argument(
|
|
287
|
+
"--hex", action="store_true", help="Print as hex values (default)"
|
|
288
|
+
)
|
|
289
|
+
print_format.add_argument(
|
|
290
|
+
"--raw", action="store_true", help="Print raw bytes object"
|
|
291
|
+
)
|
|
292
|
+
print_format.add_argument(
|
|
293
|
+
"--c", action="store_true", help="Print bytes for copying to c"
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
encode_subparsers = encode_parser.add_subparsers(
|
|
297
|
+
title="Message type", dest="type", required=True
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
# sensor measurement
|
|
301
|
+
measurement_parser = encode_subparsers.add_parser(
|
|
302
|
+
"meas", help='Proto "Measurement" message'
|
|
303
|
+
)
|
|
304
|
+
measurement_parser.add_argument("--ts", type=int, help="Unix epoch timestamp")
|
|
305
|
+
measurement_parser.add_argument("--cell", type=int, help="Cell Id")
|
|
306
|
+
measurement_parser.add_argument("--logger", type=int, help="Logger Id")
|
|
307
|
+
measurement_parser.add_argument("--idx", type=int, default=1, help="Upload index")
|
|
308
|
+
measurement_parser.add_argument(
|
|
309
|
+
"--sensor",
|
|
310
|
+
nargs=2,
|
|
311
|
+
metavar=("type", "value"),
|
|
312
|
+
action="append",
|
|
313
|
+
required=True,
|
|
314
|
+
help="Specify as: --sensor <type> <value>",
|
|
315
|
+
)
|
|
316
|
+
measurement_parser.set_defaults(func=handle_encode_generic_measurement)
|
|
317
|
+
|
|
318
|
+
# response
|
|
319
|
+
response_parser = encode_subparsers.add_parser(
|
|
320
|
+
"resp", help='Proto "Response" message'
|
|
321
|
+
)
|
|
322
|
+
response_parser.add_argument(
|
|
323
|
+
"--resp",
|
|
324
|
+
nargs=2,
|
|
325
|
+
metavar=("idx", "status"),
|
|
326
|
+
action="append",
|
|
327
|
+
required=True,
|
|
328
|
+
help="Specify as: --resp <idx> <status>",
|
|
329
|
+
)
|
|
330
|
+
response_parser.set_defaults(func=handle_encode_generic_response)
|
|
331
|
+
|
|
332
|
+
return encode_parser
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def parse_number(s: str) -> tuple:
|
|
336
|
+
try:
|
|
337
|
+
i = int(s)
|
|
338
|
+
if i >= 0:
|
|
339
|
+
return i, "unsignedInt"
|
|
340
|
+
return i, "signedInt"
|
|
341
|
+
except ValueError:
|
|
342
|
+
pass
|
|
343
|
+
|
|
344
|
+
try:
|
|
345
|
+
return float(s), "decimal"
|
|
346
|
+
except ValueError:
|
|
347
|
+
pass
|
|
348
|
+
|
|
349
|
+
raise ValueError(f"Invalid numeric value: {s}")
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def handle_encode_generic_measurement(args):
|
|
353
|
+
"""Take arguments and encode a repeated senosr measurement message"""
|
|
354
|
+
|
|
355
|
+
meas = {
|
|
356
|
+
"meta": {
|
|
357
|
+
"ts": args.ts,
|
|
358
|
+
"cellId": args.cell,
|
|
359
|
+
"loggerId": args.logger,
|
|
360
|
+
},
|
|
361
|
+
"measurements": [],
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
for s in args.sensor:
|
|
365
|
+
val, val_type = parse_number(s[1])
|
|
366
|
+
meas["measurements"].append(
|
|
367
|
+
{
|
|
368
|
+
"type": s[0],
|
|
369
|
+
val_type: val,
|
|
370
|
+
"idx": args.idx,
|
|
371
|
+
}
|
|
372
|
+
)
|
|
373
|
+
args.idx += 1
|
|
374
|
+
|
|
375
|
+
data = encode_repeated_sensor_measurements(meas)
|
|
376
|
+
print_data(args, data)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def handle_encode_generic_response(args):
|
|
380
|
+
"""Takes arguments and encode a sensor response message"""
|
|
381
|
+
|
|
382
|
+
resp = {
|
|
383
|
+
"responses": [],
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
for r in args.resp:
|
|
387
|
+
idx = int(r[0])
|
|
388
|
+
status = r[1]
|
|
389
|
+
resp["responses"].append(
|
|
390
|
+
{
|
|
391
|
+
"uploadIndex": idx,
|
|
392
|
+
"status": status,
|
|
393
|
+
}
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
data = encode_sensor_response(resp)
|
|
397
|
+
print_data(args, data)
|
|
398
|
+
|
|
399
|
+
|
|
172
400
|
def create_encode_parser(subparsers):
|
|
173
401
|
"""Create encode command subparser
|
|
174
402
|
|
|
@@ -192,14 +420,19 @@ def create_encode_parser(subparsers):
|
|
|
192
420
|
"--c", action="store_true", help="Print bytes for copying to c"
|
|
193
421
|
)
|
|
194
422
|
|
|
195
|
-
encode_subparsers = encode_parser.add_subparsers(
|
|
423
|
+
encode_subparsers = encode_parser.add_subparsers(
|
|
424
|
+
title="Message type",
|
|
425
|
+
dest="type",
|
|
426
|
+
required=True,
|
|
427
|
+
)
|
|
196
428
|
|
|
197
429
|
def create_measurement_parser(encode_subparsers):
|
|
198
430
|
measurement_parser = encode_subparsers.add_parser(
|
|
199
431
|
"measurement", help='Proto "Measurement" message'
|
|
200
432
|
)
|
|
201
433
|
measurement_subparser = measurement_parser.add_subparsers(
|
|
202
|
-
title="Measurement type"
|
|
434
|
+
title="Measurement type",
|
|
435
|
+
required=True,
|
|
203
436
|
)
|
|
204
437
|
|
|
205
438
|
# metadata
|
|
@@ -246,7 +479,9 @@ def create_encode_parser(subparsers):
|
|
|
246
479
|
"esp32command", help='Proto "Esp32Command" message'
|
|
247
480
|
)
|
|
248
481
|
esp32command_subparser = esp32command_parser.add_subparsers(
|
|
249
|
-
title="type",
|
|
482
|
+
title="type",
|
|
483
|
+
help="PageCommand",
|
|
484
|
+
required=True,
|
|
250
485
|
)
|
|
251
486
|
|
|
252
487
|
test_parser = esp32command_subparser.add_parser("test", help="TestCommand")
|
|
@@ -383,6 +618,55 @@ def print_bytes_c(data: bytes) -> str:
|
|
|
383
618
|
print(f"size_t data_len = {len(hex_str)};")
|
|
384
619
|
|
|
385
620
|
|
|
621
|
+
def create_decode_generic_parser(subparsers):
|
|
622
|
+
"""Create generic decode command parser
|
|
623
|
+
|
|
624
|
+
Args:
|
|
625
|
+
subparsers: Reference to subparser group
|
|
626
|
+
|
|
627
|
+
Returns:
|
|
628
|
+
Reference to new subparser
|
|
629
|
+
"""
|
|
630
|
+
|
|
631
|
+
decode_parser = subparsers.add_parser("decode_generic", help="Decode generic data")
|
|
632
|
+
|
|
633
|
+
decode_subparsers = decode_parser.add_subparsers(
|
|
634
|
+
title="Message type",
|
|
635
|
+
dest="type",
|
|
636
|
+
required=True,
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
decode_parser.add_argument(
|
|
640
|
+
"data", type=str, help="Protobuf serialized data in hex format"
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
# sensor measurement
|
|
644
|
+
measurement_parser = decode_subparsers.add_parser(
|
|
645
|
+
"meas", help='Proto "Measurement" message'
|
|
646
|
+
)
|
|
647
|
+
measurement_parser.set_defaults(func=handle_decode_generic_measurement)
|
|
648
|
+
|
|
649
|
+
# response
|
|
650
|
+
response_parser = decode_subparsers.add_parser(
|
|
651
|
+
"resp", help='Proto "Response" message'
|
|
652
|
+
)
|
|
653
|
+
response_parser.set_defaults(func=handle_decode_generic_response)
|
|
654
|
+
|
|
655
|
+
return decode_parser
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def handle_decode_generic_measurement(args):
|
|
659
|
+
data = bytes.fromhex(args.data)
|
|
660
|
+
vals = decode_repeated_sensor_measurements(data)
|
|
661
|
+
print(vals)
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def handle_decode_generic_response(args):
|
|
665
|
+
data = bytes.fromhex(args.data)
|
|
666
|
+
vals = decode_sensor_response(data)
|
|
667
|
+
print(vals)
|
|
668
|
+
|
|
669
|
+
|
|
386
670
|
def create_decode_parser(subparsers):
|
|
387
671
|
"""Create decode command parser
|
|
388
672
|
|
|
@@ -395,7 +679,11 @@ def create_decode_parser(subparsers):
|
|
|
395
679
|
|
|
396
680
|
decode_parser = subparsers.add_parser("decode", help="Decode data")
|
|
397
681
|
|
|
398
|
-
decode_subparsers = decode_parser.add_subparsers(
|
|
682
|
+
decode_subparsers = decode_parser.add_subparsers(
|
|
683
|
+
title="Message type",
|
|
684
|
+
dest="type",
|
|
685
|
+
required=True,
|
|
686
|
+
)
|
|
399
687
|
|
|
400
688
|
# measurement
|
|
401
689
|
measurement_parser = decode_subparsers.add_parser(
|
|
@@ -24,10 +24,7 @@ import sys
|
|
|
24
24
|
import serial
|
|
25
25
|
import serial.tools.list_ports
|
|
26
26
|
import re # For validating URL input
|
|
27
|
-
from
|
|
28
|
-
encode_user_configuration,
|
|
29
|
-
decode_user_configuration,
|
|
30
|
-
)
|
|
27
|
+
from ..proto import encode_user_configuration, decode_user_configuration
|
|
31
28
|
|
|
32
29
|
|
|
33
30
|
class Ui_MainWindow(object):
|