oep-client-python 0.0.37__py3-none-any.whl → 0.0.39__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.
- oep_client/__init__.py +1 -1
- oep_client/__main__.py +10 -10
- oep_client/core.py +0 -17
- oep_client/endpoint.py +60 -33
- oep_client/pins.py +21 -7
- oep_client/registry.py +3 -3
- oep_client/settings.py +61 -54
- oep_client/virtual_bench.py +11 -12
- {oep_client_python-0.0.37.dist-info → oep_client_python-0.0.39.dist-info}/METADATA +12 -11
- {oep_client_python-0.0.37.dist-info → oep_client_python-0.0.39.dist-info}/RECORD +13 -13
- {oep_client_python-0.0.37.dist-info → oep_client_python-0.0.39.dist-info}/WHEEL +0 -0
- {oep_client_python-0.0.37.dist-info → oep_client_python-0.0.39.dist-info}/entry_points.txt +0 -0
- {oep_client_python-0.0.37.dist-info → oep_client_python-0.0.39.dist-info}/licenses/LICENSE +0 -0
oep_client/__init__.py
CHANGED
oep_client/__main__.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""The oep command: what a probe offers (dump) and its settings (settings, core §13).
|
|
2
2
|
|
|
3
3
|
oep dump --port /run/board-identify/by-id/<probe> oep dump --virtual p4-x035 --prefix oep.target --json
|
|
4
|
-
oep settings show <probe> (every value
|
|
4
|
+
oep settings show <probe> (every value, the storage state; lock-free)
|
|
5
5
|
oep settings items <probe> (what each owner declares: names, forms, choices)
|
|
6
|
-
oep settings set <probe> core channel.GPIO23.label=nrst channel.GPIO20.idle=pull-up [--save]
|
|
6
|
+
oep settings set <probe> core channel.GPIO23.label=v003.nrst channel.GPIO20.idle=pull-up [--save]
|
|
7
7
|
oep settings unset <probe> core channel.GPIO20.idle oep settings unset --all <probe> --save (empties the storage)
|
|
8
8
|
oep settings save <probe> oep settings state <probe> oep settings network <probe>
|
|
9
9
|
oep settings export <probe> jig.oep-settings.json oep settings import <probe> jig.oep-settings.json [--save]
|
|
@@ -336,18 +336,18 @@ def _settings_parser(sub) -> None:
|
|
|
336
336
|
c = sub.add_parser("settings", help="the probe's settings (core §13): show, set / unset by name, save, export / "
|
|
337
337
|
"import a settings file, the network")
|
|
338
338
|
cs = c.add_subparsers(dest="action", required=True)
|
|
339
|
-
show = cs.add_parser("show", help="every value (category, name, value
|
|
339
|
+
show = cs.add_parser("show", help="every value (category, name, value) and the storage state (lock-free)")
|
|
340
340
|
show.add_argument("probe")
|
|
341
341
|
show.add_argument("--json", action="store_true")
|
|
342
342
|
items = cs.add_parser("items", help="what the probe declares: each category's items, forms and choices")
|
|
343
343
|
items.add_argument("probe")
|
|
344
344
|
items.add_argument("--json", action="store_true")
|
|
345
|
-
st = cs.add_parser("set", help="values by name in one category: NAME=VALUE ... (core channel.GPIO2.label=nrst)")
|
|
345
|
+
st = cs.add_parser("set", help="values by name in one category: NAME=VALUE ... (core channel.GPIO2.label=v003.nrst)")
|
|
346
346
|
st.add_argument("probe")
|
|
347
347
|
st.add_argument("category", help="core, or an interface's name/instance (oep.fixture.uart/0)")
|
|
348
348
|
st.add_argument("values", nargs="+", metavar="NAME=VALUE")
|
|
349
349
|
st.add_argument("--save", action="store_true")
|
|
350
|
-
un = cs.add_parser("unset", help="values by name
|
|
350
|
+
un = cs.add_parser("unset", help="remove values by name (the item has none after)")
|
|
351
351
|
un.add_argument("probe")
|
|
352
352
|
un.add_argument("category", nargs="?")
|
|
353
353
|
un.add_argument("names", nargs="*")
|
|
@@ -360,10 +360,10 @@ def _settings_parser(sub) -> None:
|
|
|
360
360
|
net = cs.add_parser("network", help="the network now: the TCP port, each netif's state and address (lock-free)")
|
|
361
361
|
net.add_argument("probe")
|
|
362
362
|
net.add_argument("--json", action="store_true")
|
|
363
|
-
ex = cs.add_parser("export", help="write
|
|
363
|
+
ex = cs.add_parser("export", help="write every value to a settings file (JSON; a secret as present only)")
|
|
364
364
|
ex.add_argument("probe")
|
|
365
365
|
ex.add_argument("file", help="the file to write, - for stdout")
|
|
366
|
-
im = cs.add_parser("import", help="make the probe's
|
|
366
|
+
im = cs.add_parser("import", help="make the probe's values a settings file's (shows what changes first)")
|
|
367
367
|
im.add_argument("probe")
|
|
368
368
|
im.add_argument("file")
|
|
369
369
|
im.add_argument("--yes", action="store_true", help="apply without asking")
|
|
@@ -520,16 +520,16 @@ def _show(st, as_json: bool) -> int:
|
|
|
520
520
|
state = st.state() if st.offers("state") else None
|
|
521
521
|
if as_json:
|
|
522
522
|
print(json.dumps({"generation": gen, "state": vars(state) if state else None,
|
|
523
|
-
"values": [dict(category=c, name=n, value=v
|
|
523
|
+
"values": [dict(category=c, name=n, value=v) for c, n, v in rows]}, indent=2))
|
|
524
524
|
return 0
|
|
525
525
|
if state is not None:
|
|
526
526
|
why = f" ({state.reason})" if state.reason else ""
|
|
527
527
|
print(f"storage: {state.storage}{why}, saved generation {state.saved_generation}; now {gen}")
|
|
528
528
|
else:
|
|
529
529
|
print(f"no storage; generation {gen}")
|
|
530
|
-
for c, n, v
|
|
530
|
+
for c, n, v in rows:
|
|
531
531
|
shown = "(secret)" if isinstance(v, dict) else json.dumps(v)
|
|
532
|
-
print(f" {c} {n} = {shown}"
|
|
532
|
+
print(f" {c} {n} = {shown}")
|
|
533
533
|
return 0
|
|
534
534
|
|
|
535
535
|
|
oep_client/core.py
CHANGED
|
@@ -170,23 +170,6 @@ def require(hst: h.Host, fn: int, op: int) -> None:
|
|
|
170
170
|
raise not_offered(fn, op)
|
|
171
171
|
|
|
172
172
|
|
|
173
|
-
def firmware_labels(hst: h.Host) -> list[tuple[int, str]]:
|
|
174
|
-
"""The labels the firmware fixes (channel.{}.label of origin fixed, core §13.4) as (channel, text), in get's order.
|
|
175
|
-
A probe without the settings: none."""
|
|
176
|
-
from . import settings
|
|
177
|
-
if not offers(hst, 0, settings.OP["get"]):
|
|
178
|
-
return []
|
|
179
|
-
st = settings.Settings(hst)
|
|
180
|
-
cat = st.catalog()
|
|
181
|
-
return [(v.key, m.shown(v.data)) for v in st.get()[1] if v.fn == 0 and v.origin == "fixed"
|
|
182
|
-
and (cat.item(0, v.number) or settings.Item(0, 0, "", "", "", 0, 0, 0)).name == settings.LABEL]
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
def probe_labels(hst: h.Host) -> dict[str, int]:
|
|
186
|
-
"""The firmware's fixed channel labels: {"NRST": 23, ...}."""
|
|
187
|
-
return {text: ch for ch, text in firmware_labels(hst)}
|
|
188
|
-
|
|
189
|
-
|
|
190
173
|
def max_op_ms(hst: h.Host) -> int:
|
|
191
174
|
"""The longest one request may take on this probe (fn 0's describe max_op_ms, core §7.5): the ceiling of run's
|
|
192
175
|
timeout_ms, a dmi list's waits, an attach's hold_ms, and the argument time of attach, scan and riscv-dm's reset
|
oep_client/endpoint.py
CHANGED
|
@@ -77,7 +77,8 @@ define (oep-spec a09fa45):
|
|
|
77
77
|
oep.fixture.spi-target (§4: configure, arm, read_rx, status; errors at most 1 a transfer; `spi_transfer`),
|
|
78
78
|
oep.fixture.logic / analog / capture-group (`virtual_bench_capture`)
|
|
79
79
|
- the settings (core §13), fn 0's get / set / unset / save / state / network: each fn's items from its describe, the
|
|
80
|
-
|
|
80
|
+
values by (fn, item, key) with no origin, the initial values put in on a boot without a save (`initial`, core §13.4),
|
|
81
|
+
the labels unique but for ASCII case (§13.8); a set checked whole (forms
|
|
81
82
|
malformed, what this probe cannot take unsupported with the value's item index, the whole against what runs); the
|
|
82
83
|
generation counts the changes of one boot from 1; the save by category and item name, applied at boot one value at a
|
|
83
84
|
time (disable first, then idle): what fails is left out (partial, reason 2); a disabled channel is refused everywhere
|
|
@@ -118,7 +119,7 @@ READ_FROM = reg.COMMON.enum["read_from"] # position, oldest, no
|
|
|
118
119
|
STEP_LEFT = _RV.tlv["step_answer"]["step_left"] # step's answer: the hart could not be halted again (§4.2)
|
|
119
120
|
# the settings (core §13): fn 0's ops, the items' forms, key kinds and origins, the core's and the wires' choices
|
|
120
121
|
_SET_OP = reg.CORE.op
|
|
121
|
-
FORM, KEY_KIND
|
|
122
|
+
FORM, KEY_KIND = reg.CORE.enum["setting_form"], reg.CORE.enum["setting_key_kind"]
|
|
122
123
|
SETTING_SECRET = reg.CORE.enum["setting_flag"]["secret"]
|
|
123
124
|
ITEM_TLV = reg.CORE.tlv["unsupported_payload"]["item"]
|
|
124
125
|
IDLE_MODE = reg.CORE.enum["channel_idle"]
|
|
@@ -736,10 +737,10 @@ class Endpoint:
|
|
|
736
737
|
# i2c-target internal pull-ups (fixture §3): features bit2, else none
|
|
737
738
|
self.i2c_pullups = {fn: bool(self.target_decl[fn][1] & I2C_FEATURES["internal_pullups"])
|
|
738
739
|
for fn, name in self.names.items() if name == _I2C.name}
|
|
739
|
-
# the settings (core §13): each fn's items (its describe), the firmware's values (
|
|
740
|
+
# the settings (core §13): each fn's items (its describe), the firmware's initial values (§13.4), the netifs
|
|
740
741
|
self.setting_items = {fn: setting_items(decl) for fn, decl in self.decl.items() if setting_items(decl)}
|
|
741
|
-
self.
|
|
742
|
-
(fn, name, key):
|
|
742
|
+
self.initial: dict[tuple[int, str, int], bytes] = {
|
|
743
|
+
(fn, name, key): bytes(d) for fn, name, key, d in self.inner.get(0, {}).get("initial_values", ())}
|
|
743
744
|
# the network (transports §7): the netifs, a named list (numbers from 1); around the bench: Wi-Fi access points
|
|
744
745
|
# (ssid -> passphrase), the Ethernet cable, what DHCP gives; every drop and reconnect of a netif (a value in use changed)
|
|
745
746
|
self.netif_names = [v.decode() for tag, v in self.decl.get(0, ()) if tag == virtual_bench.NETIF]
|
|
@@ -858,8 +859,10 @@ class Endpoint:
|
|
|
858
859
|
self.speed_frame_busy = False # TEST HOOK: a frame on the raised port is not finished yet
|
|
859
860
|
if self.saved is not None:
|
|
860
861
|
self._apply_saved() # disable before idle (core §13.7)
|
|
861
|
-
|
|
862
|
-
|
|
862
|
+
else:
|
|
863
|
+
if self.saved_unreadable:
|
|
864
|
+
self.storage_state, self.storage_reason = 2, 1 # nothing applied: state 2, reason 1 at the next boot (§3)
|
|
865
|
+
self._initial_values() # no save to use: the initial values (core §13.4)
|
|
863
866
|
self._park(self._boot_channels()) # every free pin's idle state before the first answer (core §8)
|
|
864
867
|
|
|
865
868
|
def _own(self, fn: int, tag: int, fmt: str, default: int) -> int:
|
|
@@ -3263,8 +3266,8 @@ class Endpoint:
|
|
|
3263
3266
|
return (tx + bytes(n))[:n]
|
|
3264
3267
|
|
|
3265
3268
|
# ---- the settings (core §13) ----------------------------------------------------------------------
|
|
3266
|
-
# The items each fn's describe declares (setting, setting_choice); the
|
|
3267
|
-
# the
|
|
3269
|
+
# The items each fn's describe declares (setting, setting_choice); the values as (fn, item, key) -> data (no origin,
|
|
3270
|
+
# §13.4: the initial values go in on a boot without a save); fn 0's ops get / set / unset / save / state / network.
|
|
3268
3271
|
def _items(self, fn: int) -> list[SettingItem]:
|
|
3269
3272
|
return self.setting_items.get(fn, [])
|
|
3270
3273
|
|
|
@@ -3273,18 +3276,43 @@ class Endpoint:
|
|
|
3273
3276
|
return next((k for k, it in enumerate(self._items(fn), 1) if it.name == name), 0)
|
|
3274
3277
|
|
|
3275
3278
|
def _value(self, fn: int, name: str, key: int) -> bytes | None:
|
|
3276
|
-
"""The value now of an item
|
|
3277
|
-
|
|
3278
|
-
if v is None and (fn, name, key) in self.firm:
|
|
3279
|
-
v = self.firm[(fn, name, key)][1]
|
|
3280
|
-
return v
|
|
3279
|
+
"""The value now of an item."""
|
|
3280
|
+
return self.config.get((fn, name, key))
|
|
3281
3281
|
|
|
3282
3282
|
def _values_of(self, name: str, fn: int | None = None, config: dict | None = None) -> dict[tuple[int, int], bytes]:
|
|
3283
|
-
"""Every value of item `name` (of fn `fn`, else of any fn): (fn, key) -> data
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3283
|
+
"""Every value of item `name` (of fn `fn`, else of any fn): (fn, key) -> data."""
|
|
3284
|
+
return {(f, k): v for (f, n, k), v in (self.config if config is None else config).items() if n == name and fn in (None, f)}
|
|
3285
|
+
|
|
3286
|
+
@staticmethod
|
|
3287
|
+
def _fold(d: bytes) -> bytes:
|
|
3288
|
+
return d.translate(bytes.maketrans(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ", b"abcdefghijklmnopqrstuvwxyz"))
|
|
3289
|
+
|
|
3290
|
+
def _label_clash(self, config: dict) -> int | None:
|
|
3291
|
+
"""The later channel of two whose labels are equal but for ASCII case (core §13.8), else None."""
|
|
3292
|
+
seen: dict[bytes, int] = {}
|
|
3293
|
+
for (_, ch), d in sorted(self._values_of("channel.{}.label", 0, config).items()):
|
|
3294
|
+
if self._fold(d) in seen:
|
|
3295
|
+
return ch
|
|
3296
|
+
seen[self._fold(d)] = ch
|
|
3297
|
+
return None
|
|
3298
|
+
|
|
3299
|
+
def _initial_values(self) -> None:
|
|
3300
|
+
"""A boot without a save (core §13.4): the initial values, checked as a set's; ones that do not pass (the
|
|
3301
|
+
firmware's mistake) stay out."""
|
|
3302
|
+
values = {}
|
|
3303
|
+
for (fn, name, key), d in self.initial.items():
|
|
3304
|
+
number = self._item_number(fn, name)
|
|
3305
|
+
if not number:
|
|
3306
|
+
continue
|
|
3307
|
+
it = self._items(fn)[number - 1]
|
|
3308
|
+
try:
|
|
3309
|
+
self._value_form(it, key, fn, d)
|
|
3310
|
+
except Reject:
|
|
3311
|
+
continue
|
|
3312
|
+
if not self._value_refused(fn, it, key, d):
|
|
3313
|
+
values[(fn, name, key)] = d
|
|
3314
|
+
if self._label_clash(values) is None:
|
|
3315
|
+
self.config = values
|
|
3288
3316
|
|
|
3289
3317
|
def _key_ok(self, fn: int, it: SettingItem, key: int) -> bool:
|
|
3290
3318
|
kind = it.key_kind
|
|
@@ -3325,12 +3353,10 @@ class Endpoint:
|
|
|
3325
3353
|
raise Reject(m.MALFORMED)
|
|
3326
3354
|
|
|
3327
3355
|
def _value_refused(self, fn: int, it: SettingItem, key: int, d: bytes) -> bool:
|
|
3328
|
-
"""Whether this probe cannot take the value (unsupported, core §13.3): a key it has no value for, a
|
|
3329
|
-
|
|
3356
|
+
"""Whether this probe cannot take the value (unsupported, core §13.3): a key it has no value for, a choice it does
|
|
3357
|
+
not declare, a channel or fn it does not have, and what the item's owner refuses."""
|
|
3330
3358
|
if not self._key_ok(fn, it, key):
|
|
3331
3359
|
return True
|
|
3332
|
-
if (fn, it.name, key) in self.firm and self.firm[(fn, it.name, key)][0] == ORIGIN["fixed"]:
|
|
3333
|
-
return True
|
|
3334
3360
|
if not d:
|
|
3335
3361
|
return False # a secret kept
|
|
3336
3362
|
if it.form == FORM["enum"] and d[0] not in it.choices:
|
|
@@ -3390,14 +3416,9 @@ class Endpoint:
|
|
|
3390
3416
|
rows = []
|
|
3391
3417
|
for fn in sorted(self.setting_items):
|
|
3392
3418
|
for number, it in enumerate(self._items(fn), 1):
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
user = self.config.get((fn, it.name, k))
|
|
3397
|
-
origin, d = (ORIGIN["user"], user) if user is not None else self.firm[(fn, it.name, k)]
|
|
3398
|
-
if it.flags & SETTING_SECRET:
|
|
3399
|
-
d = b"" # a secret is never read (§13.5)
|
|
3400
|
-
rows.append(struct.pack("<HBHBH", fn, number, k, origin, len(d)) + d)
|
|
3419
|
+
for k in sorted(k for (f, n, k) in self.config if f == fn and n == it.name):
|
|
3420
|
+
d = b"" if it.flags & SETTING_SECRET else self.config[(fn, it.name, k)] # a secret is never read
|
|
3421
|
+
rows.append(struct.pack("<HBHH", fn, number, k, len(d)) + d) # fn number key len data
|
|
3401
3422
|
budget = self.probe.max_frame - m.RESULT_HEADER - 6
|
|
3402
3423
|
out, sent = b"", 0
|
|
3403
3424
|
for row in rows[first:]:
|
|
@@ -3432,7 +3453,7 @@ class Endpoint:
|
|
|
3432
3453
|
values = self._resolve(self._read_values(t, t.take("B"), False))
|
|
3433
3454
|
t.tail()
|
|
3434
3455
|
for i, (fn, it, key, _) in enumerate(values):
|
|
3435
|
-
if it is None
|
|
3456
|
+
if it is None:
|
|
3436
3457
|
raise self._unsupported_item(i)
|
|
3437
3458
|
new = dict(self.config)
|
|
3438
3459
|
for fn, it, key, _ in values:
|
|
@@ -3553,6 +3574,9 @@ class Endpoint:
|
|
|
3553
3574
|
return
|
|
3554
3575
|
if self.generation == 0xFFFFFFFF:
|
|
3555
3576
|
raise unavailable("limit") # no generation left before a restart (§13.5)
|
|
3577
|
+
clash = self._label_clash(new)
|
|
3578
|
+
if clash is not None:
|
|
3579
|
+
raise unavailable("held_by_settings", clash) # two labels equal but for case (§13.8)
|
|
3556
3580
|
disabled = {k for (f, k), v in self._values_of("channel.{}.disable", 0, new).items() if v == b"\x01"}
|
|
3557
3581
|
idles = self._values_of("channel.{}.idle", 0, new)
|
|
3558
3582
|
outputs = (IDLE_MODE["low"], IDLE_MODE["high"])
|
|
@@ -3604,7 +3628,8 @@ class Endpoint:
|
|
|
3604
3628
|
|
|
3605
3629
|
def _apply_saved(self) -> None:
|
|
3606
3630
|
"""At boot (core §13.7): every saved value, by name, checked by itself against this firmware - disable first,
|
|
3607
|
-
then idle (a channel with both keeps the disable), then the rest;
|
|
3631
|
+
then idle (a channel with both keeps the disable), then the rest; of two labels equal but for case the lower
|
|
3632
|
+
channel's; one it cannot take (a category or item it does
|
|
3608
3633
|
not have, a form or value it refuses) is left out and the rest applied (storage_state 3 partial, reason 2)."""
|
|
3609
3634
|
def rank(kv):
|
|
3610
3635
|
(ident, name, key), _ = kv
|
|
@@ -3626,6 +3651,8 @@ class Endpoint:
|
|
|
3626
3651
|
ok = False
|
|
3627
3652
|
if ok and name == "channel.{}.idle" and applied.get((0, "channel.{}.disable", key)) == b"\x01":
|
|
3628
3653
|
ok = False # disable wins over idle on one channel
|
|
3654
|
+
if ok and name == "channel.{}.label" and self._label_clash({**applied, (fn, name, key): d}) is not None:
|
|
3655
|
+
ok = False # labels in key order: the lower channel's stays
|
|
3629
3656
|
if ok:
|
|
3630
3657
|
applied[(fn, name, key)] = d
|
|
3631
3658
|
bad |= not ok
|
oep_client/pins.py
CHANGED
|
@@ -21,8 +21,8 @@ Steps, each optional and each saying what it did:
|
|
|
21
21
|
5. reset line, confirmed: attach under reset through each channel that stopped the activity (or, with nothing
|
|
22
22
|
active, through each candidate) - the real line stops the hart at the reset vector.
|
|
23
23
|
6. print the connection profile the host keeps (wire, pins, line settings: host guide §5.3 - the probe keeps no
|
|
24
|
-
wiring) and the labels that name the lines found:
|
|
25
|
-
|
|
24
|
+
wiring) and the labels that name the lines found: `<port>.nrst` on the reset line (5) and `<port>.power_hi` on
|
|
25
|
+
--power, the port being --name or the one port the probe's labels name; --save writes the labels
|
|
26
26
|
(settings set + save). Nothing is written otherwise.
|
|
27
27
|
|
|
28
28
|
Everything is bounded in time (the whole run under a minute) and every plan is released at the end. A plan_apply
|
|
@@ -562,10 +562,16 @@ class PinFinder:
|
|
|
562
562
|
r.profile = ", ".join(parts)
|
|
563
563
|
self.say(f"profile{' ' + self.name if self.name else ''}: {r.profile} (the host's to keep: the probe keeps "
|
|
564
564
|
f"no wiring)")
|
|
565
|
-
# the lines found are named by label (host guide §5.3):
|
|
566
|
-
# looks up - and
|
|
567
|
-
|
|
565
|
+
# the lines found are named by label (host guide §5.3): `<port>.nrst` - the reset line a host's attach under
|
|
566
|
+
# reset looks up - and `<port>.power_hi`; the port is --name, else the one port the probe's labels name
|
|
567
|
+
port = self.name or self._only_port()
|
|
568
568
|
labels = []
|
|
569
|
+
if port is None:
|
|
570
|
+
if r.reset_channel is not None or self.power is not None:
|
|
571
|
+
self.note("labels: name the port with --name (a line is named <port>.<line>, host guide §5.3)")
|
|
572
|
+
r.labels = labels
|
|
573
|
+
return
|
|
574
|
+
prefix = f"{port}."
|
|
569
575
|
if r.reset_channel is not None:
|
|
570
576
|
labels.append((r.reset_channel, f"{prefix}nrst"))
|
|
571
577
|
if self.power is not None:
|
|
@@ -585,6 +591,14 @@ class PinFinder:
|
|
|
585
591
|
r.saved = True
|
|
586
592
|
self.say(" labels written and saved")
|
|
587
593
|
|
|
594
|
+
def _only_port(self) -> str | None:
|
|
595
|
+
"""The one port the probe's labels name (None: none or several)."""
|
|
596
|
+
try:
|
|
597
|
+
ports = settings.ports_named(settings.labels(self.hst))
|
|
598
|
+
except (LookupError, h.OepError):
|
|
599
|
+
return None
|
|
600
|
+
return ports[0] if len(ports) == 1 else None
|
|
601
|
+
|
|
588
602
|
def _name(self, channel: int) -> str:
|
|
589
603
|
"""A channel's name (fn 0's channel_name, core §7.5); its number when the probe names none."""
|
|
590
604
|
try:
|
|
@@ -603,8 +617,8 @@ class PinFinder:
|
|
|
603
617
|
relabelled = {ch for ch, _ in labels}
|
|
604
618
|
after = [(ch, t) for ch, t in have if ch not in relabelled] + list(labels)
|
|
605
619
|
for ch, text in labels:
|
|
606
|
-
line = text.
|
|
607
|
-
found = settings.find_line(after, line,
|
|
620
|
+
port, _, line = text.partition(".")
|
|
621
|
+
found = settings.find_line(after, line, port)
|
|
608
622
|
if found != ch:
|
|
609
623
|
clash = sorted(c for c, t in after if c != ch and settings.fold_name(t) == settings.fold_name(text))
|
|
610
624
|
self.note(f"label {text}: the settings already name channel(s) {clash} the same way - a host would "
|
oep_client/registry.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
from types import SimpleNamespace as _NS
|
|
5
5
|
|
|
6
|
-
REGISTRY_HASH = "
|
|
6
|
+
REGISTRY_HASH = "2fdee123ed6fb422"
|
|
7
7
|
SCHEMA = 1
|
|
8
8
|
PROTOCOL_REVISION = 1
|
|
9
9
|
CONFIRM_REQUEST_MAGIC = 'OEP?'
|
|
@@ -22,12 +22,12 @@ STATUS = {"ok": 0x00, "wait": 0x01, "line": 0x02, "fault": 0x03, "timeout": 0x04
|
|
|
22
22
|
DESCRIBE_COMMON = {"role_channels": 0x01, "max_clock_hz": 0x02, "max_length": 0x03, "min_clock_hz": 0x04, "features": 0x05, "channel_group": 0x06, "ops": 0x07, "setting": 0x08, "setting_choice": 0x09}
|
|
23
23
|
TIMING = {"probe_frame_gap_ms": 0xC8, "uart_bridge_boot_baud": 0x1C200, "port_speed_idle_ms": 0xBB8, "host_wait_add_ms": 0x3E8, "core_quick_op_max_ms": 0x3E8, "notify_pending_max_frames": 0x02, "port_speed_switch_wait_ms": 0x14}
|
|
24
24
|
USB = {"project_vid": 0x1209, "project_pid": 0x4F45, "vendor_bulk_class": 0xFF, "vendor_bulk_subclass": 0x4F, "vendor_bulk_protocol": 0x45, "hid_usage_page": 0xFF4F, "hid_usage": 0x45}
|
|
25
|
-
LIMITS = {"request_header_bytes": 0x0A, "result_header_bytes": 0x05, "corr_max": 0xFFFF, "corr_half_range": 0x8000, "resource_id_bytes": 0x04, "resource_id_max": 0xFFFFFFFF, "lease_min_ms": 0x3E8, "lease_max_ms": 0xEA60, "owner_max_bytes": 0x20, "unit_id_max_bytes": 0x20, "wifi_ssid_max_bytes": 0x20, "wifi_min_max_frame":
|
|
25
|
+
LIMITS = {"request_header_bytes": 0x0A, "result_header_bytes": 0x05, "corr_max": 0xFFFF, "corr_half_range": 0x8000, "resource_id_bytes": 0x04, "resource_id_max": 0xFFFFFFFF, "lease_min_ms": 0x3E8, "lease_max_ms": 0xEA60, "owner_max_bytes": 0x20, "unit_id_max_bytes": 0x20, "wifi_ssid_max_bytes": 0x20, "wifi_min_max_frame": 0x52, "label_max_bytes": 0x20, "setting_name_max_bytes": 0x30, "setting_choice_name_max_bytes": 0x10, "channel_name_max_bytes": 0x10, "setting_get_overhead_bytes": 0x12, "interface_name_max_bytes": 0x30, "port_speed_tolerance_pct": 0x02, "uart_baud_tolerance_pct": 0x05, "max_op_ms_max": 0x927C0}
|
|
26
26
|
COMMON = _NS(enum={"target_id_scheme": {"dmi_7f": 0x01, "dpidr": 0x02}, "read_from": {"position": 0x00, "oldest": 0x01, "now": 0x02, "last_mark": 0x03}, "read_flags": {"more": 0x01, "gap": 0x02}, "mark_kind": {"lost": 0x01, "clear": 0x02, "host": 0x03}, "mark_detail_lost": {"overflow": 0x01, "framing": 0x02, "parity": 0x03}})
|
|
27
27
|
|
|
28
28
|
INTERFACES = {}
|
|
29
29
|
CORE = _NS(op={"confirm": 0x01, "list": 0x02, "describe": 0x03, "clock": 0x04, "open": 0x10, "end": 0x11, "keepalive": 0x12, "lock_state": 0x13, "get": 0x20, "set": 0x21, "unset": 0x22, "save": 0x23, "state": 0x24, "network": 0x25}, lock_free={0x01, 0x02, 0x03, 0x04, 0x10, 0x13, 0x20, 0x24, 0x25},
|
|
30
|
-
tlv={"confirm_answer": {"transport": 0x01}, "open": {"owner": 0x01}, "lock_state_answer": {"owner": 0x01}, "locked_payload": {"owner": 0x01}, "unavailable_payload": {"cause": 0x01, "channel": 0x02, "item": 0x03, "fn": 0x05}, "unsupported_payload": {"supported": 0x01, "channel": 0x02, "item": 0x03, "fn": 0x05}, "describe": {"firmware": 0x40, "model": 0x41, "unit_id": 0x42, "channels": 0x43, "channel_name": 0x44, "netif": 0x45, "transport": 0x49, "chip": 0x4C, "max_op_ms": 0x4D}}, event={}, enum={"unavailable_cause": {"pin_in_use": 0x01, "limit": 0x02, "storage_full": 0x03, "bound_in_group": 0x04, "held_by_settings": 0x05, "wrong_state": 0x06, "policy_denied": 0x07}, "transport_kind": {"uart_bridge": 0x01, "usb_cdc": 0x02, "usb_serial_jtag": 0x03, "vendor_bulk": 0x04, "hid": 0x05, "tcp": 0x06}, "route_kind": {"bridge": 0x01, "cdc": 0x02, "usj": 0x03, "bulk": 0x04, "hid": 0x05, "tcp": 0x06}, "setting_form": {"bytes": 0x01, "text": 0x02, "u32": 0x03, "ipv4": 0x04, "bool": 0x05, "enum": 0x06, "channel": 0x07, "transport": 0x08, "fn": 0x09}, "setting_key_kind": {"none": 0x00, "channel": 0x01, "index": 0x02, "transport": 0x03, "netif": 0x04}, "setting_flag": {"secret": 0x01, "restart": 0x02}, "
|
|
30
|
+
tlv={"confirm_answer": {"transport": 0x01}, "open": {"owner": 0x01}, "lock_state_answer": {"owner": 0x01}, "locked_payload": {"owner": 0x01}, "unavailable_payload": {"cause": 0x01, "channel": 0x02, "item": 0x03, "fn": 0x05}, "unsupported_payload": {"supported": 0x01, "channel": 0x02, "item": 0x03, "fn": 0x05}, "describe": {"firmware": 0x40, "model": 0x41, "unit_id": 0x42, "channels": 0x43, "channel_name": 0x44, "netif": 0x45, "transport": 0x49, "chip": 0x4C, "max_op_ms": 0x4D}}, event={}, enum={"unavailable_cause": {"pin_in_use": 0x01, "limit": 0x02, "storage_full": 0x03, "bound_in_group": 0x04, "held_by_settings": 0x05, "wrong_state": 0x06, "policy_denied": 0x07}, "transport_kind": {"uart_bridge": 0x01, "usb_cdc": 0x02, "usb_serial_jtag": 0x03, "vendor_bulk": 0x04, "hid": 0x05, "tcp": 0x06}, "route_kind": {"bridge": 0x01, "cdc": 0x02, "usj": 0x03, "bulk": 0x04, "hid": 0x05, "tcp": 0x06}, "setting_form": {"bytes": 0x01, "text": 0x02, "u32": 0x03, "ipv4": 0x04, "bool": 0x05, "enum": 0x06, "channel": 0x07, "transport": 0x08, "fn": 0x09}, "setting_key_kind": {"none": 0x00, "channel": 0x01, "index": 0x02, "transport": 0x03, "netif": 0x04}, "setting_flag": {"secret": 0x01, "restart": 0x02}, "storage_state": {"none": 0x00, "applied": 0x01, "unreadable": 0x02, "partial": 0x03}, "storage_reason": {"none": 0x00, "form": 0x01, "item": 0x02, "capacity": 0x03}, "channel_idle": {"hi_z": 0x00, "pull_up": 0x01, "pull_down": 0x02, "low": 0x03, "high": 0x04}, "ipv4_mode": {"dhcp": 0x00, "static": 0x01}, "network_state": {"off": 0x00, "connecting": 0x01, "up": 0x02, "waiting": 0x03}, "network_reason": {"none": 0x00, "not_found": 0x01, "auth": 0x02, "no_address": 0x03, "other": 0x04}}, own={},
|
|
31
31
|
line_names={'swdio': 'the debug data line (2 wires: rvswd, swd) (Host only)', 'swclk': 'the debug clock line (2 wires: rvswd, swd) (Host only)', 'swio': 'the one debug wire (swio) (Host only)', 'rx': 'the fixture UART receive, wired to the target TX (Host only)', 'tx': 'the fixture UART transmit, wired to the target RX (Host only)', 'nrst': 'the target reset line (Host only)', 'power_hi': 'target power on when high (Host only)', 'power_lo': 'target power on when low (Host only)'}, names={'netif': {'wifi': 'Wi-Fi station', 'ethernet': 'wired Ethernet'}}, settings={'channel.{}.label': "text 1-32, channel; the pin's label (NRST)", 'channel.{}.idle': 'enum channel_idle, channel; the idle state (core §8)', 'channel.{}.disable': 'bool, channel; true: the probe neither drives nor configures the pin', 'wifi.{}.ssid': "text 1-32, index (entry); the network's name", 'wifi.{}.passphrase': 'text 8-64, index (entry), secret; 8-63 printable passphrase or 64 hex digits', 'ipv4.{}.mode': 'enum ipv4_mode, netif; dhcp when absent', 'ipv4.{}.address': 'ipv4, netif; static address', 'ipv4.{}.prefix': 'u32 1-32, netif; static prefix length', 'ipv4.{}.gateway': 'ipv4, netif; static gateway, none when absent', 'ipv4.{}.dns': 'ipv4, netif; static DNS server, none when absent'})
|
|
32
32
|
PROBE_PLAN = _NS(name="oep.probe.plan", revision=1, target=None, op={"plan_apply": 0x10, "plan_release": 0x11}, lock_free=set(),
|
|
33
33
|
tlv={"plan_apply": {"role_assignment": 0x10}, "describe": {"plan_roles": 0x40}}, event={}, enum={}, own={},
|
oep_client/settings.py
CHANGED
|
@@ -7,10 +7,10 @@ with storage, network with the network items). A value is (fn, item number, key)
|
|
|
7
7
|
and the value with references as names - are what a host keeps, shows and writes to a file.
|
|
8
8
|
|
|
9
9
|
st = settings.Settings(hst)
|
|
10
|
-
st.show() # [(category, flat name, value
|
|
10
|
+
st.show() # [(category, flat name, value)]
|
|
11
11
|
st.set_named("core", {"channel.GPIO14.label": "NRST", "channel.GPIO14.idle": "pull-up"})
|
|
12
12
|
st.save()
|
|
13
|
-
doc = st.export() # the settings file (JSON),
|
|
13
|
+
doc = st.export() # the settings file (JSON), every value
|
|
14
14
|
plan = st.plan_import(doc); st.apply_import(plan)
|
|
15
15
|
|
|
16
16
|
Secrets (core §13.5) are never read: get gives no data, the file a ``{"secret": true}``.
|
|
@@ -19,6 +19,7 @@ Secrets (core §13.5) are never read: get gives no data, the file a ``{"secret":
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
21
21
|
import ipaddress
|
|
22
|
+
import re
|
|
22
23
|
import struct
|
|
23
24
|
from dataclasses import dataclass, field
|
|
24
25
|
|
|
@@ -28,7 +29,6 @@ _C = reg.CORE
|
|
|
28
29
|
OP = {k: _C.op[k] for k in ("get", "set", "unset", "save", "state", "network")}
|
|
29
30
|
FORM = {v: k for k, v in _C.enum["setting_form"].items()}
|
|
30
31
|
KEY_KIND = {v: k for k, v in _C.enum["setting_key_kind"].items()}
|
|
31
|
-
ORIGIN = {v: k for k, v in _C.enum["setting_origin"].items()}
|
|
32
32
|
SECRET = _C.enum["setting_flag"]["secret"]
|
|
33
33
|
STORAGE_STATE = {v: k for k, v in _C.enum["storage_state"].items()}
|
|
34
34
|
STORAGE_REASON = {v: k for k, v in _C.enum["storage_reason"].items()}
|
|
@@ -37,8 +37,7 @@ NET_REASON = {v: k for k, v in _C.enum["network_reason"].items()}
|
|
|
37
37
|
ROUTE = {v: k for k, v in _C.enum["route_kind"].items()} # a transport kind's short name (core §13.3)
|
|
38
38
|
_D = _C.tlv["describe"]
|
|
39
39
|
SETTING, SETTING_CHOICE = reg.DESCRIBE_COMMON["setting"], reg.DESCRIBE_COMMON["setting_choice"]
|
|
40
|
-
VALUE_HEADER =
|
|
41
|
-
SET_HEADER = 7 # fn number key len
|
|
40
|
+
VALUE_HEADER = 7 # fn number key len, get's and set's alike (core §13.5)
|
|
42
41
|
FILE_VERSION = 1 # the settings file's form (oep_settings)
|
|
43
42
|
CORE_CATEGORY = "core"
|
|
44
43
|
|
|
@@ -63,11 +62,10 @@ class Item:
|
|
|
63
62
|
|
|
64
63
|
@dataclass
|
|
65
64
|
class Value:
|
|
66
|
-
"""One value as get gives it."""
|
|
65
|
+
"""One value as get gives it (no origin: core §13.4)."""
|
|
67
66
|
fn: int
|
|
68
67
|
number: int
|
|
69
68
|
key: int
|
|
70
|
-
origin: str
|
|
71
69
|
data: bytes
|
|
72
70
|
|
|
73
71
|
|
|
@@ -264,22 +262,22 @@ def values_from(payload_pages) -> list[Value]:
|
|
|
264
262
|
for raw in payload_pages:
|
|
265
263
|
at = 0
|
|
266
264
|
while at + VALUE_HEADER <= len(raw):
|
|
267
|
-
fn, number, key,
|
|
268
|
-
out.append(Value(fn, number, key,
|
|
265
|
+
fn, number, key, n = struct.unpack_from("<HBHH", raw, at)
|
|
266
|
+
out.append(Value(fn, number, key, raw[at + VALUE_HEADER:at + VALUE_HEADER + n]))
|
|
269
267
|
at += VALUE_HEADER + n
|
|
270
268
|
return out
|
|
271
269
|
|
|
272
270
|
|
|
273
271
|
def split_values(data: bytes, count: int) -> tuple[list[Value], bytes]:
|
|
274
|
-
"""count values (fn number key
|
|
272
|
+
"""count values (fn number key len data) from get's answer, then the answer's own TLV tail."""
|
|
275
273
|
out, at = [], 0
|
|
276
274
|
for _ in range(count):
|
|
277
275
|
if at + VALUE_HEADER > len(data):
|
|
278
276
|
raise m.ProtocolError("get: a value past the answer")
|
|
279
|
-
fn, number, key,
|
|
277
|
+
fn, number, key, n = struct.unpack_from("<HBHH", data, at)
|
|
280
278
|
if at + VALUE_HEADER + n > len(data):
|
|
281
279
|
raise m.ProtocolError("get: a value's data past the answer")
|
|
282
|
-
out.append(Value(fn, number, key,
|
|
280
|
+
out.append(Value(fn, number, key, data[at + VALUE_HEADER:at + VALUE_HEADER + n]))
|
|
283
281
|
at += VALUE_HEADER + n
|
|
284
282
|
return out, data[at:]
|
|
285
283
|
|
|
@@ -364,7 +362,7 @@ class Settings:
|
|
|
364
362
|
return gen
|
|
365
363
|
|
|
366
364
|
def save(self) -> int:
|
|
367
|
-
"""Save the
|
|
365
|
+
"""Save the values (a probe with storage). -> the current generation (completed failed raises: `state`
|
|
368
366
|
says what is kept). Its argument time is max_op_ms (core §13.7)."""
|
|
369
367
|
return self._generation(self._call("save", expect_ms=core.max_op_ms(self.host)))
|
|
370
368
|
|
|
@@ -394,8 +392,8 @@ class Settings:
|
|
|
394
392
|
return Network(port, out)
|
|
395
393
|
|
|
396
394
|
# -- by name --
|
|
397
|
-
def show(self) -> list[tuple[str, str, object
|
|
398
|
-
"""Every value as (category, flat name, value
|
|
395
|
+
def show(self) -> list[tuple[str, str, object]]:
|
|
396
|
+
"""Every value as (category, flat name, value); a secret's value is {"secret": True}."""
|
|
399
397
|
cat = self.catalog()
|
|
400
398
|
out = []
|
|
401
399
|
for v in self.get()[1]:
|
|
@@ -404,7 +402,7 @@ class Settings:
|
|
|
404
402
|
continue
|
|
405
403
|
flat = cat.flat(it, v.key) or f"{it.name}#{v.key}"
|
|
406
404
|
out.append((cat.category.get(v.fn, str(v.fn)), flat,
|
|
407
|
-
{"secret": True} if it.secret else cat.to_json(it, v.data)
|
|
405
|
+
{"secret": True} if it.secret else cat.to_json(it, v.data)))
|
|
408
406
|
return out
|
|
409
407
|
|
|
410
408
|
def _named(self, cat: Catalog, category: str, flat: str) -> tuple[Item, int]:
|
|
@@ -432,10 +430,15 @@ class Settings:
|
|
|
432
430
|
keys.append((it.fn, it.number, key))
|
|
433
431
|
return self.unset(keys)
|
|
434
432
|
|
|
433
|
+
def changeable(self) -> bool:
|
|
434
|
+
"""Whether the probe lets its values change: set / unset offered (a probe of initial values only has get alone,
|
|
435
|
+
core §13.5)."""
|
|
436
|
+
return self.offers("set") and self.offers("unset")
|
|
437
|
+
|
|
435
438
|
def unset_all(self) -> int | None:
|
|
436
|
-
"""Every
|
|
437
|
-
|
|
438
|
-
return self.unset(
|
|
439
|
+
"""Every value away (then save to empty the storage: core §13.7). None: there was none."""
|
|
440
|
+
keys = [(v.fn, v.number, v.key) for v in self.get()[1]]
|
|
441
|
+
return self.unset(keys) if keys else None
|
|
439
442
|
|
|
440
443
|
# -- the file (settings file §3, §4) --
|
|
441
444
|
def export(self) -> dict:
|
|
@@ -455,11 +458,9 @@ class Settings:
|
|
|
455
458
|
|
|
456
459
|
|
|
457
460
|
def export_file(cat: Catalog, values: list[Value]) -> dict:
|
|
458
|
-
"""The settings file of
|
|
461
|
+
"""The settings file of every value (settings file §3)."""
|
|
459
462
|
out: dict = {}
|
|
460
463
|
for v in values:
|
|
461
|
-
if v.origin != "user":
|
|
462
|
-
continue # fixed and default stay with the firmware
|
|
463
464
|
it = cat.item(v.fn, v.number)
|
|
464
465
|
flat = cat.flat(it, v.key) if it is not None else None
|
|
465
466
|
if flat is None:
|
|
@@ -477,7 +478,7 @@ def check_file(doc) -> None:
|
|
|
477
478
|
|
|
478
479
|
|
|
479
480
|
def plan_import(cat: Catalog, current: list[Value], doc: dict) -> ImportPlan:
|
|
480
|
-
"""What making the file's
|
|
481
|
+
"""What making the file's values the probe's takes (settings file §4)."""
|
|
481
482
|
check_file(doc)
|
|
482
483
|
plan = ImportPlan()
|
|
483
484
|
probe = doc.get("probe", {})
|
|
@@ -502,55 +503,61 @@ def plan_import(cat: Catalog, current: list[Value], doc: dict) -> ImportPlan:
|
|
|
502
503
|
plan.unresolved.append((category, flat))
|
|
503
504
|
continue
|
|
504
505
|
now = have.get((it.fn, it.number, key))
|
|
505
|
-
if now is None or now.
|
|
506
|
+
if now is None or now.data != data:
|
|
506
507
|
plan.sets.append((it.fn, it.number, key, data))
|
|
507
|
-
plan.unsets = [(v.fn, v.number, v.key) for v in current if
|
|
508
|
+
plan.unsets = [(v.fn, v.number, v.key) for v in current if (v.fn, v.number, v.key) not in wanted]
|
|
508
509
|
return plan
|
|
509
510
|
|
|
510
511
|
|
|
511
|
-
# ----
|
|
512
|
-
#
|
|
513
|
-
#
|
|
512
|
+
# ---- ports and lines by label (host guide §5.3) -----------------------------------------------------------
|
|
513
|
+
# A label "S.N" names port S's line N: S 1-16 of letters, digits and '-', a letter first; N letters, digits, '-' and '_';
|
|
514
|
+
# one '.' between. Other labels are a pin's names only. Kept as written, compared ignoring ASCII case; a probe keeps its
|
|
515
|
+
# labels unique that way (core §13.8). Line names: `swdio`, `swclk`, `swio`, `rx`, `tx`, `nrst`, `power_hi`, `power_lo`.
|
|
514
516
|
LINE_NAMES = tuple(_C.line_names)
|
|
515
517
|
_ASCII_LOWER = str.maketrans("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")
|
|
516
518
|
LABEL = "channel.{}.label"
|
|
519
|
+
_PORT = re.compile(r"[A-Za-z][A-Za-z0-9-]{0,15}")
|
|
520
|
+
_LINE = re.compile(r"[A-Za-z0-9_-]+")
|
|
517
521
|
|
|
518
522
|
|
|
519
523
|
def fold_name(text: str) -> str:
|
|
520
|
-
"""ASCII case folded (only A-Z:
|
|
524
|
+
"""ASCII case folded (only A-Z): names compare ignoring ASCII case."""
|
|
521
525
|
return text.translate(_ASCII_LOWER)
|
|
522
526
|
|
|
523
527
|
|
|
528
|
+
def port_line(text: str) -> tuple[str, str] | None:
|
|
529
|
+
"""(S, N) of a label that names a port's line, as written; None for any other label."""
|
|
530
|
+
port, dot, line = text.partition(".")
|
|
531
|
+
if not dot or not _PORT.fullmatch(port) or not _LINE.fullmatch(line):
|
|
532
|
+
return None
|
|
533
|
+
return port, line
|
|
534
|
+
|
|
535
|
+
|
|
524
536
|
def ports_named(labels) -> list[str]:
|
|
525
|
-
"""The ports the labels name (host guide §5.3):
|
|
526
|
-
out = []
|
|
537
|
+
"""The ports the labels name (host guide §5.3): each S once (ignoring case), as written, in order."""
|
|
538
|
+
out: list[str] = []
|
|
527
539
|
for _, text in labels:
|
|
528
|
-
|
|
529
|
-
if
|
|
530
|
-
out.append(
|
|
540
|
+
pl = port_line(text)
|
|
541
|
+
if pl and fold_name(pl[0]) not in {fold_name(p) for p in out}:
|
|
542
|
+
out.append(pl[0])
|
|
531
543
|
return out
|
|
532
544
|
|
|
533
545
|
|
|
534
|
-
def line_from_labels(labels, name: str,
|
|
535
|
-
"""
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
single = len(ports_named(labels)) <= 1
|
|
541
|
-
steps = ([f"{profile}.{name}"] if profile is not None else []) + ([name] if single else [])
|
|
542
|
-
for text in steps:
|
|
543
|
-
found = {ch for ch, t in labels if fold_name(t) == fold_name(text)}
|
|
544
|
-
if len(found) > 1:
|
|
546
|
+
def line_from_labels(labels, name: str, port: str | None = None) -> int | None:
|
|
547
|
+
"""The channel of port `port`'s line `name` among labels as (channel, text), or None (host guide §5.3). Without a
|
|
548
|
+
port, the only port the labels name (a tool's shortcut when exactly one is there). Compared ignoring ASCII case."""
|
|
549
|
+
if port is None:
|
|
550
|
+
ports = ports_named(labels)
|
|
551
|
+
if len(ports) != 1:
|
|
545
552
|
return None
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
553
|
+
port = ports[0]
|
|
554
|
+
found = {ch for ch, text in labels
|
|
555
|
+
if (pl := port_line(text)) and fold_name(pl[0]) == fold_name(port) and fold_name(pl[1]) == fold_name(name)}
|
|
556
|
+
return found.pop() if len(found) == 1 else None
|
|
549
557
|
|
|
550
558
|
|
|
551
559
|
def labels(hst) -> list[tuple[int, str]]:
|
|
552
|
-
"""Every channel label now (channel.{}.label
|
|
553
|
-
settings ops: none."""
|
|
560
|
+
"""Every channel label now (channel.{}.label) as (channel, text). A probe without the settings ops: none."""
|
|
554
561
|
if not core.offers(hst, 0, OP["get"]):
|
|
555
562
|
return []
|
|
556
563
|
st = Settings(hst)
|
|
@@ -563,8 +570,8 @@ def labels(hst) -> list[tuple[int, str]]:
|
|
|
563
570
|
return out
|
|
564
571
|
|
|
565
572
|
|
|
566
|
-
def find_line(source, name: str,
|
|
567
|
-
"""The channel of the line `name` (LINE_NAMES) by the
|
|
568
|
-
(its labels read with get, no lock) or labels as (channel, text)."""
|
|
573
|
+
def find_line(source, name: str, port: str | None = None) -> int | None:
|
|
574
|
+
"""The channel of the line `name` (LINE_NAMES) of port `port` by the labels (host guide §5.3), or None. source: a
|
|
575
|
+
Host (its labels read with get, no lock) or labels as (channel, text)."""
|
|
569
576
|
found = list(source) if isinstance(source, (list, tuple)) else labels(source)
|
|
570
|
-
return line_from_labels(found, name,
|
|
577
|
+
return line_from_labels(found, name, port)
|
oep_client/virtual_bench.py
CHANGED
|
@@ -276,7 +276,6 @@ def _transports(kinds: list[tuple[int, int]]) -> tuple[bytes, ...]:
|
|
|
276
276
|
# ---- the settings (core §13) -------------------------------------------------------------------------------
|
|
277
277
|
SETTING, SETTING_CHOICE = reg.DESCRIBE_COMMON["setting"], reg.DESCRIBE_COMMON["setting_choice"]
|
|
278
278
|
FORM, KEY_KIND, FLAG = reg.CORE.enum["setting_form"], reg.CORE.enum["setting_key_kind"], reg.CORE.enum["setting_flag"]
|
|
279
|
-
ORIGIN = reg.CORE.enum["setting_origin"]
|
|
280
279
|
SETTINGS_OPS = ("get", "set", "unset") # fn 0's ops with settings; save / state with storage, network with netifs
|
|
281
280
|
|
|
282
281
|
|
|
@@ -345,25 +344,25 @@ def channel_name(channel: int) -> str:
|
|
|
345
344
|
|
|
346
345
|
|
|
347
346
|
def with_settings(offered: list[Offered], *, storage: bool = True, netifs=(), labels: dict[int, str] | None = None,
|
|
348
|
-
|
|
347
|
+
initial=(), changeable: bool = True) -> list[Offered]:
|
|
349
348
|
"""A profile's fns with the settings (core §13): fn 0 offers get / set / unset (save / state with `storage`, network
|
|
350
349
|
with `netifs`) and declares its channel items (the network's with `netifs`, a named list of netifs); each debug wire
|
|
351
|
-
its hold (rvswd its rest too), each fixture UART and console its pass-through. `labels`: channel labels
|
|
352
|
-
|
|
350
|
+
its hold (rvswd its rest too), each fixture UART and console its pass-through. `labels`: channel labels as initial
|
|
351
|
+
values; `initial`: more initial values as (fn, item, key, data) (core §13.4: in on a boot without a save);
|
|
352
|
+
`changeable` False: a probe of initial values only, get alone (§13.5)."""
|
|
353
353
|
out = []
|
|
354
354
|
for o in offered:
|
|
355
355
|
extra: tuple[Item, ...] = ()
|
|
356
356
|
inner = o.inner
|
|
357
357
|
if o.fn == CORE_FN:
|
|
358
|
-
ops = [reg.CORE.op[k] for k in CORE_REQUIRED + SETTINGS_OPS]
|
|
359
|
-
ops += [reg.CORE.op[k] for k in ("save", "state")] if storage else []
|
|
358
|
+
ops = [reg.CORE.op[k] for k in CORE_REQUIRED + (SETTINGS_OPS if changeable else ("get",))]
|
|
359
|
+
ops += [reg.CORE.op[k] for k in ("save", "state")] if storage and changeable else []
|
|
360
360
|
ops += [reg.CORE.op["network"]] if netifs else []
|
|
361
361
|
items = CORE_ITEMS + (NETWORK_ITEMS if netifs else ())
|
|
362
|
-
firm = tuple((0, "channel.{}.label", ch,
|
|
363
|
-
firm += tuple((fn, item, key, ORIGIN["default"], data) for fn, item, key, data in defaults)
|
|
362
|
+
firm = tuple((0, "channel.{}.label", ch, n.encode()) for ch, n in (labels or {}).items()) + tuple(initial)
|
|
364
363
|
out.append(Offered(o.fn, o.instance, o.name, (catalog.ops_tlv(ops),) + tuple(t for t in o.tlvs if t[0] != catalog.OPS)
|
|
365
364
|
+ tuple(catalog.tlv(NETIF, n.encode()) for n in netifs) + items_tlvs(items), o.revision,
|
|
366
|
-
inner + (("
|
|
365
|
+
inner + (("initial_values", firm),)))
|
|
367
366
|
continue
|
|
368
367
|
if o.name == "oep.wire.rvswd":
|
|
369
368
|
extra = (REST, HOLD)
|
|
@@ -496,7 +495,7 @@ def p4_x035() -> VirtualProbe:
|
|
|
496
495
|
_link(13),
|
|
497
496
|
_plan(14),
|
|
498
497
|
_restart(15),
|
|
499
|
-
], netifs=("ethernet",), labels={2: "
|
|
498
|
+
], netifs=("ethernet",), labels={2: "x035.swdio", 54: "x035.swclk"}), own_channels=own)
|
|
500
499
|
|
|
501
500
|
|
|
502
501
|
def esp32_v003(max_frame: int = 512) -> VirtualProbe:
|
|
@@ -532,7 +531,7 @@ def esp32_v003(max_frame: int = 512) -> VirtualProbe:
|
|
|
532
531
|
_link(10, port_speed=True),
|
|
533
532
|
_plan(11),
|
|
534
533
|
_restart(12),
|
|
535
|
-
], netifs=("wifi",), labels={16: "
|
|
534
|
+
], netifs=("wifi",), labels={16: "v003.swio", 23: "v003.nrst", 22: "v003.rx", 21: "v003.tx"}), own_channels=own)
|
|
536
535
|
|
|
537
536
|
|
|
538
537
|
def esp32_v003_64() -> VirtualProbe:
|
|
@@ -562,7 +561,7 @@ def p4_bench() -> VirtualProbe:
|
|
|
562
561
|
_link(7),
|
|
563
562
|
_plan(8),
|
|
564
563
|
_restart(9),
|
|
565
|
-
], labels={2: "
|
|
564
|
+
], labels={2: "a.swdio", 3: "a.swclk", 4: "b.swdio", 5: "b.swclk", 6: "c.swdio", 7: "c.swclk"}), own_channels=own)
|
|
566
565
|
|
|
567
566
|
|
|
568
567
|
def rp2350_pins() -> VirtualProbe:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: oep-client-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.39
|
|
4
4
|
Summary: Open Embedded Probe (OEP) v1 host: serial / USB / TCP transports, the session rules, the standard interfaces, and a virtual bench
|
|
5
5
|
Project-URL: Homepage, https://github.com/Open-Embedded-Probe/oep-client-python
|
|
6
6
|
Project-URL: Repository, https://github.com/Open-Embedded-Probe/oep-client-python
|
|
@@ -84,7 +84,7 @@ names starting with `_`, may change. Check probe/client compatibility using the
|
|
|
84
84
|
| `pins` | `oep pins`: classify the channels, hold-low search, scan, identify, confirm the reset line, suggest the connection profile and the labels (`PinFinder`) |
|
|
85
85
|
| `console` | `oep.target.console` (position streams: read answers carry their length; marks carry `time_ns`, the common kinds lost / clear / host and the console's reset 0x40 / restart 0x41; the lock-free `streams()` list of stream, connection and mechanism; `open(conn, mechanism)` always makes a new stream (u32) on the connection - a second on a connection with a live one is unavailable cause 6 -; a stream is the session's - close, its connection's release and the session's end release it with its buffer, and its number answers no_resource after that; a write goes into the stream's send queue - its size is the probe's own, not declared -, accepted = min(count, its free space), 0 only when full; an SDI stream's write is unsupported; marks, clear, mark and write are optional) and `ConsoleIO`, read as bytes and written in chunks of at most one frame, on from each answer's accepted (`stall_s`: TimeoutError when nothing is taken for that long) |
|
|
86
86
|
| `fixture` | `oep.fixture.gpio` (`set([(ch, mode)])` applied in order in one request, `read(channels)` the levels; the output strength is the probe's own - there is no drive selection) / `uart` (its stream is the plan's; nothing is received until `configure(baud, fmt)`; `status()` = `UartStatus(baud, format)` in force, baud 0 and format 0 while not configured; a configure the probe could not bring up raises `Failed` and leaves it not configured) / `i2c-target` (one form: `configure(address)` - the target made anew -, every controller write with data one frame for `read_rx`, reads answered from `preload_tx(data)` slots or 0xFF, `status()` = `I2cStatus(state, queued, rx_frames, tx_slots, errors)`, `stretch` when the ops offer it, `internal_pullups`: whether it enables pull-ups of its own; the reserved addresses 0x00-0x07 / 0x78-0x7F refused before sending) / `spi-target` (no reset: configure again; `cs_setup_ns`: how long after CS the first bit is sure; `oep dump` shows it) |
|
|
87
|
-
| `settings` | the core's settings (oep-spec core §13): fn 0's get / set / unset / save / state / network. `Settings(hst)`: `catalog()` = the names of this boot (each fn's category `core` / `name/instance`, items, choices, channel / route / netif names), `show()` = every value as (category, flat name, value,
|
|
87
|
+
| `settings` | the core's settings (oep-spec core §13): fn 0's get / set / unset / save / state / network. `Settings(hst)`: `catalog()` = the names of this boot (each fn's category `core` / `name/instance`, items, choices, channel / route / netif names), `show()` = every value as (category, flat name, value) (no origin: the firmware's initial values are values like any other, core §13.4), `changeable()` (set / unset offered), `set_named(category, {flat: value})` / `unset_named` / `unset_all`, raw `set` / `unset` by (fn, number, key), `save()`, `state()`, `network()`; `export()` / `plan_import(doc)` / `apply_import(plan)` = the settings file (oep-spec docs/oep-settings-file.ja.md; secrets never read); `find_line(source, "nrst", port=None)` = host guide §5.3's line lookup: port S's line N is the label `S.N` (ignoring ASCII case; without a port the one port named), `ports_named(labels)` the ports as written |
|
|
88
88
|
| `capture` | `oep.fixture.logic` / `analog` / `capture-group` (revision 1, oep-spec oep-if-capture). configure's TLVs follow core §2.3: a value the probe cannot honour is refused unsupported with the tag as sent (the table's TLVs go without the critical bit); the answer holds the actual values, its samples among them (analog: a scale for every role, a skew left out read as 0). Every `start()` returns a new generation (`LogicCapture.generation`, never re-used within a boot: past 0xFFFFFFFF a start is refused until a reboot) that read and release name - `read_segment(segment)` passes the segment's own - and every event carries (`events()` drops an earlier start's); streaming data frames carry their generation and their segment (segment_info, never crossing a segment: `Received.segments`); `status()` returns a `Status`. A capture-group binds configured tracks with an immediate trigger, all in one mode, and starts them at once (`CaptureGroup.start` -> the group's start_ns; the group's and each track's generation). Every segment read goes to the `Host.on_capture` callbacks as a `CaptureRecord` (the hook for run recorders; no wireskein dependency) |
|
|
89
89
|
| `decode` | decoding capture channels (I2C) |
|
|
90
90
|
| `registry` | generated from oep-spec's number table (never edited; copied again from oep-spec). The public way to reach an interface by name is `registry.INTERFACES[name]` (`.revision`, `.op`, `.tlv`, `.enum`, e.g. `INTERFACES["oep.fixture.uart"].enum["role"]`); the module-level names (`FIXTURE_UART`, ...) are the same objects |
|
|
@@ -135,12 +135,12 @@ A probe's CDC ports (`/dev/ttyACM*`) need no rule beyond the usual `dialout` gro
|
|
|
135
135
|
|
|
136
136
|
```sh
|
|
137
137
|
oep dump --port <probe> # what the probe offers (--virtual p4-x035: no hardware)
|
|
138
|
-
oep settings show <probe> # every value
|
|
138
|
+
oep settings show <probe> # every value, and the storage state
|
|
139
139
|
oep settings items <probe> # what each owner declares (names, forms, choices)
|
|
140
|
-
oep settings set <probe> core channel.GPIO23.label=nrst channel.GPIO20.idle=pull-up [--save]
|
|
140
|
+
oep settings set <probe> core channel.GPIO23.label=v003.nrst channel.GPIO20.idle=pull-up [--save]
|
|
141
141
|
oep settings unset <probe> core channel.GPIO20.idle [--save] # or --all (then --save empties the storage)
|
|
142
142
|
oep settings save <probe> # kept over a restart
|
|
143
|
-
oep settings export <probe> jig.oep-settings.json #
|
|
143
|
+
oep settings export <probe> jig.oep-settings.json # every value by name (secrets as present only)
|
|
144
144
|
oep settings import <probe> jig.oep-settings.json [--save] # shows the changes, asks, then sets / unsets
|
|
145
145
|
oep speed <probe> [921600,500000] [--verify] [--flows out:2] # port_speed: raise a UART bridge (default 500000), print the report (below)
|
|
146
146
|
oep pins <probe> --power 5 --wire swio # where the target is wired: debug pins, reset line, labels (below)
|
|
@@ -223,8 +223,8 @@ option-byte reader for the reset line, max_speed / idle_clock).
|
|
|
223
223
|
NRST exists (read only, never written); then resume.
|
|
224
224
|
5. **reset**: attach under reset through each candidate - the real line stops the hart at the reset vector.
|
|
225
225
|
6. **labels**: the connection profile the host keeps (wire, pins, line settings: the probe keeps no wiring) and the
|
|
226
|
-
labels
|
|
227
|
-
checked with `
|
|
226
|
+
labels `<port>.nrst` / `<port>.power_hi` (the port: `--name`, else the one port the probe's labels name; neither:
|
|
227
|
+
a note asks for `--name`), checked with `settings.find_line` against the probe's settings (a note when another channel already carries the name:
|
|
228
228
|
the line would not be found); `--save` writes the labels (set + save). Nothing is written without `--save`.
|
|
229
229
|
|
|
230
230
|
Safety: a driven or active channel is never driven, scanned or held; the power channel is touched only with `--power`;
|
|
@@ -236,7 +236,7 @@ pulls; its "pulled-up" row (channel 4, the reset line's weak pull-up) is left ou
|
|
|
236
236
|
alone that channel reads floating:
|
|
237
237
|
|
|
238
238
|
```
|
|
239
|
-
$ oep pins /run/board-identify/by-id/esp32-series-30eda0ea068b --power 5 --wire swio
|
|
239
|
+
$ oep pins /run/board-identify/by-id/esp32-series-30eda0ea068b --power 5 --wire swio --name v003
|
|
240
240
|
power: channel 5 low 300 ms (target off: read), then high 400 ms before the reads
|
|
241
241
|
classify: 52 channels, 16 reads each under pull-up, pull-down
|
|
242
242
|
floating 0-3,6,9-20,26-34,36-50,52-54
|
|
@@ -252,8 +252,8 @@ attach swio 19: target_id 00310510 (WCH DMI 0x7F) -> ch32v00x, halted at dpc 0x1
|
|
|
252
252
|
option bytes (read only): RST_MODE 10 (USER 0xf7): NRST on PD7, 12 ms ignore window
|
|
253
253
|
attach under reset through 4 (held 20 ms): dpc 0x0 -> the reset line
|
|
254
254
|
profile: wire swio, pins 19 (the host's to keep: the probe keeps no wiring)
|
|
255
|
-
label: oep settings set ... core channel.GPIO4.label=nrst (not written)
|
|
256
|
-
label: oep settings set ... core channel.GPIO5.label=power_hi (not written)
|
|
255
|
+
label: oep settings set ... core channel.GPIO4.label=v003.nrst (not written)
|
|
256
|
+
label: oep settings set ... core channel.GPIO5.label=v003.power_hi (not written)
|
|
257
257
|
idle: oep settings set ... core channel.GPIO5.idle=high (keeps the target powered while no plan holds channel 5; not written by this tool)
|
|
258
258
|
released every plan (channel 5 is back to its idle state: the target is powered only while something drives it)
|
|
259
259
|
done in 7.3 s
|
|
@@ -398,7 +398,8 @@ lines outside through `gpio_world`); uart receives nothing until configured, sta
|
|
|
398
398
|
(a write with data one frame, cut at max_length; reads from preload_tx slots, else 0xFF); spi-target without reset.
|
|
399
399
|
Capture: configure's TLVs follow core §2.3 alone; a bound track's start / configure is unavailable cause 4, a bind short
|
|
400
400
|
of resources cause 2 with the fn; a read at or past the write position answers write_pos and len 0. The settings (core §13): each fn's items from its
|
|
401
|
-
describe, values
|
|
401
|
+
describe, values without origin (the profile's initial values in on a boot without a save; labels unique but for ASCII
|
|
402
|
+
case, a probe of get alone with `changeable=False`); a value of another length
|
|
402
403
|
malformed; the generation counts the changes of a boot from 1; state is storage_state, saved_generation, reason; at boot
|
|
403
404
|
the saved values (by category and item name) are checked one by one, disable first, and what fails is left out (partial). port_speed: baud / step / verify_ms on the UART bridge the request came in on, within
|
|
404
405
|
port_speed_tolerance_pct, phases 0-3 read by port_state; the virtual bench goes back after verify_ms, after
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
oep_client/__init__.py,sha256=
|
|
2
|
-
oep_client/__main__.py,sha256=
|
|
1
|
+
oep_client/__init__.py,sha256=fPqGYg5bvjixvo-QAD2owR5YvUr0AopjUZEJpXsE1Jo,164
|
|
2
|
+
oep_client/__main__.py,sha256=DwmDPwTT0NpPQLm8KphkjSu5b_l9djUwyOsvYl15srI,30186
|
|
3
3
|
oep_client/arm.py,sha256=SszSLae8zErD4T-a1uQ6s6o3Lz7pe2SR8d3Qy-wi5rY,15633
|
|
4
4
|
oep_client/capture.py,sha256=uSzQT5MRvYlnKkxsob7vLtlvzQl_ffM8pbOC_re5ZjY,69259
|
|
5
5
|
oep_client/capture_blocks.py,sha256=GpENb0c2xIC8WkkPdHb5S1nQBV02v0rzRxN2ak_eQLQ,15283
|
|
@@ -162,11 +162,11 @@ oep_client/conformance_wire_options_sample.py,sha256=cWrssQEO6yEz8CdbEUI5HcaK_Mv
|
|
|
162
162
|
oep_client/conformance_writer_lease.py,sha256=dZv9AlyKHB--3YtqFQrBWK8wRYu2FetN78FR5OmERbw,7364
|
|
163
163
|
oep_client/conformance_writer_lease_sample.py,sha256=VBaq3Fk7Pd_6bbHQf9qLUZGBBO3XRYR1ax2b8z3xR60,467
|
|
164
164
|
oep_client/console.py,sha256=hDxoaXDCzk6KiOo-Y3RqC4yZuLPkCXOVi886_C4xXaE,12910
|
|
165
|
-
oep_client/core.py,sha256=
|
|
165
|
+
oep_client/core.py,sha256=qyOCFlujvSzIjbBbZmvA6cMjBn2FEOQYlq1KU2WLdOQ,22040
|
|
166
166
|
oep_client/decode.py,sha256=6pTFVBT0MLAKedZLVKyd5yT8hjrRw4fdDjnoDiE9XRs,2380
|
|
167
167
|
oep_client/discovery.py,sha256=5MurWfhiTGIz_bTNpB35Qz6HFe1vY_2HlINdTh3cV7U,20170
|
|
168
168
|
oep_client/dump.py,sha256=rnPOwbcXKk0ATk2Ka8O8p_XN6lTzmAQAhIHjm-iwT7I,13014
|
|
169
|
-
oep_client/endpoint.py,sha256=
|
|
169
|
+
oep_client/endpoint.py,sha256=SNYvhIltK2xn2smpz700fJVRBmTny4nrwx76l6vNqDw,234859
|
|
170
170
|
oep_client/fixture.py,sha256=GbAPXAk9GQ0qG6STL-rY4-EqXftmKfNKVI2xZZUiFzA,18108
|
|
171
171
|
oep_client/frames.py,sha256=44h0dy4xlW3S3y39k3jwxHoKkI2Ib2fsmHdjlPYF310,4990
|
|
172
172
|
oep_client/hardware.py,sha256=RboxkRxkQJ3DbNJEXyyiOFYlLFKGxrlcSwHho8zFC1U,30256
|
|
@@ -178,12 +178,12 @@ oep_client/link.py,sha256=E_8nnNJCr6O2NpUXl9qSmru-5j49p_mrImM70_ZQ6E4,142023
|
|
|
178
178
|
oep_client/linktest.py,sha256=zj9Ek28VkjyuIPsiU3iF-UEZmY_xMnNjNL-vK-jOQ3Q,10258
|
|
179
179
|
oep_client/message.py,sha256=Kw-PwkMCwa8VGUBd1HWTXe8Epe4FwNcBx8262mWx2rk,14152
|
|
180
180
|
oep_client/names.py,sha256=ohrRvQNURraYyJ-BtjHRwxGt_GLq-t8IO0aOji9Dhe0,3531
|
|
181
|
-
oep_client/pins.py,sha256=
|
|
181
|
+
oep_client/pins.py,sha256=6fAyJOF724Na-Yx1uxDI_wZ-5s0Qm7SAJ0GyglTmWaI,33256
|
|
182
182
|
oep_client/pytest_conformance.py,sha256=-bcJU2E1UE4xjQ7HZjaIgtp0l65cIibBTNFnaW2tews,2929
|
|
183
|
-
oep_client/registry.py,sha256=
|
|
183
|
+
oep_client/registry.py,sha256=2gmzJvCDynq5BmmxrahVOO5frcI21bmCiXHT2yGRAiw,21138
|
|
184
184
|
oep_client/riscv.py,sha256=ywBg1VvvpJydOO-D1UlpuKgNAVV8w1lvWhKc6JXeDlo,60428
|
|
185
185
|
oep_client/rp2350.py,sha256=7OMyj270k3AY1e3-ljicPwOjf9jOlokbT6QNkA0b0yY,4227
|
|
186
|
-
oep_client/settings.py,sha256=
|
|
186
|
+
oep_client/settings.py,sha256=dccTo1m56oN4fca0b69GAORHI2H9L9tic6K-F2OqkTQ,24938
|
|
187
187
|
oep_client/speed_record.py,sha256=6THUKbN7gQKpfIdUO2551P8mXiAFqbFDky72Xbki7L0,7229
|
|
188
188
|
oep_client/targets.py,sha256=zFekSr76NVO7tzdgyQsLDHLWRvkgylfwvGNTz_hQGf8,5041
|
|
189
189
|
oep_client/uiapduino.py,sha256=oafP1SyKPO3cox-2RYbWUZusy4vc6NsEVC7Z-Y1sjWI,6337
|
|
@@ -192,7 +192,7 @@ oep_client/virtual_allocator_model.py,sha256=XBrbjG2Zupf0P28gjUhQVDw6UzO2jQXsYZ1
|
|
|
192
192
|
oep_client/virtual_analog_model.py,sha256=9ohE6xm3Av0o37VgiKRu4sD9-D0bOqW2rUvwrguCPJU,13853
|
|
193
193
|
oep_client/virtual_arm_adi_model.py,sha256=xlqhjr_QCF6MNVOnVelTanlNWpmaCJ5uWXf6V9WkqUM,8121
|
|
194
194
|
oep_client/virtual_attach_model.py,sha256=YhGYz4X2IcdBMsBb0vjkV2UWUst6tSVieZHUwQZ89gA,10433
|
|
195
|
-
oep_client/virtual_bench.py,sha256=
|
|
195
|
+
oep_client/virtual_bench.py,sha256=4wTcB3Z3Ofv5FbsNJ4LNihcBQyrt_2ALoCO5rhGG2wE,41625
|
|
196
196
|
oep_client/virtual_bench_capture.py,sha256=SJcsW_dvKAc7k_BvRbY-3PZF60F0rbfrlGholoNyUnw,77412
|
|
197
197
|
oep_client/virtual_bench_mdns.py,sha256=cnJNBKg5zTduXUcH-wqN_ZBDGl7DyL-tL3CBn2jro0E,13305
|
|
198
198
|
oep_client/virtual_bench_serial.py,sha256=EvbdOYeGrM6CRti2b90I0PzpmmherfoJRaAg4v7Iq9Y,13205
|
|
@@ -246,8 +246,8 @@ oep_client/virtual_tlv_request_model.py,sha256=gp-eiN784NnloQBANG_4TnM6oUCmrj046
|
|
|
246
246
|
oep_client/virtual_uart_model.py,sha256=Sd_MWLjl1-6_bemsCuI2at9zpVJRBehFQgZ6By97PdQ,9386
|
|
247
247
|
oep_client/virtual_wire_options_model.py,sha256=1QNF16hRWmdFt0J7Zd1WKZdEc07vns6osMQLetMBlXw,9206
|
|
248
248
|
oep_client/virtual_writer_lease_model.py,sha256=E1MAQsGuShqONVdIeLubun7Cop0URNUSk8wicPicbt4,1728
|
|
249
|
-
oep_client_python-0.0.
|
|
250
|
-
oep_client_python-0.0.
|
|
251
|
-
oep_client_python-0.0.
|
|
252
|
-
oep_client_python-0.0.
|
|
253
|
-
oep_client_python-0.0.
|
|
249
|
+
oep_client_python-0.0.39.dist-info/METADATA,sha256=DKEFrj6sNxWzMUlMTosL36BankLLWWsEuioOuS6Ml3c,53653
|
|
250
|
+
oep_client_python-0.0.39.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
251
|
+
oep_client_python-0.0.39.dist-info/entry_points.txt,sha256=XEt39Jz7oQyU9Hfg_tV6eDGofNxWYrjSfslxKP-fh70,135
|
|
252
|
+
oep_client_python-0.0.39.dist-info/licenses/LICENSE,sha256=ntlalztb3L1OaNAxmQdHlubu5fVhjMc9oRZi3PeQhXg,1076
|
|
253
|
+
oep_client_python-0.0.39.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|