ents 2.3.4__py3-none-any.whl → 2.3.6__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/calibrate/recorder.py +4 -4
- ents/cli.py +321 -6
- ents/config/user_config.py +1 -4
- ents/dirtviz/__init__.py +8 -0
- ents/dirtviz/client.py +223 -0
- ents/dirtviz/plots.py +50 -0
- 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 +159 -0
- {ents-2.3.4.dist-info → ents-2.3.6.dist-info}/METADATA +58 -104
- ents-2.3.6.dist-info/RECORD +30 -0
- {ents-2.3.4.dist-info → ents-2.3.6.dist-info}/WHEEL +1 -1
- ents/demo/demoPullRequests.py +0 -119
- ents-2.3.4.dist-info/RECORD +0 -26
- {ents-2.3.4.dist-info → ents-2.3.6.dist-info}/entry_points.txt +0 -0
- {ents-2.3.4.dist-info → ents-2.3.6.dist-info}/licenses/LICENSE +0 -0
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,160 @@ 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
|
+
# all requests in format (headers, body)
|
|
176
|
+
requests: list[tuple[str, str]] = []
|
|
177
|
+
|
|
178
|
+
# metrics for uploads
|
|
179
|
+
metrics: dict[str, int] = {
|
|
180
|
+
"total_requests": 0,
|
|
181
|
+
"failed_requests": 0,
|
|
182
|
+
"successful_requests": 0,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
latency: list[float] = []
|
|
186
|
+
|
|
187
|
+
def __init__(
|
|
188
|
+
self, cell: int, logger: int, sensors: list[str], _min=-1, _max=1, fn=sin
|
|
189
|
+
):
|
|
190
|
+
"""Initializes the simulation class.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
cell: Cell ID of the node.
|
|
194
|
+
logger: Logger ID of the node.
|
|
195
|
+
sensors: List of sensors to simulate.
|
|
196
|
+
_min: Minimum value for the simulated sensor data.
|
|
197
|
+
_max: Maximum value for the simulated sensor data.
|
|
198
|
+
fn: Function to generate the simulated sensor data.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
self.cell = cell
|
|
202
|
+
self.logger = logger
|
|
203
|
+
self.sensors = sensors
|
|
204
|
+
self.fn = fn
|
|
205
|
+
self._min = _min
|
|
206
|
+
self._max = _max
|
|
207
|
+
|
|
208
|
+
def __str__(self):
|
|
209
|
+
"""String representation of the simulation class
|
|
210
|
+
|
|
211
|
+
Shows the current upload metrics
|
|
212
|
+
"""
|
|
213
|
+
avg = np.array(self.latency).mean()
|
|
214
|
+
|
|
215
|
+
last = 0
|
|
216
|
+
if len(self.latency) > 0:
|
|
217
|
+
last = self.latency[-1]
|
|
218
|
+
|
|
219
|
+
return "total: {}, failed: {}, avg (ms): {}, last (ms): {}".format(
|
|
220
|
+
self.metrics["total_requests"],
|
|
221
|
+
self.metrics["failed_requests"],
|
|
222
|
+
avg * 100,
|
|
223
|
+
last * 100,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def send_next(self, url: str) -> bool:
|
|
227
|
+
"""Sends measurements to a dirtviz instance
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
url: URL of the dirtviz instance
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
True if there are measurements to send, False otherwise
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
# get next measurement
|
|
237
|
+
try:
|
|
238
|
+
meas = self.measurement_buffer.pop()
|
|
239
|
+
except IndexError as _:
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
headers = {
|
|
243
|
+
"Content-Type": "application/octet-stream",
|
|
244
|
+
"SensorVersion": "2",
|
|
245
|
+
}
|
|
246
|
+
result = requests.post(url, data=meas, headers=headers)
|
|
247
|
+
|
|
248
|
+
# store result
|
|
249
|
+
self.requests.append((result.request.headers, result.request.body))
|
|
250
|
+
self.responses.append(result.text)
|
|
251
|
+
self.metrics["total_requests"] += 1
|
|
252
|
+
if result.status_code == 200:
|
|
253
|
+
self.metrics["successful_requests"] += 1
|
|
254
|
+
else:
|
|
255
|
+
self.metrics["failed_requests"] += 1
|
|
256
|
+
self.latency.append(result.elapsed.total_seconds())
|
|
257
|
+
|
|
258
|
+
return True
|
|
259
|
+
|
|
260
|
+
def measure(self, ts: int):
|
|
261
|
+
"""Simulate measurements
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
ts: Timestamp of the measurement
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
meas = {
|
|
268
|
+
"meta": {
|
|
269
|
+
"ts": ts,
|
|
270
|
+
"loggerId": self.logger,
|
|
271
|
+
"cellId": self.cell,
|
|
272
|
+
},
|
|
273
|
+
"measurements": [],
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
scale = (self._max - self._min) / 2
|
|
277
|
+
offset = (self._max + self._min) / 2
|
|
278
|
+
|
|
279
|
+
for s in self.sensors:
|
|
280
|
+
meas["measurements"].append(
|
|
281
|
+
{
|
|
282
|
+
"type": s,
|
|
283
|
+
"decimal": self.fn(ts) * scale + offset,
|
|
284
|
+
}
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
serialized = encode_repeated_sensor_measurements(meas)
|
|
288
|
+
self.measurements.append(serialized)
|
|
289
|
+
self.measurement_buffer.append(serialized)
|
|
290
|
+
|
|
291
|
+
def last_measurement(self) -> bytes:
|
|
292
|
+
"""Gets the last encoded measurement.
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Last encoded measurement.
|
|
297
|
+
"""
|
|
298
|
+
|
|
299
|
+
return self.measurements[-1]
|
|
300
|
+
|
|
301
|
+
def last_request(self) -> str:
|
|
302
|
+
"""Gets the last sent request.
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Formatted headers and body.
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
headers = self.requests[-1][0]
|
|
309
|
+
body = self.requests[-1][1]
|
|
310
|
+
request_str = f"{headers}\n\n{body}"
|
|
311
|
+
return request_str
|
|
312
|
+
|
|
313
|
+
def last_response(self) -> str:
|
|
314
|
+
"""Gets the last response.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Response from server.
|
|
318
|
+
"""
|
|
319
|
+
|
|
320
|
+
return self.responses[-1]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ents
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.6
|
|
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.4
|
|
16
16
|
Requires-Dist: pyserial
|
|
17
17
|
Requires-Dist: requests
|
|
18
18
|
Requires-Dist: scikit-learn
|
|
@@ -34,14 +34,14 @@ The soil power sensor protobuf protocol is implemented as a Python package that
|
|
|
34
34
|
Use the following to install the `ents` package with gui via `pip`:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
pip install ents
|
|
37
|
+
pip install ents
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
You can also install the package from source with the following:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
43
|
# install package
|
|
44
|
-
pip install .
|
|
44
|
+
pip install .
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
If you are planning to develop the package we recommend you install the package
|
|
@@ -51,114 +51,68 @@ reinstall it.
|
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
53
|
# install development dependencies
|
|
54
|
-
pip install -e .[
|
|
54
|
+
pip install -e .[dev]
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
To install the *deprecated* user config gui, use the following: =
|
|
58
|
+
```bash
|
|
59
|
+
pip install -e ents[gui]
|
|
60
|
+
```
|
|
58
61
|
|
|
59
|
-
The following example code demonstrates decoding the measurement message and encoding a response.
|
|
60
62
|
|
|
61
|
-
```python
|
|
62
|
-
from ents import encode, decode
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
data = ...
|
|
64
|
+
## Simulator (New)
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
The webserver `tools/http_decoder.py` can be used to decode uploaded measurements.
|
|
67
|
+
|
|
68
|
+
### CLI Usage
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
usage: ents sim_generic [-h] [-v] [--url URL] --sensor SENSOR [SENSOR ...] [--min MIN] [--max MAX] --cell CELL --logger LOGGER [--start START] [--end END] [--freq FREQ] {batch,stream}
|
|
72
|
+
|
|
73
|
+
positional arguments:
|
|
74
|
+
{batch,stream} Upload mode
|
|
75
|
+
|
|
76
|
+
options:
|
|
77
|
+
-h, --help show this help message and exit
|
|
78
|
+
-v, --verbose Print addiitional request information.
|
|
79
|
+
--url URL URL of the dirtviz instance (default: http://localhost:8000)
|
|
80
|
+
--sensor SENSOR [SENSOR ...]
|
|
81
|
+
Type of sensor to simulate
|
|
82
|
+
--min MIN Minimum sensor value (default: -1.0)
|
|
83
|
+
--max MAX Maximum sensor value (default: 1.0)
|
|
84
|
+
--cell CELL Cell Id
|
|
85
|
+
--logger LOGGER Logger Id
|
|
86
|
+
|
|
87
|
+
Batch:
|
|
88
|
+
--start START Start date
|
|
89
|
+
--end END End date
|
|
90
|
+
|
|
91
|
+
Stream:
|
|
92
|
+
--freq FREQ Frequency of uploads (default: 10s)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Examples
|
|
96
|
+
|
|
97
|
+
You can find the available sensors in the `sensors.proto` file.
|
|
98
|
+
|
|
99
|
+
Example uploading single measurement
|
|
100
|
+
```
|
|
101
|
+
ents sim_generic stream --sensor POWER_VOLTAGE --min 20 --max 30 --cell 1 --logger 1
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Example uploading multiple measuremnets
|
|
105
|
+
```
|
|
106
|
+
ents sim_generic stream --sensor TEROS12_VWC_ADJ TEROS12_TEMP TEROS12_EC --min 10 --max 100 --cell 1 --logger 1
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Example batch uploads
|
|
110
|
+
```
|
|
111
|
+
ents sim_generic batch --sensor POWER_CURRENT --cell 1 --logger 1 --start 2026-01-19 --end 2026-01-20 --freq 60
|
|
112
|
+
```
|
|
68
113
|
|
|
69
|
-
# process data
|
|
70
|
-
...
|
|
71
114
|
|
|
72
|
-
|
|
73
|
-
resp_str = encode(success=True)
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
The formatting of the dictionary depends on the type of measurement sent. The key `type` is included on all measurement types and can be used to determine the type of message. See the source `*.proto` files to get the full list of types to get the full list of types and keys. A list is provided in [Message Types](#message-types). The Python protobuf API uses camel case when naming keys. The key `ts` is in ISO 8601 format as a string.
|
|
77
|
-
|
|
78
|
-
## Message Types
|
|
79
|
-
|
|
80
|
-
Type `power`
|
|
81
|
-
```python
|
|
82
|
-
meas_dict = {
|
|
83
|
-
"type": "power",
|
|
84
|
-
"loggerId": ...,
|
|
85
|
-
"cellId": ...,
|
|
86
|
-
"ts": ...,
|
|
87
|
-
"data": {
|
|
88
|
-
"voltage": ...,
|
|
89
|
-
"current": ...
|
|
90
|
-
},
|
|
91
|
-
"data_type": {
|
|
92
|
-
"voltage": float,
|
|
93
|
-
"voltage": float
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Type `teros12`
|
|
99
|
-
```python
|
|
100
|
-
meas_dict = {
|
|
101
|
-
"type": "teros12",
|
|
102
|
-
"loggerId": ...,
|
|
103
|
-
"cellId": ...,
|
|
104
|
-
"ts": ...,
|
|
105
|
-
"data": {
|
|
106
|
-
"vwcRaw": ...,
|
|
107
|
-
"vwcAdj": ...,
|
|
108
|
-
"temp": ...,
|
|
109
|
-
"ec": ...
|
|
110
|
-
},
|
|
111
|
-
"data_type": {
|
|
112
|
-
"vwcRaw": float,
|
|
113
|
-
"vwcAdj": float,
|
|
114
|
-
"temp": float,
|
|
115
|
-
"ec": int
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Type `bme280` with `raw=True` (default)
|
|
121
|
-
```python
|
|
122
|
-
meas_dict = {
|
|
123
|
-
"type": "bme280",
|
|
124
|
-
"loggerId": ...,
|
|
125
|
-
"cellId": ...,
|
|
126
|
-
"ts": ...,
|
|
127
|
-
"data": {
|
|
128
|
-
"pressure": ...,
|
|
129
|
-
"temperature": ...,
|
|
130
|
-
"humidity": ...,
|
|
131
|
-
},
|
|
132
|
-
"data_type": {
|
|
133
|
-
"pressure": int,
|
|
134
|
-
"temperature": int,
|
|
135
|
-
"humidity": int,
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
Type `bme280` with `raw=False`
|
|
141
|
-
```python
|
|
142
|
-
meas_dict = {
|
|
143
|
-
"type": "bme280",
|
|
144
|
-
"loggerId": ...,
|
|
145
|
-
"cellId": ...,
|
|
146
|
-
"ts": ...,
|
|
147
|
-
"data": {
|
|
148
|
-
"pressure": ...,
|
|
149
|
-
"temperature": ...,
|
|
150
|
-
"humidity": ...,
|
|
151
|
-
},
|
|
152
|
-
"data_type": {
|
|
153
|
-
"pressure": float,
|
|
154
|
-
"temperature": float,
|
|
155
|
-
"humidity": float,
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
## Simulator
|
|
115
|
+
## Simulator (Old)
|
|
162
116
|
|
|
163
117
|
Simulate WiFi sensor uploads without requiring ENTS hardware.
|
|
164
118
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
ents/__init__.py,sha256=IVe193OL25_F5AMMx272iGabJ1e4jPVNLFAJDktA0oU,706
|
|
2
|
+
ents/cli.py,sha256=pQQTnj0B-OwBnk3d8E4wY03EM9XVDcmolsBvK13nHqg,25029
|
|
3
|
+
ents/calibrate/PingSMU.py,sha256=1iXozKaPE0ucgB3qsTI5mVk__n6pqC8Z2nV_GRfU2gA,1253
|
|
4
|
+
ents/calibrate/PingSPS.py,sha256=pNQN1ngWQ2Z7Q1DoWCIe4DN02hZfNOrKpLH7I0bAd8U,1876
|
|
5
|
+
ents/calibrate/README.md,sha256=ctdpfS5tN-PnDBn8sCTAn4gi0E-ZLUDXYnLZuqleDmM,109
|
|
6
|
+
ents/calibrate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
ents/calibrate/linear_regression.py,sha256=U-m55mRn-kMj9m-lP-YsbVwIU5ZuA8PiHfNiibefokY,1717
|
|
8
|
+
ents/calibrate/plots.py,sha256=RmuvyL_oTxA4oMNqDR5k82XelvF6whRfYKXNmFbeYeo,2411
|
|
9
|
+
ents/calibrate/recorder.py,sha256=jTSaXUAKHSy_O6g1N7LJQxbZnpXIWZySuNxOKgx5xFo,18479
|
|
10
|
+
ents/config/README.md,sha256=SRtr5YuM64MSZQdy60F8Ib_AI2MR-MbbAdjS3jcjVD4,4447
|
|
11
|
+
ents/config/__init__.py,sha256=3eyHUKg-ZPx82h2CGiNyzaaI7Y7koEOUX9pzrlnVcJw,51
|
|
12
|
+
ents/config/adv_trace.py,sha256=hEDBNbVslJ2lJ-8tfJhPXxQMZN9Nylqv8RK42waRhsM,1290
|
|
13
|
+
ents/config/user_config.py,sha256=evaM3Lb0IU_qFugEeRiXUCMiCUyEbtnRNTXayqE6Qig,39182
|
|
14
|
+
ents/dirtviz/__init__.py,sha256=GLjair60tPxGePVmkifOnfDeMEryDe1HOwVXzOXGrBI,117
|
|
15
|
+
ents/dirtviz/client.py,sha256=LukuhBWFt4n6Z__BbIlWPxaALSrrxaJkp_kS_pvHcxI,5629
|
|
16
|
+
ents/dirtviz/plots.py,sha256=Op_sROjZow5b-eXWz1IQU-ZS8XulCvorbIW4pLWp2lY,982
|
|
17
|
+
ents/proto/__init__.py,sha256=5THafGlxirjEUATIXB2EVpdOY5APEjztzOAHlRGNG2c,720
|
|
18
|
+
ents/proto/decode.py,sha256=_dn9Agv41YgUtMVHZ7l5-Y13ci3hnjyXrRPGKDIHzvo,2964
|
|
19
|
+
ents/proto/encode.py,sha256=kPAD5-bL6zNEjh6z1n1FusdM9z5UretypxHHAiuGGA4,7753
|
|
20
|
+
ents/proto/esp32.py,sha256=4QOHZNH1SqkJg4TLvJgrAbhdJg88ZNtqoMNLBcWUX2U,4657
|
|
21
|
+
ents/proto/sensor.py,sha256=zdZXG-TtX5kpSc6aETOywHApM8xLMY_J2OO8oBlEqU4,7148
|
|
22
|
+
ents/proto/sensor_pb2.py,sha256=Kxe7ZMeaJwQbqv4MHWUK5_4LxTY8Ou1n1bXMDq4KfQ0,3673
|
|
23
|
+
ents/proto/soil_power_sensor_pb2.py,sha256=r7lcuo8A3YW-vsiR7gT0dvG2UMdolpMe3r8kGfKx54U,13442
|
|
24
|
+
ents/simulator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
+
ents/simulator/node.py,sha256=I3NQXK4xipJKQNmRjsGrt1hUGFiS0US0WgrnXWLHsmI,8726
|
|
26
|
+
ents-2.3.6.dist-info/METADATA,sha256=eVW-dnErw-JoWaAvatBeiH9SMD_HLBpt8mtUgnEuBWM,4956
|
|
27
|
+
ents-2.3.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
28
|
+
ents-2.3.6.dist-info/entry_points.txt,sha256=1U6ViZDTy4vv1jFp5RisBPC0NFqmtV3f18eUnWKX0Nk,95
|
|
29
|
+
ents-2.3.6.dist-info/licenses/LICENSE,sha256=2WLObzfS99jXXcPS5INqnmDVZ1nlO8Rj8mzQn2xkn14,1086
|
|
30
|
+
ents-2.3.6.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)}")
|
ents-2.3.4.dist-info/RECORD
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
ents/__init__.py,sha256=KyqH3dz_BdFHWxD9n4DFItgsrxO7rDtt2IObWkdLsns,582
|
|
2
|
-
ents/cli.py,sha256=O071Ot2u7yeMFZyoOY--hp4UepXOHEX2nNiiQ5TxpI4,16573
|
|
3
|
-
ents/calibrate/PingSMU.py,sha256=1iXozKaPE0ucgB3qsTI5mVk__n6pqC8Z2nV_GRfU2gA,1253
|
|
4
|
-
ents/calibrate/PingSPS.py,sha256=pNQN1ngWQ2Z7Q1DoWCIe4DN02hZfNOrKpLH7I0bAd8U,1876
|
|
5
|
-
ents/calibrate/README.md,sha256=ctdpfS5tN-PnDBn8sCTAn4gi0E-ZLUDXYnLZuqleDmM,109
|
|
6
|
-
ents/calibrate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
ents/calibrate/linear_regression.py,sha256=U-m55mRn-kMj9m-lP-YsbVwIU5ZuA8PiHfNiibefokY,1717
|
|
8
|
-
ents/calibrate/plots.py,sha256=RmuvyL_oTxA4oMNqDR5k82XelvF6whRfYKXNmFbeYeo,2411
|
|
9
|
-
ents/calibrate/recorder.py,sha256=fce5cW2DiCSOMQ-CSRL7raghbR2Yeg8Nx9obyY-0yOg,18416
|
|
10
|
-
ents/config/README.md,sha256=SRtr5YuM64MSZQdy60F8Ib_AI2MR-MbbAdjS3jcjVD4,4447
|
|
11
|
-
ents/config/__init__.py,sha256=3eyHUKg-ZPx82h2CGiNyzaaI7Y7koEOUX9pzrlnVcJw,51
|
|
12
|
-
ents/config/adv_trace.py,sha256=hEDBNbVslJ2lJ-8tfJhPXxQMZN9Nylqv8RK42waRhsM,1290
|
|
13
|
-
ents/config/user_config.py,sha256=SAudV12wyE-5Z0weZJQzBELRXR3-oevIgbrimUF2TmI,39198
|
|
14
|
-
ents/demo/demoPullRequests.py,sha256=YSTVa59puMtER8sjOppiMshUCDjdsIikXLNOM0U6zmU,3864
|
|
15
|
-
ents/proto/__init__.py,sha256=5THafGlxirjEUATIXB2EVpdOY5APEjztzOAHlRGNG2c,720
|
|
16
|
-
ents/proto/decode.py,sha256=_dn9Agv41YgUtMVHZ7l5-Y13ci3hnjyXrRPGKDIHzvo,2964
|
|
17
|
-
ents/proto/encode.py,sha256=kPAD5-bL6zNEjh6z1n1FusdM9z5UretypxHHAiuGGA4,7753
|
|
18
|
-
ents/proto/esp32.py,sha256=4QOHZNH1SqkJg4TLvJgrAbhdJg88ZNtqoMNLBcWUX2U,4657
|
|
19
|
-
ents/proto/soil_power_sensor_pb2.py,sha256=sNf-97udroXqmimooH0x1IWOTSm-4IoRsFzbyb9aLRg,9491
|
|
20
|
-
ents/simulator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
ents/simulator/node.py,sha256=vDdt_Q2BiEo1Z-aMZnm9_Y-aAtPU_lhcWW-xSDYR54c,4444
|
|
22
|
-
ents-2.3.4.dist-info/METADATA,sha256=FMxd6PnezpUeq3ZBVty08iRL-CzgVo5YTugULlzxHZ4,5258
|
|
23
|
-
ents-2.3.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
24
|
-
ents-2.3.4.dist-info/entry_points.txt,sha256=1U6ViZDTy4vv1jFp5RisBPC0NFqmtV3f18eUnWKX0Nk,95
|
|
25
|
-
ents-2.3.4.dist-info/licenses/LICENSE,sha256=2WLObzfS99jXXcPS5INqnmDVZ1nlO8Rj8mzQn2xkn14,1086
|
|
26
|
-
ents-2.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|