egauge-python 0.9.8__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.
- egauge/ctid/__init__.py +7 -0
- egauge/ctid/bit_stuffer.py +65 -0
- egauge/ctid/ctid.py +967 -0
- egauge/ctid/encoder.py +436 -0
- egauge/ctid/intel_hex_encoder.py +98 -0
- egauge/ctid/waveform.py +299 -0
- egauge/examples/data/test-ctid-decoder.raw +0 -0
- egauge/examples/test_capture.py +77 -0
- egauge/examples/test_common.py +26 -0
- egauge/examples/test_ctid.py +89 -0
- egauge/examples/test_ctid_decoder.py +93 -0
- egauge/examples/test_local.py +201 -0
- egauge/examples/test_register.py +104 -0
- egauge/loggers.py +72 -0
- egauge/pyside/__init__.py +0 -0
- egauge/pyside/ansi2html.py +112 -0
- egauge/pyside/terminal.py +295 -0
- egauge/webapi/__init__.py +34 -0
- egauge/webapi/auth.py +364 -0
- egauge/webapi/cloud/__init__.py +30 -0
- egauge/webapi/cloud/credentials.py +86 -0
- egauge/webapi/cloud/credentials_dialog.py +58 -0
- egauge/webapi/cloud/gui/credentials_dialog.py +100 -0
- egauge/webapi/cloud/serial_number.py +276 -0
- egauge/webapi/device/__init__.py +38 -0
- egauge/webapi/device/capture.py +453 -0
- egauge/webapi/device/ctid_info.py +553 -0
- egauge/webapi/device/device.py +349 -0
- egauge/webapi/device/local.py +268 -0
- egauge/webapi/device/physical_quantity.py +439 -0
- egauge/webapi/device/physical_units.py +473 -0
- egauge/webapi/device/register.py +338 -0
- egauge/webapi/device/register_row.py +145 -0
- egauge/webapi/device/register_type.py +851 -0
- egauge/webapi/device/slop.py +334 -0
- egauge/webapi/device/virtual_register.py +353 -0
- egauge/webapi/error.py +34 -0
- egauge/webapi/json_api.py +332 -0
- egauge_python-0.9.8.dist-info/METADATA +148 -0
- egauge_python-0.9.8.dist-info/RECORD +44 -0
- egauge_python-0.9.8.dist-info/WHEEL +5 -0
- egauge_python-0.9.8.dist-info/entry_points.txt +2 -0
- egauge_python-0.9.8.dist-info/licenses/LICENSE +22 -0
- egauge_python-0.9.8.dist-info/top_level.txt +1 -0
egauge/ctid/encoder.py
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2018-2022, 2024 eGauge Systems LLC
|
|
3
|
+
# 1644 Conestoga St, Suite 2
|
|
4
|
+
# Boulder, CO 80301
|
|
5
|
+
# voice: 720-545-9767
|
|
6
|
+
# email: davidm@egauge.net
|
|
7
|
+
#
|
|
8
|
+
# All rights reserved.
|
|
9
|
+
#
|
|
10
|
+
# This code is the property of eGauge Systems LLC and may not be
|
|
11
|
+
# copied, modified, or disclosed without any prior and written
|
|
12
|
+
# permission from eGauge Systems LLC.
|
|
13
|
+
#
|
|
14
|
+
# MIT License
|
|
15
|
+
#
|
|
16
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
17
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
18
|
+
# in the Software without restriction, including without limitation the rights
|
|
19
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
20
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
21
|
+
# furnished to do so, subject to the following conditions:
|
|
22
|
+
#
|
|
23
|
+
# The above copyright notice and this permission notice shall be included in
|
|
24
|
+
# all copies or substantial portions of the Software.
|
|
25
|
+
#
|
|
26
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
27
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
28
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
29
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
30
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
31
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
32
|
+
# THE SOFTWARE.
|
|
33
|
+
#
|
|
34
|
+
import argparse
|
|
35
|
+
import sys
|
|
36
|
+
|
|
37
|
+
from intelhex import IntelHex
|
|
38
|
+
|
|
39
|
+
import egauge.ctid as CTid
|
|
40
|
+
|
|
41
|
+
CTid_table_addr = 0x3C0 # table goes in last 64 bytes
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def is_abbrev(abbrev: str, full: str) -> bool:
|
|
45
|
+
if not abbrev:
|
|
46
|
+
return False
|
|
47
|
+
if len(abbrev) > len(full):
|
|
48
|
+
return False
|
|
49
|
+
return abbrev == full[0 : len(abbrev)]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def auto_int(x: str) -> int:
|
|
53
|
+
"""Accept base-prefixed decimal."""
|
|
54
|
+
return int(x, 0)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def edge_mask(x: str) -> int:
|
|
58
|
+
try:
|
|
59
|
+
return int(x)
|
|
60
|
+
except ValueError:
|
|
61
|
+
pass
|
|
62
|
+
if is_abbrev(x, "rising"): # rising
|
|
63
|
+
return 0x1
|
|
64
|
+
if is_abbrev(x, "falling"):
|
|
65
|
+
return 0x2
|
|
66
|
+
if is_abbrev(x, "both"):
|
|
67
|
+
return 0x3
|
|
68
|
+
raise ValueError(
|
|
69
|
+
'Edge mask must be one of "rising", "falling", or "both".'
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def cal_table_entry(string: str) -> tuple[float, float, float]:
|
|
74
|
+
pair = string.split(":")
|
|
75
|
+
if len(pair) != 2:
|
|
76
|
+
raise argparse.ArgumentTypeError("missing `:'")
|
|
77
|
+
idx = float(pair[0])
|
|
78
|
+
if idx not in table.cal_table:
|
|
79
|
+
allowed = ", ".join(f"{k:g}" for k in table.cal_table)
|
|
80
|
+
raise argparse.ArgumentTypeError(
|
|
81
|
+
f"Invalid index {idx} (must be one of {allowed})."
|
|
82
|
+
)
|
|
83
|
+
val = pair[1].split("/")
|
|
84
|
+
if len(val) != 2:
|
|
85
|
+
raise argparse.ArgumentTypeError("missing `/'")
|
|
86
|
+
v_adj = float(val[0])
|
|
87
|
+
p_adj = float(val[1])
|
|
88
|
+
return (idx, v_adj, p_adj)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def hexdump(title: str, data: bytes):
|
|
92
|
+
print(title + ":", end="")
|
|
93
|
+
c = 0
|
|
94
|
+
for b in data:
|
|
95
|
+
if c % 8 == 0:
|
|
96
|
+
print(f"\n 0x{c:08x}:", end="")
|
|
97
|
+
print(f" 0x{b:02x}", end="")
|
|
98
|
+
c += 1
|
|
99
|
+
print("")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def main():
|
|
103
|
+
global table
|
|
104
|
+
table = CTid.Table()
|
|
105
|
+
|
|
106
|
+
parser = argparse.ArgumentParser(
|
|
107
|
+
description="Encode CTid parameters.",
|
|
108
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
109
|
+
)
|
|
110
|
+
parser.add_argument(
|
|
111
|
+
"program_template",
|
|
112
|
+
help="Filename of the program template.",
|
|
113
|
+
default=None,
|
|
114
|
+
)
|
|
115
|
+
parser.add_argument(
|
|
116
|
+
"-d", "--debug", type=int, default=0, help="Debug level"
|
|
117
|
+
)
|
|
118
|
+
parser.add_argument(
|
|
119
|
+
"-o",
|
|
120
|
+
"--output-file",
|
|
121
|
+
type=argparse.FileType("w"),
|
|
122
|
+
default=sys.stdout,
|
|
123
|
+
)
|
|
124
|
+
#
|
|
125
|
+
# Common information:
|
|
126
|
+
#
|
|
127
|
+
parser.add_argument(
|
|
128
|
+
"-S",
|
|
129
|
+
"--sensor-type",
|
|
130
|
+
default="AC",
|
|
131
|
+
choices=CTid.SENSOR_TYPE_NAME,
|
|
132
|
+
help="Sensor-type.",
|
|
133
|
+
)
|
|
134
|
+
parser.add_argument(
|
|
135
|
+
"-M",
|
|
136
|
+
"--manufacturer",
|
|
137
|
+
default="0",
|
|
138
|
+
help="Manufacturer name or ID.",
|
|
139
|
+
)
|
|
140
|
+
parser.add_argument(
|
|
141
|
+
"-m",
|
|
142
|
+
"--model",
|
|
143
|
+
default="",
|
|
144
|
+
help="Model name (up to 4 characters).",
|
|
145
|
+
)
|
|
146
|
+
parser.add_argument(
|
|
147
|
+
"-n",
|
|
148
|
+
"--serial-number",
|
|
149
|
+
type=auto_int,
|
|
150
|
+
default=0,
|
|
151
|
+
help="Serial number.",
|
|
152
|
+
)
|
|
153
|
+
parser.add_argument(
|
|
154
|
+
"-r",
|
|
155
|
+
"--r-source",
|
|
156
|
+
type=float,
|
|
157
|
+
default=0,
|
|
158
|
+
help="Source resistance in Ohms.",
|
|
159
|
+
)
|
|
160
|
+
parser.add_argument(
|
|
161
|
+
"-l",
|
|
162
|
+
"--r-load",
|
|
163
|
+
type=float,
|
|
164
|
+
default=0,
|
|
165
|
+
help="Rated load resistance in Ohms.",
|
|
166
|
+
)
|
|
167
|
+
parser.add_argument(
|
|
168
|
+
"-V",
|
|
169
|
+
"--table-version",
|
|
170
|
+
type=int,
|
|
171
|
+
default=None,
|
|
172
|
+
help="Version of the CTid table to generate.",
|
|
173
|
+
)
|
|
174
|
+
#
|
|
175
|
+
# CT Parameters:
|
|
176
|
+
#
|
|
177
|
+
parser.add_argument(
|
|
178
|
+
"-a",
|
|
179
|
+
"--calibration-entry",
|
|
180
|
+
type=cal_table_entry,
|
|
181
|
+
action="append",
|
|
182
|
+
help="Calibration-entry of the form X:VA/PA, where "
|
|
183
|
+
"X is the percentage of the rated current to which the "
|
|
184
|
+
"entry applies, VA is the voltage-adjustment in %% and "
|
|
185
|
+
"PA is the phase-adjustment in \u00b0.",
|
|
186
|
+
)
|
|
187
|
+
parser.add_argument(
|
|
188
|
+
"-c",
|
|
189
|
+
"--rated-current",
|
|
190
|
+
type=float,
|
|
191
|
+
default=100,
|
|
192
|
+
help="Rated current of CT in Ampère.",
|
|
193
|
+
)
|
|
194
|
+
parser.add_argument(
|
|
195
|
+
"-i",
|
|
196
|
+
"--manufacturer-info",
|
|
197
|
+
type=auto_int,
|
|
198
|
+
default=0,
|
|
199
|
+
help="Manufacturer-specific information.",
|
|
200
|
+
)
|
|
201
|
+
parser.add_argument(
|
|
202
|
+
"-p",
|
|
203
|
+
"--phase",
|
|
204
|
+
type=float,
|
|
205
|
+
default=0,
|
|
206
|
+
help="Phase at rated current in \u00b0.",
|
|
207
|
+
)
|
|
208
|
+
parser.add_argument(
|
|
209
|
+
"-s",
|
|
210
|
+
"--size",
|
|
211
|
+
type=float,
|
|
212
|
+
default=0,
|
|
213
|
+
help="Size of the sensor in millimeter.",
|
|
214
|
+
)
|
|
215
|
+
parser.add_argument(
|
|
216
|
+
"-t",
|
|
217
|
+
"--voltage-temp-coeff",
|
|
218
|
+
type=float,
|
|
219
|
+
default=0,
|
|
220
|
+
help="Voltage temperature coefficient in ppm/\u00b0C.",
|
|
221
|
+
)
|
|
222
|
+
parser.add_argument(
|
|
223
|
+
"-T",
|
|
224
|
+
"--phase-temp-coeff",
|
|
225
|
+
type=float,
|
|
226
|
+
default=0,
|
|
227
|
+
help="Phase temperature coefficient in m\u00b0/\u00b0C.",
|
|
228
|
+
)
|
|
229
|
+
parser.add_argument(
|
|
230
|
+
"-v",
|
|
231
|
+
"--rated-voltage",
|
|
232
|
+
type=float,
|
|
233
|
+
default=1 / 3.0,
|
|
234
|
+
help="Rated voltage of CT in Volt.",
|
|
235
|
+
)
|
|
236
|
+
parser.add_argument(
|
|
237
|
+
"-b",
|
|
238
|
+
"--bias-voltage",
|
|
239
|
+
type=float,
|
|
240
|
+
default=0,
|
|
241
|
+
help="Output voltage when no current is flowing through the CD.",
|
|
242
|
+
)
|
|
243
|
+
#
|
|
244
|
+
# Linear Parameters:
|
|
245
|
+
#
|
|
246
|
+
parser.add_argument("--scale", type=float, default=None, help="Scale.")
|
|
247
|
+
parser.add_argument("--offset", type=float, default=None, help="Offset.")
|
|
248
|
+
parser.add_argument(
|
|
249
|
+
"--delay", type=float, default=None, help="Signal delay in μs."
|
|
250
|
+
)
|
|
251
|
+
parser.add_argument(
|
|
252
|
+
"--unit",
|
|
253
|
+
type=int,
|
|
254
|
+
default=0,
|
|
255
|
+
help=f"Physical unit code (0..{len(CTid.SENSOR_UNITS) - 1}).",
|
|
256
|
+
)
|
|
257
|
+
#
|
|
258
|
+
# NTC Temperature Parameters:
|
|
259
|
+
#
|
|
260
|
+
parser.add_argument(
|
|
261
|
+
"--ntc-a", type=float, default=None, help="NTC A coefficient."
|
|
262
|
+
)
|
|
263
|
+
parser.add_argument(
|
|
264
|
+
"--ntc-b", type=float, default=None, help="NTC B coefficient."
|
|
265
|
+
)
|
|
266
|
+
parser.add_argument(
|
|
267
|
+
"--ntc-c", type=float, default=None, help="NTC C coefficient."
|
|
268
|
+
)
|
|
269
|
+
parser.add_argument(
|
|
270
|
+
"--ntc-m", type=float, default=None, help="NTC parameter M."
|
|
271
|
+
)
|
|
272
|
+
parser.add_argument(
|
|
273
|
+
"--ntc-n", type=float, default=None, help="NTC parameter N."
|
|
274
|
+
)
|
|
275
|
+
parser.add_argument(
|
|
276
|
+
"--ntc-k", type=float, default=None, help="NTC parameter K."
|
|
277
|
+
)
|
|
278
|
+
#
|
|
279
|
+
# Pulse Temperature Parameters:
|
|
280
|
+
#
|
|
281
|
+
parser.add_argument(
|
|
282
|
+
"--threshold",
|
|
283
|
+
type=float,
|
|
284
|
+
default=None,
|
|
285
|
+
help="Pulse threshold in Volts.",
|
|
286
|
+
)
|
|
287
|
+
parser.add_argument(
|
|
288
|
+
"--hysteresis",
|
|
289
|
+
type=float,
|
|
290
|
+
default=None,
|
|
291
|
+
help="Hysteresis for threhold in Volts.",
|
|
292
|
+
)
|
|
293
|
+
parser.add_argument(
|
|
294
|
+
"--debounce-time",
|
|
295
|
+
type=float,
|
|
296
|
+
default=None,
|
|
297
|
+
help="Debounce time for pulse in milli-seconds.",
|
|
298
|
+
)
|
|
299
|
+
parser.add_argument(
|
|
300
|
+
"--edge-mask",
|
|
301
|
+
type=edge_mask,
|
|
302
|
+
default=None,
|
|
303
|
+
help="Edge-mask. May be an integer or one of "
|
|
304
|
+
'"rising", "falling", or "both".',
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
args = parser.parse_args()
|
|
308
|
+
|
|
309
|
+
table_version = args.table_version
|
|
310
|
+
|
|
311
|
+
mfg_name = args.manufacturer
|
|
312
|
+
try:
|
|
313
|
+
mfg_id = CTid.get_mfg_id_for_name(mfg_name)
|
|
314
|
+
except CTid.Error as e:
|
|
315
|
+
print(
|
|
316
|
+
f'{parser.prog}: "{mfg_name}" is invalid: {e}',
|
|
317
|
+
file=sys.stderr,
|
|
318
|
+
)
|
|
319
|
+
sys.exit(1)
|
|
320
|
+
|
|
321
|
+
type_name = args.sensor_type
|
|
322
|
+
try:
|
|
323
|
+
sensor_type = CTid.get_sensor_type_id(type_name)
|
|
324
|
+
except CTid.Error as e:
|
|
325
|
+
print(
|
|
326
|
+
f'{parser.prog}: "{type_name}" is invalid: {e}',
|
|
327
|
+
file=sys.stderr,
|
|
328
|
+
)
|
|
329
|
+
sys.exit(1)
|
|
330
|
+
|
|
331
|
+
table.mfg_id = mfg_id
|
|
332
|
+
table.model = args.model
|
|
333
|
+
table.size = args.size
|
|
334
|
+
table.serial_number = args.serial_number
|
|
335
|
+
table.sensor_type = sensor_type
|
|
336
|
+
table.r_source = args.r_source
|
|
337
|
+
table.r_load = args.r_load
|
|
338
|
+
# CT parameters:
|
|
339
|
+
table.rated_current = args.rated_current
|
|
340
|
+
table.voltage_at_rated_current = args.rated_voltage
|
|
341
|
+
table.bias_voltage = args.bias_voltage
|
|
342
|
+
table.phase_at_rated_current = args.phase
|
|
343
|
+
table.voltage_temp_coeff = args.voltage_temp_coeff
|
|
344
|
+
table.phase_temp_coeff = args.phase_temp_coeff
|
|
345
|
+
if args.calibration_entry is not None:
|
|
346
|
+
for idx, v_adj, p_adj in args.calibration_entry:
|
|
347
|
+
table.cal_table[idx][0] = v_adj
|
|
348
|
+
table.cal_table[idx][1] = p_adj
|
|
349
|
+
|
|
350
|
+
# Voltage and temperature parameters:
|
|
351
|
+
|
|
352
|
+
if args.scale is not None:
|
|
353
|
+
table.scale = args.scale
|
|
354
|
+
if args.offset is not None:
|
|
355
|
+
table.offset = args.offset
|
|
356
|
+
if args.delay is not None:
|
|
357
|
+
table.delay = args.delay
|
|
358
|
+
if args.unit is not None:
|
|
359
|
+
table.sensor_unit = args.unit
|
|
360
|
+
|
|
361
|
+
# NTC parameters:
|
|
362
|
+
|
|
363
|
+
if args.ntc_a is not None:
|
|
364
|
+
table.ntc_a = args.ntc_a
|
|
365
|
+
if args.ntc_b is not None:
|
|
366
|
+
table.ntc_b = args.ntc_b
|
|
367
|
+
if args.ntc_c is not None:
|
|
368
|
+
table.ntc_c = args.ntc_c
|
|
369
|
+
if args.ntc_m is not None:
|
|
370
|
+
table.ntc_m = args.ntc_m
|
|
371
|
+
if args.ntc_n is not None:
|
|
372
|
+
table.ntc_n = args.ntc_n
|
|
373
|
+
if args.ntc_k is not None:
|
|
374
|
+
table.ntc_k = args.ntc_k
|
|
375
|
+
|
|
376
|
+
# Pulse parameters:
|
|
377
|
+
|
|
378
|
+
if args.threshold is not None:
|
|
379
|
+
table.threshold = args.threshold
|
|
380
|
+
if args.hysteresis is not None:
|
|
381
|
+
table.hysteresis = args.hysteresis
|
|
382
|
+
if args.debounce_time is not None:
|
|
383
|
+
table.debounce_time = args.debounce_time
|
|
384
|
+
if args.edge_mask is not None:
|
|
385
|
+
table.edge_mask = args.edge_mask
|
|
386
|
+
|
|
387
|
+
table.mfg_info = int(args.manufacturer_info)
|
|
388
|
+
|
|
389
|
+
if args.debug > 0:
|
|
390
|
+
print("CTid params:\n\t", table)
|
|
391
|
+
|
|
392
|
+
exception = None
|
|
393
|
+
try:
|
|
394
|
+
if table_version is not None:
|
|
395
|
+
table_data = table.encode(table_version)
|
|
396
|
+
else:
|
|
397
|
+
table_data = table.encode()
|
|
398
|
+
except CTid.Error as e:
|
|
399
|
+
table_data = None
|
|
400
|
+
exception = e
|
|
401
|
+
if table_version is None and CTid.CTID_VERSION < 6:
|
|
402
|
+
try:
|
|
403
|
+
table_data = table.encode(version=5)
|
|
404
|
+
print(f"{parser.prog}: using v5 encoding")
|
|
405
|
+
exception = None
|
|
406
|
+
except CTid.Error as e:
|
|
407
|
+
exception = e
|
|
408
|
+
if table_data is None:
|
|
409
|
+
print(f"{parser.prog}: {exception}", file=sys.stderr)
|
|
410
|
+
sys.exit(1)
|
|
411
|
+
|
|
412
|
+
if args.debug > 0:
|
|
413
|
+
hexdump("table", bytes((CTid.START_SYM,)) + table_data)
|
|
414
|
+
|
|
415
|
+
bitstream = CTid.bitstuff(table_data)
|
|
416
|
+
|
|
417
|
+
if args.debug > 0:
|
|
418
|
+
hexdump("bit stream", bitstream)
|
|
419
|
+
|
|
420
|
+
ihex = IntelHex(args.program_template)
|
|
421
|
+
|
|
422
|
+
max_addr = max(ihex.todict().keys())
|
|
423
|
+
if max_addr > CTid_table_addr:
|
|
424
|
+
print(
|
|
425
|
+
f"{parser.prog}: max address in program file "
|
|
426
|
+
f"0x{max_addr:x} is >= table address 0x{CTid_table_addr:x}"
|
|
427
|
+
)
|
|
428
|
+
sys.exit(1)
|
|
429
|
+
|
|
430
|
+
addr = CTid_table_addr
|
|
431
|
+
ihex[addr] = len(bitstream)
|
|
432
|
+
addr += 1
|
|
433
|
+
for i, byte in enumerate(bitstream):
|
|
434
|
+
ihex[addr + i] = byte
|
|
435
|
+
|
|
436
|
+
ihex.write_hex_file(args.output_file)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2024 eGauge Systems LLC
|
|
3
|
+
# 1644 Conestoga St, Suite 2
|
|
4
|
+
# Boulder, CO 80301
|
|
5
|
+
# voice: 720-545-9767
|
|
6
|
+
# email: davidm@egauge.net
|
|
7
|
+
#
|
|
8
|
+
# All rights reserved.
|
|
9
|
+
#
|
|
10
|
+
# This code is the property of eGauge Systems LLC and may not be
|
|
11
|
+
# copied, modified, or disclosed without any prior and written
|
|
12
|
+
# permission from eGauge Systems LLC.
|
|
13
|
+
#
|
|
14
|
+
# MIT License
|
|
15
|
+
#
|
|
16
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
17
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
18
|
+
# in the Software without restriction, including without limitation the rights
|
|
19
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
20
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
21
|
+
# furnished to do so, subject to the following conditions:
|
|
22
|
+
#
|
|
23
|
+
# The above copyright notice and this permission notice shall be included in
|
|
24
|
+
# all copies or substantial portions of the Software.
|
|
25
|
+
#
|
|
26
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
27
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
28
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
29
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
30
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
31
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
32
|
+
# THE SOFTWARE.
|
|
33
|
+
#
|
|
34
|
+
import typing
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
|
|
37
|
+
from intelhex import IntelHex
|
|
38
|
+
|
|
39
|
+
from . import ctid
|
|
40
|
+
|
|
41
|
+
# the default (ATtiny10) address at which the CTid table is stored:
|
|
42
|
+
CTID_TABLE_ADDR = 0x3C0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def intel_hex_encode(
|
|
46
|
+
program_image: Path | str | typing.IO | dict | IntelHex,
|
|
47
|
+
table: ctid.Table,
|
|
48
|
+
table_addr: int = CTID_TABLE_ADDR,
|
|
49
|
+
ctid_version: int = ctid.CTID_VERSION,
|
|
50
|
+
) -> bytes:
|
|
51
|
+
"""Encode a program image and a CTid table into an Intel hex
|
|
52
|
+
formatted file and return it as a byte stream.
|
|
53
|
+
|
|
54
|
+
This raises ctid.Error if there is a problem.
|
|
55
|
+
|
|
56
|
+
Required arguments:
|
|
57
|
+
|
|
58
|
+
program_image -- The program image to encode the CTid table into.
|
|
59
|
+
|
|
60
|
+
table -- The CTid table to encode.
|
|
61
|
+
|
|
62
|
+
Keyword arguments:
|
|
63
|
+
|
|
64
|
+
table_addr -- The base address at which to store the CTid table.
|
|
65
|
+
It is an error if the program_image already has content at the
|
|
66
|
+
addresses to be occupied by the CTid table (default 0x3c0).
|
|
67
|
+
|
|
68
|
+
ctid_version -- The CTid specification version to use when encoding
|
|
69
|
+
the table (default ctid.CTID_VERSION).
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
bitstream = ctid.bitstuff(table.encode(ctid_version))
|
|
73
|
+
bitstream_len = len(bitstream)
|
|
74
|
+
|
|
75
|
+
if bitstream_len > 255:
|
|
76
|
+
raise ctid.Error("CTid table too large", bitstream_len, 255)
|
|
77
|
+
|
|
78
|
+
if isinstance(program_image, Path):
|
|
79
|
+
program_image = str(program_image)
|
|
80
|
+
|
|
81
|
+
ihex = IntelHex(program_image)
|
|
82
|
+
|
|
83
|
+
max_addr = ihex.maxaddr()
|
|
84
|
+
if max_addr is None:
|
|
85
|
+
raise ctid.Error("program image is empty")
|
|
86
|
+
|
|
87
|
+
if max_addr >= table_addr:
|
|
88
|
+
raise ctid.Error(
|
|
89
|
+
"CTid table overlaps program image", max_addr, table_addr
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# the byte at table_addr stores the bitstream length:
|
|
93
|
+
ihex[table_addr] = bitstream_len
|
|
94
|
+
|
|
95
|
+
for i, byte in enumerate(bitstream):
|
|
96
|
+
ihex[table_addr + 1 + i] = byte
|
|
97
|
+
|
|
98
|
+
return ihex.tobinstr()
|