- Tidy Layout lays linked boxes out left to right by signal flow, in as few
columns as the longest chain allows; feedback loops are cut where they run
backwards, and sources sit just before the boxes they feed.
- Boxes with no link on the open sheet go to the bottom of the first column,
alphabetically.
- A box that wires skipping its column would cross is pushed down below them.
- The wiresheet is fixed at meta's 255-step reach; outside it is light grey.
Dragging keeps a box's top-left corner on the sheet.
- Sox write_i4 wraps to signed, so meta with x >= 128 reaches the device.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The device goes down without answering, so the editor waited out the 30 s
Sox timeout before dropping the session. The invoke now waits 3 s (time for
DASP to resend it once) and the editor disconnects either way; an error
reply from the device is still reported and keeps the connection. Covers the
App right-click actions and the restart Put with Files offers.
- sox: request/requests/invoke take a timeout
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follows Ontrol's Workbench tool (BPutWithFilesTool / PutApp): the backup's
app.sax is encoded for the device's kits and put as app.sab.writing, renamed
to app.sab.stage; the platform service's deleteFile clears fonts/* and
images/*, then images, fonts (from Sedona home's bank, in the device's cut)
and the boot logo go. The zip is checked against the device first: same
platform (Orion, Rion or P-ION, from the platform id), and kits the device
can load. Falls back to the backup's app.sab, with its kits.scode if needed.
- sab_app.encode_sax: .sax -> .sab, ported from Py_sab2sax with Java's value
parsing (fromLiteral, nullbool/nullfloat, Integer.decode); byte-identical
to sedonac on the saved backups
- app_backup.native_image: port of OntOxImageUtil's png -> native .bin
- sox.put_file / rename_file
- Get with Files: Orion's boot logo is system/logo.jpg
- A restart that goes unanswered is taken as the device going down
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tested on a P-ION (fw 1.32) at 192.168.1.252 with everything else as
in 5acca52: with the 0x81 header forced on, the app save is answered
but the app.sab file open never is, and the device drops off the
network. Without it, the same Get with File works.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The save and file-open requests used their own fixed resend intervals
(5 s / 15 s) and the hello asked for 256-byte packets. Ontrol's Sedona
Java stack does neither and works with the P-ION: the standard backoff
(1, 2, 4, 8 s) sends a busy device a few copies with the same seqNum,
which its receive window drops. Both are removed.
Also fixes a race in SendWindow.send(): the packet entered the window
before its payload and sent_time were set, so housekeeping could resend
it with an empty payload, and the device would then drop the real one
as a duplicate.
Tested on a P-ION at 192.168.1.252, App > Get with File: save 3.2 s
(2 resends), app.sab + kits.scode in 11.3 s with 500-byte chunks
(was 14.6 s at 256), device stayed up, files identical to a 256-byte
backup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A real P-ION (fw 1.32) restarted on every Sox file get. Ontrol's Niagara 4
driver gets app.sab from the same device, so its settings are copied:
- DASP hello asks for 256-byte packets (idealMax/absMax), so file chunks
are 238 bytes
- the message-length header (0x81) goes to Orion only, decided once the
platform id is read
- the app is saved on the device before app.sab is read, the save resent
every 5 s rather than 1 s (a P-ION save takes 2-3+ s)
- the file open is resent every 15 s; DASP gains a per-packet resend
interval for this
Tested on a P-ION at 192.168.1.252: save 3.2 s, app.sab + kits.scode +
app.sax in 15.8 s, no resends, device stayed up. The unsaved-changes
question became a notice, since the app is now always saved first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reads app.sab and kits.scode over Sox file transfer, decodes app.sab to
app.sax (byte-identical to sedonac), and on Orion/Rion also fetches the
images and fonts the app names, with their sources from Sedona home's
banks. Offers to save the app on the device first when it is dirty, and
asks where to save with <Sedona home>/store/apps and
app-<device>-<app>-<timestamp>.zip filled in.
DASP trace lines are now printed by their own thread: printing from the
network threads to an unread stdout pipe blocked the receiver and made
file transfers time out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The device builds the schema and version replies in a fixed 512-byte
packet and drops whatever does not fit. Read strings defensively, recover
a cut-off checksum from the one local manifest matching its prefix, and
fail with a clear message when the kit list cannot be recovered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix add component from palette in live mode: resolve <app> element
as comp_id 0, fix type_id lookup to use type_name_map, set type_name
on newly added SoxComponent, sync device values into DOM after add,
write meta position back to device, and subscribe new component to COV.
Fix delete component in live mode: clean up subscriptions for deleted
component and all its descendants.
Add tree root context menu with device app actions (save, hibernate,
quit, restart, reboot) in live mode. Device save clears dirty state.
Clean up connection lost handling to do a full disconnect.
Fix slot registry to copy base slots and renumber IDs correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Slots with no flags (sensor, controlSignal, out_*) were not skipped
during config property reads, consuming bytes that the device never
sent and misaligning every subsequent config value (setpoint,
proportionalBand, etc.) into tiny denormalized floats like 2.36875e-41.
Changed the filter in _apply_props from "skip if runtime" to "skip if
not config" so only explicitly config-flagged slots are read from config
messages. Also switched iter("slot") to findall("slot") in the slot
registry builder to avoid picking up nested descendants.
Float display now defaults to 2 decimal places everywhere (wiresheet
and properties panel), honouring the precision facet when one exists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add type_name_map on SoxClient so components loaded after connect
resolve proper names (e.g. control::And2) instead of kit::typeN.
This lets the slot registry and final_components_registry match,
so component boxes show their slots and property values.
- Load live schema gracefully: skip missing kit manifests instead of
aborting, compile the kits we have, and warn about missing ones in
the status bar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use RLock instead of Lock for _close_lock so close() can call
_shutdown() without deadlocking — previously the session never
marked itself closed, causing infinite keepalive sends.
Mask kit checksums to unsigned 32-bit for hex display so negative
i4 values render as proper 8-digit hex (e.g. d3984c51 not -2c67b3af).
Tested against live device at 192.168.1.251 — full handshake,
schema read, and component tree load confirmed working.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port the DASP transport and Sox protocol stack from sedonaOntrol Java
to Python, enabling live device editing alongside the existing offline
SAX editor. Lazy-loads components on navigation, caches them, and
subscribes to COV events for the active wiresheet.
New files: dasp.py, sox.py, device_session.py, connect_dialog.py,
live_hooks.py. Modified: app_state.py (live-mode state), main.py
(Device menu, COV polling, lazy tree expand), properties.py (Sox
write-through), sax_file.py (structural mutations route through
live_hooks), schema.py (find_manifest helper).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>