ldcon 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ldcon/__init__.py +302 -0
- ldcon/_moved.py +49 -0
- ldcon/agent_tools.py +4 -0
- ldcon/agents/__init__.py +1 -0
- ldcon/agents/adjustment_guidance.py +73 -0
- ldcon/agents/adjustment_quality.py +399 -0
- ldcon/agents/agent_adapters.py +709 -0
- ldcon/agents/agent_background.py +307 -0
- ldcon/agents/agent_bundle.py +75 -0
- ldcon/agents/agent_cards.py +171 -0
- ldcon/agents/agent_config_access.py +22 -0
- ldcon/agents/agent_configuration.py +224 -0
- ldcon/agents/agent_configuration_policy.py +67 -0
- ldcon/agents/agent_connect.py +93 -0
- ldcon/agents/agent_device_parameters.py +41 -0
- ldcon/agents/agent_execution.py +77 -0
- ldcon/agents/agent_history.py +112 -0
- ldcon/agents/agent_inbox_watch.py +121 -0
- ldcon/agents/agent_interactive.py +386 -0
- ldcon/agents/agent_interactive_hooks.py +381 -0
- ldcon/agents/agent_language.py +48 -0
- ldcon/agents/agent_log_context.py +308 -0
- ldcon/agents/agent_log_read.py +250 -0
- ldcon/agents/agent_log_watch.py +27 -0
- ldcon/agents/agent_metrics.py +111 -0
- ldcon/agents/agent_monitor.py +272 -0
- ldcon/agents/agent_opencode_http.py +270 -0
- ldcon/agents/agent_opencode_mcp.py +64 -0
- ldcon/agents/agent_owner_status.py +216 -0
- ldcon/agents/agent_profiles.py +186 -0
- ldcon/agents/agent_providers.py +53 -0
- ldcon/agents/agent_repairs.py +105 -0
- ldcon/agents/agent_routing.py +90 -0
- ldcon/agents/agent_runtime.py +70 -0
- ldcon/agents/agent_sandbox.py +141 -0
- ldcon/agents/agent_startup_notice.py +142 -0
- ldcon/agents/agent_task.py +336 -0
- ldcon/agents/agent_tools.py +488 -0
- ldcon/agents/agent_window.py +70 -0
- ldcon/agents/agent_workspace.py +340 -0
- ldcon/agents/card_fallback.py +284 -0
- ldcon/agents/dlc_pi_adjustment.py +250 -0
- ldcon/agents/dlc_pi_plan.py +24 -0
- ldcon/agents/dlc_pi_response_plan.py +26 -0
- ldcon/agents/failure_policy.py +473 -0
- ldcon/agents/fallback_service.py +146 -0
- ldcon/agents/fallback_targets.py +96 -0
- ldcon/agents/gateway_role.py +31 -0
- ldcon/agents/manual_adjustment_grant.py +66 -0
- ldcon/agents/measurement_grant.py +95 -0
- ldcon/agents/monitor_quality_binding.py +33 -0
- ldcon/agents/notification_policy.py +346 -0
- ldcon/agents/operating_mode_client.py +99 -0
- ldcon/agents/report_pdf.py +105 -0
- ldcon/agents/resident_adjustment.py +572 -0
- ldcon/agents/resident_agent.py +438 -0
- ldcon/agents/resident_quality_apply.py +233 -0
- ldcon/agents/task_decisions.py +218 -0
- ldcon/agents/teams_card.py +100 -0
- ldcon/agents/teams_destinations.py +157 -0
- ldcon/cld_owner.py +4 -0
- ldcon/cli.py +19 -0
- ldcon/core/__init__.py +1 -0
- ldcon/core/adjustment_plan.py +47 -0
- ldcon/core/adjustment_registry.py +100 -0
- ldcon/core/agent_authority.py +903 -0
- ldcon/core/card_admission.py +15 -0
- ldcon/core/card_definition.py +14 -0
- ldcon/core/card_reporting.py +238 -0
- ldcon/core/configuration.py +357 -0
- ldcon/core/configuration_transaction.py +225 -0
- ldcon/core/contracts.py +86 -0
- ldcon/core/current_settings.py +86 -0
- ldcon/core/deck_yaml.py +45 -0
- ldcon/core/device_conditions.py +133 -0
- ldcon/core/execution_context.py +3 -0
- ldcon/core/instrument_client.py +190 -0
- ldcon/core/interface_cards.py +520 -0
- ldcon/core/interface_schema.py +640 -0
- ldcon/core/laser_assets.py +238 -0
- ldcon/core/metric_quantities.py +38 -0
- ldcon/core/modulation.py +107 -0
- ldcon/core/operation_registry.py +195 -0
- ldcon/core/paths.py +26 -0
- ldcon/core/period_context.py +118 -0
- ldcon/core/records.py +4 -0
- ldcon/core/result_reservations.py +469 -0
- ldcon/core/site_bindings.py +25 -0
- ldcon/core/state_layout.py +70 -0
- ldcon/core/target_policy.py +79 -0
- ldcon/core/telegraf.py +106 -0
- ldcon/core/telemetry.py +130 -0
- ldcon/core/temperature_settings.py +12 -0
- ldcon/dashboard_metrics.py +4 -0
- ldcon/device_registry.py +4 -0
- ldcon/drivers/__init__.py +1 -0
- ldcon/drivers/cld1015.py +296 -0
- ldcon/drivers/cld1015_config.py +31 -0
- ldcon/drivers/dlcpro.py +177 -0
- ldcon/drivers/dlcpro_analog.py +33 -0
- ldcon/drivers/dlcpro_console.py +133 -0
- ldcon/drivers/dlcpro_priority.py +46 -0
- ldcon/drivers/ecdl.py +334 -0
- ldcon/drivers/esp32_servo.py +187 -0
- ldcon/drivers/fizeau.py +114 -0
- ldcon/drivers/frequency_sweep.py +69 -0
- ldcon/drivers/influx_wavelength.py +70 -0
- ldcon/drivers/metric_sources.py +82 -0
- ldcon/drivers/native_dither_telemetry.py +50 -0
- ldcon/drivers/rigol_scope.py +126 -0
- ldcon/drivers/siglent.py +418 -0
- ldcon/drivers/staircase.py +155 -0
- ldcon/drivers/wsu_udp.py +164 -0
- ldcon/esp32_time_telemetry.py +4 -0
- ldcon/example_config.py +41 -0
- ldcon/fallback_service.py +4 -0
- ldcon/instrument_owner.py +4 -0
- ldcon/instrument_service.py +4 -0
- ldcon/interface_worker.py +4 -0
- ldcon/job_service.py +4 -0
- ldcon/jobs/__init__.py +1 -0
- ldcon/jobs/adaptive_pid.py +234 -0
- ldcon/jobs/adaptive_window_session.py +922 -0
- ldcon/jobs/adjustment_continuation.py +284 -0
- ldcon/jobs/axis_frequency.py +219 -0
- ldcon/jobs/axis_frequency_report.py +86 -0
- ldcon/jobs/background_parameter_session.py +142 -0
- ldcon/jobs/background_pid_session.py +447 -0
- ldcon/jobs/branch_maps.py +71 -0
- ldcon/jobs/branch_target_migration.py +33 -0
- ldcon/jobs/card_runtime.py +157 -0
- ldcon/jobs/change_measure.py +293 -0
- ldcon/jobs/change_measure_adapter.py +360 -0
- ldcon/jobs/change_measure_dlc.py +94 -0
- ldcon/jobs/change_measure_parameters.py +222 -0
- ldcon/jobs/change_measure_target.py +135 -0
- ldcon/jobs/current_cards.py +159 -0
- ldcon/jobs/current_devices.py +71 -0
- ldcon/jobs/dashboard_api.py +61 -0
- ldcon/jobs/device_parameters.py +426 -0
- ldcon/jobs/dlc_pi_measurement.py +151 -0
- ldcon/jobs/dlcpro_awg_analysis.py +77 -0
- ldcon/jobs/dlcpro_awg_card.py +116 -0
- ldcon/jobs/dlcpro_cards.py +240 -0
- ldcon/jobs/ecdl_branch_hop.py +175 -0
- ldcon/jobs/ecdl_branch_recovery.py +176 -0
- ldcon/jobs/ecdl_devices.py +306 -0
- ldcon/jobs/ecdl_locked_sweep_proxy.py +526 -0
- ldcon/jobs/ecdl_mapped_recovery.py +6 -0
- ldcon/jobs/ecdl_mode_map.py +469 -0
- ldcon/jobs/ecdl_mode_recovery.py +20 -0
- ldcon/jobs/ecdl_online.py +158 -0
- ldcon/jobs/ecdl_operating_point.py +403 -0
- ldcon/jobs/ecdl_pi_commission.py +161 -0
- ldcon/jobs/ecdl_recovery.py +25 -0
- ldcon/jobs/ecdl_recovery_plan.py +90 -0
- ldcon/jobs/ecdl_recovery_test.py +59 -0
- ldcon/jobs/ecdl_relock.py +7 -0
- ldcon/jobs/ecdl_response_study.py +369 -0
- ldcon/jobs/ecdl_steady.py +343 -0
- ldcon/jobs/ecdl_thermal_context.py +242 -0
- ldcon/jobs/ecdl_window_center.py +734 -0
- ldcon/jobs/emission_card.py +102 -0
- ldcon/jobs/fringe_sweep_reference.py +89 -0
- ldcon/jobs/interface_devices.py +122 -0
- ldcon/jobs/interface_worker.py +471 -0
- ldcon/jobs/job_service.py +125 -0
- ldcon/jobs/laser_live.py +270 -0
- ldcon/jobs/learned_settling.py +450 -0
- ldcon/jobs/lock_target.py +461 -0
- ldcon/jobs/locked_triangle.py +36 -0
- ldcon/jobs/measurement_acquisition.py +51 -0
- ldcon/jobs/measurement_analysis.py +59 -0
- ldcon/jobs/measurement_execution.py +40 -0
- ldcon/jobs/measurement_notice.py +78 -0
- ldcon/jobs/measurement_presets.py +52 -0
- ldcon/jobs/measurement_report.py +76 -0
- ldcon/jobs/measurement_statistics.py +22 -0
- ldcon/jobs/metric_cards.py +34 -0
- ldcon/jobs/metric_interface.py +100 -0
- ldcon/jobs/modulation_sweep.py +274 -0
- ldcon/jobs/native_background_session.py +91 -0
- ldcon/jobs/native_current_acquire.py +143 -0
- ldcon/jobs/native_current_card.py +328 -0
- ldcon/jobs/native_current_online.py +129 -0
- ldcon/jobs/native_ff_proxy.py +1361 -0
- ldcon/jobs/native_ff_sweep.py +802 -0
- ldcon/jobs/online_pid.py +178 -0
- ldcon/jobs/operating_point.py +1399 -0
- ldcon/jobs/operating_point_proxy.py +236 -0
- ldcon/jobs/operation_execution.py +126 -0
- ldcon/jobs/operation_history.py +419 -0
- ldcon/jobs/optical_broker.py +23 -0
- ldcon/jobs/pi_algorithms.py +30 -0
- ldcon/jobs/pi_response.py +239 -0
- ldcon/jobs/progress.py +63 -0
- ldcon/jobs/quality_baseline.py +411 -0
- ldcon/jobs/recovery_decisions.py +131 -0
- ldcon/jobs/recovery_history.py +152 -0
- ldcon/jobs/recovery_procedure.py +285 -0
- ldcon/jobs/recovery_reference.py +123 -0
- ldcon/jobs/recovery_stationary.py +138 -0
- ldcon/jobs/response_algorithms.py +47 -0
- ldcon/jobs/response_comparison.py +62 -0
- ldcon/jobs/response_record.py +90 -0
- ldcon/jobs/runtime_parameters.py +434 -0
- ldcon/jobs/settling_profile.py +140 -0
- ldcon/jobs/shot_analysis.py +1300 -0
- ldcon/jobs/simulation.py +127 -0
- ldcon/jobs/slow_control.py +208 -0
- ldcon/jobs/slow_current.py +88 -0
- ldcon/jobs/study_adoption.py +122 -0
- ldcon/jobs/study_execution.py +182 -0
- ldcon/jobs/sweep_ff_analysis.py +320 -0
- ldcon/jobs/sweep_ff_optimization.py +202 -0
- ldcon/jobs/sweep_ff_plan.py +166 -0
- ldcon/jobs/sweep_ff_report.py +254 -0
- ldcon/jobs/sweep_ff_transport.py +326 -0
- ldcon/jobs/sweep_tracking.py +424 -0
- ldcon/jobs/sweep_verdict.py +288 -0
- ldcon/jobs/target_response.py +638 -0
- ldcon/jobs/target_response_analysis.py +65 -0
- ldcon/jobs/target_response_control.py +215 -0
- ldcon/jobs/target_response_decisions.py +152 -0
- ldcon/jobs/target_response_experiment.py +110 -0
- ldcon/jobs/target_response_feedforward.py +130 -0
- ldcon/jobs/target_response_search.py +225 -0
- ldcon/jobs/target_response_shift.py +475 -0
- ldcon/jobs/target_response_thermal.py +113 -0
- ldcon/jobs/target_response_thermal_experiment.py +120 -0
- ldcon/jobs/target_sequence.py +166 -0
- ldcon/jobs/temperature_identification.py +86 -0
- ldcon/jobs/wavelength_history.py +17 -0
- ldcon/jobs/window_actions.py +641 -0
- ldcon/jobs/window_center.py +784 -0
- ldcon/jobs/window_control.py +130 -0
- ldcon/jobs/window_decisions.py +105 -0
- ldcon/jobs/window_sampling.py +66 -0
- ldcon/jobs/workflow_artifacts.py +73 -0
- ldcon/migration/__init__.py +810 -0
- ldcon/migration/migration_decks.py +239 -0
- ldcon/migration/migration_readiness.py +397 -0
- ldcon/migration/migration_relocation.py +283 -0
- ldcon/monitor_owner.py +4 -0
- ldcon/owner_telemetry.py +4 -0
- ldcon/owners/__init__.py +1 -0
- ldcon/owners/adc_log_threshold.py +209 -0
- ldcon/owners/background_grants.py +44 -0
- ldcon/owners/cld_instrument_owner.py +96 -0
- ldcon/owners/cld_owner.py +706 -0
- ldcon/owners/cld_projection.py +194 -0
- ldcon/owners/cld_system_owner.py +276 -0
- ldcon/owners/current_owner.py +68 -0
- ldcon/owners/device_registry.py +229 -0
- ldcon/owners/dlcpro_awg_plan.py +115 -0
- ldcon/owners/dlcpro_awg_port.py +98 -0
- ldcon/owners/dlcpro_awg_protocol.py +190 -0
- ldcon/owners/dlcpro_awg_return.py +63 -0
- ldcon/owners/dlcpro_awg_sweep.py +437 -0
- ldcon/owners/dlcpro_awg_timing.py +35 -0
- ldcon/owners/dlcpro_awg_tracking.py +175 -0
- ldcon/owners/dlcpro_awg_tracking_guard.py +87 -0
- ldcon/owners/dlcpro_awg_tracking_report.py +49 -0
- ldcon/owners/dlcpro_awg_zero.py +82 -0
- ldcon/owners/dlcpro_deployment.py +66 -0
- ldcon/owners/dlcpro_ff.py +64 -0
- ldcon/owners/dlcpro_ff_adjustment.py +159 -0
- ldcon/owners/dlcpro_ff_delta_adjustment.py +194 -0
- ldcon/owners/dlcpro_frequency_window.py +61 -0
- ldcon/owners/dlcpro_observation.py +62 -0
- ldcon/owners/dlcpro_owner.py +830 -0
- ldcon/owners/dlcpro_pi_protocol.py +44 -0
- ldcon/owners/dlcpro_pi_trial.py +161 -0
- ldcon/owners/dlcpro_readback.py +50 -0
- ldcon/owners/dlcpro_service.py +203 -0
- ldcon/owners/dlcpro_target_changes.py +330 -0
- ldcon/owners/dlcpro_target_step.py +44 -0
- ldcon/owners/dlcpro_target_store.py +75 -0
- ldcon/owners/dlcpro_telemetry.py +72 -0
- ldcon/owners/ecdl_background_parameters.py +248 -0
- ldcon/owners/ecdl_background_pid.py +184 -0
- ldcon/owners/ecdl_hot_control.py +255 -0
- ldcon/owners/ecdl_independent_pid.py +95 -0
- ldcon/owners/ecdl_locked_study.py +122 -0
- ldcon/owners/ecdl_locked_sweep.py +1875 -0
- ldcon/owners/ecdl_native_pid.py +181 -0
- ldcon/owners/ecdl_optical.py +37 -0
- ldcon/owners/ecdl_owner.py +1058 -0
- ldcon/owners/ecdl_policy_reload.py +494 -0
- ldcon/owners/ecdl_prelock.py +612 -0
- ldcon/owners/ecdl_projection.py +636 -0
- ldcon/owners/ecdl_steady_owner.py +1859 -0
- ldcon/owners/ecdl_steady_policy.py +212 -0
- ldcon/owners/ecdl_target_ff.py +214 -0
- ldcon/owners/ecdl_target_settling.py +163 -0
- ldcon/owners/ecdl_tec_pair.py +88 -0
- ldcon/owners/ecdl_thermal_study.py +251 -0
- ldcon/owners/emission.py +365 -0
- ldcon/owners/emission_adapters.py +268 -0
- ldcon/owners/esp32_native_client.py +335 -0
- ldcon/owners/esp32_native_owner.py +2166 -0
- ldcon/owners/esp32_time_telemetry.py +253 -0
- ldcon/owners/esp32_udp_gate.py +136 -0
- ldcon/owners/external_sweep_guard.py +44 -0
- ldcon/owners/flexible_native_trial.py +172 -0
- ldcon/owners/fringe_peaks.py +92 -0
- ldcon/owners/full_data_log.py +133 -0
- ldcon/owners/instrument_identity.py +125 -0
- ldcon/owners/instrument_owner.py +1041 -0
- ldcon/owners/instrument_projection.py +104 -0
- ldcon/owners/instrument_service.py +638 -0
- ldcon/owners/migration_physical_lock.py +162 -0
- ldcon/owners/monitor_owner.py +322 -0
- ldcon/owners/native_assembly.py +37 -0
- ldcon/owners/native_assembly_host.py +260 -0
- ldcon/owners/native_background.py +233 -0
- ldcon/owners/native_current_tuning.py +251 -0
- ldcon/owners/native_ff_proof.py +433 -0
- ldcon/owners/native_instrument_owner.py +161 -0
- ldcon/owners/native_modulation_limits.py +45 -0
- ldcon/owners/native_policy_reload.py +186 -0
- ldcon/owners/native_policy_safety.py +213 -0
- ldcon/owners/native_policy_update.py +473 -0
- ldcon/owners/native_system_owner.py +420 -0
- ldcon/owners/operating_mode_owner.py +128 -0
- ldcon/owners/owner_projection.py +530 -0
- ldcon/owners/owner_telemetry.py +166 -0
- ldcon/owners/pi_handover.py +154 -0
- ldcon/owners/projection_transport.py +143 -0
- ldcon/owners/remote_instruments.py +323 -0
- ldcon/owners/sdg_client.py +122 -0
- ldcon/owners/sdg_owner.py +468 -0
- ldcon/owners/tec_rate.py +29 -0
- ldcon/owners/wsu_udp_monitor.py +113 -0
- ldcon/report_pdf.py +4 -0
- ldcon/resources/agent-model-options.json +142 -0
- ldcon/resources/agent_contract/adjustment.txt +45 -0
- ldcon/resources/agent_contract/common.txt +42 -0
- ldcon/resources/agent_contract/diagnosis.txt +11 -0
- ldcon/resources/agent_contract/research.txt +20 -0
- ldcon/resources/agent_contract/response_optimization.txt +13 -0
- ldcon/resources/examples/README.html +10 -0
- ldcon/resources/examples/assignments/sr1092.yaml +100 -0
- ldcon/resources/examples/assignments/sr422.yaml +677 -0
- ldcon/resources/examples/assignments/thf685.yaml +7 -0
- ldcon/resources/examples/interface-decks/dfb-dbr-ldc-tec.yaml +430 -0
- ldcon/resources/examples/interface-decks/ecdl-dlcpro.yaml +288 -0
- ldcon/resources/examples/interface-decks/ecdl-pzt-ldc-tec.yaml +698 -0
- ldcon/resources/examples/lasers/sr1092.yaml +267 -0
- ldcon/resources/examples/lasers/sr422.yaml +403 -0
- ldcon/resources/examples/lasers/thf685.yaml +158 -0
- ldcon/resources/report_template/ldconreport.cls +42 -0
- ldcon/resources/report_template/report.tex +47 -0
- ldcon/web/__init__.py +1 -0
- ldcon/web/agent_approval_api.py +126 -0
- ldcon/web/agent_settings_api.py +54 -0
- ldcon/web/agent_task_api.py +211 -0
- ldcon/web/control_readback.py +63 -0
- ldcon/web/dashboard_activity.py +109 -0
- ldcon/web/dashboard_controls_revision.py +29 -0
- ldcon/web/dashboard_metrics.py +146 -0
- ldcon/web/dashboard_snapshot.py +163 -0
- ldcon/web/dashboard_state.py +104 -0
- ldcon/web/dashboard_wavelength.py +150 -0
- ldcon/web/device_parameters_api.py +52 -0
- ldcon/web/dither_quality.py +46 -0
- ldcon/web/dlcpro_target_api.py +91 -0
- ldcon/web/dlcpro_target_query.py +23 -0
- ldcon/web/failure_api.py +62 -0
- ldcon/web/gateway_lan.py +208 -0
- ldcon/web/grafana_activity.py +84 -0
- ldcon/web/grafana_dashboard.py +492 -0
- ldcon/web/grafana_dlcpro.py +158 -0
- ldcon/web/grafana_quality.py +167 -0
- ldcon/web/interface_projection.py +27 -0
- ldcon/web/job_registry.py +68 -0
- ldcon/web/job_report.py +122 -0
- ldcon/web/laser_operations.py +445 -0
- ldcon/web/lock_target_api.py +554 -0
- ldcon/web/lock_target_queue.py +212 -0
- ldcon/web/log_archive.py +191 -0
- ldcon/web/metric_query_profiles.py +378 -0
- ldcon/web/online_position.py +26 -0
- ldcon/web/operating_mode_api.py +72 -0
- ldcon/web/operation_api.py +34 -0
- ldcon/web/operation_observation.py +57 -0
- ldcon/web/operations_settings.py +92 -0
- ldcon/web/quality_threshold_metrics.py +64 -0
- ldcon/web/shot_analysis_api.py +136 -0
- ldcon/web/sweep_api.py +1186 -0
- ldcon/web/sweep_guard.py +45 -0
- ldcon/web/target_reference_panel.py +38 -0
- ldcon/web/teams_destinations_api.py +81 -0
- ldcon/web/web_fleet.py +482 -0
- ldcon/web/web_runtime.py +811 -0
- ldcon/web/web_server.py +402 -0
- ldcon/web/window_api.py +28 -0
- ldcon/web_assets/agent-settings.css +4 -0
- ldcon/web_assets/agent-settings.html +53 -0
- ldcon/web_assets/agent-settings.js +270 -0
- ldcon/web_assets/app.css +9 -0
- ldcon/web_assets/app.js +802 -0
- ldcon/web_assets/card-browser.js +160 -0
- ldcon/web_assets/card-guide.js +9 -0
- ldcon/web_assets/cards/acquire.metrics.html +82 -0
- ldcon/web_assets/cards/agent.history_review.html +13 -0
- ldcon/web_assets/cards/agent.log_watch.html +1 -0
- ldcon/web_assets/cards/agent.monitor.html +13 -0
- ldcon/web_assets/cards/agent.request.html +1 -0
- ldcon/web_assets/cards/agent.resident.html +1 -0
- ldcon/web_assets/cards/agent.task.html +17 -0
- ldcon/web_assets/cards/commission.ecdl_pi.html +1 -0
- ldcon/web_assets/cards/identify.axis_frequency.html +1 -0
- ldcon/web_assets/cards/identify.current_response.html +1 -0
- ldcon/web_assets/cards/laser.emission.html +8 -0
- ldcon/web_assets/cards/measure.batch.html +1 -0
- ldcon/web_assets/cards/measure.change.html +1 -0
- ldcon/web_assets/cards/measure.target_response.html +1 -0
- ldcon/web_assets/cards/monitor.wavelength.html +1 -0
- ldcon/web_assets/cards/notify.teams.html +1 -0
- ldcon/web_assets/cards/optimize.settling_criteria.html +1 -0
- ldcon/web_assets/cards/optimize.target_feedforward.html +1 -0
- ldcon/web_assets/cards/optimize.target_step.html +1 -0
- ldcon/web_assets/cards/optimize.target_thermal.html +1 -0
- ldcon/web_assets/cards/plan.ecdl_recovery.html +14 -0
- ldcon/web_assets/cards/prepare.ecdl_recovery.html +18 -0
- ldcon/web_assets/cards/recover.ecdl_mode_hop.html +12 -0
- ldcon/web_assets/cards/report.grafana_dashboard.html +22 -0
- ldcon/web_assets/cards/servo.current_pid.html +1 -0
- ldcon/web_assets/cards/servo.dlcpro_frequency.html +1 -0
- ldcon/web_assets/cards/servo.ecdl_steady_lock.html +11 -0
- ldcon/web_assets/cards/servo.native_current.html +1 -0
- ldcon/web_assets/cards/study.ecdl_current_response.html +1 -0
- ldcon/web_assets/cards/study.ecdl_locked_current_map.html +1 -0
- ldcon/web_assets/cards/study.ecdl_mode_map.html +6 -0
- ldcon/web_assets/cards/study.ecdl_thermal_context.html +5 -0
- ldcon/web_assets/cards/study.target_response.html +1 -0
- ldcon/web_assets/cards/sweep.current_modulation.html +1 -0
- ldcon/web_assets/cards/sweep.dlcpro_frequency.html +1 -0
- ldcon/web_assets/cards/sweep.servo_modulation.html +1 -0
- ldcon/web_assets/cards/test.ecdl_recovery.html +4 -0
- ldcon/web_assets/cards/tune.ecdl_initial_point.html +1 -0
- ldcon/web_assets/cards/tune.ecdl_operating_point.html +13 -0
- ldcon/web_assets/cards/tune.ecdl_window_center.html +7 -0
- ldcon/web_assets/cards/tune.native_current.html +1 -0
- ldcon/web_assets/cards/tune.pid_coordinate_search.html +1 -0
- ldcon/web_assets/cards/tune.pid_factor_reduction.html +1 -0
- ldcon/web_assets/cards/tune.pid_online.html +1 -0
- ldcon/web_assets/cards/tune.temperature_once.html +18 -0
- ldcon/web_assets/committing-input.js +35 -0
- ldcon/web_assets/configuration-editor.js +194 -0
- ldcon/web_assets/dashboard-state.js +39 -0
- ldcon/web_assets/failures.html +1 -0
- ldcon/web_assets/failures.js +19 -0
- ldcon/web_assets/fallback.css +3 -0
- ldcon/web_assets/fallback.html +1 -0
- ldcon/web_assets/fallback.js +21 -0
- ldcon/web_assets/fleet-settings.js +20 -0
- ldcon/web_assets/fleet.css +9 -0
- ldcon/web_assets/fleet.html +8 -0
- ldcon/web_assets/fleet.js +130 -0
- ldcon/web_assets/gateway-presentation.js +27 -0
- ldcon/web_assets/grafana-help.js +31 -0
- ldcon/web_assets/help.html +22 -0
- ldcon/web_assets/i18n.js +279 -0
- ldcon/web_assets/index.html +14 -0
- ldcon/web_assets/laser-workspace.js +279 -0
- ldcon/web_assets/modulation.html +1 -0
- ldcon/web_assets/modulation.js +14 -0
- ldcon/web_assets/reservations.html +16 -0
- ldcon/web_assets/reservations.js +77 -0
- ldcon/web_assets/teams-settings.js +91 -0
- ldcon/web_assets/window-actions.js +37 -0
- ldcon/web_assets/workspace.css +99 -0
- ldcon/web_fleet.py +4 -0
- ldcon/web_server.py +4 -0
- ldcon-0.2.0.dist-info/METADATA +85 -0
- ldcon-0.2.0.dist-info/RECORD +482 -0
- ldcon-0.2.0.dist-info/WHEEL +5 -0
- ldcon-0.2.0.dist-info/entry_points.txt +5 -0
- ldcon-0.2.0.dist-info/licenses/LICENSE +21 -0
- ldcon-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
(() => {
|
|
3
|
+
const $ = selector => document.querySelector(selector);
|
|
4
|
+
const root = location.pathname.replace(/\/?$/, '/');
|
|
5
|
+
const views = new Set(['lasers', 'laser-settings', 'decks', 'catalog', 'settings']);
|
|
6
|
+
let lasers = [], selected = '', view = 'lasers', polling = false;
|
|
7
|
+
let historyOffset = 0, historyVersion = 0, historyRefreshAt = 0;
|
|
8
|
+
const expanded = new Set(), rows = new Map();
|
|
9
|
+
const key = row => JSON.stringify([row.service_id, row.set_id]);
|
|
10
|
+
function node(tag, text = '') { const value = document.createElement(tag); value.textContent = text; return value; }
|
|
11
|
+
function status(row) { return row.state + (row.reachable ? '' : ' · 接続できません'); }
|
|
12
|
+
function isBusy() { return false; }
|
|
13
|
+
function updateSwitching() {
|
|
14
|
+
for(const button of document.querySelectorAll('[data-service-id]'))button.disabled=button.dataset.reachable!=='true';
|
|
15
|
+
}
|
|
16
|
+
function serviceURL(row) {return root+'services/'+encodeURIComponent(row.service_id)+'/?set='+encodeURIComponent(row.set_id)+'#'+(view==='lasers'?'laser-settings':view);}
|
|
17
|
+
function renderDetail() {
|
|
18
|
+
const row=lasers.find(value=>key(value)===selected),area=$('#fleet-detail');
|
|
19
|
+
$('#fleet-selected-status').textContent=row?row.name+' · '+status(row):'レーザーを選択してください。';
|
|
20
|
+
let link=area.querySelector('a');
|
|
21
|
+
if(!link){link=node('a','レーザー画面を開く →');link.className='workspace-open';area.append(link);}
|
|
22
|
+
const enabled=Boolean(row?.reachable&&row.set_id);
|
|
23
|
+
link.style.visibility=enabled?'visible':'hidden';link.tabIndex=enabled?0:-1;
|
|
24
|
+
if(enabled)link.href=serviceURL(row);else link.removeAttribute('href');
|
|
25
|
+
}
|
|
26
|
+
function switchView(value) {
|
|
27
|
+
view = views.has(value) ? value : 'lasers';
|
|
28
|
+
$('#fleet-overview').hidden = view !== 'lasers';
|
|
29
|
+
$('#fleet-history').hidden = view !== 'lasers';
|
|
30
|
+
$('#fleet-details').hidden = view === 'lasers';
|
|
31
|
+
if($('#fleet-defaults')) $('#fleet-defaults').hidden = view !== 'settings';
|
|
32
|
+
for (const button of document.querySelectorAll('[data-fleet-view]')) button.setAttribute('aria-selected', String(button.dataset.fleetView === view));
|
|
33
|
+
if (location.hash !== '#' + view) location.hash = view;
|
|
34
|
+
renderDetail();
|
|
35
|
+
}
|
|
36
|
+
function render() {
|
|
37
|
+
const list = $('#fleet-list'), select = $('#fleet-select');list.closest('table').classList.add('fleet-overview-table');
|
|
38
|
+
const seen=new Set();for(const empty of list.querySelectorAll('[data-empty]'))empty.remove();
|
|
39
|
+
for(const row of lasers){
|
|
40
|
+
const id=key(row);seen.add(id);let v=rows.get(id);
|
|
41
|
+
if(!v){
|
|
42
|
+
const tr=node('tr'),name=node('td'),state=node('td'),wavelength=node('td'),reason=node('td'),actions=node('td');
|
|
43
|
+
state.className='fleet-state';reason.className='fleet-reason';actions.className='actions';
|
|
44
|
+
const detailRow=node('tr'),detailCell=node('td'),details=node('dl');detailRow.dataset.fleetDetail=id;detailCell.colSpan=5;
|
|
45
|
+
const fields={};for(const [field,label] of [['reason','状態の根拠'],['service','サービス'],['laser','レーザーID'],['job','制御ジョブ'],['connection','接続'],['dither','Dither quality'],['response','Dither response / threshold'],['evidence','Dither evidence']]){
|
|
46
|
+
const value=node('dd');fields[field]=value;details.append(node('dt',label),value);
|
|
47
|
+
}
|
|
48
|
+
detailCell.append(details);detailRow.append(detailCell);
|
|
49
|
+
const toggle=node('button','状態の詳細');toggle.type='button';toggle.dataset.detailToggle=id;
|
|
50
|
+
toggle.onclick=()=>{detailRow.hidden=!detailRow.hidden;if(detailRow.hidden)expanded.delete(id);else expanded.add(id);toggle.setAttribute('aria-expanded',String(!detailRow.hidden));};
|
|
51
|
+
const choose=node('button','設定・詳細');choose.type='button';
|
|
52
|
+
choose.onclick=()=>{const latest=rows.get(id)?.current;if(!latest?.reachable||!latest.set_id)return;selected=id;view='laser-settings';location.assign(serviceURL(latest));};
|
|
53
|
+
actions.append(toggle);tr.append(name,state,wavelength,reason,actions);list.append(tr,detailRow);
|
|
54
|
+
const option=node('option');option.value=id;select.append(option);
|
|
55
|
+
v={tr,name,state,wavelength,reason,detailRow,fields,toggle,choose,option};rows.set(id,v);
|
|
56
|
+
}
|
|
57
|
+
v.current=row;let workspace=v.name.querySelector('a');if(!workspace){workspace=node('a');v.name.replaceChildren(workspace);}workspace.textContent=row.name;workspace.href=root+'services/'+encodeURIComponent(row.service_id)+'/?set='+encodeURIComponent(row.set_id)+'#laser-settings';v.state.textContent=status(row);v.state.dataset.code=row.code;
|
|
58
|
+
v.reason.textContent=row.reason;v.reason.title=row.reason;
|
|
59
|
+
v.choose.dataset.serviceId=row.service_id;v.choose.dataset.setId=row.set_id||'';
|
|
60
|
+
v.choose.dataset.reachable=String(Boolean(row.set_id&&row.reachable));v.choose.disabled=!row.set_id||!row.reachable;
|
|
61
|
+
v.detailRow.hidden=!expanded.has(id);v.toggle.setAttribute('aria-expanded',String(!v.detailRow.hidden));
|
|
62
|
+
const d=row.dither_quality;
|
|
63
|
+
for(const [field,value] of Object.entries({reason:row.reason,service:row.service_label||row.service_id,laser:row.set_id||'未取得',job:row.job_id||'確認できません',connection:row.reachable?'接続済み':'接続不明',dither:d?.state||'No current controller evidence',response:d?.value!=null?`${d.value.toPrecision(4)} / ${d.threshold?.toPrecision(4)??'—'} ${d.unit}`:'—',evidence:d?.reason||'No current controller measurement'}))v.fields[field].textContent=value;
|
|
64
|
+
const wave=row.wavelength,fresh=wave?.valid===true&&Date.now()/1000-wave.acquired_unix_s>=0&&Date.now()/1000-wave.acquired_unix_s<=15;
|
|
65
|
+
v.wavelength.textContent=fresh?wave.wavelength_nm.toFixed(9):'—';v.wavelength.title=fresh?`CH ${wave.channel} · ${new Date(wave.acquired_unix_s*1000).toLocaleString()}`:'No fresh wavelength observation';
|
|
66
|
+
v.option.textContent=row.name+(row.reachable?'':' (接続不明)');
|
|
67
|
+
}
|
|
68
|
+
for(const [id,v] of rows)if(!seen.has(id)){v.tr.remove();v.detailRow.remove();v.option.remove();rows.delete(id);expanded.delete(id);}
|
|
69
|
+
if(!lasers.length){const tr=node('tr'),td=node('td','登録されたレーザーはありません。');tr.dataset.empty='';td.colSpan=5;tr.append(td);list.append(tr);}
|
|
70
|
+
if(!lasers.some(row=>key(row)===selected))selected=lasers.length?key(lasers[0]):'';
|
|
71
|
+
select.value=selected;renderDetail();updateSwitching();
|
|
72
|
+
}
|
|
73
|
+
async function refresh() {
|
|
74
|
+
if (polling) return; polling = true;
|
|
75
|
+
try {
|
|
76
|
+
const response = await fetch(root + 'api/fleet', {headers: {'X-LDCon-Client': 'web'}});
|
|
77
|
+
if (!response.ok) throw Error('一覧の取得に失敗しました。');
|
|
78
|
+
const value = await response.json(); lasers = value.lasers; render();
|
|
79
|
+
$('#fleet-notice').hidden = true;
|
|
80
|
+
$('#fleet-connection').textContent = '更新 · ' + new Date().toLocaleTimeString();
|
|
81
|
+
if (view === 'lasers' && Date.now()-historyRefreshAt>30000) { historyRefreshAt=Date.now(); void history(); }
|
|
82
|
+
} catch (error) {
|
|
83
|
+
// A failed refresh invalidates previous status, not just the header.
|
|
84
|
+
lasers = lasers.map(row => ({...row, reachable: false, wavelength: {valid:false}, code: 12, state: 'UNKNOWN', reason: '最新の状態を確認できません。'}));
|
|
85
|
+
render(); $('#fleet-notice').textContent = error.message; $('#fleet-notice').hidden = false;
|
|
86
|
+
$('#fleet-connection').textContent = '接続不明';
|
|
87
|
+
} finally { polling = false; }
|
|
88
|
+
}
|
|
89
|
+
async function history() {
|
|
90
|
+
const version = ++historyVersion, limit = Number($('#fleet-history-limit').value);
|
|
91
|
+
try {
|
|
92
|
+
const query = new URLSearchParams({q: $('#fleet-history-search').value, limit, offset: historyOffset});
|
|
93
|
+
const response = await fetch(root + 'api/history?' + query, {headers: {'X-LDCon-Client': 'web'}});
|
|
94
|
+
if (!response.ok) throw Error('履歴の取得に失敗しました。');
|
|
95
|
+
const value = await response.json(); if (version !== historyVersion) return;
|
|
96
|
+
const list = $('#fleet-history-list'); list.replaceChildren();
|
|
97
|
+
for (const job of value.jobs) {
|
|
98
|
+
const row = node('tr');
|
|
99
|
+
row.append(node('td', job.created_utc ? new Date(job.created_utc).toLocaleString() : '—'),
|
|
100
|
+
node('td', (job.set_name || job.set_id || job.service_label) + ' / ' + (job.deck_id || '—')),
|
|
101
|
+
node('td', job.status), node('td', job.archived ? '圧縮済み' : 'ローカル'));
|
|
102
|
+
const cell = node('td'), link = node('a', job.id.slice(0, 12) + ' ↗');
|
|
103
|
+
link.href = root + 'services/' + encodeURIComponent(job.service_id) + '/api/jobs/' + encodeURIComponent(job.id) + '/report.pdf';
|
|
104
|
+
link.target = '_blank'; link.rel = 'noopener'; cell.append(link); row.append(cell); list.append(row);
|
|
105
|
+
}
|
|
106
|
+
$('#fleet-history-count').textContent = value.total + ' 件 / ' + (value.total ? historyOffset + 1 : 0) + '–' + Math.min(value.total, historyOffset + limit);
|
|
107
|
+
$('#fleet-history-prev').disabled = historyOffset === 0;
|
|
108
|
+
$('#fleet-history-next').disabled = historyOffset + limit >= value.total || historyOffset + limit > 1000;
|
|
109
|
+
const notices = [];
|
|
110
|
+
if(value.unavailable_services.length) notices.push('一部サービスの履歴を取得できません。表示件数は取得できた分だけです。');
|
|
111
|
+
if(historyOffset + limit > 1000 && historyOffset + limit < value.total) notices.push('表示できるページ範囲に達しました。検索で絞ってください。');
|
|
112
|
+
$('#fleet-history-notice').textContent = notices.join(' ');
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (version === historyVersion) $('#fleet-history-notice').textContent = error.message;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
$('#fleet-select').onchange = () => { if(isBusy()){ $('#fleet-select').value=selected; return; } selected = $('#fleet-select').value; renderDetail(); };
|
|
118
|
+
function expandAll(open) { for(const row of document.querySelectorAll('[data-fleet-detail]')) { row.hidden=!open; if(open)expanded.add(row.dataset.fleetDetail);else expanded.delete(row.dataset.fleetDetail); } for(const button of document.querySelectorAll('[data-detail-toggle]'))button.setAttribute('aria-expanded',String(open)); }
|
|
119
|
+
$('#fleet-expand').onclick = () => expandAll(true);
|
|
120
|
+
$('#fleet-collapse').onclick = () => expandAll(false);
|
|
121
|
+
$('#fleet-refresh').onclick = refresh;
|
|
122
|
+
let debounce;
|
|
123
|
+
$('#fleet-history-search').oninput = () => { historyOffset = 0; ++historyVersion; clearTimeout(debounce); debounce = setTimeout(history, 200); };
|
|
124
|
+
$('#fleet-history-limit').onchange = () => { historyOffset = 0; history(); };
|
|
125
|
+
$('#fleet-history-prev').onclick = () => { historyOffset = Math.max(0, historyOffset - Number($('#fleet-history-limit').value)); history(); };
|
|
126
|
+
$('#fleet-history-next').onclick = () => { historyOffset += Number($('#fleet-history-limit').value); history(); };
|
|
127
|
+
for (const button of document.querySelectorAll('[data-fleet-view]')) button.onclick = () => switchView(button.dataset.fleetView);
|
|
128
|
+
window.addEventListener('hashchange', () => switchView(location.hash.slice(1)));
|
|
129
|
+
switchView(location.hash.slice(1)); refresh(); setInterval(()=>{if(!document.hidden)refresh();},5000);
|
|
130
|
+
})();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* Presentation only. Stable card IDs, request keys and inbox routes are not renamed. */
|
|
2
|
+
(() => {
|
|
3
|
+
const legacy = new Set(['diagnostic-agent', 'gateway-agent']);
|
|
4
|
+
function cardName(card = {}) {
|
|
5
|
+
const operation = card.operation || card.card;
|
|
6
|
+
if (operation !== 'agent.resident' && !legacy.has(card.id)) return null;
|
|
7
|
+
return (card.params || card.parameters || {}).role === 'operator' ? 'Operator agent' : 'Gateway agent';
|
|
8
|
+
}
|
|
9
|
+
function render(container, agent, card) {
|
|
10
|
+
if (!container._gatewayParts) {
|
|
11
|
+
const summary = document.createElement('p'), details = document.createElement('details');
|
|
12
|
+
const heading = document.createElement('summary'), raw = document.createElement('pre');
|
|
13
|
+
heading.textContent = 'Technical connection details (IDs and raw records)';
|
|
14
|
+
details.append(heading, raw); container.replaceChildren(summary, details);
|
|
15
|
+
container._gatewayParts = {summary, raw};
|
|
16
|
+
}
|
|
17
|
+
const session = agent?.session || {}, model = agent?.model_info || {};
|
|
18
|
+
const requests = agent?.requests || [];
|
|
19
|
+
const pending = requests.filter(r => ['queued', 'running'].includes(r.status)).length;
|
|
20
|
+
container._gatewayParts.summary.textContent = [cardName(card) || 'Gateway agent',
|
|
21
|
+
agent?.quarantined ? 'Review required' : (session.status || (agent?.connected ? 'Connected' : 'Disconnected')), session.provider,
|
|
22
|
+
model.resolved_model, model.resolved_effort, `${pending} pending requests`].filter(Boolean).join(' · ');
|
|
23
|
+
// Keep the original evidence available explicitly, without replacing IDs in it.
|
|
24
|
+
container._gatewayParts.raw.textContent = JSON.stringify(agent || {status: 'disconnected'}, null, 2);
|
|
25
|
+
}
|
|
26
|
+
window.ldconGatewayPresentation = {cardName, render};
|
|
27
|
+
})();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const grafanaHelp={
|
|
3
|
+
ja:{
|
|
4
|
+
title:'カードの報告量からGrafana JSONを生成',
|
|
5
|
+
operation:'選んだデッキの各カードが宣言する物理量を集め、Grafana用JSONを生成します。ECDLではPZT・フリンジ・ディザー、DFB/DBRでは測定電流・電流変調DAC・推定電流などが候補になります。ロックと並行実行でき、機器接続や出力変更は行いません。',
|
|
6
|
+
selectionTitle:'カードと表示量の選択',
|
|
7
|
+
selection:'未指定ならデッキの全報告量を集めます。カードIDと量IDで絞り込めます。同じ量は重複表示せず、提供元カードをmanifestに残します。明示した選択が利用できなければ理由付きエラーになり、自動収集で取得先が未対応の量は利用不可として記録します。空のリストは意図的に何も選ばない指定です。',
|
|
8
|
+
referenceTitle:'実行時の基準値',
|
|
9
|
+
reference:'基準波長または周波数を明示すれば優先します。未指定なら指定した稼働カードの目標値、次に割当の目標値を使います。確定した真空波長を編集可能なline_nmへ保存します。表示用許容差も同じ目標のスナップショットまたは明示値から取得し、装置固有の許容差や赤・緑の周波数閾値を共通値として埋め込みません。目標がなければ理由付きエラーになります。',
|
|
10
|
+
layoutTitle:'配置と物理量の意味',
|
|
11
|
+
layout:'選んだ量に応じた要約値と、制御器があれば状態タイムラインを表示し、詳しい時系列は4列に並べます。DAC count、測定電圧、測定電流、推定電流を区別します。TEC入力電圧は測定温度ではありません。機器の座標・単位はレーザー設定から読み、レーザー名や種類の固定判定でパネルを選びません。',
|
|
12
|
+
limits:'フリンジ占有率、側峰、ディザー応答は条件付きの指標であり、普遍的な単一モード証明ではありません。ディザー応答は有効なfitだけを表示し、欠測をゼロにしません。物理量の契約と保存先・クエリは別モジュールです。acquire.metricsのVictoriaMetrics設定も利用できますが、SQLiteだけに保存された量はこのPrometheusダッシュボードでは取得不可と明示します。生成だけではGrafanaへ公開・インポートしません。',
|
|
13
|
+
filesTitle:'保存される成果物',
|
|
14
|
+
files:'manifestには確定した基準値と許容差、提供元カード、選択量の意味・単位、実際のクエリ、取得できない量と理由を記録します。実行条件と生成イベントは通常のjobログへ残ります。'
|
|
15
|
+
},
|
|
16
|
+
'zh-CN':{
|
|
17
|
+
title:'根据卡片报告量生成Grafana JSON',
|
|
18
|
+
operation:'收集所选卡组中各卡片声明的物理量,生成Grafana JSON。ECDL卡片提供PZT、条纹和抖动量;DFB/DBR卡片提供实测电流、电流调制DAC和估算电流。可与锁定并行运行,不连接设备或更改输出。',
|
|
19
|
+
selectionTitle:'选择卡片和显示量',
|
|
20
|
+
selection:'不指定时收集卡组的所有报告量,也可按卡片ID和量ID筛选。共享量去重,并在manifest中保留来源卡片。显式选择不可用的量会报告原因;自动收集中不支持的数据源会记录为不可用。空列表表示不选择任何量。',
|
|
21
|
+
referenceTitle:'运行时的参考值',
|
|
22
|
+
reference:'优先使用明确指定的参考波长或频率,否则采用所选运行卡片的目标值,再采用分配目标值。确定的真空波长保存为可编辑的line_nm。显示容差来自同一目标快照或显式值,不硬编码设备专属容差或红绿阈值。没有目标值时显示错误。',
|
|
23
|
+
layoutTitle:'布局和物理意义',
|
|
24
|
+
layout:'摘要和控制器时间线随所选量生成,详细时序采用四列布局。DAC计数、实测电压、实测电流和估算电流分开。TEC输入电压不是实测温度。设备坐标及单位来自激光器配置,不根据固定名称或类型选择面板。',
|
|
25
|
+
limits:'条纹填充率、副峰和抖动响应是有条件的代理指标,并非普适的单模证明。仅显示有效拟合,缺失数据不会变成零。物理量契约与存储查询属于不同模块。可使用acquire.metrics的VictoriaMetrics绑定;仅SQLite的数据会明确标注为此Prometheus仪表板不支持。生成JSON不会自动导入或发布到Grafana。',
|
|
26
|
+
filesTitle:'保存的文件',
|
|
27
|
+
files:'manifest记录参考值及容差、来源卡片、所选量的意义和单位、实际查询,以及不可用数据源及原因。运行参数与事件保存在常规job日志中。'
|
|
28
|
+
}};
|
|
29
|
+
let grafanaHelpLanguage='en';for(const value of navigator.languages||[navigator.language]){const lang=value.toLowerCase();if(lang.startsWith('ja')){grafanaHelpLanguage='ja';break;}if(lang==='zh'||/^zh-(cn|sg|hans)/.test(lang)){grafanaHelpLanguage='zh-CN';break;}if(lang.startsWith('en'))break;}
|
|
30
|
+
document.documentElement.lang=grafanaHelpLanguage;
|
|
31
|
+
if(grafanaHelp[grafanaHelpLanguage])for(const element of document.querySelectorAll('[data-i18n]'))element.textContent=grafanaHelp[grafanaHelpLanguage][element.dataset.i18n]||element.textContent;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!doctype html><html lang="ja"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>LDCon 運用ガイド</title><style>body{font:16px/1.8 system-ui;max-width:1050px;margin:3rem auto;padding:0 24px;color:#243844}h1,h2{line-height:1.3}pre{background:#edf2f5;padding:18px;white-space:pre-wrap;overflow-wrap:anywhere}td,th{padding:10px;border:1px solid #ccd7de;text-align:left}table{border-collapse:collapse;width:100%}a{color:#176b64}</style><h1>LDCon 運用ガイド</h1><p>共有コードと運用データを分離した配備案です。この作業では実機サービスの停止・再起動・ファーム書込みは行いません。</p>
|
|
2
|
+
<h2>配置</h2><table><tr><th>場所</th><th>内容</th></tr><tr><td>/opt/ldcon/app</td><td>共通カード・所有者・Webコード。FanLabAdmin:FanLabAdmin、グループ書込み可。</td></tr><tr><td>/opt/ldcon/venv</td><td>専用Python環境。appをeditable install。</td></tr><tr><td>/opt/ldcon/var/lasers/<ID>.yaml</td><td>レーザーの機器、安全条件、制御設定。</td></tr><tr><td>var/interface-decks、var/assignments</td><td>共通デッキとレーザー別割当・既定値。</td></tr><tr><td>var/laser-assets/<ID>/scripts、evidence</td><td>レーザー固有スクリプト、検証証拠・所有者記録。</td></tr><tr><td>var/jobs/<laser-id>/<job-id></td><td>実行ログ・SQLite・開始時の不変設定。</td></tr><tr><td>var/archive-index</td><td>圧縮後も残る検索索引とHTMLレポート。</td></tr><tr><td>コンソール指定バックアップ/<laser-id>/<job-id>.tar.gz</td><td>期限超過の終了済みログ。</td></tr><tr><td>/etc/systemd/system、/etc/sudoers.d</td><td>root所有のサービスと厳密な操作許可。</td></tr></table>
|
|
3
|
+
<h2>準備とインストール</h2><pre>bash scripts/install.sh --destdir /tmp/ldcon-install-preview
|
|
4
|
+
# rootで実際にインストール。ここではサービスを起動・enableしない。
|
|
5
|
+
sudo bash scripts/install.sh
|
|
6
|
+
# 必要な場合だけsudoersも検査して配置する。
|
|
7
|
+
sudo bash scripts/install.sh --install-sudoers</pre><p>最後の2コマンドは代替です。二重実行しません。既存appがあれば上書きを拒否します。更新時は全ジョブ・リースが空、旧所有者停止、出力保持を確認した後にappをapp.previousとして保全してください。依存パッケージの導入にはネットワークとpython3-venvが必要です。</p>
|
|
8
|
+
<h2>現行状態の移行</h2><pre>python3 scripts/migrate_state.py --source-root /home/kshu/work/development/ldcon --instrument-config /home/kshu/work/development/ldcon/examples/instruments/sr1092-interface.json --plan
|
|
9
|
+
# 旧Web・旧所有者を停止し、全リース空・出力保持を本人の運用手順で確認してから:
|
|
10
|
+
sudo -u FanLabAdmin -g FanLabAdmin /opt/ldcon/venv/bin/python /opt/ldcon/app/scripts/migrate_state.py --source-root /home/kshu/work/development/ldcon --instrument-config /home/kshu/work/development/ldcon/examples/instruments/sr1092-interface.json</pre><p>移行は読めるソースと空の配備先を必要とします。FanLabAdminへソースの読取り権限を与えるか、レビュー済みの停止時コピーを渡してください。実行中ジョブ、Webのserver.lock、既存データがあれば拒否します。srcや過去の不変ジョブ設定は変更しません。現在設定の既知の証拠パスだけを新配置へ変換し、SHA256のmanifestを保存します。途中失敗は自動で上書き再開せず、manifestとコピー状態を調べて復元します。旧リポジトリと新所有者を同時に起動しません。特にUIDの異なる所有者には旧per-userロックだけでは重複防止できません。</p>
|
|
11
|
+
<h2>権限と起動順</h2><p>既存ユーザーをFanLabAdminグループに追加する操作は管理者が実施します。USB機器が必要なら既存udev規則・デバイスグループの権限を確認してください。API認証トークンがある場合はvar/service.envへ保存しFanLabAdmin:FanLabAdminの0660とします。権限を緩めて機器の認証を迂回しません。Webは既定で127.0.0.1のみです。LAN公開は既存の認証付きプロキシまたは管理者のunit overrideで設定してください。</p><pre>sudo /usr/bin/systemctl start ldcon-instruments.service
|
|
12
|
+
sudo /usr/bin/systemctl start ldcon-web.service
|
|
13
|
+
systemctl status ldcon-instruments.service ldcon-web.service
|
|
14
|
+
# 停止はWebのカードを通常停止して所有者解放を確認した後:
|
|
15
|
+
sudo /usr/bin/systemctl stop ldcon-web.service
|
|
16
|
+
sudo /usr/bin/systemctl stop ldcon-instruments.service
|
|
17
|
+
# 再起動は実行ジョブ・所有者がidleのときだけ:
|
|
18
|
+
sudo /usr/bin/systemctl restart ldcon-instruments.service
|
|
19
|
+
sudo /usr/bin/systemctl restart ldcon-web.service</pre><p>sudoersはこの2サービスについてstart/stop/restartの6コマンドだけを許可します。ワイルドカード、edit、daemon-reload、任意unit、shellは許可しません。statusはsudo不要です。自動再起動は設定せず、予期しない再開でハードウェア所有権を重複させません。</p><pre>sudo visudo -cf deploy/ldcon.sudoers
|
|
20
|
+
sudo install -o root -g root -m 0440 deploy/ldcon.sudoers /etc/sudoers.d/ldcon</pre>
|
|
21
|
+
<h2>ログ保管</h2><p>他設定タブで保持日数(1〜36500)とサービスユーザーが書けるバックアップフォルダを指定し、有効化します。既定は30日・無効で、保存先指定前に移動しません。終了後の最終ファイル変更時刻から期限を計算し、稼働中・解放中・symlinkを含むジョブは対象外です。圧縮後に全内容のSHA256を検証し、archive・索引・HTMLを永続化してから元ディレクトリを削除します。容量不足・接続切断・検証失敗は元ログを保持して他設定にエラー表示します。同名バックアップを上書きしません。HTMLレポートと索引には自動削除期限を設けません。運用に必要な検証証拠・所有者auditは実行ログとは別に保存し、このローテーションでは削除しません。</p>
|
|
22
|
+
<h2>復元</h2><p>旧実機所有者の停止を確認してから新所有者へ切り替えます。切替失敗時は新サービスを止め、全ジョブ・リースと実出力を照合して旧配置へ戻します。旧コードの復旧元はreorganization-baselineの初期コミットです。バックアップはまず別フォルダで内容一覧・ハッシュを確認し、元のlaser-id/job-idへ戻してください。展開を稼働中ジョブに上書きしません。</p></html>
|
ldcon/web_assets/i18n.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
(() => {
|
|
3
|
+
const dictionary = {
|
|
4
|
+
'接続中':'Connecting', '更新':'Refresh', 'レーザー一覧':'Lasers', '各レーザーの設定':'Laser settings', 'デッキ一覧':'Decks', 'カタログ':'Catalog', '他設定':'Settings', '全レーザー':'All lasers',
|
|
5
|
+
'すべて展開':'Expand all', 'すべて折り畳む':'Collapse all', 'レーザー':'Laser', '状態':'State', '根拠':'Evidence', '操作':'Actions', '設定・詳細':'Settings / details', '状態の詳細':'State details', '状態の根拠':'State evidence', 'サービス':'Service', 'レーザーID':'Laser ID', '制御ジョブ':'Controller job', '接続':'Connection', '接続済み':'Connected', '接続不明':'Connection unknown', '確認できません':'Unverified', '未取得':'Unavailable',
|
|
6
|
+
'全レーザーの実行ログ':'Run history — all lasers', '検索':'Search', '件数':'Rows', '表示件数':'Rows', '前へ':'Previous', '次へ':'Next', '開始日時':'Started', 'レーザー / デッキ':'Laser / deck', '保管':'Storage', 'レポート':'Report', 'HTMLレポート':'HTML report', '圧縮済み':'Archived', 'ローカル':'Local', '対象レーザー':'Selected laser', 'レーザーを選択してください。':'Select a laser.', '登録されたレーザーはありません。':'No lasers are registered.',
|
|
7
|
+
'レーザー・カード・状態・日時・ID':'Laser, card, state, date or ID', '機能・名前・必要インターフェース':'Function, name or required interface', 'カード検索':'Search cards', 'カード':'Card', '概要':'Description', '必要機能':'Required capabilities', '説明':'Help', 'デッキ':'Deck', 'カード数':'Cards', '割当':'Assignments', '詳細・設定':'Details / settings', '実行カード':'Running card', '設定':'Settings', '停止':'Stop', '開始':'Start', '詳細':'Details', '待機':'Ready', '要設定':'Needs configuration', '結果で予約':'Schedule from result', '失敗時の通知 ↗':'Failure notifications ↗', 'カードをすべて展開':'Expand all cards', '使える条件・測定内容 ↗':'Requirements / measurements ↗', '次回の実行パラメーター':'Parameters for the next run', '今回の実行パラメーター':'Parameters for this run',
|
|
8
|
+
'実行ログ':'Run history', 'リテンション設定 →':'Retention settings →', '結果による予約実行':'Result-based scheduling', '運用ガイド ↗':'Operations guide ↗', '全レーザー共通の既定値':'Defaults for all lasers', '実行ログの保管':'Run log retention', '期限後の圧縮移動を有効にする':'Archive expired logs', '保持日数':'Retention (days)', 'バックアップフォルダ(サーバー上の絶対パス)':'Backup folder (absolute server path)', '共通の既定値を保存':'Save shared defaults', '再読み込み':'Reload', 'レーザー別の上書き':'Per-laser overrides', '全レーザー共通の既定値を使う':'Inherit defaults for all lasers', '全レーザー共通の既定値を使用中':'Using shared defaults', 'このレーザーの個別設定を使用中':'Using this laser’s override', '共通の既定値を読み込みました。':'Shared defaults loaded.', '共通の既定値を保存しました。共通設定を継承するレーザーに適用されます。個別上書きは維持します。':'Shared defaults saved. Inheriting lasers use these settings; individual overrides are preserved.',
|
|
9
|
+
'個別上書きがないレーザーと、今後追加するレーザーの既定値です。安全条件とカードの実行パラメーターは共有しません。':'Defaults for lasers without an override, including future lasers. Device safety conditions and card execution parameters remain laser-specific.',
|
|
10
|
+
'対象レーザーを選び、「全レーザー共通の既定値を使う」を外して保存すると個別設定になります。既存の個別設定は自動では変更しません。':'Select a laser, clear “Inherit defaults for all lasers”, and save to create an override. Existing individual settings are preserved.',
|
|
11
|
+
'安全パラメーター・機器設定':'Safety parameters / devices', '機器 / 軸':'Device / axis', '絶対範囲':'Absolute range', '1回の変化上限':'Maximum step', '速度上限':'Maximum slew', '補足':'Notes', '未設定':'Not configured', '保持条件':'Hold condition', '機器構成・単位・制御設定の詳細':'Device configuration, units and control details', 'レーザー設定・安全パラメーターを編集':'Edit laser settings / safety parameters', '高度な編集(YAML全文)':'Advanced editing (full YAML)', '項目':'Parameter', '現在値':'Current value', '変更値':'New value', '単位':'Unit', '変更前':'Before', '変更後':'After', '変更差分を確認':'Review changes', '承認した差分を検証して保存':'Validate and save approved changes', '上記の差分を確認し、この設定変更を承認します。':'I reviewed the changes above and approve this configuration change.', '変更はありません。':'No changes.', '差分を確認して承認してください。':'Review and approve the changes first.',
|
|
12
|
+
'安全範囲・較正・制御上限の変更は、人間が具体的な差分を確認してください。保存時に既存の安全検証・稼働中編集制限・版の競合検査を行います。保存だけでは機器を操作しません。':'A human must review exact changes to safety ranges, calibration and control limits. Saving retains safety validation, active-use restrictions and revision checks. Saving does not operate devices.',
|
|
13
|
+
'電流ロック(コントローラー内PI)':'Current lock (controller PI)', 'ESP32サーボコントローラー内のPIで電流を調整して周波数を保ちます。nativeはこの制御方式を指し、ロック状態ではありません。':'The ESP32 controller runs the PI loop and adjusts current to maintain frequency. “Native” identifies this control method, not the current lock state.',
|
|
14
|
+
'有効な制御記録が一意に確認できません。':'No unique active controller record is available.', '制御記録が古いか、時刻が不明です。':'The controller record is stale or its timestamp is unavailable.', '測定の復帰待ちです。':'Waiting for measurement recovery.', '新しい測定記録がありません。':'No fresh measurement is available.', '新しい出力無効の読戻しを確認しました。':'Fresh readback confirms output disabled.', 'PIは停止。光出力OFFとは限りません。':'PI is stopped; optical emission is not known to be off.', '制御器が光学品質の低下を報告しています。':'The controller reports degraded optical quality.', '新しい制御標本が設定されたロック条件を満たしています。':'A fresh sample satisfies the configured lock criteria.', '制御器の最新記録。':'Latest controller record.', '最新の状態を確認できません。':'The current state could not be verified.',
|
|
15
|
+
'状態は新しい制御記録から表示します。未測定・通信不明はUNKNOWNです。PIの停止やジョブ終了は光出力OFFを意味しません。':'State is based on fresh controller evidence. UNKNOWN means unmeasured or unavailable. A stopped PI or completed job does not mean optical output is off.',
|
|
16
|
+
'LOCKEDは新しい制御記録、DEGRADEDは品質低下、HELDはPI停止を示します。出力の無効化を確認したときだけOFFと表示します。未測定・通信不明はUNKNOWNです。':'LOCKED uses fresh controller evidence, DEGRADED indicates reduced quality, and HELD indicates PI off. OFF requires output-disabled readback. UNKNOWN means unmeasured or unavailable.',
|
|
17
|
+
'カードの実行条件':'Card requirements', '定義の検証済み':'Definition validated', '設定を保存':'Save settings', '期限超過ログを今確認':'Check expired logs now', 'FanLabAdmin が書き込める既存フォルダを指定してください。':'Choose an existing folder writable by FanLabAdmin.',
|
|
18
|
+
'通知はデッキのnotify.teams、エージェントはagent.residentカードで設定します。各カードのfallbackで参照してください。':'Configure notifications with notify.teams and agents with agent.resident cards. Reference them from each card’s fallback.',
|
|
19
|
+
'終了済みログを毎時確認します。保存期限を過ぎたログは圧縮・検証後にバックアップへ移し、検索用の索引とHTMLレポートは残します。実行中のログは移動しません。':'Completed logs are checked hourly. Expired logs are compressed, verified and moved to backup; searchable indexes and HTML reports remain. Running job logs are retained.',
|
|
20
|
+
'Web画面と機器ownerは独立しています。画面の更新でロックは停止しません。機器ownerの停止・再起動は制御に影響します。':'The Web UI and device owners are independent. UI updates do not stop locks. Stopping or restarting an owner affects control.',
|
|
21
|
+
'割当・レーザー固有の既定パラメーター':'Assignment / laser-specific defaults', '共通デッキの設定を編集':'Edit shared deck', '共通デッキに機器アドレスやレーザー固有の制限を保存しません。':'Device addresses and laser-specific limits belong to laser configuration, not shared decks.', '公開する測定値':'Reported measurements', '詳細なHTML説明 ↗':'Detailed HTML help ↗', 'HTML説明 ↗':'HTML help ↗', '実行中のパラメーター':'Live parameters', '定期ログ確認':'Periodic log review', 'エージェントの接続・依頼結果':'Agent connection / request results',
|
|
22
|
+
'レーザー・機器・デッキ・割当を追加':'Add a laser, device, deck or assignment', '定義の種類':'Definition type', 'レーザー・機器':'Laser / devices', '共通デッキ':'Shared deck', 'レーザーとデッキの割当':'Laser / deck assignment', '定義ID':'Definition ID', '新規テンプレートを作る':'Create a template', '既存の定義を開く':'Open an existing definition', '検証して保存':'Validate and save', '新規作成。既存のIDは上書きしません。':'New definition. Existing IDs are not overwritten.',
|
|
23
|
+
'この定義を使うカードの稼働中は保存できません。無関係な定義は編集できます。保存だけではownerへ反映しません。安全条件の変更は対象のカード・所有者リースが空であることを確認して反映してください。':'Definitions in use by an active card cannot be saved. Unrelated definitions may be edited. Saving does not update owners. Apply safety changes only after checking the affected card and owner leases are idle.',
|
|
24
|
+
'LDCon · レーザーごとの独立した制御 · 共通の入口':'LDCon · Independent laser control · One dashboard', 'LDCon · 共通カード / レーザー別の安全条件 / 実行時スナップショット':'LDCon · Shared cards / laser-specific safety / execution snapshots'
|
|
25
|
+
};
|
|
26
|
+
dictionary['目標変更を中止']='Cancel target change';
|
|
27
|
+
dictionary['残りの目標ステップを中止します。ロックと反映済みの目標は維持します。']='Cancel remaining target steps; retain the lock and applied target.';
|
|
28
|
+
dictionary['PIは稼働中。最新の測定は設定されたロック条件の範囲外です。']='PI is active; latest sample is outside the configured lock criteria';
|
|
29
|
+
dictionary['共通設定への接続が未設定です。サービス既定値を使用中']='Shared defaults are not connected; using service defaults.';
|
|
30
|
+
dictionary['承認・根拠の参照(承認者・日時・理由)']='Approval reference (who, when and why)';
|
|
31
|
+
Object.assign(dictionary, {
|
|
32
|
+
"レーザーを操作": "Laser workspace",
|
|
33
|
+
"目標波長・動作点": "Target and operating point",
|
|
34
|
+
"目標波長": "Target wavelength",
|
|
35
|
+
"動作点の調整": "Operating point adjustment",
|
|
36
|
+
"パラメーター": "Parameters",
|
|
37
|
+
"機器構成": "Device configuration",
|
|
38
|
+
"エージェント・特性": "Agent / laser characteristics",
|
|
39
|
+
"操作カード": "Operation cards",
|
|
40
|
+
"エージェント・レーザーの特性": "Agent and laser characteristics",
|
|
41
|
+
"このレーザーの実行ログ": "Run history for this laser",
|
|
42
|
+
"保存された目標": "Saved target",
|
|
43
|
+
"最新の測定": "Latest measurement",
|
|
44
|
+
"ロックの実行あり": "Lock job active",
|
|
45
|
+
"ロックの実行なし": "No active lock job",
|
|
46
|
+
"保存値": "Saved value",
|
|
47
|
+
"機器・安全条件・較正": "Devices, safety and calibration",
|
|
48
|
+
"カードのレーザー別設定(復旧・探索・エージェントなど)": "Laser-specific card settings (recovery, search, agents)",
|
|
49
|
+
"パラメーター検索": "Search parameters",
|
|
50
|
+
"名前・パス・値(例: current_excursion、offsets)": "Name, path or value (e.g. current_excursion, offsets)",
|
|
51
|
+
"定義全体の編集(YAML)": "Edit complete definitions (YAML)",
|
|
52
|
+
"特性の記載欄を追加": "Add laser characteristics template",
|
|
53
|
+
"今回の実行パラメーター(JSON)": "Parameters for this run (JSON)",
|
|
54
|
+
"実行レポートを読む ↗": "Read run report ↗",
|
|
55
|
+
"実行レポート ↗": "Run report ↗",
|
|
56
|
+
"レーザー画面を開く →": "Open laser workspace →",
|
|
57
|
+
"← 全レーザー": "← All lasers",
|
|
58
|
+
"接続するエージェント": "Agent provider",
|
|
59
|
+
"機能": "Function",
|
|
60
|
+
"制御担当": "Controller / owner",
|
|
61
|
+
"機器・チャンネル": "Devices / channels",
|
|
62
|
+
"機器・チャンネル(設定値)": "Devices / channels (configured)",
|
|
63
|
+
"接続先・役割の一覧": "Connections and roles",
|
|
64
|
+
"設定上の役割": "Configured role",
|
|
65
|
+
"割当なし": "Unassigned",
|
|
66
|
+
"電流": "Current",
|
|
67
|
+
"電流 AWG": "Current AWG",
|
|
68
|
+
"波長計": "Wavemeter",
|
|
69
|
+
"光学観測": "Optical observation",
|
|
70
|
+
"定常ロック": "Stationary lock",
|
|
71
|
+
"電流ロック": "Current lock",
|
|
72
|
+
"ループ状態": "Loop state",
|
|
73
|
+
"変調": "Modulation",
|
|
74
|
+
"読込中": "Loading",
|
|
75
|
+
"保存された設定です。現在の実機への反映状態は動作点・実行ログで確認してください。": "Saved configuration. Check operating-point evidence and run reports for the applied state.",
|
|
76
|
+
"保存された構成から自動生成しています。接続の成功や実配線の確認結果ではありません。レーザー方式・個体特性は共通コンテキストに記録します。": "Generated from saved configuration; this does not verify connections or wiring. Document laser architecture and individual characteristics in the shared context.",
|
|
77
|
+
"全エージェントで共有する知識と、カードごとの個別依頼を分けて保存します。": "Shared laser knowledge and individual card requests are stored separately.",
|
|
78
|
+
"開始は1回の実行です。今回のパラメーターと保存済みの既定値を区別してください。": "Start launches one run. Parameters for this run are separate from saved defaults.",
|
|
79
|
+
"このレーザーのエージェント指示": "Shared instructions for this laser",
|
|
80
|
+
"Markdownテンプレートを読み込む": "Load Markdown template",
|
|
81
|
+
"Markdownを保存": "Save Markdown",
|
|
82
|
+
"レーザー別の接続履歴": "Saved laser sessions",
|
|
83
|
+
"接続履歴を更新": "Refresh sessions",
|
|
84
|
+
"クリック・矢印キー1回につき1操作です。押し続けても反復しません。機器の制限・インターロックは継続します。": "One action per click or arrow-key press. Hold-to-repeat is disabled. Existing owner limits and interlocks remain in force.",
|
|
85
|
+
"目標の絶対値": "Absolute target",
|
|
86
|
+
"入力単位": "Input unit",
|
|
87
|
+
"波長 (nm)": "Wavelength (nm)",
|
|
88
|
+
"周波数 (THz)": "Frequency (THz)",
|
|
89
|
+
"目標を反映": "Apply target",
|
|
90
|
+
"微調整幅": "Nudge step",
|
|
91
|
+
"微調整の単位": "Step unit",
|
|
92
|
+
"周波数 (MHz)": "Frequency (MHz)",
|
|
93
|
+
"波長 (pm)": "Wavelength (pm)",
|
|
94
|
+
"波長を増やすと周波数は下がります。矢印キーは+/−の操作欄にフォーカスがあるときだけ有効です。": "Positive wavelength steps reduce frequency. Arrow keys act only while the +/− control has focus.",
|
|
95
|
+
"調整軸": "Knob",
|
|
96
|
+
"出力の記録": "Current output",
|
|
97
|
+
"調整対象・モード": "Editable value / mode",
|
|
98
|
+
"新しい値": "New value",
|
|
99
|
+
"変化幅": "Step",
|
|
100
|
+
"反映": "Apply",
|
|
101
|
+
"電流変調": "Current modulation",
|
|
102
|
+
"未取得": "Unavailable",
|
|
103
|
+
"ロック中のPZT調整はTEC外側ループの中心目標を変更します。TECの直接変更はそのループの稼働中はできません。新しい読戻しがない値は未取得と表示し、操作時はカードがownerを再確認します。": "During lock, PZT changes the centre sought by the TEC outer loop, not the servo DAC output. Direct TEC changes are disabled while that loop controls it. Without a fresh readback, values are shown as unavailable; the card reads the owner again before moving.",
|
|
104
|
+
"ループの中心目標": "Loop centre target",
|
|
105
|
+
"ロックの基準値": "Lock reference",
|
|
106
|
+
"保持出力": "Held output",
|
|
107
|
+
"ロック中は読取りのみ/未対応": "Read only while locked / not supported",
|
|
108
|
+
"前の要求の結果が未確認です。実行ログを確認してから続けてください。": "A previous request has an unconfirmed outcome. Check job history before further changes.",
|
|
109
|
+
"実行履歴の確認後にブラウザーの保留表示を解除": "Clear local pending marker after reviewing job history",
|
|
110
|
+
"ブラウザーの保留表示だけ解除しました。命令は送信していません。": "Local marker cleared. No command was sent."
|
|
111
|
+
});
|
|
112
|
+
Object.assign(dictionary, {
|
|
113
|
+
"必要なインターフェースとパラメーターの検証済み。開始時に再検証します。": "Required interfaces and parameters validated. They are checked again at Start.",
|
|
114
|
+
"ここでの変更は現在のジョブだけに適用します。次回の実行設定とは別です。変更できる項目と適用条件は実行中カードから読み込みます。": "Changes here apply only to the current job, separately from the next run. Editable fields and conditions are loaded from the running card.",
|
|
115
|
+
"実行中の値・適用結果を更新": "Refresh live values and application results",
|
|
116
|
+
"選択項目を実行中カードへ一括適用": "Apply selected fields to the running card",
|
|
117
|
+
"変更する項目を選び、安全な処理境界で一括適用します。": "Select fields to apply together at a supported execution boundary.",
|
|
118
|
+
"このカードは実行中の変更に対応していません。": "This card does not support parameter changes while running.",
|
|
119
|
+
"適用待ち。適用結果を更新して確認してください。": "Pending. Refresh to check the application result.",
|
|
120
|
+
"入力の検証待ち": "Waiting for input validation",
|
|
121
|
+
"入力の検証済み": "Input validated",
|
|
122
|
+
"変更": "Change",
|
|
123
|
+
"項目": "Field",
|
|
124
|
+
"現在値": "Current value",
|
|
125
|
+
"変更後": "New value",
|
|
126
|
+
"適用条件": "Application conditions",
|
|
127
|
+
"数値・true/false・文字列・配列はJSONで入力してください。関連項目はまとめて送信します。": "Enter numbers, true/false, strings and arrays as JSON. Coupled fields are submitted together.",
|
|
128
|
+
"適用待ち": "Pending",
|
|
129
|
+
"適用済み": "Applied",
|
|
130
|
+
"拒否": "Rejected",
|
|
131
|
+
"適用結果が不明": "Application outcome unknown",
|
|
132
|
+
"対象カードは実行中ではありません。": "This card is not running.",
|
|
133
|
+
"実行中のジョブが変わりました。もう一度読み込んでください。": "The running job changed. Reload its values.",
|
|
134
|
+
"対象ジョブが変わりました。現在の値を読み直してください。": "The selected job changed. Reload its current values.",
|
|
135
|
+
"変更する項目を選んでください。": "Select fields to change.",
|
|
136
|
+
"次回の実行パラメーター": "Parameters for the next run",
|
|
137
|
+
"測定・計画の成果物": "Measurement and planning results",
|
|
138
|
+
"完了した実行の成果物を表示": "Show results from completed runs",
|
|
139
|
+
"完了した成果物はありません。": "No completed results are available.",
|
|
140
|
+
"スタディの実行": "Study execution",
|
|
141
|
+
"実行方式": "Execution mode",
|
|
142
|
+
"常駐エージェント": "Resident agent",
|
|
143
|
+
"測定とプロット": "Measure and plot",
|
|
144
|
+
"測定・プロットと整定判定の最適化": "Measure, plot and optimize settling criteria",
|
|
145
|
+
"両モードとも全取得データとプロットを保存します。エージェントは予想整定時間・閾値・確認条件を提案します。PI・FF・保存済みの既定値は変更しません。": "Both modes keep full-rate data and plots. Agent mode proposes expected settling time, thresholds and confirmation settings. PI, FF and saved defaults are not changed.",
|
|
146
|
+
"選択した常駐エージェントが記録された応答を解析します。": "The selected resident will analyse the recorded response.",
|
|
147
|
+
"先に選択した常駐エージェントを起動するか、測定とプロットを選んでください。": "Start the selected resident before this study, or choose Measure and plot.",
|
|
148
|
+
"エージェントは不要です。": "No agent is required.",
|
|
149
|
+
"過去のスタディ結果を表示": "Show previous study results",
|
|
150
|
+
"応答プロット ↗": "Response plot ↗",
|
|
151
|
+
"元データJSON ↗": "Raw data JSON ↗",
|
|
152
|
+
"提案された設定を次回の実行に使用": "Use proposed settings for next run",
|
|
153
|
+
"この結果には検証済みのエージェント候補がありません。": "No validated agent candidate is available in this result.",
|
|
154
|
+
"候補を今回の実行用JSONにコピーしました。開始前に確認してください。保存済みの既定値は変わりません。": "Candidate copied into this run draft. Review the JSON before Start; saved defaults are unchanged.",
|
|
155
|
+
"完了したスタディ結果はありません。": "No completed study results are available."
|
|
156
|
+
});
|
|
157
|
+
Object.assign(dictionary, {
|
|
158
|
+
'日常の設定':'Routine settings',
|
|
159
|
+
'目標・制御・光学品質':'Target, control and optical quality',
|
|
160
|
+
'詳細設定 — 制限・較正・機器定義':'Advanced configuration — limits, calibration and device definitions',
|
|
161
|
+
'制限・較正・出力許可':'Limits, calibration and output permissions',
|
|
162
|
+
'機器定義・接続・測定元':'Device definitions, connections and measurement sources',
|
|
163
|
+
'YAMLファイル':'YAML file', 'レーザーのYAML':'Laser YAML', '割当のYAML':'Assignment YAML',
|
|
164
|
+
'保存先の情報なし':'Source path unavailable',
|
|
165
|
+
'調整出力':'Control outputs', '測定・読み戻し':'Measurements and readbacks',
|
|
166
|
+
'量・用途':'Quantity / purpose', '機器・担当':'Device / owner', '接続先':'Connection',
|
|
167
|
+
'チャンネル・入力':'Channel / input', '方法・解釈':'Method / interpretation',
|
|
168
|
+
'PDFレポート':'PDF report', 'PDFレポート ↗':'PDF report ↗', 'PDFレポートを読む ↗':'Read PDF report ↗',
|
|
169
|
+
'応答のPDF ↗':'Response PDF ↗',
|
|
170
|
+
'設定された調整・測定経路です。チャンネルと方法は保存設定から表示しており、実配線の確認結果ではありません。不明な項目は未指定と表示します。':'Configured control and measurement routes. Channels and methods come from the saved definition; this is not a live wiring test. Missing details are marked as unspecified.',
|
|
171
|
+
'機器・較正・承認範囲を変更するときに開いてください。分類によって編集権限や検証条件は変わりません。':'Open these settings when changing hardware, calibration or approved limits. Grouping does not change edit permissions or validation.'
|
|
172
|
+
});
|
|
173
|
+
Object.assign(dictionary, {
|
|
174
|
+
'ゲートウェイエージェント':'Gateway agent', '操作エージェント':'Operator agent',
|
|
175
|
+
'ゲートウェイは依頼の受付・タスクカードの起動・結果の返答を担当します。監視や実機調整は別のタスクで実行します。':'Gateway agent receives requests, dispatches task cards and relays their results. Monitoring and physical adjustments run as separate tasks.',
|
|
176
|
+
'監視は継続動作です。起動時に単発評価を行い、その後は周期や設定条件に応じて再評価します。調整タスクが終了しても監視は継続します。':'Continuous monitoring. Runs an initial one-shot assessment, then another when a schedule or configured condition is due. A completed adjustment does not stop monitoring.'
|
|
177
|
+
});
|
|
178
|
+
Object.assign(dictionary, {
|
|
179
|
+
'レーザーごとのカード既定値':'Card defaults for this laser',
|
|
180
|
+
'失敗履歴・エージェントからの質問 ↗':'Failure history and agent questions ↗',
|
|
181
|
+
'矢印で送信':'Send on arrows', '編集した値をまとめて送信':'Send edited values',
|
|
182
|
+
'未送信の編集を戻す':'Revert unsent edits',
|
|
183
|
+
'出力の記録':'Current output', '取得日時':'Recorded time',
|
|
184
|
+
'ゲートウェイエージェント':'Gateway agent', 'レーザー操作エージェント':'Laser operator agent'
|
|
185
|
+
});
|
|
186
|
+
Object.assign(dictionary, {
|
|
187
|
+
'小数点以下の桁数':'Decimals', '現在のモードの理由':'Current reason',
|
|
188
|
+
'変更日時':'Changed at', '記録なし':'Not recorded'
|
|
189
|
+
});
|
|
190
|
+
Object.assign(dictionary, {
|
|
191
|
+
"ロック・動作点": "Lock and operating point",
|
|
192
|
+
"測定・校正": "Measurement and calibration",
|
|
193
|
+
"最適化": "Optimization",
|
|
194
|
+
"復旧": "Recovery",
|
|
195
|
+
"掃引": "Sweeps",
|
|
196
|
+
"エージェント・監視": "Agents and monitoring",
|
|
197
|
+
"レポート・通知": "Reports and notifications",
|
|
198
|
+
"その他のカード": "Other cards",
|
|
199
|
+
"名前・機能・ID(部分一致・あいまい検索)": "Name, function or ID; partial names and typos work",
|
|
200
|
+
"検索を解除": "Clear search",
|
|
201
|
+
"カテゴリをすべて展開": "Expand categories",
|
|
202
|
+
"カテゴリをすべて折り畳む": "Collapse categories",
|
|
203
|
+
"検索は閉じたカテゴリも含みます。解除すると元の開閉状態に戻ります。": "Search includes closed categories. Clearing it restores your category selection.",
|
|
204
|
+
"一致するカードがありません。": "No matching cards.",
|
|
205
|
+
"PIゲイン縮小比較": "PI gain reduction comparison",
|
|
206
|
+
"PI最適化(P・I独立)": "PI optimization (P/I independently)",
|
|
207
|
+
"目標応答測定": "Target response measurement",
|
|
208
|
+
"整定判定の最適化": "Settling criteria optimization",
|
|
209
|
+
"目標FFの最適化": "Target feedforward optimization",
|
|
210
|
+
"TEC中心化の最適化": "TEC recentering optimization",
|
|
211
|
+
"目標ステップ幅の最適化": "Target step-size optimization",
|
|
212
|
+
"電流窓の中点探索": "Current window center search",
|
|
213
|
+
"電流の枝マップ取得": "Current branch mapping",
|
|
214
|
+
"TECによる枝合わせ": "TEC branch alignment",
|
|
215
|
+
"目標変更": "Change target",
|
|
216
|
+
"動作点変更": "Change operating point",
|
|
217
|
+
"ECDL定常ロック": "ECDL stationary lock",
|
|
218
|
+
"DLC Pro周波数ロック": "DLC Pro frequency lock",
|
|
219
|
+
"モードホップ復旧": "Mode-hop recovery",
|
|
220
|
+
"復旧手順の作成": "Prepare recovery procedure",
|
|
221
|
+
"復旧手順の実証試験": "Recovery qualification test",
|
|
222
|
+
"定常監視": "Continuous monitoring",
|
|
223
|
+
"エージェント単発調整": "One-shot agent adjustment",
|
|
224
|
+
"ゲートウェイへの依頼": "Request gateway agent",
|
|
225
|
+
"過去データの分析": "Historical data review",
|
|
226
|
+
"Grafanaダッシュボード出力": "Grafana dashboard export",
|
|
227
|
+
"Teams通知": "Teams notification"
|
|
228
|
+
});
|
|
229
|
+
Object.assign(dictionary, {"PI最適化(エージェント)": "PI optimization (agent)", "電流動作点の最適化": "Current operating-point optimization", "TEC・PZT中心化の最適化": "TEC / PZT-centering optimization", "TEC・電流FFの最適化": "TEC-current feedforward optimization"});
|
|
230
|
+
Object.assign(dictionary, {'周波数三角掃引':'Triangle frequency sweep','周波数階段掃引':'Staircase frequency sweep'});
|
|
231
|
+
Object.assign(dictionary, {
|
|
232
|
+
'開始不可':'Blocked', '制御スロット使用中':'Control slot in use',
|
|
233
|
+
'開始するには、実行中の制御カードを停止してください。':'Stop the running control card before starting this card.',
|
|
234
|
+
'接続を確認できません':'Connection unavailable', '再接続してからカードを開始してください。':'Reconnect before starting a card.',
|
|
235
|
+
'パラメータの検証が必要です':'Parameters need validation', '今回の実行パラメータを確認してください。':'Check the parameters for this run.',
|
|
236
|
+
'実行パラメータを検証中です。':'Checking run parameters.', '制御器が利用できないか使用中です':'Controller unavailable or busy',
|
|
237
|
+
'稼働中ロックが1件あり、競合する操作がないことが必要です。':'Requires one running lock and no conflicting operation.',
|
|
238
|
+
'別の制御操作が実行中です。':'Another control operation is running.', '稼働中ロックが必要です':'Running lock required',
|
|
239
|
+
'対応するロックを先に開始してください。':'Start the compatible lock before this card.',
|
|
240
|
+
'調整がすでに実行中です':'Adjustment already running', 'このカードはすでに実行中です':'This card is already running',
|
|
241
|
+
'マップ JSON ↗':'Map JSON ↗', '復旧 JSON ↗':'Recovery JSON ↗'
|
|
242
|
+
});
|
|
243
|
+
const reverse=Object.fromEntries(Object.entries(dictionary).map(([ja,en])=>[en,ja]));
|
|
244
|
+
Object.assign(reverse, {'Fresh controller sample satisfies its configured lock criteria':'新しい制御標本が設定されたロック条件を満たしています。','No unique active controller; physical output is unknown':'有効な制御記録が一意に確認できません。光出力は未確認です。','No fresh controller measurement':'新しい測定記録がありません。','Controller record is stale':'制御記録が古くなっています。','Fresh controller state':'制御器の最新記録。','Fresh data does not establish lock or output-off':'最新情報だけではロック成立・光出力OFFを確認できません。'});
|
|
245
|
+
for(const [en,ja] of Object.entries(reverse))if(!dictionary[ja])dictionary[ja]=en;
|
|
246
|
+
let language='ja'; try {language=(localStorage.getItem('ldcon.language')||'ja').startsWith('ja')?'ja':'en';}catch(_){}
|
|
247
|
+
const records=new WeakMap();
|
|
248
|
+
function translate(text){
|
|
249
|
+
const source=reverse[text]||text;
|
|
250
|
+
if(language==='ja')return reverse[text]||(text.includes(' · ')?text.split(' · ').map(part=>reverse[part]||part).join(' · '):text);
|
|
251
|
+
if(dictionary[source])return dictionary[source];
|
|
252
|
+
if(source.includes(' · '))return source.split(' · ').map(part=>dictionary[part]||part).join(' · ');
|
|
253
|
+
const patterns=[[/^(\d+) 件$/,'$1 rows'],[/^現在の版: (.*)$/,'Current revision: $1'],[/^一括変更: (.*)$/,'Coupled change: $1'],[/^上限 (.*)$/,'Maximum $1'],[/^更新: (.*)$/,'Updated: $1'],[/^更新 · (.*)$/,'Updated · $1'],[/^接続済み · (.*)$/,'Connected · $1'],[/^(.*) 件 \/ (.*)$/,'$1 rows / $2'],[/^出力許可: 有効$/,'Output permission: enabled'],[/^出力許可: 無効$/,'Output permission: disabled'],[/^運用データ: (.*)$/,'Operational data: $1']];
|
|
254
|
+
for(const [pattern,replacement] of patterns)if(pattern.test(source))return source.replace(pattern,replacement);
|
|
255
|
+
return source;
|
|
256
|
+
}
|
|
257
|
+
function updateText(text){
|
|
258
|
+
if(text.parentElement?.closest('script,style,pre,code,textarea, [data-no-translate]'))return;
|
|
259
|
+
const current=text.nodeValue, record=records.get(text), source=record&¤t===record.rendered?record.source:current;
|
|
260
|
+
const trimmed=source.trim(), rendered=source.replace(trimmed,translate(trimmed));
|
|
261
|
+
records.set(text,{source,rendered});if(current!==rendered)text.nodeValue=rendered;
|
|
262
|
+
}
|
|
263
|
+
function apply(root){
|
|
264
|
+
if(root.nodeType===3){updateText(root);return;}
|
|
265
|
+
if(!root.querySelectorAll)return;
|
|
266
|
+
const walker=document.createTreeWalker(root,NodeFilter.SHOW_TEXT);while(walker.nextNode())updateText(walker.currentNode);
|
|
267
|
+
for(const element of root.querySelectorAll('[placeholder]')){const initial=element.dataset.originalPlaceholder||element.getAttribute('placeholder');element.dataset.originalPlaceholder=initial;element.setAttribute('placeholder',translate(initial));}
|
|
268
|
+
}
|
|
269
|
+
function set(value,persist=true){
|
|
270
|
+
language=value==='ja'?'ja':'en';document.documentElement.lang=language;
|
|
271
|
+
if(persist)try{localStorage.setItem('ldcon.language',language);}catch(_){}
|
|
272
|
+
apply(document.body);for(const button of document.querySelectorAll('[data-language]'))button.setAttribute('aria-pressed',String(button.dataset.language===language));
|
|
273
|
+
}
|
|
274
|
+
window.ldconTranslate=translate;
|
|
275
|
+
for(const button of document.querySelectorAll('[data-language]'))button.onclick=()=>set(button.dataset.language);
|
|
276
|
+
new MutationObserver(events=>{for(const event of events){if(event.type==='characterData')updateText(event.target);else for(const child of event.addedNodes)apply(child);}}).observe(document.body,{childList:true,characterData:true,subtree:true});
|
|
277
|
+
window.addEventListener('storage',event=>{if(event.key==='ldcon.language')set(event.newValue,false);});
|
|
278
|
+
set(language,false);
|
|
279
|
+
})();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="ja"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>LDCon — レーザー管理</title><link rel="stylesheet" href="assets/app.css"><script defer src="assets/dashboard-state.js"></script><script defer src="assets/window-actions.js"></script><link rel="stylesheet" href="assets/workspace.css"><script defer src="assets/configuration-editor.js"></script><script defer src="assets/laser-workspace.js"></script><script defer src="assets/committing-input.js"></script><script defer src="assets/card-browser.js"></script><script defer src="assets/gateway-presentation.js"></script><script defer src="assets/app.js"></script><script defer src="assets/i18n.js"></script><script defer src="assets/teams-settings.js"></script></head>
|
|
3
|
+
<body><header><a class="brand" href="#lasers">LDCon <small>LASER CONTROL</small></a><span id="connection" role="status">接続中</span><a href="help" target="_blank" rel="noopener">運用ガイド ↗</a><div class="language-switch" aria-label="Language"><button data-language="ja" type="button">日本語</button><button data-language="en" type="button">English</button></div></header>
|
|
4
|
+
<nav aria-label="メインナビゲーション"><button data-view="lasers">レーザー一覧</button><button data-view="laser-settings">レーザーを操作</button><button data-view="decks">デッキ一覧</button><button data-view="catalog">カタログ</button><button data-view="settings">他設定</button></nav>
|
|
5
|
+
<main><p id="notice" role="alert" hidden></p>
|
|
6
|
+
<section id="lasers" class="view"><div class="heading"><div><p class="eyebrow">OVERVIEW</p><h1>レーザー一覧</h1></div><div><button data-expand="laser-details" data-open="true">すべて展開</button><button data-expand="laser-details" data-open="false">すべて折り畳む</button></div></div><p class="muted">状態は新しい制御記録から表示します。未測定・通信不明はUNKNOWNです。PIの停止やジョブ終了は光出力OFFを意味しません。</p><div class="table-wrap"><table><thead><tr><th>レーザー</th><th>状態</th><th>実行カード</th><th>更新</th><th>操作</th></tr></thead><tbody id="laser-list"></tbody></table></div>
|
|
7
|
+
<div class="heading"><div><p class="eyebrow">RUN HISTORY</p><h2>実行ログ</h2></div><a href="#settings">リテンション設定 →</a></div><div class="toolbar"><label>検索<input id="history-search" type="search" placeholder="レーザー・カード・状態・日時・ID"></label><label>表示件数<select id="history-limit"><option>10</option><option selected>25</option><option>50</option><option>100</option></select></label><span id="history-count"></span><button id="history-prev">前へ</button><button id="history-next">次へ</button></div><div class="table-wrap"><table><thead><tr><th>開始日時</th><th>レーザー / デッキ</th><th>状態</th><th>保管</th><th>PDFレポート</th></tr></thead><tbody id="history-list"></tbody></table></div></section>
|
|
8
|
+
<section id="laser-settings" class="view" hidden><p class="eyebrow">LASER WORKSPACE</p><h1>レーザーを操作</h1><label class="selector">対象レーザー<select id="laser-select"></select></label><div id="laser-configuration"></div></section>
|
|
9
|
+
<section id="decks" class="view" hidden><div class="heading"><div><p class="eyebrow">REUSABLE DECKS</p><h1>デッキ一覧</h1></div><div><button data-expand="deck-details" data-open="true">すべて展開</button><button data-expand="deck-details" data-open="false">すべて折り畳む</button></div></div><p class="muted">共通デッキに機器アドレスやレーザー固有の制限を保存しません。</p><div class="table-wrap"><table><thead><tr><th>デッキ</th><th>カード数</th><th>割当</th><th>詳細・設定</th></tr></thead><tbody id="deck-list"></tbody></table></div></section>
|
|
10
|
+
<section id="catalog" class="view" hidden><div class="heading"><div><p class="eyebrow">CARD CATALOG</p><h1>カタログ</h1></div><div><button data-expand="catalog-details" data-open="true">すべて展開</button><button data-expand="catalog-details" data-open="false">すべて折り畳む</button></div></div><label>カード検索<input type="search" id="catalog-search" placeholder="機能・名前・必要インターフェース"></label><div class="table-wrap"><table><thead><tr><th>カード</th><th>概要</th><th>必要機能</th><th>説明</th></tr></thead><tbody id="catalog-list"></tbody></table></div></section>
|
|
11
|
+
<section id="settings" class="view" hidden><p><a href="agent-settings">Agent execution profiles and notification policy ↗</a></p><p class="eyebrow">OPERATIONS</p><h1>他設定</h1><p>通知はデッキのnotify.teams、エージェントはagent.residentカードで設定します。各カードのfallbackで参照してください。</p>
|
|
12
|
+
<div id="teams-settings" data-no-translate data-teams-scope="laser"></div><h2>実行ログの保管</h2><p>終了済みログを毎時確認します。保存期限を過ぎたログは圧縮・検証後にバックアップへ移し、検索用の索引とHTMLレポートは残します。実行中のログは移動しません。</p><form id="retention-form"><label class="check"><input id="retention-inherit" type="checkbox">全レーザー共通の既定値を使う</label><p id="retention-source" class="muted"></p><label class="check"><input id="retention-enabled" type="checkbox">期限後の圧縮移動を有効にする</label><label>保持日数<input id="retention-days" type="number" min="1" max="36500" required></label><label>バックアップフォルダ(サーバー上の絶対パス)<input id="backup-directory" placeholder="/mnt/backup/ldcon"></label><p class="muted">FanLabAdmin が書き込める既存フォルダを指定してください。</p><button type="submit" class="primary">設定を保存</button><button type="button" id="archive-now">期限超過ログを今確認</button></form><pre id="archive-status"></pre><details id="configuration-drafts"><summary>レーザー・機器・デッキ・割当を追加</summary><p>レーザーのdevicesに機器を定義し、共通デッキと割当を別に保存します。新しいレーザーは割当の作成後に一覧へ表示されます。保存だけではownerや機器を起動しません。稼働中の対象と、同じ書込機器を使う定義の変更は拒否します。</p><form id="configuration-draft-form"><label>定義の種類<select id="configuration-kind"><option value="lasers">レーザー・機器</option><option value="interface-decks">共通デッキ</option><option value="assignments">レーザーとデッキの割当</option></select></label><label>定義ID<input id="configuration-id" required pattern="[A-Za-z0-9][A-Za-z0-9_.-]{0,79}" value="new-laser"></label><button type="button" id="configuration-template">新規テンプレートを作る</button><button type="button" id="configuration-load">既存の定義を開く</button><label>YAML / JSON<textarea id="configuration-yaml" rows="16" required></textarea></label><button type="submit" class="primary">検証して保存</button><p id="configuration-result" role="status"></p></form></details><h2>サービス</h2><p>状態・開始・停止・再起動は端末から行います。整理版の独立launcher構成ではWebだけの再起動は定常ロックを止めません。ownerの停止・再起動は制御に影響します。</p><pre>systemctl --user list-units 'ldcon*'
|
|
13
|
+
# FanLabAdmin の端末で実際のユニット名を確認してください。</pre><p id="storage-directory" class="muted"></p></section>
|
|
14
|
+
</main><footer>LDCon · 共通カード / レーザー別の安全条件 / 実行時スナップショット</footer></body></html>
|