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.
Files changed (33) hide show
  1. {ents-2.3.4 → ents-2.3.5}/PKG-INFO +2 -2
  2. {ents-2.3.4 → ents-2.3.5}/pyproject.toml +2 -2
  3. {ents-2.3.4 → ents-2.3.5}/src/ents/__init__.py +4 -1
  4. {ents-2.3.4 → ents-2.3.5}/src/ents/cli.py +293 -5
  5. {ents-2.3.4 → ents-2.3.5}/src/ents/config/user_config.py +1 -4
  6. ents-2.3.5/src/ents/proto/sensor.py +290 -0
  7. ents-2.3.5/src/ents/proto/sensor_pb2.py +48 -0
  8. ents-2.3.5/src/ents/proto/soil_power_sensor_pb2.py +118 -0
  9. {ents-2.3.4 → ents-2.3.5}/src/ents/simulator/node.py +121 -0
  10. ents-2.3.5/tests/test_sensor.py +219 -0
  11. {ents-2.3.4 → ents-2.3.5}/tests/test_simulator.py +3 -0
  12. ents-2.3.4/src/ents/demo/demoPullRequests.py +0 -119
  13. ents-2.3.4/src/ents/proto/soil_power_sensor_pb2.py +0 -90
  14. {ents-2.3.4 → ents-2.3.5}/.gitignore +0 -0
  15. {ents-2.3.4 → ents-2.3.5}/LICENSE +0 -0
  16. {ents-2.3.4 → ents-2.3.5}/README.md +0 -0
  17. {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/PingSMU.py +0 -0
  18. {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/PingSPS.py +0 -0
  19. {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/README.md +0 -0
  20. {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/__init__.py +0 -0
  21. {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/linear_regression.py +0 -0
  22. {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/plots.py +0 -0
  23. {ents-2.3.4 → ents-2.3.5}/src/ents/calibrate/recorder.py +0 -0
  24. {ents-2.3.4 → ents-2.3.5}/src/ents/config/README.md +0 -0
  25. {ents-2.3.4 → ents-2.3.5}/src/ents/config/__init__.py +0 -0
  26. {ents-2.3.4 → ents-2.3.5}/src/ents/config/adv_trace.py +0 -0
  27. {ents-2.3.4 → ents-2.3.5}/src/ents/proto/__init__.py +0 -0
  28. {ents-2.3.4 → ents-2.3.5}/src/ents/proto/decode.py +0 -0
  29. {ents-2.3.4 → ents-2.3.5}/src/ents/proto/encode.py +0 -0
  30. {ents-2.3.4 → ents-2.3.5}/src/ents/proto/esp32.py +0 -0
  31. {ents-2.3.4 → ents-2.3.5}/src/ents/simulator/__init__.py +0 -0
  32. {ents-2.3.4 → ents-2.3.5}/tests/__init__.py +0 -0
  33. {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.4
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.31.1
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.4"
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.31.1',
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(title="Message type", dest="type")
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", help="PageCommand"
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(title="Message type", dest="type")
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 ents.proto import (
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):