ents 2.3.4__py3-none-any.whl → 2.3.5__py3-none-any.whl
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/__init__.py +4 -1
- ents/cli.py +293 -5
- ents/config/user_config.py +1 -4
- ents/proto/sensor.py +290 -0
- ents/proto/sensor_pb2.py +48 -0
- ents/proto/soil_power_sensor_pb2.py +83 -55
- ents/simulator/node.py +121 -0
- {ents-2.3.4.dist-info → ents-2.3.5.dist-info}/METADATA +2 -2
- {ents-2.3.4.dist-info → ents-2.3.5.dist-info}/RECORD +12 -11
- {ents-2.3.4.dist-info → ents-2.3.5.dist-info}/WHEEL +1 -1
- ents/demo/demoPullRequests.py +0 -119
- {ents-2.3.4.dist-info → ents-2.3.5.dist-info}/entry_points.txt +0 -0
- {ents-2.3.4.dist-info → ents-2.3.5.dist-info}/licenses/LICENSE +0 -0
ents/__init__.py
CHANGED
|
@@ -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
|
]
|
ents/cli.py
CHANGED
|
@@ -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(
|
ents/config/user_config.py
CHANGED
|
@@ -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):
|
ents/proto/sensor.py
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"""Module for sensor measurements
|
|
2
|
+
|
|
3
|
+
Encode/decoding functions are wrappers around the protobuf messages. These take
|
|
4
|
+
in the json dictionary format of the messages and return serialized byte
|
|
5
|
+
arrays.
|
|
6
|
+
|
|
7
|
+
Format/parse functions implement the protocol for repeated sensor measurements.
|
|
8
|
+
These take in a list of measurements and automatically optimize repeated
|
|
9
|
+
metadata fields.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from google.protobuf.json_format import MessageToDict, ParseDict
|
|
13
|
+
|
|
14
|
+
from .sensor_pb2 import (
|
|
15
|
+
SensorMeasurement,
|
|
16
|
+
RepeatedSensorMeasurements,
|
|
17
|
+
SensorType,
|
|
18
|
+
RepeatedSensorResponses,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def parse_sensor_measurement(data: bytes) -> list:
|
|
23
|
+
"""Parses a sensor measurement into a usable dictionary.
|
|
24
|
+
|
|
25
|
+
Function does the following:
|
|
26
|
+
1. Decodes the serialized byte array
|
|
27
|
+
2. Updates metadata for each measurement if missing
|
|
28
|
+
3. Adds names, descriptions, and units to metadata
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
data: Byte array of message.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Dictionary of sensor measurement.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
meas = decode_repeated_sensor_measurements(data)
|
|
38
|
+
meas = update_repeated_metadata(meas)
|
|
39
|
+
for m in meas["measurements"]:
|
|
40
|
+
sensor_data = get_sensor_data(m["type"])
|
|
41
|
+
m.update(sensor_data)
|
|
42
|
+
|
|
43
|
+
return meas
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def format_sensor_measurement(meas: list) -> bytes:
|
|
47
|
+
"""Formats a sensor measurement dictionary into a serialized byte array.
|
|
48
|
+
|
|
49
|
+
Function does the following:
|
|
50
|
+
1. Uses top level metadata for duplicate measurement metadata fields
|
|
51
|
+
2. Encodes the dictionary into a serialized byte array
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
meas: Dictionary of sensor measurement.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Byte array of serialized message.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
# TODO Implement optimization of repeated metadata fields
|
|
61
|
+
meas_dict = {
|
|
62
|
+
"measurements": meas,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
data = encode_repeated_sensor_measurements(meas_dict)
|
|
66
|
+
return data
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_sensor_data(meas_type: int) -> dict:
|
|
70
|
+
"""Gets sensor data information.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
meas_type: Sensor measurement dictionary.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Metadata associated with the sensor type.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
SENSOR_DATA = {
|
|
80
|
+
SensorType.POWER_VOLTAGE: {
|
|
81
|
+
"name": "Voltage",
|
|
82
|
+
"unit": "mV",
|
|
83
|
+
},
|
|
84
|
+
SensorType.POWER_CURRENT: {
|
|
85
|
+
"name": "Current",
|
|
86
|
+
"unit": "uA",
|
|
87
|
+
},
|
|
88
|
+
SensorType.TEROS12_VWC: {
|
|
89
|
+
"name": "Volumetric Water Content",
|
|
90
|
+
"unit": "%",
|
|
91
|
+
},
|
|
92
|
+
SensorType.TEROS12_TEMP: {
|
|
93
|
+
"name": "Temperature",
|
|
94
|
+
"unit": "C",
|
|
95
|
+
},
|
|
96
|
+
SensorType.TEROS12_EC: {
|
|
97
|
+
"name": "Electrical Conductivity",
|
|
98
|
+
"unit": "uS/cm",
|
|
99
|
+
},
|
|
100
|
+
SensorType.PHYTOS31_VOLTAGE: {
|
|
101
|
+
"name": "Voltage",
|
|
102
|
+
"unit": "mV",
|
|
103
|
+
},
|
|
104
|
+
SensorType.PHYTOS31_LEAF_WETNESS: {
|
|
105
|
+
"name": "Leaf Wetness",
|
|
106
|
+
"unit": "%",
|
|
107
|
+
},
|
|
108
|
+
SensorType.BME280_PRESSURE: {
|
|
109
|
+
"name": "Pressure",
|
|
110
|
+
"unit": "kPa",
|
|
111
|
+
},
|
|
112
|
+
SensorType.BME280_TEMP: {
|
|
113
|
+
"name": "Temperature",
|
|
114
|
+
"unit": "C",
|
|
115
|
+
},
|
|
116
|
+
SensorType.BME280_HUMIDITY: {
|
|
117
|
+
"name": "Humidity",
|
|
118
|
+
"unit": "%",
|
|
119
|
+
},
|
|
120
|
+
SensorType.TEROS21_MATRIC_POT: {
|
|
121
|
+
"name": "Matric Potential",
|
|
122
|
+
"unit": "kPa",
|
|
123
|
+
},
|
|
124
|
+
SensorType.TEROS21_TEMP: {
|
|
125
|
+
"name": "Temperature",
|
|
126
|
+
"unit": "C",
|
|
127
|
+
},
|
|
128
|
+
SensorType.SEN0308_VOLTAGE: {
|
|
129
|
+
"name": "Voltage",
|
|
130
|
+
"unit": "mV",
|
|
131
|
+
},
|
|
132
|
+
SensorType.SEN0308_HUMIDITY: {
|
|
133
|
+
"name": "Humidity",
|
|
134
|
+
"unit": "%",
|
|
135
|
+
},
|
|
136
|
+
SensorType.SEN0257_VOLTAGE: {
|
|
137
|
+
"name": "Voltage",
|
|
138
|
+
"unit": "mV",
|
|
139
|
+
},
|
|
140
|
+
SensorType.SEN0257_PRESSURE: {
|
|
141
|
+
"name": "Pressure",
|
|
142
|
+
"unit": "kPa",
|
|
143
|
+
},
|
|
144
|
+
SensorType.YFS210C_FLOW: {
|
|
145
|
+
"name": "Flow Rate",
|
|
146
|
+
"unit": "L/min",
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
meta = SENSOR_DATA[SensorType.Value(meas_type)]
|
|
151
|
+
return meta
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def encode_sensor_measurement(meas_dict: dict) -> bytes:
|
|
155
|
+
meas = SensorMeasurement()
|
|
156
|
+
ParseDict(meas_dict, meas)
|
|
157
|
+
|
|
158
|
+
return meas.SerializeToString()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def encode_repeated_sensor_measurements(meas_dict: dict) -> bytes:
|
|
162
|
+
"""Encodes a SensorMeasurement message
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
rep_meas: Repeated sensor measurement dictionary.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Byte array of encoded RepeatedSensorMeasurements message.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
meas = RepeatedSensorMeasurements()
|
|
172
|
+
ParseDict(meas_dict, meas)
|
|
173
|
+
|
|
174
|
+
return meas.SerializeToString()
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def decode_sensor_measurement(data: bytes) -> dict:
|
|
178
|
+
"""Decodes a SensorMeasurement message
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
data: Byte array of SensorMeasurement message.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Decoded sensor measurement dictionary.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
meas = SensorMeasurement()
|
|
188
|
+
meas.ParseFromString(data)
|
|
189
|
+
|
|
190
|
+
parsed_meas = MessageToDict(meas)
|
|
191
|
+
|
|
192
|
+
return parsed_meas
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def decode_repeated_sensor_measurements(data: bytes) -> dict:
|
|
196
|
+
"""Decodes repeated sensor measurements
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
data: Byte array from RepeatedSensorMeasurements
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
List of decoded sensor measurement dictionaries.
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
rep_meas = RepeatedSensorMeasurements()
|
|
206
|
+
rep_meas.ParseFromString(data)
|
|
207
|
+
return MessageToDict(rep_meas)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def update_repeated_metadata(meas: dict) -> dict:
|
|
211
|
+
"""Ensures every measurements has metadata field set.
|
|
212
|
+
|
|
213
|
+
If a measurement is missing the metadata field, it is filled in from the
|
|
214
|
+
repeated sensor measurement. Existing measurement metadata fields are not
|
|
215
|
+
overwritten.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
meas: Sensor measurement dictionary.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Updated sensor measurement dictionary.
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
# if top level meta does not exist, ensure all measurements have meta
|
|
225
|
+
if "meta" not in meas:
|
|
226
|
+
for m in meas["measurements"]:
|
|
227
|
+
if "meta" not in m:
|
|
228
|
+
raise ValueError("Repeated measurement missing metadata field.")
|
|
229
|
+
# otherwise populate missing measurement meta from top level
|
|
230
|
+
else:
|
|
231
|
+
for m in meas["measurements"]:
|
|
232
|
+
if "meta" not in m:
|
|
233
|
+
m["meta"] = meas["meta"]
|
|
234
|
+
del meas["meta"]
|
|
235
|
+
|
|
236
|
+
return meas
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def encode_sensor_response(resp_dict: dict) -> bytes:
|
|
240
|
+
"""Encodes a sensor response message.
|
|
241
|
+
|
|
242
|
+
{
|
|
243
|
+
responses: [
|
|
244
|
+
{
|
|
245
|
+
status: int,
|
|
246
|
+
message: str,
|
|
247
|
+
},
|
|
248
|
+
...
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
resp_dict: Sensor response dictionary.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Byte array of encoded SensorResponse message.
|
|
257
|
+
"""
|
|
258
|
+
|
|
259
|
+
resp = RepeatedSensorResponses()
|
|
260
|
+
ParseDict(resp_dict, resp)
|
|
261
|
+
|
|
262
|
+
return resp.SerializeToString()
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def decode_sensor_response(data: bytes) -> dict:
|
|
266
|
+
"""Decodes a sensor response message.
|
|
267
|
+
|
|
268
|
+
{
|
|
269
|
+
responses: [
|
|
270
|
+
{
|
|
271
|
+
status: int,
|
|
272
|
+
message: str,
|
|
273
|
+
},
|
|
274
|
+
...
|
|
275
|
+
]
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
data: Byte array of SensorResponse message.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Decoded sensor response dictionary.
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
resp = RepeatedSensorResponses()
|
|
286
|
+
resp.ParseFromString(data)
|
|
287
|
+
|
|
288
|
+
parsed_resp = MessageToDict(resp)
|
|
289
|
+
|
|
290
|
+
return parsed_resp
|
ents/proto/sensor_pb2.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
|
+
# source: sensor.proto
|
|
5
|
+
# Protobuf Python Version: 6.33.1
|
|
6
|
+
"""Generated protocol buffer code."""
|
|
7
|
+
from google.protobuf import descriptor as _descriptor
|
|
8
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
10
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
11
|
+
from google.protobuf.internal import builder as _builder
|
|
12
|
+
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
|
+
_runtime_version.Domain.PUBLIC,
|
|
14
|
+
6,
|
|
15
|
+
33,
|
|
16
|
+
1,
|
|
17
|
+
'',
|
|
18
|
+
'sensor.proto'
|
|
19
|
+
)
|
|
20
|
+
# @@protoc_insertion_point(imports)
|
|
21
|
+
|
|
22
|
+
_sym_db = _symbol_database.Default()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0csensor.proto\":\n\x08Metadata\x12\x0f\n\x07\x63\x65ll_id\x18\x01 \x01(\r\x12\x11\n\tlogger_id\x18\x02 \x01(\r\x12\n\n\x02ts\x18\x03 \x01(\r\":\n\x0eSensorResponse\x12\x0b\n\x03idx\x18\x01 \x01(\r\x12\x1b\n\x05\x65rror\x18\x02 \x01(\x0e\x32\x0c.SensorError\"=\n\x17RepeatedSensorResponses\x12\"\n\tresponses\x18\x01 \x03(\x0b\x32\x0f.SensorResponse\"\x9e\x01\n\x11SensorMeasurement\x12\x17\n\x04meta\x18\x01 \x01(\x0b\x32\t.Metadata\x12\x19\n\x04type\x18\x02 \x01(\x0e\x32\x0b.SensorType\x12\x16\n\x0cunsigned_int\x18\x03 \x01(\rH\x00\x12\x14\n\nsigned_int\x18\x04 \x01(\x05H\x00\x12\x11\n\x07\x64\x65\x63imal\x18\x05 \x01(\x01H\x00\x12\x0b\n\x03idx\x18\x06 \x01(\rB\x07\n\x05value\"z\n\x1aRepeatedSensorMeasurements\x12\x17\n\x04meta\x18\x01 \x01(\x0b\x32\t.Metadata\x12\x19\n\x04type\x18\x02 \x01(\x0e\x32\x0b.SensorType\x12(\n\x0cmeasurements\x18\x03 \x03(\x0b\x32\x12.SensorMeasurement*\x9a\x03\n\nSensorType\x12\x08\n\x04NONE\x10\x00\x12\x11\n\rPOWER_VOLTAGE\x10\x01\x12\x11\n\rPOWER_CURRENT\x10\x02\x12\x0f\n\x0bTEROS12_VWC\x10\x03\x12\x13\n\x0fTEROS12_VWC_ADJ\x10\x04\x12\x10\n\x0cTEROS12_TEMP\x10\x05\x12\x0e\n\nTEROS12_EC\x10\x06\x12\x14\n\x10PHYTOS31_VOLTAGE\x10\x07\x12\x19\n\x15PHYTOS31_LEAF_WETNESS\x10\x08\x12\x13\n\x0f\x42ME280_PRESSURE\x10\t\x12\x0f\n\x0b\x42ME280_TEMP\x10\n\x12\x13\n\x0f\x42ME280_HUMIDITY\x10\x0b\x12\x16\n\x12TEROS21_MATRIC_POT\x10\x0c\x12\x10\n\x0cTEROS21_TEMP\x10\r\x12\x13\n\x0fSEN0308_VOLTAGE\x10\x0e\x12\x14\n\x10SEN0308_HUMIDITY\x10\x0f\x12\x13\n\x0fSEN0257_VOLTAGE\x10\x10\x12\x14\n\x10SEN0257_PRESSURE\x10\x11\x12\x10\n\x0cYFS210C_FLOW\x10\x12\x12\x16\n\x12PCAP02_CAPACITANCE\x10\x13*b\n\x0bSensorError\x12\x06\n\x02OK\x10\x00\x12\x0b\n\x07GENERAL\x10\x01\x12\n\n\x06LOGGER\x10\x02\x12\x08\n\x04\x43\x45LL\x10\x03\x12\x0f\n\x0bUNSUPPORTED\x10\x04\x12\x0b\n\x07INVALID\x10\x05\x12\n\n\x06\x44\x45\x43ODE\x10\x06\x62\x06proto3')
|
|
28
|
+
|
|
29
|
+
_globals = globals()
|
|
30
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
31
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'sensor_pb2', _globals)
|
|
32
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
33
|
+
DESCRIPTOR._loaded_options = None
|
|
34
|
+
_globals['_SENSORTYPE']._serialized_start=485
|
|
35
|
+
_globals['_SENSORTYPE']._serialized_end=895
|
|
36
|
+
_globals['_SENSORERROR']._serialized_start=897
|
|
37
|
+
_globals['_SENSORERROR']._serialized_end=995
|
|
38
|
+
_globals['_METADATA']._serialized_start=16
|
|
39
|
+
_globals['_METADATA']._serialized_end=74
|
|
40
|
+
_globals['_SENSORRESPONSE']._serialized_start=76
|
|
41
|
+
_globals['_SENSORRESPONSE']._serialized_end=134
|
|
42
|
+
_globals['_REPEATEDSENSORRESPONSES']._serialized_start=136
|
|
43
|
+
_globals['_REPEATEDSENSORRESPONSES']._serialized_end=197
|
|
44
|
+
_globals['_SENSORMEASUREMENT']._serialized_start=200
|
|
45
|
+
_globals['_SENSORMEASUREMENT']._serialized_end=358
|
|
46
|
+
_globals['_REPEATEDSENSORMEASUREMENTS']._serialized_start=360
|
|
47
|
+
_globals['_REPEATEDSENSORMEASUREMENTS']._serialized_end=482
|
|
48
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
3
|
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
4
|
# source: soil_power_sensor.proto
|
|
5
|
-
# Protobuf Python Version: 6.
|
|
5
|
+
# Protobuf Python Version: 6.33.1
|
|
6
6
|
"""Generated protocol buffer code."""
|
|
7
7
|
from google.protobuf import descriptor as _descriptor
|
|
8
8
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
@@ -12,7 +12,7 @@ from google.protobuf.internal import builder as _builder
|
|
|
12
12
|
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
13
|
_runtime_version.Domain.PUBLIC,
|
|
14
14
|
6,
|
|
15
|
-
|
|
15
|
+
33,
|
|
16
16
|
1,
|
|
17
17
|
'',
|
|
18
18
|
'soil_power_sensor.proto'
|
|
@@ -24,67 +24,95 @@ _sym_db = _symbol_database.Default()
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17soil_power_sensor.proto\"E\n\x13MeasurementMetadata\x12\x0f\n\x07\x63\x65ll_id\x18\x01 \x01(\r\x12\x11\n\tlogger_id\x18\x02 \x01(\r\x12\n\n\x02ts\x18\x03 \x01(\r\"4\n\x10PowerMeasurement\x12\x0f\n\x07voltage\x18\x02 \x01(\x01\x12\x0f\n\x07\x63urrent\x18\x03 \x01(\x01\"P\n\x12Teros12Measurement\x12\x0f\n\x07vwc_raw\x18\x02 \x01(\x01\x12\x0f\n\x07vwc_adj\x18\x03 \x01(\x01\x12\x0c\n\x04temp\x18\x04 \x01(\x01\x12\n\n\x02\x65\x63\x18\x05 \x01(\r\"6\n\x12Teros21Measurement\x12\x12\n\nmatric_pot\x18\x01 \x01(\x01\x12\x0c\n\x04temp\x18\x02 \x01(\x01\"<\n\x13Phytos31Measurement\x12\x0f\n\x07voltage\x18\x01 \x01(\x01\x12\x14\n\x0cleaf_wetness\x18\x02 \x01(\x01\"L\n\x11\x42ME280Measurement\x12\x10\n\x08pressure\x18\x01 \x01(\r\x12\x13\n\x0btemperature\x18\x02 \x01(\x05\x12\x10\n\x08humidity\x18\x03 \x01(\r\"7\n\x12SEN0308Measurement\x12\x0f\n\x07voltage\x18\x01 \x01(\x01\x12\x10\n\x08humidity\x18\x02 \x01(\x01\"7\n\x12SEN0257Measurement\x12\x0f\n\x07voltage\x18\x01 \x01(\x01\x12\x10\n\x08pressure\x18\x02 \x01(\x01\"\"\n\x12YFS210CMeasurement\x12\x0c\n\x04\x66low\x18\x01 \x01(\x01\"\
|
|
27
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17soil_power_sensor.proto\"E\n\x13MeasurementMetadata\x12\x0f\n\x07\x63\x65ll_id\x18\x01 \x01(\r\x12\x11\n\tlogger_id\x18\x02 \x01(\r\x12\n\n\x02ts\x18\x03 \x01(\r\"4\n\x10PowerMeasurement\x12\x0f\n\x07voltage\x18\x02 \x01(\x01\x12\x0f\n\x07\x63urrent\x18\x03 \x01(\x01\"*\n\x17VoltageDeltaMeasurement\x12\x0f\n\x07voltage\x18\x01 \x01(\r\"*\n\x17\x43urrentDeltaMeasurement\x12\x0f\n\x07\x63urrent\x18\x01 \x01(\r\"%\n\x12VoltageMeasurement\x12\x0f\n\x07voltage\x18\x01 \x01(\x01\"%\n\x12\x43urrentMeasurement\x12\x0f\n\x07\x63urrent\x18\x01 \x01(\x01\"K\n\x0fPowerDeltaEntry\x12\n\n\x02ts\x18\x01 \x01(\r\x12\x15\n\rvoltage_delta\x18\x02 \x01(\r\x12\x15\n\rcurrent_delta\x18\x03 \x01(\r\"E\n\x15PowerMeasurementDelta\x12\x15\n\rvoltage_delta\x18\x02 \x01(\r\x12\x15\n\rcurrent_delta\x18\x03 \x01(\r\"\\\n\x13RepeatedPowerDeltas\x12\x11\n\tlogger_id\x18\x01 \x01(\r\x12\x0f\n\x07\x63\x65ll_id\x18\x02 \x01(\r\x12!\n\x07\x65ntries\x18\x03 \x03(\x0b\x32\x10.PowerDeltaEntry\"P\n\x12Teros12Measurement\x12\x0f\n\x07vwc_raw\x18\x02 \x01(\x01\x12\x0f\n\x07vwc_adj\x18\x03 \x01(\x01\x12\x0c\n\x04temp\x18\x04 \x01(\x01\x12\n\n\x02\x65\x63\x18\x05 \x01(\r\"6\n\x12Teros21Measurement\x12\x12\n\nmatric_pot\x18\x01 \x01(\x01\x12\x0c\n\x04temp\x18\x02 \x01(\x01\"<\n\x13Phytos31Measurement\x12\x0f\n\x07voltage\x18\x01 \x01(\x01\x12\x14\n\x0cleaf_wetness\x18\x02 \x01(\x01\"L\n\x11\x42ME280Measurement\x12\x10\n\x08pressure\x18\x01 \x01(\r\x12\x13\n\x0btemperature\x18\x02 \x01(\x05\x12\x10\n\x08humidity\x18\x03 \x01(\r\"7\n\x12SEN0308Measurement\x12\x0f\n\x07voltage\x18\x01 \x01(\x01\x12\x10\n\x08humidity\x18\x02 \x01(\x01\"7\n\x12SEN0257Measurement\x12\x0f\n\x07voltage\x18\x01 \x01(\x01\x12\x10\n\x08pressure\x18\x02 \x01(\x01\"\"\n\x12YFS210CMeasurement\x12\x0c\n\x04\x66low\x18\x01 \x01(\x01\"(\n\x11PCAP02Measurement\x12\x13\n\x0b\x63\x61pacitance\x18\x01 \x01(\x01\"\xa2\x03\n\x0bMeasurement\x12\"\n\x04meta\x18\x01 \x01(\x0b\x32\x14.MeasurementMetadata\x12\"\n\x05power\x18\x02 \x01(\x0b\x32\x11.PowerMeasurementH\x00\x12&\n\x07teros12\x18\x03 \x01(\x0b\x32\x13.Teros12MeasurementH\x00\x12(\n\x08phytos31\x18\x04 \x01(\x0b\x32\x14.Phytos31MeasurementH\x00\x12$\n\x06\x62me280\x18\x05 \x01(\x0b\x32\x12.BME280MeasurementH\x00\x12&\n\x07teros21\x18\x06 \x01(\x0b\x32\x13.Teros21MeasurementH\x00\x12&\n\x07sen0308\x18\x07 \x01(\x0b\x32\x13.SEN0308MeasurementH\x00\x12&\n\x07sen0257\x18\x08 \x01(\x0b\x32\x13.SEN0257MeasurementH\x00\x12&\n\x07yfs210c\x18\t \x01(\x0b\x32\x13.YFS210CMeasurementH\x00\x12$\n\x06pcap02\x18\n \x01(\x0b\x32\x12.PCAP02MeasurementH\x00\x42\r\n\x0bmeasurement\"X\n\x08Response\x12$\n\x04resp\x18\x01 \x01(\x0e\x32\x16.Response.ResponseType\"&\n\x0cResponseType\x12\x0b\n\x07SUCCESS\x10\x00\x12\t\n\x05\x45RROR\x10\x01\"\xc4\x02\n\x0c\x45sp32Command\x12$\n\x0cpage_command\x18\x01 \x01(\x0b\x32\x0c.PageCommandH\x00\x12$\n\x0ctest_command\x18\x02 \x01(\x0b\x32\x0c.TestCommandH\x00\x12$\n\x0cwifi_command\x18\x03 \x01(\x0b\x32\x0c.WiFiCommandH\x00\x12*\n\x0fmicrosd_command\x18\x04 \x01(\x0b\x32\x0f.MicroSDCommandH\x00\x12\x30\n\x12irrigation_command\x18\x05 \x01(\x0b\x32\x12.IrrigationCommandH\x00\x12\x31\n\x13user_config_command\x18\x06 \x01(\x0b\x32\x12.UserConfigCommandH\x00\x12&\n\rpower_command\x18\x07 \x01(\x0b\x32\r.PowerCommandH\x00\x42\t\n\x07\x63ommand\"\xb6\x01\n\x0bPageCommand\x12.\n\x0c\x66ile_request\x18\x01 \x01(\x0e\x32\x18.PageCommand.RequestType\x12\x17\n\x0f\x66ile_descriptor\x18\x02 \x01(\r\x12\x12\n\nblock_size\x18\x03 \x01(\r\x12\x11\n\tnum_bytes\x18\x04 \x01(\r\"7\n\x0bRequestType\x12\x08\n\x04OPEN\x10\x00\x12\t\n\x05\x43LOSE\x10\x01\x12\x08\n\x04READ\x10\x02\x12\t\n\x05WRITE\x10\x03\"\x82\x01\n\x0bTestCommand\x12\'\n\x05state\x18\x01 \x01(\x0e\x32\x18.TestCommand.ChangeState\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x05\"<\n\x0b\x43hangeState\x12\x0b\n\x07RECEIVE\x10\x00\x12\x13\n\x0fRECEIVE_REQUEST\x10\x01\x12\x0b\n\x07REQUEST\x10\x02\"\xc5\x02\n\x0bWiFiCommand\x12\x1f\n\x04type\x18\x01 \x01(\x0e\x32\x11.WiFiCommand.Type\x12\x0c\n\x04ssid\x18\x02 \x01(\t\x12\x0e\n\x06passwd\x18\x03 \x01(\t\x12\x0b\n\x03url\x18\x04 \x01(\t\x12\x0c\n\x04port\x18\x08 \x01(\r\x12\n\n\x02rc\x18\x05 \x01(\r\x12\n\n\x02ts\x18\x06 \x01(\r\x12\x0c\n\x04resp\x18\x07 \x01(\x0c\x12\x0b\n\x03mac\x18\t \x01(\t\x12\x0f\n\x07\x63lients\x18\n \x01(\r\"\x97\x01\n\x04Type\x12\x0b\n\x07\x43ONNECT\x10\x00\x12\x08\n\x04POST\x10\x01\x12\t\n\x05\x43HECK\x10\x02\x12\x08\n\x04TIME\x10\x03\x12\x0e\n\nDISCONNECT\x10\x04\x12\x0e\n\nCHECK_WIFI\x10\x05\x12\r\n\tCHECK_API\x10\x06\x12\x0c\n\x08NTP_SYNC\x10\x07\x12\x08\n\x04HOST\x10\x08\x12\r\n\tSTOP_HOST\x10\t\x12\r\n\tHOST_INFO\x10\n\"\xad\x01\n\x11UserConfigCommand\x12,\n\x04type\x18\x01 \x01(\x0e\x32\x1e.UserConfigCommand.RequestType\x12\'\n\x0b\x63onfig_data\x18\x02 \x01(\x0b\x32\x12.UserConfiguration\"A\n\x0bRequestType\x12\x12\n\x0eREQUEST_CONFIG\x10\x00\x12\x13\n\x0fRESPONSE_CONFIG\x10\x01\x12\t\n\x05START\x10\x02\"\x86\x03\n\x0eMicroSDCommand\x12\"\n\x04type\x18\x01 \x01(\x0e\x32\x14.MicroSDCommand.Type\x12\x10\n\x08\x66ilename\x18\x02 \x01(\t\x12&\n\x02rc\x18\x03 \x01(\x0e\x32\x1a.MicroSDCommand.ReturnCode\x12\x1c\n\x04meas\x18\x04 \x01(\x0b\x32\x0c.MeasurementH\x00\x12 \n\x02uc\x18\x05 \x01(\x0b\x32\x12.UserConfigurationH\x00\" \n\x04Type\x12\x08\n\x04SAVE\x10\x00\x12\x0e\n\nUSERCONFIG\x10\x01\"\xab\x01\n\nReturnCode\x12\x0b\n\x07SUCCESS\x10\x00\x12\x11\n\rERROR_GENERAL\x10\x01\x12\x1e\n\x1a\x45RROR_MICROSD_NOT_INSERTED\x10\x02\x12#\n\x1f\x45RROR_FILE_SYSTEM_NOT_MOUNTABLE\x10\x03\x12\x1d\n\x19\x45RROR_PAYLOAD_NOT_DECODED\x10\x04\x12\x19\n\x15\x45RROR_FILE_NOT_OPENED\x10\x05\x42\x06\n\x04\x64\x61ta\"\x94\x01\n\x11IrrigationCommand\x12%\n\x04type\x18\x01 \x01(\x0e\x32\x17.IrrigationCommand.Type\x12\'\n\x05state\x18\x02 \x01(\x0e\x32\x18.IrrigationCommand.State\"\x11\n\x04Type\x12\t\n\x05\x43HECK\x10\x00\"\x1c\n\x05State\x12\x08\n\x04OPEN\x10\x00\x12\t\n\x05\x43LOSE\x10\x01\"\xab\x03\n\x0cPowerCommand\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.PowerCommand.Type\x12*\n\x06reason\x18\x02 \x01(\x0e\x32\x1a.PowerCommand.WakeupReason\x12\x12\n\nboot_count\x18\x03 \x01(\r\"\x1d\n\x04Type\x12\t\n\x05SLEEP\x10\x00\x12\n\n\x06WAKEUP\x10\x01\"\x99\x02\n\x0cWakeupReason\x12\x15\n\x11POWER_WAKEUP_EXT0\x10\x00\x12\x15\n\x11POWER_WAKEUP_EXT1\x10\x01\x12\x16\n\x12POWER_WAKEUP_TIMER\x10\x02\x12\x19\n\x15POWER_WAKEUP_TOUCHPAD\x10\x03\x12\x14\n\x10POWER_WAKEUP_ULP\x10\x04\x12\x15\n\x11POWER_WAKEUP_GPIO\x10\x05\x12\x15\n\x11POWER_WAKEUP_UART\x10\x06\x12\x15\n\x11POWER_WAKEUP_WIFI\x10\x07\x12\x16\n\x12POWER_WAKEUP_COCPU\x10\x08\x12 \n\x1cPOWER_WAKEUP_COCPU_TRAP_TRIG\x10\t\x12\x13\n\x0fPOWER_WAKEUP_BT\x10\n\"\xdc\x02\n\x11UserConfiguration\x12\x11\n\tlogger_id\x18\x01 \x01(\r\x12\x0f\n\x07\x63\x65ll_id\x18\x02 \x01(\r\x12$\n\rUpload_method\x18\x03 \x01(\x0e\x32\r.Uploadmethod\x12\x17\n\x0fUpload_interval\x18\x04 \x01(\r\x12\'\n\x0f\x65nabled_sensors\x18\x05 \x03(\x0e\x32\x0e.EnabledSensor\x12\x15\n\rVoltage_Slope\x18\x06 \x01(\x01\x12\x16\n\x0eVoltage_Offset\x18\x07 \x01(\x01\x12\x15\n\rCurrent_Slope\x18\x08 \x01(\x01\x12\x16\n\x0e\x43urrent_Offset\x18\t \x01(\x01\x12\x11\n\tWiFi_SSID\x18\n \x01(\t\x12\x15\n\rWiFi_Password\x18\x0b \x01(\t\x12\x18\n\x10\x41PI_Endpoint_URL\x18\x0c \x01(\t\x12\x19\n\x11\x41PI_Endpoint_Port\x18\r \x01(\r\"\x17\n\x08\x61\x64\x63Value\x12\x0b\n\x03\x61\x64\x63\x18\x01 \x01(\r*\x90\x01\n\rEnabledSensor\x12\x0b\n\x07Voltage\x10\x00\x12\x0b\n\x07\x43urrent\x10\x01\x12\x0b\n\x07Teros12\x10\x02\x12\x0b\n\x07Teros21\x10\x03\x12\n\n\x06\x42ME280\x10\x04\x12\x0c\n\x08Phytos31\x10\x05\x12\x0b\n\x07SEN0308\x10\x06\x12\x0b\n\x07SEN0257\x10\x07\x12\x0b\n\x07YFS210C\x10\x08\x12\n\n\x06PCAP02\x10\t*\"\n\x0cUploadmethod\x12\x08\n\x04LoRa\x10\x00\x12\x08\n\x04WiFi\x10\x01\x62\x06proto3')
|
|
28
28
|
|
|
29
29
|
_globals = globals()
|
|
30
30
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
31
31
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'soil_power_sensor_pb2', _globals)
|
|
32
32
|
if not _descriptor._USE_C_DESCRIPTORS:
|
|
33
33
|
DESCRIPTOR._loaded_options = None
|
|
34
|
-
_globals['_ENABLEDSENSOR']._serialized_start=
|
|
35
|
-
_globals['_ENABLEDSENSOR']._serialized_end=
|
|
36
|
-
_globals['_UPLOADMETHOD']._serialized_start=
|
|
37
|
-
_globals['_UPLOADMETHOD']._serialized_end=
|
|
34
|
+
_globals['_ENABLEDSENSOR']._serialized_start=4041
|
|
35
|
+
_globals['_ENABLEDSENSOR']._serialized_end=4185
|
|
36
|
+
_globals['_UPLOADMETHOD']._serialized_start=4187
|
|
37
|
+
_globals['_UPLOADMETHOD']._serialized_end=4221
|
|
38
38
|
_globals['_MEASUREMENTMETADATA']._serialized_start=27
|
|
39
39
|
_globals['_MEASUREMENTMETADATA']._serialized_end=96
|
|
40
40
|
_globals['_POWERMEASUREMENT']._serialized_start=98
|
|
41
41
|
_globals['_POWERMEASUREMENT']._serialized_end=150
|
|
42
|
-
_globals['
|
|
43
|
-
_globals['
|
|
44
|
-
_globals['
|
|
45
|
-
_globals['
|
|
46
|
-
_globals['
|
|
47
|
-
_globals['
|
|
48
|
-
_globals['
|
|
49
|
-
_globals['
|
|
50
|
-
_globals['
|
|
51
|
-
_globals['
|
|
52
|
-
_globals['
|
|
53
|
-
_globals['
|
|
54
|
-
_globals['
|
|
55
|
-
_globals['
|
|
56
|
-
_globals['
|
|
57
|
-
_globals['
|
|
58
|
-
_globals['
|
|
59
|
-
_globals['
|
|
60
|
-
_globals['
|
|
61
|
-
_globals['
|
|
62
|
-
_globals['
|
|
63
|
-
_globals['
|
|
64
|
-
_globals['
|
|
65
|
-
_globals['
|
|
66
|
-
_globals['
|
|
67
|
-
_globals['
|
|
68
|
-
_globals['
|
|
69
|
-
_globals['
|
|
70
|
-
_globals['
|
|
71
|
-
_globals['
|
|
72
|
-
_globals['
|
|
73
|
-
_globals['
|
|
74
|
-
_globals['
|
|
75
|
-
_globals['
|
|
76
|
-
_globals['
|
|
77
|
-
_globals['
|
|
78
|
-
_globals['
|
|
79
|
-
_globals['
|
|
80
|
-
_globals['
|
|
81
|
-
_globals['
|
|
82
|
-
_globals['
|
|
83
|
-
_globals['
|
|
84
|
-
_globals['
|
|
85
|
-
_globals['
|
|
86
|
-
_globals['
|
|
87
|
-
_globals['
|
|
88
|
-
_globals['
|
|
89
|
-
_globals['
|
|
42
|
+
_globals['_VOLTAGEDELTAMEASUREMENT']._serialized_start=152
|
|
43
|
+
_globals['_VOLTAGEDELTAMEASUREMENT']._serialized_end=194
|
|
44
|
+
_globals['_CURRENTDELTAMEASUREMENT']._serialized_start=196
|
|
45
|
+
_globals['_CURRENTDELTAMEASUREMENT']._serialized_end=238
|
|
46
|
+
_globals['_VOLTAGEMEASUREMENT']._serialized_start=240
|
|
47
|
+
_globals['_VOLTAGEMEASUREMENT']._serialized_end=277
|
|
48
|
+
_globals['_CURRENTMEASUREMENT']._serialized_start=279
|
|
49
|
+
_globals['_CURRENTMEASUREMENT']._serialized_end=316
|
|
50
|
+
_globals['_POWERDELTAENTRY']._serialized_start=318
|
|
51
|
+
_globals['_POWERDELTAENTRY']._serialized_end=393
|
|
52
|
+
_globals['_POWERMEASUREMENTDELTA']._serialized_start=395
|
|
53
|
+
_globals['_POWERMEASUREMENTDELTA']._serialized_end=464
|
|
54
|
+
_globals['_REPEATEDPOWERDELTAS']._serialized_start=466
|
|
55
|
+
_globals['_REPEATEDPOWERDELTAS']._serialized_end=558
|
|
56
|
+
_globals['_TEROS12MEASUREMENT']._serialized_start=560
|
|
57
|
+
_globals['_TEROS12MEASUREMENT']._serialized_end=640
|
|
58
|
+
_globals['_TEROS21MEASUREMENT']._serialized_start=642
|
|
59
|
+
_globals['_TEROS21MEASUREMENT']._serialized_end=696
|
|
60
|
+
_globals['_PHYTOS31MEASUREMENT']._serialized_start=698
|
|
61
|
+
_globals['_PHYTOS31MEASUREMENT']._serialized_end=758
|
|
62
|
+
_globals['_BME280MEASUREMENT']._serialized_start=760
|
|
63
|
+
_globals['_BME280MEASUREMENT']._serialized_end=836
|
|
64
|
+
_globals['_SEN0308MEASUREMENT']._serialized_start=838
|
|
65
|
+
_globals['_SEN0308MEASUREMENT']._serialized_end=893
|
|
66
|
+
_globals['_SEN0257MEASUREMENT']._serialized_start=895
|
|
67
|
+
_globals['_SEN0257MEASUREMENT']._serialized_end=950
|
|
68
|
+
_globals['_YFS210CMEASUREMENT']._serialized_start=952
|
|
69
|
+
_globals['_YFS210CMEASUREMENT']._serialized_end=986
|
|
70
|
+
_globals['_PCAP02MEASUREMENT']._serialized_start=988
|
|
71
|
+
_globals['_PCAP02MEASUREMENT']._serialized_end=1028
|
|
72
|
+
_globals['_MEASUREMENT']._serialized_start=1031
|
|
73
|
+
_globals['_MEASUREMENT']._serialized_end=1449
|
|
74
|
+
_globals['_RESPONSE']._serialized_start=1451
|
|
75
|
+
_globals['_RESPONSE']._serialized_end=1539
|
|
76
|
+
_globals['_RESPONSE_RESPONSETYPE']._serialized_start=1501
|
|
77
|
+
_globals['_RESPONSE_RESPONSETYPE']._serialized_end=1539
|
|
78
|
+
_globals['_ESP32COMMAND']._serialized_start=1542
|
|
79
|
+
_globals['_ESP32COMMAND']._serialized_end=1866
|
|
80
|
+
_globals['_PAGECOMMAND']._serialized_start=1869
|
|
81
|
+
_globals['_PAGECOMMAND']._serialized_end=2051
|
|
82
|
+
_globals['_PAGECOMMAND_REQUESTTYPE']._serialized_start=1996
|
|
83
|
+
_globals['_PAGECOMMAND_REQUESTTYPE']._serialized_end=2051
|
|
84
|
+
_globals['_TESTCOMMAND']._serialized_start=2054
|
|
85
|
+
_globals['_TESTCOMMAND']._serialized_end=2184
|
|
86
|
+
_globals['_TESTCOMMAND_CHANGESTATE']._serialized_start=2124
|
|
87
|
+
_globals['_TESTCOMMAND_CHANGESTATE']._serialized_end=2184
|
|
88
|
+
_globals['_WIFICOMMAND']._serialized_start=2187
|
|
89
|
+
_globals['_WIFICOMMAND']._serialized_end=2512
|
|
90
|
+
_globals['_WIFICOMMAND_TYPE']._serialized_start=2361
|
|
91
|
+
_globals['_WIFICOMMAND_TYPE']._serialized_end=2512
|
|
92
|
+
_globals['_USERCONFIGCOMMAND']._serialized_start=2515
|
|
93
|
+
_globals['_USERCONFIGCOMMAND']._serialized_end=2688
|
|
94
|
+
_globals['_USERCONFIGCOMMAND_REQUESTTYPE']._serialized_start=2623
|
|
95
|
+
_globals['_USERCONFIGCOMMAND_REQUESTTYPE']._serialized_end=2688
|
|
96
|
+
_globals['_MICROSDCOMMAND']._serialized_start=2691
|
|
97
|
+
_globals['_MICROSDCOMMAND']._serialized_end=3081
|
|
98
|
+
_globals['_MICROSDCOMMAND_TYPE']._serialized_start=2867
|
|
99
|
+
_globals['_MICROSDCOMMAND_TYPE']._serialized_end=2899
|
|
100
|
+
_globals['_MICROSDCOMMAND_RETURNCODE']._serialized_start=2902
|
|
101
|
+
_globals['_MICROSDCOMMAND_RETURNCODE']._serialized_end=3073
|
|
102
|
+
_globals['_IRRIGATIONCOMMAND']._serialized_start=3084
|
|
103
|
+
_globals['_IRRIGATIONCOMMAND']._serialized_end=3232
|
|
104
|
+
_globals['_IRRIGATIONCOMMAND_TYPE']._serialized_start=3185
|
|
105
|
+
_globals['_IRRIGATIONCOMMAND_TYPE']._serialized_end=3202
|
|
106
|
+
_globals['_IRRIGATIONCOMMAND_STATE']._serialized_start=3204
|
|
107
|
+
_globals['_IRRIGATIONCOMMAND_STATE']._serialized_end=3232
|
|
108
|
+
_globals['_POWERCOMMAND']._serialized_start=3235
|
|
109
|
+
_globals['_POWERCOMMAND']._serialized_end=3662
|
|
110
|
+
_globals['_POWERCOMMAND_TYPE']._serialized_start=3349
|
|
111
|
+
_globals['_POWERCOMMAND_TYPE']._serialized_end=3378
|
|
112
|
+
_globals['_POWERCOMMAND_WAKEUPREASON']._serialized_start=3381
|
|
113
|
+
_globals['_POWERCOMMAND_WAKEUPREASON']._serialized_end=3662
|
|
114
|
+
_globals['_USERCONFIGURATION']._serialized_start=3665
|
|
115
|
+
_globals['_USERCONFIGURATION']._serialized_end=4013
|
|
116
|
+
_globals['_ADCVALUE']._serialized_start=4015
|
|
117
|
+
_globals['_ADCVALUE']._serialized_end=4038
|
|
90
118
|
# @@protoc_insertion_point(module_scope)
|
ents/simulator/node.py
CHANGED
|
@@ -10,6 +10,8 @@ from ..proto.encode import (
|
|
|
10
10
|
encode_bme280_measurement,
|
|
11
11
|
)
|
|
12
12
|
|
|
13
|
+
from ..proto.sensor import encode_repeated_sensor_measurements
|
|
14
|
+
|
|
13
15
|
|
|
14
16
|
class NodeSimulator:
|
|
15
17
|
"""Simulation class to simulate measurements for different sensors"""
|
|
@@ -159,3 +161,122 @@ class NodeSimulator:
|
|
|
159
161
|
)
|
|
160
162
|
self.measurements.append(meas)
|
|
161
163
|
self.measurement_buffer.append(meas)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class NodeSimulatorGeneric:
|
|
167
|
+
"""Simulation class to simulate measurements for different sensors"""
|
|
168
|
+
|
|
169
|
+
# temporary storage for measurements to be uploaded
|
|
170
|
+
measurement_buffer: list[bytes] = []
|
|
171
|
+
# all measurements uploaded
|
|
172
|
+
measurements: list[bytes] = []
|
|
173
|
+
# all responses
|
|
174
|
+
responses: list[str] = []
|
|
175
|
+
|
|
176
|
+
# metrics for uploads
|
|
177
|
+
metrics: dict[str, int] = {
|
|
178
|
+
"total_requests": 0,
|
|
179
|
+
"failed_requests": 0,
|
|
180
|
+
"successful_requests": 0,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
latency: list[float] = []
|
|
184
|
+
|
|
185
|
+
def __init__(
|
|
186
|
+
self, cell: int, logger: int, sensors: list[str], _min=-1, _max=1, fn=sin
|
|
187
|
+
):
|
|
188
|
+
"""Initializes the simulation class.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
cell: Cell ID of the node.
|
|
192
|
+
logger: Logger ID of the node.
|
|
193
|
+
sensors: List of sensors to simulate.
|
|
194
|
+
_min: Minimum value for the simulated sensor data.
|
|
195
|
+
_max: Maximum value for the simulated sensor data.
|
|
196
|
+
fn: Function to generate the simulated sensor data.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
self.cell = cell
|
|
200
|
+
self.logger = logger
|
|
201
|
+
self.sensors = sensors
|
|
202
|
+
self.fn = fn
|
|
203
|
+
self._min = _min
|
|
204
|
+
self._max = _max
|
|
205
|
+
|
|
206
|
+
def __str__(self):
|
|
207
|
+
"""String representation of the simulation class
|
|
208
|
+
|
|
209
|
+
Shows the current upload metrics
|
|
210
|
+
"""
|
|
211
|
+
avg = np.array(self.latency).mean()
|
|
212
|
+
|
|
213
|
+
last = 0
|
|
214
|
+
if len(self.latency) > 0:
|
|
215
|
+
last = self.latency[-1]
|
|
216
|
+
|
|
217
|
+
return "total: {}, failed: {}, avg (ms): {}, last (ms): {}".format(
|
|
218
|
+
self.metrics["total_requests"],
|
|
219
|
+
self.metrics["failed_requests"],
|
|
220
|
+
avg * 100,
|
|
221
|
+
last * 100,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
def send_next(self, url: str) -> bool:
|
|
225
|
+
"""Sends measurements to a dirtviz instance
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
url: URL of the dirtviz instance
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
True if there are measurements to send, False otherwise
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
# get next measurement
|
|
235
|
+
try:
|
|
236
|
+
meas = self.measurements.pop()
|
|
237
|
+
except IndexError as _:
|
|
238
|
+
return False
|
|
239
|
+
|
|
240
|
+
headers = {"Content-Type": "application/octet-stream"}
|
|
241
|
+
result = requests.post(url, data=meas, headers=headers)
|
|
242
|
+
|
|
243
|
+
# store result
|
|
244
|
+
self.responses.append(result.text)
|
|
245
|
+
self.metrics["total_requests"] += 1
|
|
246
|
+
if result.status_code == 200:
|
|
247
|
+
self.metrics["successful_requests"] += 1
|
|
248
|
+
else:
|
|
249
|
+
self.metrics["failed_requests"] += 1
|
|
250
|
+
self.latency.append(result.elapsed.total_seconds())
|
|
251
|
+
|
|
252
|
+
return True
|
|
253
|
+
|
|
254
|
+
def measure(self, ts: int):
|
|
255
|
+
"""Simulate measurements
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
ts: Timestamp of the measurement
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
meas = {
|
|
262
|
+
"meta": {
|
|
263
|
+
"ts": ts,
|
|
264
|
+
"loggerId": self.logger,
|
|
265
|
+
"cellId": self.cell,
|
|
266
|
+
},
|
|
267
|
+
"measurements": [],
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
scale = (self._max - self._min) / 2
|
|
271
|
+
offset = (self._max + self._min) / 2
|
|
272
|
+
|
|
273
|
+
for s in self.sensors:
|
|
274
|
+
meas["measurements"].append(
|
|
275
|
+
{
|
|
276
|
+
"type": s,
|
|
277
|
+
"decimal": self.fn(ts) * scale + offset,
|
|
278
|
+
}
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
serialized = encode_repeated_sensor_measurements(meas)
|
|
282
|
+
self.measurement_buffer.append(serialized)
|
|
@@ -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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
ents/__init__.py,sha256=
|
|
2
|
-
ents/cli.py,sha256=
|
|
1
|
+
ents/__init__.py,sha256=IVe193OL25_F5AMMx272iGabJ1e4jPVNLFAJDktA0oU,706
|
|
2
|
+
ents/cli.py,sha256=rMZoyJ_FlA8OiYKpIoRK3vvCtFAgl5VoVGcLWcENtZE,24412
|
|
3
3
|
ents/calibrate/PingSMU.py,sha256=1iXozKaPE0ucgB3qsTI5mVk__n6pqC8Z2nV_GRfU2gA,1253
|
|
4
4
|
ents/calibrate/PingSPS.py,sha256=pNQN1ngWQ2Z7Q1DoWCIe4DN02hZfNOrKpLH7I0bAd8U,1876
|
|
5
5
|
ents/calibrate/README.md,sha256=ctdpfS5tN-PnDBn8sCTAn4gi0E-ZLUDXYnLZuqleDmM,109
|
|
@@ -10,17 +10,18 @@ ents/calibrate/recorder.py,sha256=fce5cW2DiCSOMQ-CSRL7raghbR2Yeg8Nx9obyY-0yOg,18
|
|
|
10
10
|
ents/config/README.md,sha256=SRtr5YuM64MSZQdy60F8Ib_AI2MR-MbbAdjS3jcjVD4,4447
|
|
11
11
|
ents/config/__init__.py,sha256=3eyHUKg-ZPx82h2CGiNyzaaI7Y7koEOUX9pzrlnVcJw,51
|
|
12
12
|
ents/config/adv_trace.py,sha256=hEDBNbVslJ2lJ-8tfJhPXxQMZN9Nylqv8RK42waRhsM,1290
|
|
13
|
-
ents/config/user_config.py,sha256=
|
|
14
|
-
ents/demo/demoPullRequests.py,sha256=YSTVa59puMtER8sjOppiMshUCDjdsIikXLNOM0U6zmU,3864
|
|
13
|
+
ents/config/user_config.py,sha256=evaM3Lb0IU_qFugEeRiXUCMiCUyEbtnRNTXayqE6Qig,39182
|
|
15
14
|
ents/proto/__init__.py,sha256=5THafGlxirjEUATIXB2EVpdOY5APEjztzOAHlRGNG2c,720
|
|
16
15
|
ents/proto/decode.py,sha256=_dn9Agv41YgUtMVHZ7l5-Y13ci3hnjyXrRPGKDIHzvo,2964
|
|
17
16
|
ents/proto/encode.py,sha256=kPAD5-bL6zNEjh6z1n1FusdM9z5UretypxHHAiuGGA4,7753
|
|
18
17
|
ents/proto/esp32.py,sha256=4QOHZNH1SqkJg4TLvJgrAbhdJg88ZNtqoMNLBcWUX2U,4657
|
|
19
|
-
ents/proto/
|
|
18
|
+
ents/proto/sensor.py,sha256=zdZXG-TtX5kpSc6aETOywHApM8xLMY_J2OO8oBlEqU4,7148
|
|
19
|
+
ents/proto/sensor_pb2.py,sha256=Kxe7ZMeaJwQbqv4MHWUK5_4LxTY8Ou1n1bXMDq4KfQ0,3673
|
|
20
|
+
ents/proto/soil_power_sensor_pb2.py,sha256=r7lcuo8A3YW-vsiR7gT0dvG2UMdolpMe3r8kGfKx54U,13442
|
|
20
21
|
ents/simulator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
ents/simulator/node.py,sha256=
|
|
22
|
-
ents-2.3.
|
|
23
|
-
ents-2.3.
|
|
24
|
-
ents-2.3.
|
|
25
|
-
ents-2.3.
|
|
26
|
-
ents-2.3.
|
|
22
|
+
ents/simulator/node.py,sha256=RRPsKKA8_7u0Ka-1hXzO_3gNj91heXIZaDxRaG0Nw_o,7797
|
|
23
|
+
ents-2.3.5.dist-info/METADATA,sha256=x8PN37a25YxVbtL2hZIF6CwaxJ1NVXGQ0xiGWLXQeQA,5258
|
|
24
|
+
ents-2.3.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
25
|
+
ents-2.3.5.dist-info/entry_points.txt,sha256=1U6ViZDTy4vv1jFp5RisBPC0NFqmtV3f18eUnWKX0Nk,95
|
|
26
|
+
ents-2.3.5.dist-info/licenses/LICENSE,sha256=2WLObzfS99jXXcPS5INqnmDVZ1nlO8Rj8mzQn2xkn14,1086
|
|
27
|
+
ents-2.3.5.dist-info/RECORD,,
|
ents/demo/demoPullRequests.py
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
from datetime import datetime, timezone
|
|
3
|
-
import pandas as pd
|
|
4
|
-
from tabulate import tabulate
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class DirtVizClient:
|
|
8
|
-
BASE_URL = "https://dirtviz.jlab.ucsc.edu/api/"
|
|
9
|
-
|
|
10
|
-
def __init__(self):
|
|
11
|
-
self.session = requests.Session()
|
|
12
|
-
|
|
13
|
-
def cell_from_name(self, name, start=None, end=None):
|
|
14
|
-
"""Get power data for a specific cell"""
|
|
15
|
-
|
|
16
|
-
def get_power_data(self, cell_id, start=None, end=None):
|
|
17
|
-
"""Get power data for a specific cell"""
|
|
18
|
-
endpoint = f"power/{cell_id}"
|
|
19
|
-
params = {}
|
|
20
|
-
|
|
21
|
-
if start and end:
|
|
22
|
-
params = {
|
|
23
|
-
"startTime": start.strftime("%a, %d %b %Y %H:%M:%S GMT"),
|
|
24
|
-
"endTime": end.strftime("%a, %d %b %Y %H:%M:%S GMT"),
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
response = self.session.get(f"{self.BASE_URL}{endpoint}", params=params)
|
|
28
|
-
response.raise_for_status()
|
|
29
|
-
return response.json()
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def format_data_display(df, cell_id):
|
|
33
|
-
"""Format the data output with timestamp as first column"""
|
|
34
|
-
|
|
35
|
-
# Ensure timestamp exists and is first column
|
|
36
|
-
if "timestamp" in df.columns:
|
|
37
|
-
cols = ["timestamp"] + [col for col in df.columns if col != "timestamp"]
|
|
38
|
-
df = df[cols]
|
|
39
|
-
|
|
40
|
-
# Format timestamp nicely
|
|
41
|
-
df["timestamp"] = pd.to_datetime(df["timestamp"])
|
|
42
|
-
df["timestamp"] = df["timestamp"].dt.strftime("%m-%d-%Y %H:%M:%S")
|
|
43
|
-
|
|
44
|
-
# Calculate statistics
|
|
45
|
-
stats = {
|
|
46
|
-
"Cell ID": cell_id,
|
|
47
|
-
"Time Range": (
|
|
48
|
-
f"{df['timestamp'].iloc[0]} to {df['timestamp'].iloc[-1]}"
|
|
49
|
-
if len(df) > 0
|
|
50
|
-
else "N/A"
|
|
51
|
-
),
|
|
52
|
-
"Data Points": len(df),
|
|
53
|
-
"Avg Voltage (mV)": f"{df['v'].mean():.2f}" if "v" in df.columns else "N/A",
|
|
54
|
-
"Max Voltage (mV)": f"{df['v'].max():.2f}" if "v" in df.columns else "N/A",
|
|
55
|
-
"Avg Current (µA)": f"{df['i'].mean():.2f}" if "i" in df.columns else "N/A",
|
|
56
|
-
"Avg Power (µW)": f"{df['p'].mean():.2f}" if "p" in df.columns else "N/A",
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
column_rename = {
|
|
60
|
-
"timestamp": "Measurement Times",
|
|
61
|
-
"v": "Voltage (mV)",
|
|
62
|
-
"i": "Current (µA)",
|
|
63
|
-
"p": "Power (µW)",
|
|
64
|
-
}
|
|
65
|
-
# Apply renaming
|
|
66
|
-
df = df.rename(columns=column_rename)
|
|
67
|
-
|
|
68
|
-
# Display header
|
|
69
|
-
print("\n" + "=" * 60)
|
|
70
|
-
print(f"CELL {cell_id} POWER DATA SUMMARY".center(60))
|
|
71
|
-
for key, value in stats.items():
|
|
72
|
-
print(f"• {key:<20}: {value}") # Display the summary information
|
|
73
|
-
print("=" * 60 + "\n")
|
|
74
|
-
|
|
75
|
-
# Display sample data with timestamp first
|
|
76
|
-
if len(df) > 0:
|
|
77
|
-
print("DATA BY TIMESTAMPS:")
|
|
78
|
-
print(
|
|
79
|
-
tabulate(
|
|
80
|
-
df,
|
|
81
|
-
headers="keys",
|
|
82
|
-
tablefmt="grid", # Changed to grid for better column alignment
|
|
83
|
-
stralign="center", # Right-align numbers
|
|
84
|
-
showindex=False,
|
|
85
|
-
numalign="center",
|
|
86
|
-
)
|
|
87
|
-
)
|
|
88
|
-
else:
|
|
89
|
-
print("No data available to display")
|
|
90
|
-
|
|
91
|
-
print("\n" + "=" * 80)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if __name__ == "__main__":
|
|
95
|
-
client = DirtVizClient()
|
|
96
|
-
|
|
97
|
-
try:
|
|
98
|
-
cell_id = 893 # Figure out how to do by name on DirtViz
|
|
99
|
-
start = datetime(2025, 8, 12, tzinfo=timezone.utc)
|
|
100
|
-
end = datetime.now(timezone.utc)
|
|
101
|
-
|
|
102
|
-
print(f"\nFetching power data for cell {cell_id}...")
|
|
103
|
-
data = client.get_power_data(cell_id, start, end)
|
|
104
|
-
|
|
105
|
-
if data:
|
|
106
|
-
df = pd.DataFrame(data)
|
|
107
|
-
format_data_display(df, cell_id)
|
|
108
|
-
|
|
109
|
-
# Save to CSV with timestamp first
|
|
110
|
-
# df.to_csv(f"cell_{cell_id}_power_data.csv", index=False)
|
|
111
|
-
# print(f"Data saved to cell_{cell_id}_power_data.csv")
|
|
112
|
-
else:
|
|
113
|
-
print("No data received for the specified time range.")
|
|
114
|
-
|
|
115
|
-
except requests.exceptions.HTTPError as e:
|
|
116
|
-
print(f"\nHTTP Error: {e}")
|
|
117
|
-
print(f"Response: {e.response.text[:500]}...")
|
|
118
|
-
except Exception as e:
|
|
119
|
-
print(f"\n⚠️ Unexpected error: {str(e)}")
|
|
File without changes
|
|
File without changes
|