pycontrol-core 3.0.0a2__tar.gz → 3.0.0a4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/PKG-INFO +7 -8
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/README.md +6 -7
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/README.md +5 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/architecture.md +8 -8
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/changelog.md +2 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/cli-reference.md +2 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/contracts.md +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/experiment-extensions.md +5 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/extension-settings.md +6 -3
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/getting-started.md +34 -36
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/hardware-definitions.md +3 -3
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/migration-from-official.md +54 -4
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/public-api.md +5 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/sharing.md +2 -5
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/subscriber-api.md +8 -5
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/task-extensions.md +35 -6
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/troubleshooting.md +7 -7
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/wire-protocol.md +8 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/writing-tasks.md +4 -4
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/pyproject.toml +4 -4
- pycontrol_core-3.0.0a4/src/pycontrol/__init__.py +5 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/__init__.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/models.py +2 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/protocol.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/run.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/zipio.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/firmware.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/handlers.py +3 -3
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/setups.py +3 -3
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/main.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/config/models.py +20 -7
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/loader.py +2 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/__init__.py +5 -5
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/device_helpers.py +10 -10
- pycontrol_core-3.0.0a4/src/pycontrol/firmware/devices/LED_driver.py +6 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/MCP.py +7 -7
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/VL53L4.py +5 -5
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/__init__.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/analog_LED.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/audio_board.py +2 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/audio_player.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/breakout_1_2.py +6 -6
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/breakout_H7.py +6 -6
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/five_poke.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/frame_logger.py +4 -4
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/frame_trigger.py +5 -5
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/grid_maze.py +14 -14
- pycontrol_core-3.0.0a4/src/pycontrol/firmware/devices/lickometer.py +18 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/load_cell.py +3 -3
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/nine_poke.py +11 -11
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/poke.py +7 -7
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/port_expander.py +4 -4
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/rotary_encoder.py +6 -6
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/schmitt_trigger.py +4 -4
- pycontrol_core-3.0.0a4/src/pycontrol/firmware/devices/solenoid_driver.py +12 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/stepper_motor.py +4 -4
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/uRFID.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/uart_handler.py +3 -3
- {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/__init__.py +4 -4
- {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/audio.py +5 -5
- {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/framework.py +6 -6
- {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/hardware.py +38 -38
- {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/profiles.py +3 -2
- {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/state_machine.py +2 -2
- {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/timer.py +1 -1
- {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/utility.py +6 -6
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/integrations/__init__.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/integrations/notify/__init__.py +1 -1
- pycontrol_core-3.0.0a4/src/pycontrol/mcu.py +32 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/protocol/analog.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/protocol/decoder.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/protocol/messages.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/provenance/__init__.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/provenance/collect.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/provenance/hashing.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/provenance/models.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/formatting.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/native_event_logger.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/board_session.py +25 -19
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/device_resolution.py +3 -3
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/services/firmware.py +7 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace/__init__.py +2 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace/creation.py +3 -3
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace/hwdef_inspect.py +6 -4
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace/task_inspect.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/devices/__init__.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/devices/example_widget.py +4 -4
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/experiments/blink_demo.json +1 -1
- pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/old_board.py → pycontrol_core-3.0.0a4/src/pycontrol/workspace_template/hardware_definitions/bliink_f4_hw.py +9 -9
- pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/karpova_TOF_h7.py → pycontrol_core-3.0.0a4/src/pycontrol/workspace_template/hardware_definitions/sequence_hw.py +18 -18
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/tasks/blinker.py +4 -4
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/tasks/examples/blinker.py +4 -4
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/tasks/examples/button.py +7 -7
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/CLASSIFICATION.md +2 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/build_synthetic.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/blinker_real.out.bin +8 -8
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/counter_demo_real.out.bin +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/encoder_real.out.bin +8 -8
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/spec_frames.py +2 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/tasks/blinker_real.task.py +2 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/tasks/counter_demo_real.task.py +2 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/tasks/encoder_real.task.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/fake_board/__init__.py +2 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/fake_board/responder.py +2 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/test_board_session.py +60 -19
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/test_cli_workspace.py +5 -5
- pycontrol_core-3.0.0a4/tests/unit/firmware/conftest.py +62 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/test_device_helpers.py +51 -9
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/test_devices_init.py +9 -8
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/test_hardware_uses_profile.py +48 -48
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/test_profiles.py +37 -37
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/test_timer_rounding.py +20 -20
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_cli.py +2 -2
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_config_models.py +8 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_firmware_root.py +2 -2
- pycontrol_core-3.0.0a4/tests/unit/test_firmware_service.py +33 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_protocol_decoder.py +1 -1
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_workspace.py +13 -11
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_workspace_hwdef_inspect.py +21 -17
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tools/README.md +3 -2
- pycontrol_core-3.0.0a2/docs/audit-2026-05-29.md +0 -408
- pycontrol_core-3.0.0a2/docs/code-review.md +0 -122
- pycontrol_core-3.0.0a2/docs/integrations.md +0 -50
- pycontrol_core-3.0.0a2/src/pycontrol/__init__.py +0 -5
- pycontrol_core-3.0.0a2/src/pycontrol/firmware/devices/LED_driver.py +0 -6
- pycontrol_core-3.0.0a2/src/pycontrol/firmware/devices/lickometer.py +0 -18
- pycontrol_core-3.0.0a2/src/pycontrol/firmware/devices/solenoid_driver.py +0 -12
- pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/encoder.py +0 -19
- pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/encoder_h7.py +0 -28
- pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/example.py +0 -17
- pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/example_h7.py +0 -16
- pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/usr_button.py +0 -17
- pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/usr_button_h7.py +0 -22
- pycontrol_core-3.0.0a2/tests/unit/firmware/conftest.py +0 -33
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/.gitignore +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/.pre-commit-config.yaml +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/DELETED.md +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/LICENSE +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/go.mod +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/go.sum +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/internal/bootstrap/bootstrap.go +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/internal/bootstrap/bootstrap_test.go +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/main.go +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/pycontrol-init +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/data-format.md +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/design-history/simplified-pycontrol-provenance-and-sharing-motivation.md +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/design-history/simplified-pycontrol-provenance-and-sharing-plan.md +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/testing.md +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/threading-and-state.md +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/collect.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/legacy.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bus/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bus/enriched.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bus/events.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bus/subscriber.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bus/timestamps.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/board.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/experiment.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/run.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/vars.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/workspace.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/config/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/config/io.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/experiment/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/experiment/fleet_fanin.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/experiment/hooks.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/controls.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/experiment_api.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/task_api.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/task_controls_context.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/fleet/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/fleet/envelope.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/integrations/_queue.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/integrations/notify/slack.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/integrations/notify/telegram.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/protocol/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/protocol/encoder.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/provenance/archive.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/analog_writer.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/console.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/fleet_aggregator.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/tsv_logger.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/repl/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/repl/device_helpers.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/repl/file_transfer.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/repl/json_repl.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/repl/raw_repl.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/commands.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/info.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/services/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/services/setup.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/base.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/mock.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/recording.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/replay.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/serial.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/experiment_extensions/notion_logger.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/experiment_extensions/s3_data_exporter.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/task_controls/example_dialog.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/task_extensions/example_extension.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/task_extensions/run_notify.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/task_extensions/trial_summary.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/settings.json +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/setups.json +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/conftest.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/contracts/test_bus_contract.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/contracts/test_data_contract.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/contracts/test_fleet_envelope_contract.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/contracts/test_session_lifecycle_contract.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/contracts/test_task_authoring_contract.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/firmware_stubs/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/firmware_stubs/micropython.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/firmware_stubs/pyb.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/firmware_stubs/ucollections.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/firmware_stubs/ujson.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/analog.in.bin +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/blinker.in.bin +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/blinker_real.in.bin +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/counter_demo_real.in.bin +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/encoder_real.in.bin +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/random_ratio.in.bin +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/replay.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/blinker_real.meta.json +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/blinker_real.tsv +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/counter_demo_real.meta.json +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/counter_demo_real.tsv +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/encoder_real.meta.json +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/encoder_real.tsv +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/encoder_real_encoder.data.npy +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/encoder_real_encoder.time.npy +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/test_experiment_runner.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/test_real_hardware_replay.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/test_repl.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/__init__.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_bundles_collect.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_bus_subscriber.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_extension.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_file_transfer_errors.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_fleet_fanin.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_golden_fixtures.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_hwdef_ordering.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_integrations_notify.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_protocol_encoder.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_provenance.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_recording_tsv.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_transport_mock.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_workspace_template.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tools/analog_diagnose.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tools/capture_fixtures.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tools/encoder_diagnose.py +0 -0
- {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pycontrol-core
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0a4
|
|
4
4
|
Summary: Sans-IO host library for the pyControl behavioural-experiment framework.
|
|
5
5
|
Project-URL: Homepage, https://github.com/karpova-lab/pycontrol-core
|
|
6
6
|
Author: pyControl contributors
|
|
@@ -183,8 +183,8 @@ See [public-api.md](docs/public-api.md) for the supported import surface.
|
|
|
183
183
|
New to the project? Start with the repo-level guides:
|
|
184
184
|
|
|
185
185
|
- [Documentation index](../rewrite-docs/README.md) - map of everything, with a reading order.
|
|
186
|
-
- [Architecture tour](../rewrite-docs/
|
|
187
|
-
- [
|
|
186
|
+
- [Architecture tour](../rewrite-docs/ARCHITECTURE-TOUR.md) - learn the system end to end.
|
|
187
|
+
- [Technical overview](../rewrite-docs/TECHNICAL.html) - what changed versus pyControl v2 and why.
|
|
188
188
|
|
|
189
189
|
Orientation and operation:
|
|
190
190
|
|
|
@@ -208,19 +208,18 @@ Contracts and internals:
|
|
|
208
208
|
|
|
209
209
|
Extending pyControl:
|
|
210
210
|
|
|
211
|
-
- [Task extensions](docs/task-extensions.md)
|
|
211
|
+
- [Task extensions](docs/task-extensions.md) - includes integrations and optional extras.
|
|
212
212
|
- [Experiment extensions](docs/experiment-extensions.md)
|
|
213
213
|
- [Extension settings](docs/extension-settings.md)
|
|
214
|
-
- [Integrations](docs/integrations.md)
|
|
215
214
|
- [Task sharing archives](docs/sharing.md)
|
|
216
215
|
|
|
217
216
|
Project process:
|
|
218
217
|
|
|
219
218
|
- [Testing guide](docs/testing.md)
|
|
220
219
|
- [Changelog](docs/changelog.md)
|
|
221
|
-
- [Current code review backlog](docs/code-review.md)
|
|
222
220
|
|
|
223
221
|
## Contributing
|
|
224
222
|
|
|
225
|
-
See the repository-level [CONTRIBUTING.md](../CONTRIBUTING.md)
|
|
226
|
-
installs, tests, contracts, firmware notes, and trusted plugin
|
|
223
|
+
See the repository-level [CONTRIBUTING.md](../pycontrol-archive/CONTRIBUTING.md)
|
|
224
|
+
for editable installs, tests, contracts, firmware notes, and trusted plugin
|
|
225
|
+
guidance.
|
|
@@ -143,8 +143,8 @@ See [public-api.md](docs/public-api.md) for the supported import surface.
|
|
|
143
143
|
New to the project? Start with the repo-level guides:
|
|
144
144
|
|
|
145
145
|
- [Documentation index](../rewrite-docs/README.md) - map of everything, with a reading order.
|
|
146
|
-
- [Architecture tour](../rewrite-docs/
|
|
147
|
-
- [
|
|
146
|
+
- [Architecture tour](../rewrite-docs/ARCHITECTURE-TOUR.md) - learn the system end to end.
|
|
147
|
+
- [Technical overview](../rewrite-docs/TECHNICAL.html) - what changed versus pyControl v2 and why.
|
|
148
148
|
|
|
149
149
|
Orientation and operation:
|
|
150
150
|
|
|
@@ -168,19 +168,18 @@ Contracts and internals:
|
|
|
168
168
|
|
|
169
169
|
Extending pyControl:
|
|
170
170
|
|
|
171
|
-
- [Task extensions](docs/task-extensions.md)
|
|
171
|
+
- [Task extensions](docs/task-extensions.md) - includes integrations and optional extras.
|
|
172
172
|
- [Experiment extensions](docs/experiment-extensions.md)
|
|
173
173
|
- [Extension settings](docs/extension-settings.md)
|
|
174
|
-
- [Integrations](docs/integrations.md)
|
|
175
174
|
- [Task sharing archives](docs/sharing.md)
|
|
176
175
|
|
|
177
176
|
Project process:
|
|
178
177
|
|
|
179
178
|
- [Testing guide](docs/testing.md)
|
|
180
179
|
- [Changelog](docs/changelog.md)
|
|
181
|
-
- [Current code review backlog](docs/code-review.md)
|
|
182
180
|
|
|
183
181
|
## Contributing
|
|
184
182
|
|
|
185
|
-
See the repository-level [CONTRIBUTING.md](../CONTRIBUTING.md)
|
|
186
|
-
installs, tests, contracts, firmware notes, and trusted plugin
|
|
183
|
+
See the repository-level [CONTRIBUTING.md](../pycontrol-archive/CONTRIBUTING.md)
|
|
184
|
+
for editable installs, tests, contracts, firmware notes, and trusted plugin
|
|
185
|
+
guidance.
|
|
@@ -20,6 +20,9 @@ enough. Use `--prerelease allow` so uv can resolve the current PyPI alpha.
|
|
|
20
20
|
```bash
|
|
21
21
|
mkdir my-workspace
|
|
22
22
|
cd my-workspace
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```bash
|
|
23
26
|
uv init --bare --python 3.11
|
|
24
27
|
uv add pycontrol-gui --prerelease allow
|
|
25
28
|
uv run pycontrol workspace init .
|
|
@@ -31,6 +34,8 @@ uv run pycontrol-gui
|
|
|
31
34
|
```powershell
|
|
32
35
|
mkdir my-workspace
|
|
33
36
|
cd my-workspace
|
|
37
|
+
```
|
|
38
|
+
```powershell
|
|
34
39
|
uv init --bare --python 3.11
|
|
35
40
|
uv add pycontrol-gui --prerelease allow
|
|
36
41
|
uv run pycontrol workspace init .
|
|
@@ -139,19 +139,19 @@ cycle is to keep:
|
|
|
139
139
|
```text
|
|
140
140
|
┌──────────────────────────────┐
|
|
141
141
|
│ hwdef (per-rig, .py) │ user authors; wires breakout pins to
|
|
142
|
-
│ pin_X = ...; led = ... │
|
|
142
|
+
│ pin_X = ...; led = ... │ DigitalInput/Output, AnalogInput, etc.
|
|
143
143
|
└──────────────┬───────────────┘
|
|
144
144
|
│ uses
|
|
145
145
|
▼
|
|
146
146
|
┌──────────────────────────────┐
|
|
147
|
-
│ Breakout (geometry only) │
|
|
147
|
+
│ Breakout (geometry only) │ Breakout12 / BreakoutH7 / ... map
|
|
148
148
|
│ port_1.DIO_A = "X1", ... │ human pin labels to MCU pin IDs.
|
|
149
149
|
└──────────────┬───────────────┘
|
|
150
150
|
│ runs on top of active profile
|
|
151
151
|
▼
|
|
152
152
|
┌──────────────────────────────┐
|
|
153
153
|
│ BoardProfile (MCU-specific) │ StmF405Profile / StmH743Profile own the
|
|
154
|
-
│ timer pool, reservations, │ MCU-only resources. `
|
|
154
|
+
│ timer pool, reservations, │ MCU-only resources. `pycontrol.profile`
|
|
155
155
|
│ encoder pins/AF │ is the runtime singleton, auto-selected
|
|
156
156
|
└──────────────────────────────┘ from detected board MCU at import time.
|
|
157
157
|
```
|
|
@@ -159,12 +159,12 @@ cycle is to keep:
|
|
|
159
159
|
A typical H7 hwdef can now be:
|
|
160
160
|
|
|
161
161
|
```python
|
|
162
|
-
from devices.breakout_H7 import
|
|
162
|
+
from devices.breakout_H7 import BreakoutH7
|
|
163
163
|
|
|
164
|
-
from devices import
|
|
164
|
+
from devices import DigitalOutput # drivers cache from `profile` here
|
|
165
165
|
```
|
|
166
166
|
|
|
167
|
-
Profile selection is automatic from the board MCU at `
|
|
167
|
+
Profile selection is automatic from the board MCU at `pycontrol` import time.
|
|
168
168
|
|
|
169
169
|
The host validates this layering both statically and at runtime:
|
|
170
170
|
|
|
@@ -189,7 +189,7 @@ Since the firmware is vendored under `src/pycontrol/firmware/`, the
|
|
|
189
189
|
host always knows what the on-board copy *should* be -- but it never
|
|
190
190
|
mutates the board behind the operator's back. `BoardSession.connect()`
|
|
191
191
|
runs a read-only djb2 diff against the baseline files under
|
|
192
|
-
`/flash/
|
|
192
|
+
`/flash/pycontrol/`, the required baseline modules in `/flash/devices/`,
|
|
193
193
|
and the top-level files listed in `_FIRMWARE_TOPLEVEL_FILES` (currently
|
|
194
194
|
just `device_helpers.py`, the on-board helper module that mirrors what
|
|
195
195
|
the host execs into the raw REPL at connect time). Optional bundled
|
|
@@ -208,7 +208,7 @@ The CLI keys off this:
|
|
|
208
208
|
`.py` files the baseline doesn't manage (override with
|
|
209
209
|
`--keep-extras`). Optional bundled drivers are re-added on demand.
|
|
210
210
|
- `pycontrol run` warns loudly when out of sync and proceeds (the run
|
|
211
|
-
will likely fail at the `from
|
|
211
|
+
will likely fail at the `from pycontrol import *`, but that's the
|
|
212
212
|
operator's call); pass `--sync-firmware` to push first.
|
|
213
213
|
|
|
214
214
|
GUIs follow the same pattern: read `firmware_status` after `connect()`,
|
|
@@ -91,4 +91,5 @@
|
|
|
91
91
|
|
|
92
92
|
> Note: performance benches and hardware-in-the-loop tests have since moved
|
|
93
93
|
> out of `pycontrol-core` into the sibling `pycontrol-archive/` tree, so they
|
|
94
|
-
> stay out of the default offline CI run.
|
|
94
|
+
> stay out of the default offline CI run. The `perf` pytest marker moved with
|
|
95
|
+
> them and is no longer defined in `pycontrol-core`. See `docs/testing.md`.
|
|
@@ -30,7 +30,7 @@ tracebacks so developer bugs remain visible during testing.
|
|
|
30
30
|
## Workspace Commands
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
pycontrol workspace init [path] [--force] [--no-configure-ruff]
|
|
33
|
+
pycontrol workspace init [path] [--force] [--configure-ruff | --no-configure-ruff]
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Creates a workspace by copying the bundled workspace template:
|
|
@@ -151,7 +151,7 @@ name-keyed `setups.json` shape before this CLI can load them.
|
|
|
151
151
|
|
|
152
152
|
```bash
|
|
153
153
|
pycontrol firmware status --setup BoxA
|
|
154
|
-
pycontrol firmware status --setup BoxA
|
|
154
|
+
pycontrol firmware status --setup BoxA --verbose
|
|
155
155
|
pycontrol firmware sync --setup BoxA
|
|
156
156
|
pycontrol firmware sync --setup BoxA --force
|
|
157
157
|
pycontrol firmware sync --setup BoxA --keep-extras
|
|
@@ -115,7 +115,7 @@ Rules:
|
|
|
115
115
|
|
|
116
116
|
The task authoring surface is defined by:
|
|
117
117
|
|
|
118
|
-
- `src/pycontrol/firmware/
|
|
118
|
+
- `src/pycontrol/firmware/pycontrol/`
|
|
119
119
|
- baseline files plus on-demand bundled drivers in `src/pycontrol/firmware/devices/`
|
|
120
120
|
- `src/pycontrol/firmware/device_helpers.py`
|
|
121
121
|
- `src/pycontrol/extension/task_api.py`
|
|
@@ -197,8 +197,8 @@ from pycontrol.extension import ExperimentExtension
|
|
|
197
197
|
|
|
198
198
|
|
|
199
199
|
SETTINGS = (
|
|
200
|
-
BoolSetting("enabled", "Enabled", default=True),
|
|
201
|
-
StringSetting("webhook_url", "Webhook URL", secret=True),
|
|
200
|
+
BoolSetting("enabled", "Enabled", default=True, hint="Enable or disable this extension."),
|
|
201
|
+
StringSetting("webhook_url", "Webhook URL", secret=True, hint="Destination used for experiment notifications."),
|
|
202
202
|
)
|
|
203
203
|
|
|
204
204
|
|
|
@@ -211,6 +211,9 @@ class Notifier(ExperimentExtension):
|
|
|
211
211
|
send_notification(webhook_url, context.to_dict())
|
|
212
212
|
```
|
|
213
213
|
|
|
214
|
+
Setting helpers accept `hint="..."`; the GUI Settings dialog shows that text as
|
|
215
|
+
a tooltip when the user hovers over the setting label.
|
|
216
|
+
|
|
214
217
|
## Examples
|
|
215
218
|
|
|
216
219
|
### Write a Run Manifest
|
|
@@ -30,8 +30,8 @@ from pycontrol.extension import TaskExtension
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
SETTINGS = (
|
|
33
|
-
BoolSetting("slack_enabled", "Slack enabled", default=False),
|
|
34
|
-
StringSetting("slack_bot_token", "Slack bot token", secret=True),
|
|
33
|
+
BoolSetting("slack_enabled", "Slack enabled", default=False, hint="Send run notifications to Slack."),
|
|
34
|
+
StringSetting("slack_bot_token", "Slack bot token", secret=True, hint="Bot token used to post messages."),
|
|
35
35
|
StringSetting("slack_channel_id", "Slack channel ID"),
|
|
36
36
|
)
|
|
37
37
|
|
|
@@ -49,7 +49,7 @@ from pycontrol.extension import TaskExtension
|
|
|
49
49
|
|
|
50
50
|
class TrialMonitor(TaskExtension):
|
|
51
51
|
SETTINGS = (
|
|
52
|
-
IntSetting("min_trials", "Minimum trials", default=20, minimum=1),
|
|
52
|
+
IntSetting("min_trials", "Minimum trials", default=20, minimum=1, hint="Ignore sessions with fewer trials."),
|
|
53
53
|
)
|
|
54
54
|
```
|
|
55
55
|
|
|
@@ -83,6 +83,9 @@ StringSetting("bot_token", "Bot token", secret=True)
|
|
|
83
83
|
|
|
84
84
|
Secret fields are masked in the Settings dialog, but they are still saved in the workspace `settings.json`.
|
|
85
85
|
|
|
86
|
+
All setting helpers accept `hint="..."`. When provided, the Settings dialog shows
|
|
87
|
+
the hint as a tooltip when the user hovers over the setting label.
|
|
88
|
+
|
|
86
89
|
## Runtime Access
|
|
87
90
|
|
|
88
91
|
At runtime, the extension receives two attributes:
|
|
@@ -54,49 +54,47 @@ go build .
|
|
|
54
54
|
--gui-path /path/to/rewrite/pycontrol-gui
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
## Manual Path
|
|
57
|
+
## Manual Path With uv
|
|
58
58
|
|
|
59
|
-
If you do not want to use `pycontrol-init`, create a workspace with
|
|
60
|
-
|
|
59
|
+
If you do not want to use `pycontrol-init`, create a workspace directly with
|
|
60
|
+
[uv](https://docs.astral.sh/uv/).
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
mkdir my-workspace
|
|
64
|
-
cd my-workspace
|
|
65
|
-
git init
|
|
66
|
-
uv init --bare --python 3.11
|
|
62
|
+
### Fastest path: install from PyPI
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
This is the same four-line setup shown on the community showcase page. In a new,
|
|
65
|
+
empty folder:
|
|
70
66
|
|
|
67
|
+
```bash
|
|
68
|
+
uv init --bare --python 3.11
|
|
69
|
+
uv add pycontrol-gui --prerelease allow
|
|
71
70
|
uv run pycontrol workspace init .
|
|
72
71
|
uv run pycontrol-gui
|
|
73
72
|
```
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
`pycontrol-gui` depends on `pycontrol-core`, so adding the GUI package is enough
|
|
75
|
+
for GUI users. To pin a specific alpha for reproducible workspaces instead of
|
|
76
|
+
taking the newest, use `uv add "pycontrol-gui==3.0.0a1"` (the pip equivalent is
|
|
77
|
+
`pip install --pre "pycontrol-gui==3.0.0a1"`). Add integration extras separately
|
|
78
|
+
when needed, for example `uv add "pycontrol-core[slack]" --prerelease allow`;
|
|
79
|
+
replace `slack` with `notion`, `s3`, or `integrations`.
|
|
77
80
|
|
|
78
|
-
|
|
79
|
-
uv add "pycontrol-gui==3.0.0a1"
|
|
80
|
-
```
|
|
81
|
+
### From a local source checkout (contributors)
|
|
81
82
|
|
|
82
|
-
To
|
|
83
|
+
To develop against editable local checkouts of the packages:
|
|
83
84
|
|
|
84
85
|
```bash
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
mkdir my-workspace
|
|
87
|
+
cd my-workspace
|
|
88
|
+
git init
|
|
89
|
+
uv init --bare --python 3.11
|
|
87
90
|
|
|
88
|
-
|
|
91
|
+
uv add --editable /path/to/rewrite/pycontrol-core
|
|
92
|
+
uv add --editable /path/to/rewrite/pycontrol-gui
|
|
89
93
|
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
uv run pycontrol workspace init .
|
|
95
|
+
uv run pycontrol-gui
|
|
92
96
|
```
|
|
93
97
|
|
|
94
|
-
`pycontrol-gui` depends on `pycontrol-core`, so adding the GUI package is enough
|
|
95
|
-
for GUI users. Add integration extras separately when needed, for example
|
|
96
|
-
`uv add "pycontrol-core[slack]==3.0.0a2"` or
|
|
97
|
-
`uv add "pycontrol-core[slack]" --prerelease allow`; replace `slack` with
|
|
98
|
-
`notion`, `s3`, or `integrations` as needed.
|
|
99
|
-
|
|
100
98
|
## GUI Integration
|
|
101
99
|
|
|
102
100
|
GUI frontends should create workspaces through the public core API rather than
|
|
@@ -126,28 +124,28 @@ the same scaffold:
|
|
|
126
124
|
```text
|
|
127
125
|
my-workspace/
|
|
128
126
|
tasks/
|
|
127
|
+
blinker.py
|
|
129
128
|
examples/
|
|
130
129
|
blinker.py
|
|
131
130
|
button.py
|
|
132
131
|
hardware_definitions/
|
|
133
|
-
example.py
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
encoder.py
|
|
138
|
-
encoder_h7.py
|
|
132
|
+
example.py / example_h7.py
|
|
133
|
+
usr_button.py / usr_button_h7.py
|
|
134
|
+
encoder.py / encoder_h7.py
|
|
135
|
+
...
|
|
139
136
|
devices/
|
|
140
137
|
example_widget.py
|
|
141
138
|
experiments/
|
|
142
139
|
blink_demo.json
|
|
143
140
|
plugins/
|
|
144
|
-
task_extensions/
|
|
145
|
-
experiment_extensions/
|
|
141
|
+
task_extensions/ # example_extension.py, run_notify.py, ...
|
|
142
|
+
experiment_extensions/ # notion_logger.py, s3_data_exporter.py
|
|
146
143
|
task_plotters/
|
|
147
|
-
task_controls/
|
|
144
|
+
task_controls/ # example_dialog.py
|
|
148
145
|
data/
|
|
149
146
|
settings.json
|
|
150
147
|
setups.json
|
|
148
|
+
launch-pycontrol-gui.command # and launch-pycontrol-gui.bat on Windows
|
|
151
149
|
```
|
|
152
150
|
|
|
153
151
|
The initializer is a safe merge by default. Existing identical files are reused,
|
|
@@ -11,7 +11,7 @@ Hardware definitions are per-rig MicroPython files in
|
|
|
11
11
|
|
|
12
12
|
- **Hardware definition** - the user-authored rig file. It maps task-friendly
|
|
13
13
|
names to breakout pins and device constructors.
|
|
14
|
-
- **Breakout** - board geometry such as `
|
|
14
|
+
- **Breakout** - board geometry such as `Breakout12` or `BreakoutH7`, mapping
|
|
15
15
|
human port labels to MCU pin IDs.
|
|
16
16
|
- **Board profile** - MCU-specific resources such as timers, alternate
|
|
17
17
|
functions, and reserved pins. The firmware chooses this automatically from the
|
|
@@ -37,11 +37,11 @@ should usually be copied and renamed for a real setup.
|
|
|
37
37
|
The breakout import communicates the intended board family:
|
|
38
38
|
|
|
39
39
|
```python
|
|
40
|
-
from devices import
|
|
40
|
+
from devices import Breakout12 # F405 pyboard breakout
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
```python
|
|
44
|
-
from devices.breakout_H7 import
|
|
44
|
+
from devices.breakout_H7 import BreakoutH7 # pyboard-H7 breakout
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
Host-side inspection uses that import to infer the expected MCU. If a setup has
|
|
@@ -48,7 +48,7 @@ pyControl/code/
|
|
|
48
48
|
| `controls_dialogs/` + `v.custom_controls_dialog` | `plugins/task_controls/` | See `pycontrol-gui/docs/task-controls.md`. |
|
|
49
49
|
| `config/setups.json` keyed by serial port | Name-keyed `setups.json` (`SetupRegistry`) | Convert once before loading the workspace. |
|
|
50
50
|
| `devices/`, `hardware_definitions/`, `tasks/` | Same roles in workspace template | Device sources are vendored under `pycontrol.firmware` for sync; task/hwdef files usually copy as-is. |
|
|
51
|
-
| `source/pyControl/` on-board framework | `pycontrol.firmware.
|
|
51
|
+
| `source/pyControl/` on-board framework | `pycontrol.firmware.pycontrol` | Sync with `pycontrol firmware sync`. |
|
|
52
52
|
|
|
53
53
|
## Host import and module map
|
|
54
54
|
|
|
@@ -97,8 +97,58 @@ Scripts and `api_classes` import from `source.*` with the repo root on `PYTHONPA
|
|
|
97
97
|
8. **Sync firmware** explicitly: `pycontrol firmware status`, then `pycontrol firmware sync`.
|
|
98
98
|
9. **Launch the GUI** with `uv run pycontrol-gui` instead of `pyControl_GUI.pyw`.
|
|
99
99
|
|
|
100
|
-
Task, device, and hardware-definition files
|
|
101
|
-
|
|
100
|
+
Task, device, and hardware-definition files need a small naming pass before they
|
|
101
|
+
run on this rewrite because the on-board firmware now uses PEP 8 class names
|
|
102
|
+
without compatibility aliases for the old mixed/snake-case class names.
|
|
103
|
+
|
|
104
|
+
## Firmware class-name changes
|
|
105
|
+
|
|
106
|
+
Update task files, hardware definitions, and custom device files that import or
|
|
107
|
+
instantiate firmware classes. Module filenames are unchanged for now, so imports
|
|
108
|
+
such as `from devices.breakout_H7 import BreakoutH7` still use the existing
|
|
109
|
+
module path while importing the renamed class.
|
|
110
|
+
|
|
111
|
+
Common hardware classes:
|
|
112
|
+
|
|
113
|
+
| pyControl v2 name | Rewrite name |
|
|
114
|
+
| --- | --- |
|
|
115
|
+
| `Digital_input` | `DigitalInput` |
|
|
116
|
+
| `Digital_output` | `DigitalOutput` |
|
|
117
|
+
| `Analog_input` | `AnalogInput` |
|
|
118
|
+
| `Analog_channel` | `AnalogChannel` |
|
|
119
|
+
| `Audio_output` | `AudioOutput` |
|
|
120
|
+
| `IO_object` | `IOObject` |
|
|
121
|
+
| `IO_expander_pin` | `IOExpanderPin` |
|
|
122
|
+
| `Analog_threshold_watcher` | `AnalogThresholdWatcher` |
|
|
123
|
+
|
|
124
|
+
Bundled device classes:
|
|
125
|
+
|
|
126
|
+
| pyControl v2 name | Rewrite name |
|
|
127
|
+
| --- | --- |
|
|
128
|
+
| `Breakout_1_2` | `Breakout12` |
|
|
129
|
+
| `Breakout_H7` | `BreakoutH7` |
|
|
130
|
+
| `Rotary_encoder` | `RotaryEncoder` |
|
|
131
|
+
| `LED_driver` | `LEDDriver` |
|
|
132
|
+
| `Analog_LED` | `AnalogLED` |
|
|
133
|
+
| `Audio_board` | `AudioBoard` |
|
|
134
|
+
| `Audio_player` | `AudioPlayer` |
|
|
135
|
+
| `Five_poke` | `FivePoke` |
|
|
136
|
+
| `Frame_logger` | `FrameLogger` |
|
|
137
|
+
| `Frame_trigger` | `FrameTrigger` |
|
|
138
|
+
| `Grid_maze` | `GridMaze` |
|
|
139
|
+
| `Grid_maze_3x3` | `GridMaze3x3` |
|
|
140
|
+
| `Grid_maze_7x7` | `GridMaze7x7` |
|
|
141
|
+
| `Load_cell` | `LoadCell` |
|
|
142
|
+
| `Nine_poke` | `NinePoke` |
|
|
143
|
+
| `Port_expander` | `PortExpander` |
|
|
144
|
+
| `Solenoid_driver` | `SolenoidDriver` |
|
|
145
|
+
| `Stepper_motor` | `StepperMotor` |
|
|
146
|
+
| `uRFID` | `URFID` |
|
|
147
|
+
| `UART_handler` | `UARTHandler` |
|
|
148
|
+
|
|
149
|
+
Utility helper classes are also CapWords (`SampleWithoutReplacement`,
|
|
150
|
+
`ExpMovingAverage`, `Variables`), but the function-style task helpers
|
|
151
|
+
`sample_without_replacement(...)` and `exp_mov_ave(...)` remain available.
|
|
102
152
|
|
|
103
153
|
## Behavioral differences worth knowing
|
|
104
154
|
|
|
@@ -123,7 +173,7 @@ are adopting MCU-profile-aware H7/F7 workflows.
|
|
|
123
173
|
|
|
124
174
|
## Further reading
|
|
125
175
|
|
|
126
|
-
- [
|
|
176
|
+
- [What's new for your lab](../../rewrite-docs/COMMUNITY.html)
|
|
127
177
|
- [Getting started](getting-started.md)
|
|
128
178
|
- [Task extensions](task-extensions.md)
|
|
129
179
|
- [Public API](public-api.md)
|
|
@@ -83,11 +83,16 @@ from pycontrol.recording import (
|
|
|
83
83
|
AnalogWriter,
|
|
84
84
|
ConsoleSubscriber,
|
|
85
85
|
FleetAggregator,
|
|
86
|
+
NativeEventLogger,
|
|
86
87
|
TsvLogger,
|
|
87
88
|
ms_to_readable_time,
|
|
88
89
|
)
|
|
89
90
|
```
|
|
90
91
|
|
|
92
|
+
`TsvLogger`/`AnalogWriter` write the legacy v2-compatible files;
|
|
93
|
+
`NativeEventLogger` writes the native run-bundle event TSV and analog arrays
|
|
94
|
+
(see `docs/data-format.md`).
|
|
95
|
+
|
|
91
96
|
## Extension API
|
|
92
97
|
|
|
93
98
|
```python
|
|
@@ -89,11 +89,8 @@ Every archive contains a top-level `meta.json` validated by
|
|
|
89
89
|
"files": [
|
|
90
90
|
{
|
|
91
91
|
"role": "task",
|
|
92
|
-
"workspace_path": "tasks/sequence.py",
|
|
93
92
|
"archive_path": "tasks/sequence.py",
|
|
94
|
-
"sha256": "0123..."
|
|
95
|
-
"size_bytes": 1234,
|
|
96
|
-
"behavior_affecting": true
|
|
93
|
+
"sha256": "0123..."
|
|
97
94
|
}
|
|
98
95
|
],
|
|
99
96
|
"task_variables": {
|
|
@@ -124,7 +121,7 @@ the GUI version.
|
|
|
124
121
|
`validate_protocol_bundle()` accepts strict schema version 1 archives. It checks
|
|
125
122
|
that the ZIP contains valid metadata, every member stays inside the allowed
|
|
126
123
|
workspace folders, every member is declared, every declared file is present, and
|
|
127
|
-
every declared file matches its SHA-256 digest
|
|
124
|
+
every declared file matches its SHA-256 digest. Archive members
|
|
128
125
|
with absolute paths, parent traversal (`..`), unknown top-level folders, missing
|
|
129
126
|
metadata entries, legacy folder-list file declarations, or digest mismatches
|
|
130
127
|
raise `ArchiveError`.
|
|
@@ -59,12 +59,15 @@ GUI thread.
|
|
|
59
59
|
|
|
60
60
|
## Built-in subscribers
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
All are importable from `pycontrol.recording`:
|
|
63
|
+
|
|
64
|
+
| Class | Purpose |
|
|
63
65
|
| --- | --- |
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
66
|
+
| `TsvLogger` | Writes the legacy session `.tsv` file (frozen format). |
|
|
67
|
+
| `AnalogWriter` | Writes per-channel `.time.npy` + `.data.npy`. |
|
|
68
|
+
| `NativeEventLogger` | Writes the native run-bundle event TSV and analog arrays. |
|
|
69
|
+
| `ConsoleSubscriber` | Pretty-prints to a logger / stdout. |
|
|
70
|
+
| `FleetAggregator` | Thread-safe cache of fleet envelopes for GUI or other frontend consumers. |
|
|
68
71
|
|
|
69
72
|
## User Extension: TaskExtension
|
|
70
73
|
|
|
@@ -183,8 +183,8 @@ from pycontrol.extension import TaskExtension
|
|
|
183
183
|
|
|
184
184
|
|
|
185
185
|
SETTINGS = (
|
|
186
|
-
BoolSetting("enabled", "Enabled", default=True),
|
|
187
|
-
IntSetting("min_trials", "Minimum trials", default=20, minimum=1),
|
|
186
|
+
BoolSetting("enabled", "Enabled", default=True, hint="Enable or disable this extension."),
|
|
187
|
+
IntSetting("min_trials", "Minimum trials", default=20, minimum=1, hint="Ignore runs with fewer trials."),
|
|
188
188
|
)
|
|
189
189
|
|
|
190
190
|
|
|
@@ -194,6 +194,9 @@ class TrialMonitor(TaskExtension):
|
|
|
194
194
|
self.min_trials = self.extension_settings.get("min_trials", 20)
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
+
Setting helpers accept `hint="..."`; the GUI Settings dialog shows that text as
|
|
198
|
+
a tooltip when the user hovers over the setting label.
|
|
199
|
+
|
|
197
200
|
Settings are available as:
|
|
198
201
|
|
|
199
202
|
```python
|
|
@@ -250,16 +253,42 @@ class AnalogMonitor(TaskExtension):
|
|
|
250
253
|
self.print_message(f"received {len(analog.data)} lick_sensor samples")
|
|
251
254
|
```
|
|
252
255
|
|
|
253
|
-
## Integrations
|
|
256
|
+
## Integrations and Optional Extras
|
|
257
|
+
|
|
258
|
+
`pycontrol.integrations` contains small helpers for extension authors. It is
|
|
259
|
+
not a home for lab-specific policy: workspace extensions still declare their
|
|
260
|
+
own `SETTINGS`, decide when to send data, and pass credentials explicitly.
|
|
261
|
+
|
|
262
|
+
`pycontrol-core` keeps cloud client libraries out of the default install. Add
|
|
263
|
+
the extra for each integration you want:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
uv add "pycontrol-core[slack]" --prerelease allow
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
| Integration | Install extra | Where it is used |
|
|
270
|
+
|-------|----------------|--------|
|
|
271
|
+
| Slack | `pycontrol-core[slack]` | `pycontrol.integrations.notify` |
|
|
272
|
+
| Notion | `pycontrol-core[notion]` | Workspace example: `plugins/experiment_extensions/notion_logger.py` |
|
|
273
|
+
| S3 | `pycontrol-core[s3]` | Workspace example: `plugins/experiment_extensions/s3_data_exporter.py` |
|
|
274
|
+
|
|
275
|
+
Use `pycontrol-core[integrations]` for all of the above at once. Telegram
|
|
276
|
+
notifications use only the Python standard library. Notion and S3 have no
|
|
277
|
+
facade modules in core: their example experiment extensions import
|
|
278
|
+
`notion_client` and `boto3` directly and keep all settings in the workspace.
|
|
279
|
+
|
|
280
|
+
When adding a new integration, prefer a workspace extension for site-specific
|
|
281
|
+
behavior; add a module under `pycontrol/integrations/` only for broadly useful
|
|
282
|
+
shared primitives, expose its client library as a named optional extra, and
|
|
283
|
+
test it with the third-party package mocked.
|
|
284
|
+
|
|
285
|
+
### Notifiers
|
|
254
286
|
|
|
255
287
|
`pycontrol.integrations.notify` provides small notifier classes for task
|
|
256
288
|
extensions that need Telegram or Slack messages. Extension files still own their
|
|
257
289
|
`SETTINGS`; the notifier classes only handle credentials, background delivery,
|
|
258
290
|
and errors.
|
|
259
291
|
|
|
260
|
-
Telegram uses only the Python standard library. Slack uses the `slack_sdk`
|
|
261
|
-
package, available through the `pycontrol-core[slack]` extra.
|
|
262
|
-
|
|
263
292
|
```python
|
|
264
293
|
from pycontrol.config import BoolSetting, StringSetting
|
|
265
294
|
from pycontrol.extension import TaskExtension
|
|
@@ -27,12 +27,12 @@ Symptoms:
|
|
|
27
27
|
|
|
28
28
|
- `pycontrol run` prints a firmware warning.
|
|
29
29
|
- The GUI shows an out-of-sync firmware banner.
|
|
30
|
-
- Tasks fail near import time with missing `
|
|
30
|
+
- Tasks fail near import time with missing `pycontrol` or `devices` modules.
|
|
31
31
|
|
|
32
32
|
Check:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
pycontrol firmware status --setup BoxA
|
|
35
|
+
pycontrol firmware status --setup BoxA --verbose
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
Fix:
|
|
@@ -125,7 +125,7 @@ Checks:
|
|
|
125
125
|
|
|
126
126
|
```bash
|
|
127
127
|
pycontrol board info --setup BoxA
|
|
128
|
-
pycontrol firmware status --setup BoxA
|
|
128
|
+
pycontrol firmware status --setup BoxA --verbose
|
|
129
129
|
```
|
|
130
130
|
|
|
131
131
|
Fixes:
|
|
@@ -149,19 +149,19 @@ Fix:
|
|
|
149
149
|
|
|
150
150
|
```bash
|
|
151
151
|
# Per run override:
|
|
152
|
-
pycontrol run --setup BoxA --task blinker --hwdef example
|
|
152
|
+
pycontrol run --setup BoxA --task blinker --hwdef example --subject m1
|
|
153
153
|
|
|
154
154
|
# Or set a setup default once, then omit --hwdef on future runs:
|
|
155
155
|
pycontrol setups add BoxA --port /dev/cu.usbmodemXXXX --hwdef example --force
|
|
156
|
-
pycontrol run --setup BoxA --task blinker
|
|
156
|
+
pycontrol run --setup BoxA --task blinker --subject m1
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
The effective hardware definition is the explicit `--hwdef`, else the setup's
|
|
160
160
|
`hardware_definition` default. The host uploads it as
|
|
161
161
|
`/flash/hardware_definition.py` before loading the task, but only when the task
|
|
162
162
|
statically imports `hardware_definition`. When a task needs one but none is
|
|
163
|
-
configured, the run
|
|
164
|
-
board.
|
|
163
|
+
configured, the run fails before connecting (exit code 2) rather than using a
|
|
164
|
+
file already cached on the board.
|
|
165
165
|
|
|
166
166
|
## Variables Do Not Change
|
|
167
167
|
|