cmvr-es/cmvr-es/manager/safety_manager/include/safety_manager.h

223 lines
7.2 KiB
C++

#pragma once
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <unordered_set>
#include <vector>
#include "manager/safety_manager/include/command_ledger.h"
#include "manager/safety_manager/include/device_safety_endpoint.h"
#include "manager/safety_manager/include/safety_participant.h"
namespace cmvr::safety {
struct SafetyManagerConfig {
EnforcementMode enforcement_mode{EnforcementMode::Shadow};
std::unordered_set<std::string> enforced_device_ids;
std::chrono::milliseconds stop_all_timeout{15000};
std::chrono::milliseconds recovery_timeout{10000};
CommandLedger::Config command_ledger;
std::size_t event_history_capacity{2048};
bool fail_startup_on_missing_control_capability{false};
};
struct AdmissionResult {
AdmissionDecision decision;
std::optional<AdmissionPermit> permit;
};
struct StartupCoverageIssue {
std::string target_id;
SafetyReason reason{SafetyReason::None};
std::string detail;
};
struct StartupCoverageResult {
bool ready{false};
std::vector<StartupCoverageIssue> issues;
};
struct DeviceSafetyStateView {
DeviceSafetyDescriptor descriptor;
SafetySnapshotView safety;
device::ManagedDeviceState lifecycle{
device::ManagedDeviceState::Unknown};
device::DeviceHealthSnapshot health;
DeviceAdmissionState admission_state{DeviceAdmissionState::Observing};
std::vector<SafetyBlocker> blockers;
};
struct ParticipantResultView {
bool recorded{false};
bool success{false};
SafetyReason reason{SafetyReason::None};
std::string detail;
};
struct ParticipantSafetyStateView {
ParticipantDescriptor descriptor;
bool registered{false};
bool barrier_active{false};
bool barrier_retained{false};
std::string operation_id;
std::uint64_t safety_epoch{0};
ParticipantResultView last_request;
ParticipantResultView last_verify;
ParticipantResultView last_release;
};
struct SafetyManagerSnapshot {
SystemAdmissionState system_state{SystemAdmissionState::Starting};
std::uint64_t safety_epoch{0};
std::string service_instance_id;
EnforcementMode enforcement_mode{EnforcementMode::Shadow};
std::string active_operation_id;
std::string active_operation_phase;
std::vector<DeviceSafetyStateView> devices;
std::vector<ParticipantSafetyStateView> participants;
std::vector<SafetyEvent> recent_events;
};
struct SafetyTargetResult {
std::string target_id;
bool success{false};
SafetyReason reason{SafetyReason::None};
std::string detail;
DeviceAdmissionState before_state{DeviceAdmissionState::Observing};
DeviceAdmissionState after_state{DeviceAdmissionState::Observing};
};
struct StopAllResult {
bool success{false};
std::string operation_id;
std::uint64_t previous_safety_epoch{0};
std::uint64_t current_safety_epoch{0};
SystemAdmissionState system_state{SystemAdmissionState::Starting};
std::vector<SafetyTargetResult> targets;
};
enum class RecoveryResultCode {
Recovered,
VerifiedButStillBlocked,
BlockerRemains,
EpochMismatch,
NothingToRecover,
TimedOut,
Failed,
};
struct RecoveryRequest {
std::string recovery_id;
std::vector<std::string> device_ids;
bool all_devices{false};
std::uint64_t expected_safety_epoch{0};
bool verify_only{true};
bool restore_operational_state{false};
std::string reason;
SafetyClock::time_point deadline{SafetyClock::time_point::max()};
// Authorizes the state-changing part of recovery. Operational restore
// calls this before invoking device recovery; software-only recovery calls
// it after hardware verification and before releasing admission barriers.
// A false result leaves admission latched and prevents device recovery.
std::function<bool()> authorize_clear;
};
struct RecoveryResult {
RecoveryResultCode result{RecoveryResultCode::Failed};
std::string recovery_id;
std::uint64_t previous_safety_epoch{0};
std::uint64_t current_safety_epoch{0};
SystemAdmissionState system_state{SystemAdmissionState::Starting};
std::vector<SafetyTargetResult> targets;
};
class SafetyManager;
class DispatchGuard final {
public:
DispatchGuard() noexcept = default;
~DispatchGuard() noexcept;
DispatchGuard(DispatchGuard&& other) noexcept;
DispatchGuard& operator=(DispatchGuard&& other) noexcept;
DispatchGuard(const DispatchGuard&) = delete;
DispatchGuard& operator=(const DispatchGuard&) = delete;
bool acquired() const noexcept { return coordinator_ != nullptr; }
const HardwareCheckResult& hardwareCheck() const noexcept
{
return hardware_check_;
}
private:
friend class SafetyManager;
DispatchGuard(SafetyManager* coordinator,
std::string device_id,
HardwareCheckResult hardware_check) noexcept;
void reset_() noexcept;
SafetyManager* coordinator_{nullptr};
std::string device_id_;
HardwareCheckResult hardware_check_;
};
class SafetyManager final {
public:
explicit SafetyManager(SafetyManagerConfig config = {});
~SafetyManager();
SafetyManager(const SafetyManager&) = delete;
SafetyManager& operator=(const SafetyManager&) = delete;
bool registerDevice(DeviceSafetyRegistration registration);
bool registerParticipant(std::shared_ptr<SafetyParticipant> participant);
bool unregisterParticipant(const std::string& participant_id);
void updateDeviceRuntimeState(
const std::string& device_id,
device::ManagedDeviceState lifecycle,
device::DeviceHealthSnapshot health = {});
std::optional<std::uint64_t> advanceDeviceGeneration(
const std::string& device_id);
StartupCoverageResult validateStartupCoverage(
SafetyClock::time_point deadline);
void markStartupComplete();
AdmissionResult admit(const AdmissionRequest& request);
// Lightweight session check. This validates the coordinator-owned epoch,
// generation, freshness, and admission state without calling the device
// endpoint or entering the hardware dispatch set.
HardwareCheckResult revalidatePermit(
const AdmissionPermit& permit) const;
DispatchGuard beginDispatch(const AdmissionPermit& permit);
void quarantineDevice(const std::string& device_id,
SafetyReason reason,
std::string operation_id = {});
StopAllResult stopAll(
std::string operation_id,
SafetyClock::time_point deadline = SafetyClock::time_point::max());
RecoveryResult recover(const RecoveryRequest& request);
SafetyManagerSnapshot snapshot() const;
CommandLedger& commandLedger() noexcept;
const std::string& serviceInstanceId() const noexcept;
const SafetyManagerConfig& config() const noexcept;
private:
friend class DispatchGuard;
struct Impl;
bool publishSafetySnapshot(DeviceSafetySnapshot snapshot);
void beginShutdown() noexcept;
AdmissionDecision evaluate(const AdmissionRequest& request) const;
void endDispatch_(const std::string& device_id) noexcept;
std::unique_ptr<Impl> impl_;
};
const char* toString(RecoveryResultCode value) noexcept;
} // namespace cmvr::safety