ldcon 0.2.1__tar.gz → 0.2.2__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.
- {ldcon-0.2.1 → ldcon-0.2.2}/MANIFEST.in +1 -1
- {ldcon-0.2.1/src/ldcon.egg-info → ldcon-0.2.2}/PKG-INFO +5 -1
- {ldcon-0.2.1 → ldcon-0.2.2}/README.md +4 -0
- ldcon-0.2.2/docs/icon.png +0 -0
- ldcon-0.2.2/docs/icon.svg +20 -0
- ldcon-0.2.2/docs/publishing.html +62 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/pyproject.toml +1 -1
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_monitor.py +21 -2
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_task.py +7 -0
- ldcon-0.2.2/src/ldcon/agents/dlc_monitor_grant.py +87 -0
- ldcon-0.2.2/src/ldcon/agents/dlc_parameter_adjustment.py +274 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/dlc_pi_adjustment.py +7 -3
- ldcon-0.2.2/src/ldcon/agents/dlc_pi_plan.py +57 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/monitor_quality_binding.py +3 -2
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/operating_mode_client.py +5 -1
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/adjustment_plan.py +3 -0
- ldcon-0.2.2/src/ldcon/jobs/dlc_parameter_measurement.py +87 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/dlc_pi_measurement.py +69 -7
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/dlcpro_cards.py +18 -1
- ldcon-0.2.2/src/ldcon/owners/dlcpro_agent_setup.py +43 -0
- ldcon-0.2.2/src/ldcon/owners/dlcpro_client_liveness.py +48 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_deployment.py +3 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_ff_delta_adjustment.py +30 -15
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_observation.py +1 -0
- ldcon-0.2.2/src/ldcon/owners/dlcpro_operating_mode.py +45 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_owner.py +17 -5
- ldcon-0.2.2/src/ldcon/owners/dlcpro_parameter_trial.py +240 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_pi_protocol.py +22 -2
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_pi_trial.py +128 -9
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_service.py +4 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/web_runtime.py +3 -8
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/app.js +2 -1
- {ldcon-0.2.1 → ldcon-0.2.2/src/ldcon.egg-info}/PKG-INFO +5 -1
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon.egg-info/SOURCES.txt +16 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/golden-sr1092.json +7 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/golden-sr422.json +7 -0
- ldcon-0.2.2/tests/test_dlc_adjustment_web.cjs +25 -0
- ldcon-0.2.2/tests/test_dlc_monitor_admission.py +98 -0
- ldcon-0.2.2/tests/test_dlc_parameter_adjustment.py +310 -0
- ldcon-0.2.2/tests/test_dlcpro_agent_setup.py +49 -0
- ldcon-0.2.2/tests/test_dlcpro_client_liveness.py +118 -0
- ldcon-0.2.2/tests/test_dlcpro_operating_mode.py +235 -0
- ldcon-0.2.2/tests/test_dlcpro_parameter_trial.py +198 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_operating_mode_routes.py +7 -2
- ldcon-0.2.1/docs/publishing.html +0 -33
- ldcon-0.2.1/src/ldcon/agents/dlc_pi_plan.py +0 -24
- {ldcon-0.2.1 → ldcon-0.2.2}/.secrets.baseline +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/LICENSE +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/package-lock.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/package.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/scripts/check_dist.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/scripts/check_secrets.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/scripts/check_tag.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/scripts/install_wheel.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/setup.cfg +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/__init__.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/_moved.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agent_tools.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/__init__.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/adjustment_guidance.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/adjustment_quality.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_adapters.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_background.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_bundle.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_cards.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_config_access.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_configuration.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_configuration_policy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_connect.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_device_parameters.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_execution.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_history.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_inbox_watch.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_interactive.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_interactive_hooks.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_language.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_log_context.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_log_read.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_log_watch.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_metrics.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_opencode_http.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_opencode_mcp.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_owner_status.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_profiles.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_providers.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_repairs.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_routing.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_runtime.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_sandbox.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_startup_notice.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_tools.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_window.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/agent_workspace.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/card_fallback.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/dlc_pi_response_plan.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/failure_policy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/fallback_service.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/fallback_targets.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/gateway_role.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/manual_adjustment_grant.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/measurement_grant.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/notification_policy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/report_pdf.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/resident_adjustment.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/resident_agent.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/resident_quality_apply.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/task_decisions.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/teams_card.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/agents/teams_destinations.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/cld_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/cli.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/__init__.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/adjustment_registry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/agent_authority.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/card_admission.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/card_definition.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/card_reporting.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/configuration.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/configuration_transaction.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/contracts.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/current_settings.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/deck_yaml.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/device_conditions.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/execution_context.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/instrument_client.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/interface_cards.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/interface_schema.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/laser_assets.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/metric_quantities.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/modulation.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/operation_registry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/paths.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/period_context.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/records.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/result_reservations.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/site_bindings.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/state_layout.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/target_policy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/telegraf.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/telemetry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/core/temperature_settings.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/dashboard_metrics.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/device_registry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/__init__.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/cld1015.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/cld1015_config.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/dlcpro.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/dlcpro_analog.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/dlcpro_console.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/dlcpro_priority.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/ecdl.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/esp32_servo.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/fizeau.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/frequency_sweep.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/influx_wavelength.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/metric_sources.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/native_dither_telemetry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/rigol_scope.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/siglent.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/staircase.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/drivers/wsu_udp.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/esp32_time_telemetry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/example_config.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/fallback_service.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/instrument_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/instrument_service.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/interface_worker.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/job_service.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/__init__.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/adaptive_pid.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/adaptive_window_session.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/adjustment_continuation.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/axis_frequency.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/axis_frequency_report.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/background_parameter_session.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/background_pid_session.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/branch_maps.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/branch_target_migration.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/card_runtime.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/change_measure.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/change_measure_adapter.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/change_measure_dlc.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/change_measure_parameters.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/change_measure_target.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/current_cards.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/current_devices.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/dashboard_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/device_parameters.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/dlcpro_awg_analysis.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/dlcpro_awg_card.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_branch_hop.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_branch_recovery.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_devices.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_locked_sweep_proxy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_mapped_recovery.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_mode_map.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_mode_recovery.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_online.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_operating_point.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_pi_commission.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_recovery.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_recovery_plan.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_recovery_test.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_relock.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_response_study.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_steady.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_thermal_context.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/ecdl_window_center.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/emission_card.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/fringe_sweep_reference.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/interface_devices.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/interface_worker.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/job_service.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/laser_live.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/learned_settling.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/lock_target.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/locked_triangle.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/measurement_acquisition.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/measurement_analysis.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/measurement_execution.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/measurement_notice.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/measurement_presets.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/measurement_report.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/measurement_statistics.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/metric_cards.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/metric_interface.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/modulation_sweep.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/native_background_session.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/native_current_acquire.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/native_current_card.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/native_current_online.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/native_ff_proxy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/native_ff_sweep.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/online_pid.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/operating_point.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/operating_point_proxy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/operation_execution.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/operation_history.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/optical_broker.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/pi_algorithms.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/pi_response.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/progress.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/quality_baseline.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/recovery_decisions.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/recovery_history.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/recovery_procedure.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/recovery_reference.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/recovery_stationary.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/response_algorithms.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/response_comparison.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/response_record.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/runtime_parameters.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/settling_profile.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/shot_analysis.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/simulation.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/slow_control.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/slow_current.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/study_adoption.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/study_execution.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/sweep_ff_analysis.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/sweep_ff_optimization.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/sweep_ff_plan.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/sweep_ff_report.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/sweep_ff_transport.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/sweep_tracking.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/sweep_verdict.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/target_response.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/target_response_analysis.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/target_response_control.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/target_response_decisions.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/target_response_experiment.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/target_response_feedforward.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/target_response_search.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/target_response_shift.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/target_response_thermal.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/target_response_thermal_experiment.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/target_sequence.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/temperature_identification.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/wavelength_history.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/window_actions.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/window_center.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/window_control.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/window_decisions.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/window_sampling.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/jobs/workflow_artifacts.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/migration/__init__.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/migration/migration_decks.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/migration/migration_readiness.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/migration/migration_relocation.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/monitor_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owner_telemetry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/__init__.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/adc_log_threshold.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/background_grants.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/cld_instrument_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/cld_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/cld_projection.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/cld_system_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/current_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/device_registry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_awg_plan.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_awg_port.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_awg_protocol.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_awg_return.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_awg_sweep.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_awg_timing.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_awg_tracking.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_awg_tracking_guard.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_awg_tracking_report.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_awg_zero.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_ff.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_ff_adjustment.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_frequency_window.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_readback.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_target_changes.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_target_step.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_target_store.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/dlcpro_telemetry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_background_parameters.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_background_pid.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_hot_control.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_independent_pid.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_locked_study.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_locked_sweep.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_native_pid.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_optical.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_policy_reload.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_prelock.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_projection.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_steady_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_steady_policy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_target_ff.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_target_settling.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_tec_pair.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/ecdl_thermal_study.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/emission.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/emission_adapters.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/esp32_native_client.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/esp32_native_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/esp32_time_telemetry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/esp32_udp_gate.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/external_sweep_guard.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/flexible_native_trial.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/fringe_peaks.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/full_data_log.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/instrument_identity.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/instrument_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/instrument_projection.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/instrument_service.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/migration_physical_lock.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/monitor_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/native_assembly.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/native_assembly_host.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/native_background.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/native_current_tuning.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/native_ff_proof.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/native_instrument_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/native_modulation_limits.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/native_policy_reload.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/native_policy_safety.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/native_policy_update.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/native_system_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/operating_mode_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/owner_projection.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/owner_telemetry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/pi_handover.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/projection_transport.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/remote_instruments.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/sdg_client.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/sdg_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/tec_rate.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/owners/wsu_udp_monitor.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/report_pdf.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/agent-model-options.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/agent_contract/adjustment.txt +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/agent_contract/common.txt +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/agent_contract/diagnosis.txt +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/agent_contract/research.txt +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/agent_contract/response_optimization.txt +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/examples/README.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/examples/assignments/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/examples/assignments/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/examples/assignments/thf685.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/examples/interface-decks/dfb-dbr-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/examples/interface-decks/ecdl-dlcpro.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/examples/interface-decks/ecdl-pzt-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/examples/lasers/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/examples/lasers/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/examples/lasers/thf685.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/report_template/ldconreport.cls +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/resources/report_template/report.tex +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/__init__.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/agent_approval_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/agent_settings_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/agent_task_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/control_readback.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/dashboard_activity.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/dashboard_controls_revision.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/dashboard_metrics.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/dashboard_snapshot.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/dashboard_state.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/dashboard_wavelength.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/device_parameters_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/dither_quality.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/dlcpro_target_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/dlcpro_target_query.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/failure_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/gateway_lan.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/grafana_activity.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/grafana_dashboard.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/grafana_dlcpro.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/grafana_quality.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/interface_projection.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/job_registry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/job_report.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/laser_operations.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/lock_target_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/lock_target_queue.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/log_archive.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/metric_query_profiles.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/online_position.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/operating_mode_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/operation_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/operation_observation.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/operations_settings.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/quality_threshold_metrics.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/shot_analysis_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/sweep_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/sweep_guard.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/target_reference_panel.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/teams_destinations_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/web_fleet.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/web_server.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web/window_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/agent-settings.css +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/agent-settings.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/agent-settings.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/app.css +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/card-browser.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/card-guide.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/acquire.metrics.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/agent.history_review.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/agent.log_watch.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/agent.monitor.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/agent.request.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/agent.resident.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/agent.task.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/commission.ecdl_pi.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/identify.axis_frequency.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/identify.current_response.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/laser.emission.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/measure.batch.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/measure.change.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/measure.target_response.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/monitor.wavelength.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/notify.teams.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/optimize.settling_criteria.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/optimize.target_feedforward.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/optimize.target_step.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/optimize.target_thermal.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/plan.ecdl_recovery.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/prepare.ecdl_recovery.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/recover.ecdl_mode_hop.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/report.grafana_dashboard.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/servo.current_pid.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/servo.dlcpro_frequency.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/servo.ecdl_steady_lock.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/servo.native_current.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/study.ecdl_current_response.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/study.ecdl_locked_current_map.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/study.ecdl_mode_map.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/study.ecdl_thermal_context.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/study.target_response.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/sweep.current_modulation.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/sweep.dlcpro_frequency.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/sweep.servo_modulation.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/test.ecdl_recovery.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/tune.ecdl_initial_point.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/tune.ecdl_operating_point.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/tune.ecdl_window_center.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/tune.native_current.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/tune.pid_coordinate_search.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/tune.pid_factor_reduction.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/tune.pid_online.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/cards/tune.temperature_once.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/committing-input.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/configuration-editor.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/dashboard-state.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/failures.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/failures.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/fallback.css +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/fallback.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/fallback.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/fleet-settings.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/fleet.css +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/fleet.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/fleet.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/gateway-presentation.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/grafana-help.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/help.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/i18n.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/index.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/laser-workspace.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/modulation.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/modulation.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/reservations.html +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/reservations.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/teams-settings.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/window-actions.js +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_assets/workspace.css +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_fleet.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon/web_server.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon.egg-info/dependency_links.txt +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon.egg-info/entry_points.txt +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon.egg-info/requires.txt +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/src/ldcon.egg-info/top_level.txt +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/__init__.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/conftest.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/dashboard_fixture.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/dlcpro-fw19.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/device-owners/cld1015-M01343851/config.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/device-owners/esp32servocon-sr1092/config.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/device-owners/scope-sr422-sr1092/config.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/device-owners/sr422/config.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/instrument-owners/bk4054b-51524H117/config.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/instrument-owners/sdg2042x-SDG2XFBCA00839/config.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/instrument-owners/sdg2042x-SDG2XFBCA00845/config.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/lasers/sr1092/config/assignments/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/lasers/sr1092/config/interface-decks/dfb-dbr-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/lasers/sr1092/config/lasers/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/lasers/sr1092/laser-layout.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/lasers/sr422/config/assignments/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/lasers/sr422/config/interface-decks/ecdl-pzt-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/lasers/sr422/config/lasers/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/effective-settings/var/lasers/sr422/laser-layout.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/entry-startup/assembly-definition.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/entry-startup/sr1092-broker.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/fixtures/pi-handover-v1.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/legacy_mapped_recovery_fixture.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/legacy_rearm_trim_fixture.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/legacy_recovery_executor_fixture.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/legacy_recovery_plan_fixture.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/legacy_recovery_test_fixture.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/measurement_fixtures.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/recovery_legacy_fixtures.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/examples/dlcpro/assignment.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/examples/dlcpro/deck.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/examples/dlcpro/existing-ecdl-calibration.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/examples/dlcpro/laser.offline.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/examples/dlcpro/owner.offline.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/examples/dlcpro/pi-agent-cards.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/examples/dlcpro/thf685/laser.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/examples/dlcpro/thf685/owner.json +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/examples/measurement-decks/dfb-dbr-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/examples/measurement-decks/ecdl-pzt-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/examples/interface-config/assignments/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/examples/interface-config/assignments/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/examples/interface-config/interface-decks/dfb-dbr-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/examples/interface-config/interface-decks/ecdl-pzt-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/examples/interface-config/lasers/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/examples/interface-config/lasers/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/tests/fixtures/live-20261022/assignments/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/tests/fixtures/live-20261022/assignments/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/tests/fixtures/live-20261022/interface-decks/dfb-dbr-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/tests/fixtures/live-20261022/interface-decks/ecdl-pzt-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/tests/fixtures/live-20261022/lasers/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/tests/fixtures/live-20261022/lasers/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/tests/test_esp32_native_current.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/tests/test_lock_target.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/tests/test_native_ff_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/jobs/tests/test_native_live_moves.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/ops/effective_settings.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/ops/inspect_dlcpro.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/ops/prepare_pi_handover.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/ops/trim_owner_config.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/ops/update_pi_handover.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/owner/tests/fake_projection.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/owner/tests/fake_rig.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/owner/tests/locked_sweep_settings.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/owner/tests/test_background_pid_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/owner/tests/test_locked_sweep_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/scripts/monitor_configuration.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/scripts/test_live_background_handoff.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/tests/test_ecdl_acquisition_gate_fixes.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/tests/test_learned_settling.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/examples/interface-config/assignments/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/examples/interface-config/assignments/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/examples/interface-config/interface-decks/dfb-dbr-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/examples/interface-config/interface-decks/ecdl-pzt-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/examples/interface-config/lasers/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/examples/interface-config/lasers/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/tests/fixtures/live-20261022/assignments/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/tests/fixtures/live-20261022/assignments/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/tests/fixtures/live-20261022/interface-decks/dfb-dbr-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/tests/fixtures/live-20261022/interface-decks/ecdl-pzt-ldc-tec.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/tests/fixtures/live-20261022/lasers/sr1092.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/support/web/tests/fixtures/live-20261022/lasers/sr422.yaml +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_adaptive_window_session.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_adjustment_continuation.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_adjustment_guidance.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_adjustment_guidance_web.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_adjustment_quality.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_adopt_after_prewrite_reject_20261028.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_agent_background_gateway.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_agent_difficulty_routing.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_agent_host_integration.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_agent_model_negotiation.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_agent_monitor_history.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_agent_providers.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_agent_routing_layout.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_agent_settings_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_agent_settings_ui.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_agent_task_tool_arguments.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_agent_tasks.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_approval_layout_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_background_foreground_native_matrix.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_background_start_observation.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_background_window_session.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_card_browser_assets.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_card_browser_ui.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_card_readiness.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_card_start_reasons.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_change_measure.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_change_measure_adapter.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_change_measure_dlc.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_change_measure_target.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_claude_interactive_metadata.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_claude_profile_transport.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_cld_system_boundary.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_committing_input.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_config_two_layer_step34.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_configuration_numeric_roundtrip.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_configuration_review_refresh.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_controller_status_projection.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dashboard_activity.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dashboard_polish.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dashboard_workspace.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_decision_progress_prefix.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_display_state_consistency.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dither_pause_20261027.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dither_quality_review_20261028.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dither_review_aggregation_20261028.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlc_pi_adjustment.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlc_pi_target_steps.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlc_response_policy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlc_trace_state.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_adc_authority.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_awg.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_awg_async.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_awg_card.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_awg_enable.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_awg_identity.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_awg_protocol.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_awg_return.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_awg_tracking.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_awg_tracking_failures.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_awg_tracking_guard.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_awg_zero.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_console.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_fast_servo.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_ff_adjustment.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_ff_delta_adjustment.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_frequency_window.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_grafana.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_lock_input_window.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_native_scan_frequency.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_offline.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_pi_trial.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_target_api.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_target_api_fleet.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_target_api_routing.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_telemetry.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_tracking_io_budget.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_tracking_timing_io.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_dlcpro_udp.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_ecdl_branch_hop.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_ecdl_independent_pid.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_ecdl_ldc_target_ff.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_ecdl_owner_readopt.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_ecdl_owner_startup_adoption.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_ecdl_ready_target.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_ecdl_rearm_trim.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_ecdl_recovery_card.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_ecdl_settings_adopt.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_ecdl_target_settling.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_ecdl_tec_alignment_approach.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_effective_settings.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_emission.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_emission_native.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_entry_startup.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_failure_review.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_fleet_pdf_reports.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_gateway_defaults_ui.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_gateway_lan.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_gateway_notifications.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_gateway_presentation.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_gateway_presentation.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_http_request_ids.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_job_report.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_legacy_broker_projection.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_live_control_bulk.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_lock_target_correlation.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_measurement_acquisition.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_measurement_execution_integration.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_measurement_parameter_transactions.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_measurement_plan_authority.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_measurement_presets.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_midpoint_pilot.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_monitor_readback.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_moved_modules.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_native_assembly_adoption.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_native_background.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_native_integral_hotload.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_native_modulation_limits.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_native_owner_readopt.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_native_post_retarget_recentre.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_native_system_boundary.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_native_target_policy_update.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_notification_policy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_opencode_mcp_lifetime.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_opencode_notification_queue.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_opencode_shared.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_operating_guidance_fleet.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_operating_mode_owner.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_operating_point_latency.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_operating_point_readback_failure.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_operation_execution.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_operation_gateway.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_operation_history.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_operation_observation.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_operation_record_delivery.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_owner_projection.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_package_boundary.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_parameter_workspace.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_pi_algorithm_identity.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_pi_public_retirement.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_pi_release_update.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_pi_response_workflow.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_pi_trial_envelope_20261028.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_preflight_reference_resolution.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_projection_transport.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_public_examples.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_quality_baseline.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_quality_baseline_hooks.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_quality_report_only.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_quality_threshold_layout.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_recovery_decisions.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_recovery_explicit_policy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_recovery_history.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_recovery_reference_v2.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_recovery_without_map_lookup.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_refresh_layout.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_report_policy.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_resident_adjustment.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_resident_adjustment_web.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_resident_quality_apply.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_response_algorithm_integration.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_response_comparison.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_rollout_site_boundaries.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_settling_profile_consumer.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_single_laser_layout.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_stationary_unlimited_recovery.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_steady_planner_hold_only.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_steady_recovery_retirement.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_study_adoption.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_study_execution.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_study_history_display.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_sweep_admission.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_sweep_ff_analysis.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_sweep_ff_optimization.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_sweep_ff_report.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_sweep_ff_transport.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_cancel_ui.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_feedforward.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_ff_completion.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_response.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_response_control.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_response_experiment.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_response_feedforward.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_response_search.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_response_shift.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_response_web.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_sequence.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_step_authority.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_target_text_formats.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_task_decisions.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_task_profile_ui.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_teams_destinations.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_teams_settings_ui.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_unified_quality.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_window_hop_boundary_20261027.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_window_observable_selection.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_workspace_inventory.py +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_workspace_selector.cjs +0 -0
- {ldcon-0.2.1 → ldcon-0.2.2}/tests/test_wsu_udp_monitor.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ldcon
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Laser operations, physical owners and fleet presentation
|
|
5
5
|
Author: FanHarvard contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -38,6 +38,8 @@ Provides-Extra: analysis
|
|
|
38
38
|
Requires-Dist: scipy>=1.10; extra == "analysis"
|
|
39
39
|
Dynamic: license-file
|
|
40
40
|
|
|
41
|
+
<img src="https://raw.githubusercontent.com/FanHarvard/ldcon/main/docs/icon.png" alt="LDCon: an agent guiding a laser feedback loop" width="64" height="64">
|
|
42
|
+
|
|
41
43
|
# LDCon
|
|
42
44
|
|
|
43
45
|
**Still in active development.**
|
|
@@ -89,3 +91,5 @@ a new physical setup. See [test suite](tests/).
|
|
|
89
91
|
laser definitions, decks and assignments, with output disabled and placeholder
|
|
90
92
|
connections. Installation starts no hardware or service and includes no credentials.
|
|
91
93
|
[Publishing](docs/publishing.html) · [MIT license](LICENSE)
|
|
94
|
+
|
|
95
|
+
FanHarvard site configuration and deployment: [ldcon-fanharvard](https://github.com/FanHarvard/ldcon-fanharvard).
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<img src="https://raw.githubusercontent.com/FanHarvard/ldcon/main/docs/icon.png" alt="LDCon: an agent guiding a laser feedback loop" width="64" height="64">
|
|
2
|
+
|
|
1
3
|
# LDCon
|
|
2
4
|
|
|
3
5
|
**Still in active development.**
|
|
@@ -49,3 +51,5 @@ a new physical setup. See [test suite](tests/).
|
|
|
49
51
|
laser definitions, decks and assignments, with output disabled and placeholder
|
|
50
52
|
connections. Installation starts no hardware or service and includes no credentials.
|
|
51
53
|
[Publishing](docs/publishing.html) · [MIT license](LICENSE)
|
|
54
|
+
|
|
55
|
+
FanHarvard site configuration and deployment: [ldcon-fanharvard](https://github.com/FanHarvard/ldcon-fanharvard).
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 96 96" fill="none">
|
|
2
|
+
<title>LDCon: a laser diode with agent-guided feedback</title>
|
|
3
|
+
<rect width="96" height="96" rx="18" fill="#f8fafc"/>
|
|
4
|
+
<g stroke="#334155" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
|
|
5
|
+
<!-- Laser diode and its emitted beam. -->
|
|
6
|
+
<rect x="10" y="15" width="38" height="31" rx="6" fill="white"/>
|
|
7
|
+
<path d="M15 31h7m0-8v16l13-8-13-8m13 0v16m0-8h7"/>
|
|
8
|
+
<path d="M29 20l5-5m-1 0h1v1M38 22l5-5m-1 0h1v1" stroke="#dc2626" stroke-width="2"/>
|
|
9
|
+
<path d="M48 31h35m-7-7 7 7-7 7" stroke="#dc2626"/>
|
|
10
|
+
<!-- Feedback returns through the agent to the diode. -->
|
|
11
|
+
<path d="M78 43v20a8 8 0 0 1-8 8h-7M33 71H24a8 8 0 0 1-8-8v-9m-5 5 5-5 5 5"/>
|
|
12
|
+
<path d="M48 57v-5"/>
|
|
13
|
+
<circle cx="48" cy="50" r="2.5" fill="white"/>
|
|
14
|
+
<rect x="33" y="58" width="30" height="26" rx="6" fill="white"/>
|
|
15
|
+
<path d="M29 67v8m38-8v8" stroke-width="2.5"/>
|
|
16
|
+
<circle cx="42" cy="68" r="2" fill="#334155" stroke="none"/>
|
|
17
|
+
<circle cx="54" cy="68" r="2" fill="#334155" stroke="none"/>
|
|
18
|
+
<path d="M43 76h10" stroke-width="2.5"/>
|
|
19
|
+
</g>
|
|
20
|
+
</svg>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<!doctype html><html lang="en"><meta charset="utf-8">
|
|
2
|
+
<meta name="viewport" content="width=device-width,initial-scale=1"><title>Publishing ldcon</title>
|
|
3
|
+
<style>body{font:16px/1.7 system-ui;max-width:900px;margin:3rem auto;padding:0 1rem}pre{background:#f2f4f6;padding:1rem;overflow:auto}code{font-size:.9em}</style>
|
|
4
|
+
<h1>Publishing ldcon</h1><p>Public repository: <a href="https://github.com/FanHarvard/ldcon">FanHarvard/ldcon</a>.
|
|
5
|
+
Current version: 0.2.2. No production service is activated by publication.</p>
|
|
6
|
+
<h2>One-time PyPI setup</h2><p>A PyPI account owner creates a pending publisher at
|
|
7
|
+
<a href="https://pypi.org/manage/account/publishing/">PyPI publishing settings</a> with:</p>
|
|
8
|
+
<table><tr><th>Project name</th><td>ldcon</td></tr><tr><th>Owner</th><td>FanHarvard</td></tr>
|
|
9
|
+
<tr><th>Repository</th><td>ldcon</td></tr><tr><th>Workflow</th><td>release.yml</td></tr>
|
|
10
|
+
<tr><th>Environment</th><td>pypi</td></tr></table>
|
|
11
|
+
<p>Use the repository's <code>pypi</code> environment to restrict release access. Authentication uses
|
|
12
|
+
GitHub OIDC and PyPI Trusted Publishing; no long-lived PyPI API token belongs in this repository.
|
|
13
|
+
See the <a href="https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/">official PyPI setup</a>.</p>
|
|
14
|
+
<h2>Updating an installation</h2>
|
|
15
|
+
<p>In the virtual environment used for the installation:</p>
|
|
16
|
+
<pre>python -m pip install --upgrade "ldcon[web,instruments,analysis,agents]"
|
|
17
|
+
python -m pip check</pre>
|
|
18
|
+
<p>This selects the latest LDCon release and its declared compatible dependencies. LDCon pins
|
|
19
|
+
experiment-core and fanharvard-agentbridge versions, so independent updates to those projects are
|
|
20
|
+
adopted when the LDCon dependency pins are updated. For standalone use, update either with
|
|
21
|
+
<code>python -m pip install --upgrade experiment-core</code> or
|
|
22
|
+
<code>python -m pip install --upgrade fanharvard-agentbridge</code>.</p>
|
|
23
|
+
<p>Updating a Python environment does not promote an immutable production release or restart a service.
|
|
24
|
+
The existing production installation uses its separate deployment and handover procedure.</p>
|
|
25
|
+
<h2>Updating GitHub</h2>
|
|
26
|
+
<p>Edit the public checkout, commit reviewed files and push to <code>main</code>. README and icon changes
|
|
27
|
+
are visible on GitHub after the push; they do not require a PyPI version bump. The icon source is
|
|
28
|
+
<code>docs/icon.svg</code>; regenerate its PNG with:</p>
|
|
29
|
+
<pre>rsvg-convert --width 512 --height 512 --output docs/icon.png docs/icon.svg</pre>
|
|
30
|
+
<p>The public checkout was split from the internal deployment repository. They do not synchronize
|
|
31
|
+
automatically. Port reviewed source changes and test fixtures explicitly; export deployment
|
|
32
|
+
configurations as disabled, sanitized examples. Preserve the public packaging metadata and exclude
|
|
33
|
+
credentials, inboxes and runtime state instead of copying the full deployment directory or its Git history.</p>
|
|
34
|
+
<h2>Publishing a new PyPI version</h2><p>Change <code>project.version</code> in <code>pyproject.toml</code>
|
|
35
|
+
to a new, unused version. Publish updated experiment-core or agentbridge dependencies first if needed.
|
|
36
|
+
When changing the experiment-core pin, update its checkout tag in both CI and release workflows too.</p>
|
|
37
|
+
<p>From a development environment with the build, test and role dependencies installed, build into an
|
|
38
|
+
empty directory so old distributions cannot be uploaded accidentally:</p>
|
|
39
|
+
<pre>release_dir=$(mktemp -d)
|
|
40
|
+
python -m build --outdir "$release_dir"
|
|
41
|
+
python -m twine check --strict "$release_dir"/*
|
|
42
|
+
python scripts/check_dist.py "$release_dir"
|
|
43
|
+
python scripts/check_secrets.py
|
|
44
|
+
python -m pip install --force-reinstall --no-deps "$release_dir"/*.whl
|
|
45
|
+
python -m pytest -q
|
|
46
|
+
python -m pip check</pre>
|
|
47
|
+
<p>Commit and push the reviewed changes. After CI succeeds, create a tag matching the new version
|
|
48
|
+
(<code>vX.Y.Z</code>) and publish a GitHub Release for it. That runs <code>release.yml</code>; alternatively
|
|
49
|
+
dispatch that workflow at the tag. The workflow checks the tag against package metadata, builds and audits distributions,
|
|
50
|
+
tests the wheel, and publishes through the dedicated environment. Do not reuse an uploaded PyPI version.
|
|
51
|
+
The CI workflow also checks Python 3.10, 3.12 and 3.13. An existing PyPI release's embedded README
|
|
52
|
+
changes only with a new package version; a GitHub push alone does not replace it.</p>
|
|
53
|
+
<h2>Site configuration</h2><p>FanHarvard deployment configuration and clone/install procedures live in
|
|
54
|
+
<a href="https://github.com/FanHarvard/ldcon-fanharvard">ldcon-fanharvard</a>.
|
|
55
|
+
That repository pins published packages; it does not vendor this library. Credential values and runtime state stay local.</p>
|
|
56
|
+
<h2>Public/private boundary</h2><p>Public: source, tests, documentation, MIT license and reviewed examples.
|
|
57
|
+
Private: credentials, .env/.pypirc files, runtime databases, job logs, agent conversations,
|
|
58
|
+
approval receipts, local deployment history and live service configuration. New public repositories
|
|
59
|
+
start from a reviewed snapshot; the internal Git history is not uploaded.</p>
|
|
60
|
+
<p>The secret scanner compares findings against individually reviewed non-secret fixtures and code
|
|
61
|
+
literals in <code>.secrets.baseline</code>. New findings fail CI. This complements artifact inspection;
|
|
62
|
+
it is not a proof that arbitrary future data is safe to publish.</p></html>
|
|
@@ -65,7 +65,17 @@ def snapshot(box):
|
|
|
65
65
|
source = node.get('dither_source_unix_s', stamp) if key == 'dither_response' else stamp
|
|
66
66
|
metrics[path] = dict(value=item, acquired_unix_s=source,
|
|
67
67
|
valid=source is not None and node.get(key+'_valid', node.get('valid', True)) is not False)
|
|
68
|
-
|
|
68
|
+
document=value.get('content', value.get('data', value))
|
|
69
|
+
collect(document)
|
|
70
|
+
# DLC optics have their own HTTP acquisition clock. A new WSU UDP
|
|
71
|
+
# frequency sample must not refresh a cached/invalid fringe frame.
|
|
72
|
+
quality=(document.get('observation') or {}).get('quality') if isinstance(document,dict) else None
|
|
73
|
+
if isinstance(quality,dict):
|
|
74
|
+
for name in quality.get('metrics') or {}:
|
|
75
|
+
key='observation.quality.metrics.'+name
|
|
76
|
+
if key in metrics:
|
|
77
|
+
metrics[key]['valid']=quality.get('fresh') is True and quality.get('quality_valid') is True
|
|
78
|
+
metrics[key]['acquired_unix_s']=quality.get('acquired_unix_s')
|
|
69
79
|
for source, target, scale, absolute in (
|
|
70
80
|
('error_hz','abs_error_hz',1.,True),
|
|
71
81
|
('native_interval_us','source_interval_s',1e-6,False),
|
|
@@ -74,6 +84,11 @@ def snapshot(box):
|
|
|
74
84
|
raw=metrics[source]
|
|
75
85
|
metrics[target]={**raw,'value':(abs(raw['value']) if absolute else raw['value'])*scale,
|
|
76
86
|
'derived_from':source}
|
|
87
|
+
if 'integral_v' in metrics and 'control.integral_limit_v' in metrics:
|
|
88
|
+
integral,limit=metrics['integral_v'],metrics['control.integral_limit_v']
|
|
89
|
+
if limit['value']>0:
|
|
90
|
+
metrics['integral_utilization']={**integral,'value':abs(integral['value'])/limit['value'],
|
|
91
|
+
'valid':integral['valid'] and limit['valid'],'derived_from':['integral_v','control.integral_limit_v']}
|
|
77
92
|
return dict(jobs=jobs, metrics=metrics,
|
|
78
93
|
authority='recorded data only; no physical state proof or new grant')
|
|
79
94
|
|
|
@@ -120,7 +135,7 @@ def run(directory, p, *, clock, sleep, cancel, emit, runtime=None, snapshot_fn=N
|
|
|
120
135
|
if grant_dir.is_symlink():raise ValueError('Monitor grant directory must not be a symlink')
|
|
121
136
|
grant_dir.mkdir(parents=True,exist_ok=True)
|
|
122
137
|
atomic_json(grant_dir/(meta['id']+'.json'),dict(generation=state['generation'],bounds=p['background'],
|
|
123
|
-
enabled=p['enabled'] and p['background']['enabled'] and not phase.startswith('stopped') and phase
|
|
138
|
+
enabled=p['enabled'] and p['background']['enabled'] and not phase.startswith('stopped') and phase not in ('control_data_unavailable','waiting_controller'),
|
|
124
139
|
last_seen=time.time(),max_age_s=max(30,p['sample_interval_s']*3),parameters_hash=digest(p)))
|
|
125
140
|
except (ValueError,OSError):
|
|
126
141
|
state['background_grant']='unavailable; passive review remains available'
|
|
@@ -203,6 +218,10 @@ def run(directory, p, *, clock, sleep, cancel, emit, runtime=None, snapshot_fn=N
|
|
|
203
218
|
from ldcon.agents.agent_background import revoke_monitor
|
|
204
219
|
revoke_monitor(box.root,laser_id,meta['id'],state['generation'])
|
|
205
220
|
publish('disabled'); sleep(p['sample_interval_s']); continue
|
|
221
|
+
if p['background'].get('controller_operation')=='servo.dlcpro_frequency' and not any(
|
|
222
|
+
j.get('status')=='running' and any(c.get('operation',c.get('card'))=='servo.dlcpro_frequency'
|
|
223
|
+
for c in j.get('cards',[])) for j in last_snapshot.get('jobs',[])):
|
|
224
|
+
publish('waiting_controller'); sleep(p['sample_interval_s']); continue
|
|
206
225
|
due = bool(p['interval_s'] and (state['last_review'] is None or now-state['last_review'] >= p['interval_s']))
|
|
207
226
|
research = bool(p['history_task_card_id'] and p['history_interval_s'] and
|
|
208
227
|
(state['last_history'] is None or now-state['last_history'] >= p['history_interval_s']) and
|
|
@@ -24,6 +24,7 @@ def parameters(card, overrides):
|
|
|
24
24
|
execution_profile='', request_delegation=True,
|
|
25
25
|
lookback_s=3600., sample_interval_s=1., timeout_s=1800., monitor_job_id='', evidence_images=[],
|
|
26
26
|
adjustment_family='auto', adjustment_plan_card_id='agent-monitor', adjustment_objective='',
|
|
27
|
+
adjustment_measurement_cards={},
|
|
27
28
|
adoption_mode='auto_within_approved')
|
|
28
29
|
p = {**defaults, **card['parameters'], **overrides}
|
|
29
30
|
if set(p) != set(defaults):
|
|
@@ -40,6 +41,10 @@ def parameters(card, overrides):
|
|
|
40
41
|
raise ValueError('Invalid task prompt')
|
|
41
42
|
# The native-only distribution need not install ECDL parameter adapters.
|
|
42
43
|
if p['adjustment_family'] not in ('auto','current','pi','slow_loop','feedforward'):raise ValueError('Unknown adjustment family')
|
|
44
|
+
plans=p['adjustment_measurement_cards']
|
|
45
|
+
if not isinstance(plans,dict) or set(plans)-{'current','pi','slow_loop','feedforward'}:
|
|
46
|
+
raise ValueError('Invalid adjustment measurement cards')
|
|
47
|
+
for value in plans.values():identifier(value)
|
|
43
48
|
if (not isinstance(p['adjustment_objective'],str) or '\x00' in p['adjustment_objective']
|
|
44
49
|
or len(p['adjustment_objective'].encode())>4000):raise ValueError('Invalid adjustment objective')
|
|
45
50
|
if card['operation']=='agent.history_review' and p['instruction_profile']!='research':
|
|
@@ -174,6 +179,8 @@ def run(directory, p, *, clock, sleep, cancel, emit, adapter_factory=None, bridg
|
|
|
174
179
|
work_started=True
|
|
175
180
|
if (invocation.get('adjustment_plan') or {}).get('kind') in ('dlc_pi_change_measure','dlc_pi_target_response'):
|
|
176
181
|
from ldcon.agents.dlc_pi_adjustment import run as adjust
|
|
182
|
+
elif (invocation.get('adjustment_plan') or {}).get('kind') == 'dlc_adjustment':
|
|
183
|
+
from ldcon.agents.dlc_parameter_adjustment import run as adjust
|
|
177
184
|
outcome=adjust(directory,p,clock=clock,sleep=sleep,cancel=cancel,emit=emit,
|
|
178
185
|
selected=parent,decision_session=child)
|
|
179
186
|
outcome['explanation']=child.explain(outcome)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Protected monitor lifetime for attached DLC parameter comparisons.
|
|
2
|
+
|
|
3
|
+
This grants only explicitly configured, owner-supported adjustment families.
|
|
4
|
+
It cannot grant target excursions or changes to physical policy.
|
|
5
|
+
The worker binds the immutable monitor invocation; the owner checks its lifetime
|
|
6
|
+
again at each effect and can restore without waiting for the model.
|
|
7
|
+
"""
|
|
8
|
+
from copy import deepcopy
|
|
9
|
+
import math
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import time
|
|
13
|
+
|
|
14
|
+
from experiment_core.records import digest, safe_id
|
|
15
|
+
from ldcon.core.interface_schema import load_mapping
|
|
16
|
+
from ldcon.core.records import read_json
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate_bounds(value):
|
|
20
|
+
fields = {'enabled', 'controller_operation', 'families', 'timeout_s'}
|
|
21
|
+
if (not isinstance(value, dict) or set(value) != fields
|
|
22
|
+
or type(value['enabled']) is not bool
|
|
23
|
+
or value['controller_operation'] != 'servo.dlcpro_frequency'
|
|
24
|
+
or not isinstance(value['families'],list) or not value['families']
|
|
25
|
+
or any(not isinstance(f,str) for f in value['families'])
|
|
26
|
+
or len(set(value['families']))!=len(value['families'])
|
|
27
|
+
or set(value['families'])-{'pi','current','slow_loop','feedforward'}
|
|
28
|
+
or type(value['timeout_s']) not in (int, float)
|
|
29
|
+
or not math.isfinite(value['timeout_s']) or not 1 <= value['timeout_s'] <= 86400):
|
|
30
|
+
raise ValueError('DLC monitor requires explicit adjustment families and a finite lifetime')
|
|
31
|
+
return deepcopy(value)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def check_grant(laser_id, binding, *, clock=time.time):
|
|
35
|
+
if not isinstance(binding, dict) or set(binding) != {'monitor_id', 'generation', 'parameters_hash'}:
|
|
36
|
+
raise ValueError('Invalid DLC monitor grant binding')
|
|
37
|
+
safe_id(laser_id); safe_id(binding['monitor_id'])
|
|
38
|
+
if any(not isinstance(binding[k], str) or not binding[k] for k in ('generation', 'parameters_hash')):
|
|
39
|
+
raise ValueError('Incomplete DLC monitor generation')
|
|
40
|
+
root = Path(os.environ.get('LDCON_ADAPTIVE_ROOT', ''))
|
|
41
|
+
if not root.is_absolute() or not root.is_dir() or root.resolve() != root:
|
|
42
|
+
raise ValueError('Protected monitor authority is unavailable')
|
|
43
|
+
path = root/laser_id/'monitors'/(binding['monitor_id']+'.json')
|
|
44
|
+
if any(p.is_symlink() for p in (path, path.parent, path.parent.parent)):
|
|
45
|
+
raise ValueError('Monitor authority must not follow symlinks')
|
|
46
|
+
grant = read_json(path, {})
|
|
47
|
+
if not isinstance(grant, dict):raise ValueError('Monitor authority is unavailable')
|
|
48
|
+
bounds = validate_bounds(grant.get('bounds'))
|
|
49
|
+
stamp, age = grant.get('last_seen'), grant.get('max_age_s')
|
|
50
|
+
if (grant.get('enabled') is not True or not bounds['enabled']
|
|
51
|
+
or any(grant.get(k) != binding[k] for k in ('generation', 'parameters_hash'))
|
|
52
|
+
or any(type(v) not in (int, float) or not math.isfinite(v) for v in (stamp, age))
|
|
53
|
+
or not 0 < age <= 180 or not 0 <= clock()-stamp <= age):
|
|
54
|
+
raise ValueError('Monitor grant stopped, changed or expired')
|
|
55
|
+
return grant
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def bind(directory, p, laser):
|
|
59
|
+
"""Bind a monitor task to its own frozen parent, never model-supplied text."""
|
|
60
|
+
if not p.get('monitor_job_id'):return None
|
|
61
|
+
from ldcon.agents.agent_monitor import parameters
|
|
62
|
+
from ldcon.agents.agent_background import control_root
|
|
63
|
+
directory = Path(directory)
|
|
64
|
+
monitor_id = safe_id(p['monitor_job_id'])
|
|
65
|
+
parent = directory.parent/monitor_id
|
|
66
|
+
if parent.is_symlink():raise ValueError('Monitor job must not be a symlink')
|
|
67
|
+
meta = read_json(parent/'job.json', {})
|
|
68
|
+
laser_id = laser['id']
|
|
69
|
+
source = load_mapping((parent/'laser.yaml').read_text())
|
|
70
|
+
invocation = read_json(parent/'invocation.json', {})
|
|
71
|
+
deck = load_mapping((parent/'interface-deck.yaml').read_text())
|
|
72
|
+
card = next((c for c in deck['cards'] if c['id'] == invocation.get('card_id')), None)
|
|
73
|
+
task = read_json(directory/'invocation.json', {})
|
|
74
|
+
if (meta.get('id') != monitor_id or source['id'] != laser_id
|
|
75
|
+
or not card or card['operation'] != 'agent.monitor'):
|
|
76
|
+
raise ValueError('Monitor belongs to another laser or operation')
|
|
77
|
+
plan = parameters(card, invocation['parameters'])
|
|
78
|
+
bounds = validate_bounds(plan['background'])
|
|
79
|
+
if (plan['task_card_id'] != task['card_id'] or not plan['enabled']
|
|
80
|
+
or not bounds['enabled'] or p['timeout_s'] > bounds['timeout_s']):
|
|
81
|
+
raise ValueError('Task differs from the monitor-approved adjustment plan')
|
|
82
|
+
root = control_root(directory.parent.parent)
|
|
83
|
+
grant = read_json(root/laser_id/'monitors'/(monitor_id+'.json'), {})
|
|
84
|
+
binding = dict(monitor_id=monitor_id, generation=grant.get('generation'), parameters_hash=digest(plan))
|
|
85
|
+
live = check_grant(laser_id, binding)
|
|
86
|
+
if live['bounds'] != bounds:raise ValueError('Monitor bounds differ from its frozen invocation')
|
|
87
|
+
return binding
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""One task-model workflow over owner-confirmed DLC adjustment families.
|
|
2
|
+
|
|
3
|
+
The monitor only starts this task. Selection, paired measurements, confirmation
|
|
4
|
+
and adoption are identical for a manual invocation. Models have evidence access,
|
|
5
|
+
not raw controller credentials or direct physical commands.
|
|
6
|
+
"""
|
|
7
|
+
from copy import deepcopy
|
|
8
|
+
import math
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from ldcon.core.contracts import DeviceError
|
|
12
|
+
from ldcon.core.interface_cards import CardCancelled
|
|
13
|
+
from ldcon.core.interface_schema import load_mapping
|
|
14
|
+
from ldcon.core.records import read_json, atomic_json
|
|
15
|
+
from ldcon.jobs.change_measure import ChangeMeasure
|
|
16
|
+
from ldcon.jobs.dlc_parameter_measurement import ParameterAdapter, candidate
|
|
17
|
+
from ldcon.jobs.study_execution import export_evidence, parse_proposal
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def evidence(directory, result, state, turn):
|
|
21
|
+
prefix = 'parameters-%03d-' % turn
|
|
22
|
+
docs = [(prefix+'summary', deepcopy(result)), (prefix+'owner', deepcopy(state))]
|
|
23
|
+
for row in result['measurements'][-3:]:
|
|
24
|
+
raw = read_json(directory/row['path'])
|
|
25
|
+
for name, values, size in (('samples', raw['samples'], 120),
|
|
26
|
+
('controls', raw.get('control_observations', []), 12)):
|
|
27
|
+
for i in range(0, len(values), size):
|
|
28
|
+
docs.append((prefix+row['id']+'-'+name+'-%05d'%i, values[i:i+size]))
|
|
29
|
+
return docs
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def response(answer, *, selection=False):
|
|
33
|
+
value = parse_proposal(answer, allow_progress_prefix=True)
|
|
34
|
+
if not isinstance(value, dict) or set(value) != {'action', 'parameters'}:
|
|
35
|
+
raise ValueError('Return exactly action and parameters')
|
|
36
|
+
action, args = value['action'], value['parameters']
|
|
37
|
+
expected = ({'choose': {'family', 'reason'}, 'finish': {'reason'}} if selection else
|
|
38
|
+
{'trial': {'candidate', 'reason'}, 'adopt': {'trial_id', 'reason'}, 'finish': {'reason'},
|
|
39
|
+
'observe': {'wait_s', 'duration_s', 'reason'}})
|
|
40
|
+
if (action not in expected or not isinstance(args, dict) or set(args) != expected[action]
|
|
41
|
+
or not isinstance(args['reason'], str) or not args['reason'].strip()):
|
|
42
|
+
raise ValueError('Unsupported decision or missing reason')
|
|
43
|
+
return value
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _run(directory, p, *, clock, sleep, cancel, emit, selected, decision_session,
|
|
47
|
+
adapter=None, report_writer=None):
|
|
48
|
+
directory = Path(directory)
|
|
49
|
+
frozen = read_json(directory/'invocation.json').get('adjustment_plan') or {}
|
|
50
|
+
if frozen.get('kind') != 'dlc_adjustment':raise ValueError('Frozen DLC adjustment plan required')
|
|
51
|
+
laser = load_mapping((directory/'laser.yaml').read_text())
|
|
52
|
+
adapter = adapter or ParameterAdapter(laser, directory.name, clock=clock)
|
|
53
|
+
if hasattr(adapter, 'supervise'):adapter.supervise(directory, p, laser)
|
|
54
|
+
result = dict(status='running', family=None, trials=[], measurements=[], adopted=False,
|
|
55
|
+
physical_outcome='no_owner_command_in_flight', target_frequency_hz=adapter.target,
|
|
56
|
+
interpretation='Finite paired comparisons; no global optimum or long-term stability claim')
|
|
57
|
+
adopted = False
|
|
58
|
+
|
|
59
|
+
def save():atomic_json(directory/'parameter-adjustment.json', result)
|
|
60
|
+
def check():
|
|
61
|
+
if cancel() or clock() >= decision_session.deadline:raise CardCancelled('Adjustment cancelled/deadline')
|
|
62
|
+
adapter.check()
|
|
63
|
+
def request(prompt, *, selecting=False):
|
|
64
|
+
for attempt in range(3):
|
|
65
|
+
check()
|
|
66
|
+
store, _, _, _ = selected
|
|
67
|
+
_, manifest = export_evidence(directory, store, laser['id'],
|
|
68
|
+
evidence(directory, result, adapter.state, decision_session.turns))
|
|
69
|
+
mode = getattr(adapter, 'operating_mode', None)
|
|
70
|
+
if mode and mode.get('mode') in ('measurement', 'characterization'):
|
|
71
|
+
from ldcon.agents.operating_mode_client import instructions
|
|
72
|
+
policy = instructions(mode)
|
|
73
|
+
else:policy = ''
|
|
74
|
+
raw = decision_session.request(dict(kind='dlc_adjustment_decision', prompt=prompt+policy+
|
|
75
|
+
'\nTask objective: '+p['prompt']+' '+p['adjustment_objective'], evidence_files=manifest), tick=check)
|
|
76
|
+
try:
|
|
77
|
+
value = response(raw, selection=selecting)
|
|
78
|
+
action, args = value['action'], value['parameters']
|
|
79
|
+
if action == 'choose' and args['family'] not in supported:
|
|
80
|
+
raise ValueError('Family is not admitted and supported by this owner')
|
|
81
|
+
if action == 'trial':candidate(result['family'], args['candidate'])
|
|
82
|
+
if action == 'observe':
|
|
83
|
+
if (any(type(args[k]) not in (int,float) or not math.isfinite(args[k]) for k in ('wait_s','duration_s'))
|
|
84
|
+
or not 0 <= args['wait_s'] <= 86400 or not 0 < args['duration_s'] <= 86400):
|
|
85
|
+
raise ValueError('Observation timing must be finite and positive')
|
|
86
|
+
if args['wait_s']+args['duration_s']+30 >= decision_session.deadline-clock():
|
|
87
|
+
raise ValueError('Observation exceeds the remaining finite task budget')
|
|
88
|
+
if action == 'adopt':
|
|
89
|
+
i = args['trial_id']
|
|
90
|
+
if type(i) is not int or not 0 <= i < len(result['trials']):raise ValueError('Unknown trial')
|
|
91
|
+
if not result['trials'][i]['eligible']:raise ValueError('Incomplete comparison evidence')
|
|
92
|
+
if p['adoption_mode'] != 'auto_within_approved':raise ValueError('This run is report-only')
|
|
93
|
+
except (ValueError, TypeError) as error:
|
|
94
|
+
result['last_rejection'] = str(error);save()
|
|
95
|
+
if attempt == 2:raise ValueError('Three invalid decisions; no command replay') from error
|
|
96
|
+
continue
|
|
97
|
+
result.pop('last_rejection', None)
|
|
98
|
+
atomic_json(directory/('parameter-decision-%03d.json'%decision_session.turns), value)
|
|
99
|
+
return value
|
|
100
|
+
|
|
101
|
+
check()
|
|
102
|
+
supported = [f for f in frozen['measurements']
|
|
103
|
+
if adapter.state.get('parameter_capabilities', {}).get(f, {}).get('supported') is True
|
|
104
|
+
and (getattr(adapter,'monitor_families',None) is None or f in adapter.monitor_families)]
|
|
105
|
+
family = frozen['family']
|
|
106
|
+
if family == 'auto':
|
|
107
|
+
answer = request('Select one useful adjustment family from the admitted owner capabilities, or finish '
|
|
108
|
+
'without physical changes if none is justified. Review current state, supplied monitor events, recent '
|
|
109
|
+
'source history and prior adjustment outcomes using the advertised read-only evidence tools. '
|
|
110
|
+
'PI compares independent P/I/integral contribution; current compares the diode current offset; slow_loop '
|
|
111
|
+
'compares TEC centering gain/period/PZT center; feedforward compares the internal current FF coefficient. '
|
|
112
|
+
'A quality warning is a prompt to assess, not proof that a particular knob must move. Return '
|
|
113
|
+
'{"action":"choose","parameters":{"family":"...","reason":"..."}} or '
|
|
114
|
+
'{"action":"finish","parameters":{"reason":"..."}}. Supported families: '+str(supported), selecting=True)
|
|
115
|
+
if answer['action'] == 'finish':
|
|
116
|
+
result.update(status='succeeded', reason=answer['parameters']['reason'],
|
|
117
|
+
message='Assessment completed without a physical trial', output_writes=0)
|
|
118
|
+
save();return result
|
|
119
|
+
family = answer['parameters']['family']
|
|
120
|
+
result['selection_reason'] = answer['parameters']['reason']
|
|
121
|
+
if family not in supported:raise ValueError('Requested family has no admitted owner capability')
|
|
122
|
+
result['family'] = family
|
|
123
|
+
adapter.select_family(family)
|
|
124
|
+
if family == 'pi':
|
|
125
|
+
from ldcon.agents.dlc_pi_adjustment import run as pi_run
|
|
126
|
+
outcome = pi_run(directory, p, clock=clock, sleep=sleep, cancel=cancel, emit=emit,
|
|
127
|
+
selected=selected, decision_session=decision_session, adapter=adapter, report_writer=report_writer,
|
|
128
|
+
plan=dict(kind='dlc_pi_change_measure', measurement=frozen['measurements']['pi']))
|
|
129
|
+
result.update(status=outcome['status'],adopted=outcome['adopted'],pi_result_path='pi-adjustment.json')
|
|
130
|
+
save();return dict(outcome,family='pi')
|
|
131
|
+
measurement = deepcopy(frozen['measurements'][family])
|
|
132
|
+
result['checkpoint'] = deepcopy(adapter.origin)
|
|
133
|
+
save()
|
|
134
|
+
|
|
135
|
+
def measure(values, role, *, restore=False):
|
|
136
|
+
check()
|
|
137
|
+
if restore:
|
|
138
|
+
receipt = adapter.restore_checkpoint()
|
|
139
|
+
result.setdefault('restoration_receipts', []).append(receipt)
|
|
140
|
+
q = deepcopy(measurement)
|
|
141
|
+
q['target_frequency_hz'] = adapter.target
|
|
142
|
+
q['changes'] = [] if restore or role == 'baseline' else [dict(parameter=k, value=v) for k,v in values.items()]
|
|
143
|
+
remaining = decision_session.deadline-clock()
|
|
144
|
+
if remaining < q['baseline_s']+q['wait_s']+q['duration_s']+10:
|
|
145
|
+
raise CardCancelled('Insufficient task time for a complete comparison window')
|
|
146
|
+
q['timeout_s'] = min(q['timeout_s'], remaining)
|
|
147
|
+
name = 'parameter-window-%03d'%len(result['measurements'])
|
|
148
|
+
runner = ChangeMeasure(adapter, q, clock=clock, wall_clock=adapter.wall_clock,
|
|
149
|
+
sleep=sleep, cancel=cancel, emit=emit)
|
|
150
|
+
try:raw = runner.run()
|
|
151
|
+
finally:atomic_json(directory/(name+'.json'), runner.result)
|
|
152
|
+
if raw['status'] != 'succeeded':raise DeviceError('Incomplete measurement window; no adoption')
|
|
153
|
+
row = dict(id=name, role=role, settings=deepcopy(values), summary=raw['summary'], path=name+'.json')
|
|
154
|
+
result['measurements'].append(row);save();emit('parameter_measurement_completed', row)
|
|
155
|
+
return row
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
adapter.begin()
|
|
159
|
+
result['checkpoint']=deepcopy(adapter.origin);save()
|
|
160
|
+
baseline = measure(adapter.origin, 'baseline')
|
|
161
|
+
while decision_session.turns < decision_session.profile['max_turns']-2:
|
|
162
|
+
check()
|
|
163
|
+
if decision_session.deadline-clock() < 30:break
|
|
164
|
+
answer = request('Choose a bounded informative candidate, request confirmation/adoption of a recorded '
|
|
165
|
+
'trial, or finish. Compare full time-stamped wavelength/optical measurements, sustained bias, '
|
|
166
|
+
'oscillation, drift, PZT/TEC coupling and ordinary scatter. Current is the DLC current OFFSET '
|
|
167
|
+
'in mA; total current including internal FF is checked independently by the owner. Slow-loop '
|
|
168
|
+
'parameters set TEC centering behavior, not a direct target change. A restored TEC command is '
|
|
169
|
+
'not proof of thermal settling: use the recorded wait/window and say if it is inadequate. '
|
|
170
|
+
'An FF candidate is justified only if PZT motion in the evidence identifies its effect; unchanged '
|
|
171
|
+
'PZT is not a coefficient calibration. Choose a small initial experiment from evidence, rather '
|
|
172
|
+
'than jumping across physical bounds. Assess metric trade-offs jointly and retain practical '
|
|
173
|
+
'headroom for drift; do not optimize to a threshold or require every metric to improve. '
|
|
174
|
+
'The host returns to the trial checkpoint for matched baseline. First adopt requests a repeated '
|
|
175
|
+
'candidate plus baseline; review that confirmation and issue adopt on its new trial_id to commit. '
|
|
176
|
+
'No target, safety, calibration, firmware, new hardware sweep or optical-threshold edits are allowed. '
|
|
177
|
+
'Foreground operations have priority. Return {"action":"trial","parameters":{"candidate":{...},'
|
|
178
|
+
'"reason":"..."}}, {"action":"adopt","parameters":{"trial_id":0,"reason":"..."}}, '
|
|
179
|
+
'or {"action":"finish","parameters":{"reason":"..."}}. To change an inadequate measurement '
|
|
180
|
+
'window, return {"action":"observe","parameters":{"wait_s":5,"duration_s":60,"reason":"..."}} '
|
|
181
|
+
'(values are illustrative): this creates a fresh checkpoint baseline and invalidates old adoption '
|
|
182
|
+
'eligibility, preserving comparable windows. Explain actual gains, costs and limits.')
|
|
183
|
+
action, args = answer['action'], answer['parameters']
|
|
184
|
+
if action == 'finish':result['reason'] = args['reason'];break
|
|
185
|
+
if action == 'observe':
|
|
186
|
+
measurement.update(wait_s=args['wait_s'],duration_s=args['duration_s'])
|
|
187
|
+
measurement['timeout_s'] = measurement['baseline_s']+measurement['wait_s']+measurement['duration_s']+30
|
|
188
|
+
for previous in result['trials']:
|
|
189
|
+
previous.update(eligible=False, ineligible_reason='Measurement duration changed; a fresh comparison is required')
|
|
190
|
+
result.setdefault('measurement_plan_changes',[]).append(deepcopy(args))
|
|
191
|
+
baseline = measure(adapter.origin, 'baseline', restore=True)
|
|
192
|
+
continue
|
|
193
|
+
prior = result['trials'][args['trial_id']] if action == 'adopt' else None
|
|
194
|
+
values = args['candidate'] if action == 'trial' else prior['candidate']
|
|
195
|
+
if prior and 'confirmation_of' in prior:
|
|
196
|
+
check()
|
|
197
|
+
proposal = adapter.preflight([dict(parameter=k, value=v) for k,v in values.items()],
|
|
198
|
+
measurement['apply_mode'], measurement['measurements'])
|
|
199
|
+
result['adoption_receipt'] = adapter.apply(proposal, lambda:None)
|
|
200
|
+
receipt = adapter.close(adopt=True)
|
|
201
|
+
result['cleanup'] = receipt
|
|
202
|
+
if (not receipt or receipt.get('state') != 'adopted' or receipt.get('confirmed') is not True
|
|
203
|
+
or receipt.get('settings') != values or receipt.get('identity') != adapter.identity
|
|
204
|
+
or receipt.get('target_restored') is not True):
|
|
205
|
+
raise CardCancelled('Adoption preempted or unconfirmed')
|
|
206
|
+
adopted = True
|
|
207
|
+
result.update(adopted=True, adopted_settings=deepcopy(values), reason=args['reason'])
|
|
208
|
+
break
|
|
209
|
+
if decision_session.deadline-clock() < 2*measurement['timeout_s']+30:
|
|
210
|
+
result['last_rejection'] = 'Insufficient time for another complete pair; finish or adopt a confirmed candidate'
|
|
211
|
+
save();continue
|
|
212
|
+
trial = measure(values, 'candidate' if action == 'trial' else 'confirmation')
|
|
213
|
+
after = measure(adapter.origin, 'matched_baseline', restore=True)
|
|
214
|
+
row = dict(id=len(result['trials']), candidate=deepcopy(values), before=baseline['id'],
|
|
215
|
+
trial=trial['id'], after=after['id'], eligible=True, reason=args['reason'])
|
|
216
|
+
if prior:row['confirmation_of'] = prior['id']
|
|
217
|
+
result['trials'].append(row);baseline = after;save()
|
|
218
|
+
result['status'] = 'succeeded'
|
|
219
|
+
result.setdefault('reason', 'Finite decision budget reached; checkpoint retained')
|
|
220
|
+
finally:
|
|
221
|
+
try:
|
|
222
|
+
if not adopted and not result.get('cleanup'):result['cleanup'] = adapter.close(adopt=False)
|
|
223
|
+
receipt = result.get('cleanup') or {}
|
|
224
|
+
if result['status'] == 'succeeded' and not adopted:
|
|
225
|
+
if (receipt.get('confirmed') is not True or receipt.get('identity') != adapter.identity
|
|
226
|
+
or receipt.get('state') != 'restored' or receipt.get('settings') != adapter.origin):
|
|
227
|
+
raise DeviceError('Checkpoint restoration is unconfirmed')
|
|
228
|
+
if receipt.get('target_restored') is not True or receipt.get('reason','completed') != 'completed':
|
|
229
|
+
result.update(status='cancelled', reason='Foreground or owner preemption; checkpoint command restoration confirmed')
|
|
230
|
+
except Exception as error:
|
|
231
|
+
result.update(status='unknown', physical_outcome='requires_inspection', cleanup_error=type(error).__name__)
|
|
232
|
+
atomic_json(directory/'device-review.json', dict(source='dlc_parameter',
|
|
233
|
+
message='Parameter cleanup unconfirmed; inspect owner, do not replay'))
|
|
234
|
+
save();raise
|
|
235
|
+
if result['status'] == 'running':result['status'] = 'interrupted'
|
|
236
|
+
save()
|
|
237
|
+
try:
|
|
238
|
+
from ldcon.jobs.measurement_report import write_report
|
|
239
|
+
frames = [read_json(directory/row['path']) for row in result['measurements']]
|
|
240
|
+
report = (report_writer or write_report)(directory, dict(schema='ldcon.parameter-comparison/1',
|
|
241
|
+
status=result['status'], steps=frames, parameter_result=result))
|
|
242
|
+
import shutil
|
|
243
|
+
shutil.copyfile(report, directory/'report.pdf');result['report'] = 'report.pdf'
|
|
244
|
+
except Exception as error:result['report_error'] = type(error).__name__
|
|
245
|
+
save()
|
|
246
|
+
result['message'] = ('Repeated measured candidate adopted for this lock' if adopted else
|
|
247
|
+
'Comparison completed; checkpoint retained' if result['status']=='succeeded' else
|
|
248
|
+
'Adjustment interrupted; inspect the recorded owner receipt')
|
|
249
|
+
return result
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def run(directory, p, *, clock, sleep, cancel, emit, selected, decision_session,
|
|
253
|
+
adapter=None, report_writer=None):
|
|
254
|
+
laser=load_mapping((Path(directory)/'laser.yaml').read_text())
|
|
255
|
+
adapter=adapter or ParameterAdapter(laser,Path(directory).name,clock=clock)
|
|
256
|
+
try:
|
|
257
|
+
return _run(directory,p,clock=clock,sleep=sleep,cancel=cancel,emit=emit,
|
|
258
|
+
selected=selected,decision_session=decision_session,adapter=adapter,report_writer=report_writer)
|
|
259
|
+
except Exception as error:
|
|
260
|
+
if isinstance(error,CardCancelled) or cancel():raise
|
|
261
|
+
if (getattr(adapter,'uncertain',False) or getattr(adapter,'begin_pending',False)
|
|
262
|
+
or not hasattr(adapter,'session')):raise
|
|
263
|
+
try:proof=adapter.session.rpc('pi_trial_receipt')
|
|
264
|
+
except Exception:raise error
|
|
265
|
+
trial=proof.get('trial') or {};last=trial.get('last') or {}
|
|
266
|
+
known=(not proof.get('fault') and trial.get('active') is None and
|
|
267
|
+
(not getattr(adapter,'begin_confirmed',False) or
|
|
268
|
+
last.get('identity')==adapter.identity and last.get('confirmed') is True and last.get('state')=='restored'))
|
|
269
|
+
if not known:raise
|
|
270
|
+
from ldcon.agents.resident_adjustment import HostProtocolStop
|
|
271
|
+
atomic_json(Path(directory)/'adjustment-known-stop.json',dict(reason=type(error).__name__,
|
|
272
|
+
owner_receipt=proof,physical_outcome='no_owner_command_in_flight'))
|
|
273
|
+
raise HostProtocolStop(str(error),dict(status=dict(state='restored' if last else 'not_started',active=False),
|
|
274
|
+
latest_result=dict(state='restored' if last else 'not_started'))) from error
|
|
@@ -70,13 +70,14 @@ def evidence_documents(directory, result, state, turn):
|
|
|
70
70
|
return docs
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
def run(directory,p,*,clock,sleep,cancel,emit,selected,decision_session,adapter=None,report_writer=None):
|
|
73
|
+
def run(directory,p,*,clock,sleep,cancel,emit,selected,decision_session,adapter=None,report_writer=None,plan=None):
|
|
74
74
|
directory=Path(directory)
|
|
75
75
|
invocation=read_json(directory/'invocation.json')
|
|
76
|
-
plan=invocation.get('adjustment_plan') or {}
|
|
76
|
+
plan=deepcopy(plan if plan is not None else invocation.get('adjustment_plan') or {})
|
|
77
77
|
if plan.get('kind') not in ('dlc_pi_change_measure','dlc_pi_target_response'):raise ValueError('Frozen PI measurement plan required')
|
|
78
78
|
laser=load_mapping((directory/'laser.yaml').read_text())
|
|
79
79
|
adapter=adapter or PIAdapter(laser,directory.name,clock=clock)
|
|
80
|
+
if hasattr(adapter,'supervise'):adapter.supervise(directory,p,laser)
|
|
80
81
|
joint=plan['kind']=='dlc_pi_target_response'
|
|
81
82
|
if joint:
|
|
82
83
|
if adapter.target!=plan['target_frequency_hz']:raise ValueError('Live target differs from frozen response reference')
|
|
@@ -154,7 +155,7 @@ def run(directory,p,*,clock,sleep,cancel,emit,selected,decision_session,adapter=
|
|
|
154
155
|
result.update(adopted=True,adopted_settings=values,reason=reason,
|
|
155
156
|
adoption_basis='Task-model decision after repeated matched measurements')
|
|
156
157
|
try:
|
|
157
|
-
adapter.begin();save()
|
|
158
|
+
adapter.begin();result['checkpoint']=deepcopy(adapter.origin);save()
|
|
158
159
|
baseline=measure(adapter.origin,'baseline')
|
|
159
160
|
invalid=0
|
|
160
161
|
while True:
|
|
@@ -179,6 +180,9 @@ def run(directory,p,*,clock,sleep,cancel,emit,selected,decision_session,adapter=
|
|
|
179
180
|
'or {"action":"adopt","parameters":{"trial_id":number,"reason":"..."}}, '
|
|
180
181
|
'or {"action":"finish","parameters":{"reason":"..."}}. No other commands. '
|
|
181
182
|
'Explain why the evidence warrants a trial, adoption, or no further changes. '+p['prompt']+' '+p['adjustment_objective'])
|
|
183
|
+
if getattr(adapter,'operating_mode_revision',None) is not None:
|
|
184
|
+
from ldcon.agents.operating_mode_client import instructions
|
|
185
|
+
prompt+=instructions(adapter.operating_mode)
|
|
182
186
|
if joint:
|
|
183
187
|
prompt+=' This is JOINT target-step and steady-noise optimization. The host executes the frozen target-step and return sequence; you only choose P/I/integral coefficients. Judge the overall trade-off among response speed, steady RMS, mean bias, overshoot, saturation and practical robustness. A modest RMS increase can be worthwhile for a substantial speed gain: the human explicitly gave +10 percent RMS with 2x faster response as an acceptable kind of trade-off, not a fixed numeric acceptance rule. Do not require RMS non-degradation or invent fixed weights. Quantify gains and costs and justify your preference. The response band is empirical from the first checkpoint and stays fixed. Null settling times are censored, not zero or the deadline. Compare request/verified-write timestamps, signed mean error, response integral and the full raw waveforms. No candidate-specific relaxation of the band. Requesting adopt for an initial trial triggers a repeat and matched baseline. Review that new confirmation row before issuing adopt again for its trial_id; only the second decision can commit. You may decline after seeing confirmation.'
|
|
184
188
|
answer=decision_session.request(dict(kind='pi_adjustment_decision',prompt=prompt,evidence_files=manifest),tick=check)
|