fix: harden task recovery and DR contract handling
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
@@ -50,7 +51,7 @@ struct SkillResponse {
|
||||
Holding holding{Holding::UNKNOWN};
|
||||
bool verified{false}, in_destination{false}, base_stopped{false};
|
||||
};
|
||||
struct ExecutionResult { ResultCode code{ResultCode::FAILED}; StopState stop{StopState::UNKNOWN}; SkillResponse response; std::string detail; };
|
||||
struct ExecutionResult { ResultCode code{ResultCode::FAILED}; StopState stop{StopState::UNKNOWN}; SkillResponse response; std::string detail; std::string error_code{}, execution_record_ref{}, wire_result_type{}, wire_result_snapshot{}; };
|
||||
struct GoalRequest {
|
||||
std::string goal_id, robot_id;
|
||||
Trace trace;
|
||||
@@ -63,6 +64,7 @@ struct GoalRequest {
|
||||
std::uint64_t geometry_epoch{0};
|
||||
RosTime capture_after{0};
|
||||
double position_tolerance_m{0.05}, orientation_tolerance_rad{0.1};
|
||||
std::string wire_request_type{}, wire_request_snapshot{};
|
||||
};
|
||||
enum class EventKind { ACCEPTED, REJECTED, FEEDBACK, CANCEL_ACK, RESULT };
|
||||
struct GoalEvent {
|
||||
@@ -72,6 +74,7 @@ struct GoalEvent {
|
||||
std::uint64_t sequence{0};
|
||||
NativeStatus native_status{NativeStatus::UNKNOWN};
|
||||
ExecutionResult result;
|
||||
std::string feedback_snapshot{};
|
||||
};
|
||||
class GoalDriver {
|
||||
public:
|
||||
@@ -81,6 +84,14 @@ class GoalDriver {
|
||||
virtual void send(const GoalRequest&) = 0;
|
||||
virtual void cancel(const std::string& goal_id) = 0;
|
||||
virtual std::vector<GoalEvent> drain_events() = 0;
|
||||
using RequestRecorder = std::function<void(const std::string&, const std::string&, const std::string&)>;
|
||||
void set_request_recorder(RequestRecorder recorder) { request_recorder_=std::move(recorder); }
|
||||
protected:
|
||||
void record_wire_request(const std::string& id,const std::string& type,const std::string& snapshot) {
|
||||
if(request_recorder_)request_recorder_(id,type,snapshot);
|
||||
}
|
||||
private:
|
||||
RequestRecorder request_recorder_;
|
||||
};
|
||||
struct Budgets { Milliseconds readiness{2000}, acceptance{2000}, feedback{5000}, execution{120000}, cancel_stop{5000}; };
|
||||
enum class GoalState { SENDING, ACTIVE, CANCEL_REQUESTED, STOP_UNKNOWN, TERMINAL };
|
||||
@@ -91,6 +102,7 @@ struct GoalRecord {
|
||||
std::uint64_t last_sequence{0};
|
||||
SteadyTime sent_at{}, accepted_at{}, last_feedback{}, cancel_at{};
|
||||
std::optional<ExecutionResult> result;
|
||||
std::string feedback_snapshot{};
|
||||
};
|
||||
class ActiveGoalRegistry {
|
||||
public:
|
||||
@@ -101,15 +113,19 @@ class ActiveGoalRegistry {
|
||||
// Registration is flushed before send. Failed journal writes throw and prevent send.
|
||||
std::optional<std::string> start(GoalRequest, SteadyTime);
|
||||
void pump(SteadyTime);
|
||||
void set_dispatch_recorder(std::function<void(const GoalRequest&)> recorder) { dispatch_recorder_=std::move(recorder); }
|
||||
void request_cancel(const std::string&, SteadyTime);
|
||||
bool robot_locked(const std::string&) const;
|
||||
bool has_unresolved() const;
|
||||
const GoalRecord* find(const std::string&) const;
|
||||
std::optional<GoalRecord> history(const std::string&) const;
|
||||
std::vector<GoalRecord> task_records(const std::string& run_id) const;
|
||||
const std::map<std::string, GoalRecord>& records() const { return records_; }
|
||||
// Caller must verify the authenticated dedicated physical-reconciliation interface.
|
||||
bool reconcile(const std::string& goal_id, const Trace&, StopState, bool authorized, const std::string& evidence_id);
|
||||
private:
|
||||
GoalDriver& driver_;
|
||||
std::function<void(const GoalRequest&)> dispatch_recorder_;
|
||||
std::string journal_path_;
|
||||
Budgets budgets_;
|
||||
std::map<std::string, GoalRecord> records_;
|
||||
@@ -118,6 +134,8 @@ class ActiveGoalRegistry {
|
||||
void append(const GoalRecord&);
|
||||
void ingest(const GoalEvent&, SteadyTime);
|
||||
void load();
|
||||
void prune_terminal();
|
||||
std::deque<std::string> terminal_order_;
|
||||
};
|
||||
class ContextStore {
|
||||
public:
|
||||
@@ -165,6 +183,7 @@ class StageRunner {
|
||||
TickStatus settle(SteadyTime, RosTime);
|
||||
void update_safety(SafetySnapshot value) { safety_ = value; }
|
||||
const std::string& detail() const { return detail_; }
|
||||
const std::string& error_code() const { return error_code_; }
|
||||
const std::string& active_goal_id() const { return active_goal_; }
|
||||
std::uint64_t geometry_epoch() const { return geometry_epoch_; }
|
||||
Holding holding() const { return holding_; }
|
||||
@@ -180,11 +199,13 @@ class StageRunner {
|
||||
SafetySnapshot safety_;
|
||||
Holding holding_{Holding::UNKNOWN};
|
||||
std::uint64_t geometry_epoch_{0};
|
||||
RosTime capture_after_{0}, last_ros_time_{0}, holding_valid_until_{0}, empty_valid_until_{0}, empty_observed_at_{0};
|
||||
RosTime capture_after_{0}, last_ros_time_{0}, holding_valid_until_{0}, holding_observed_at_{0}, empty_valid_until_{0}, empty_observed_at_{0};
|
||||
std::size_t stage_index_{0};
|
||||
std::string active_goal_, source_location_, source_side_, source_column_, source_tier_, verification_goal_, pending_posture_, detail_;
|
||||
std::string active_goal_, source_location_, source_side_, source_column_, source_tier_, verification_goal_, pending_posture_, detail_, error_code_;
|
||||
std::optional<SteadyTime> waiting_since_;
|
||||
std::optional<SteadyTime> motion_waiting_since_;
|
||||
std::optional<SteadyTime> stopped_waiting_since_;
|
||||
std::optional<SteadyTime> corroboration_waiting_since_, settlement_waiting_since_;
|
||||
unsigned reobservations_{0}, adjustments_{0}, serial_{0};
|
||||
enum class PickPhase { ASSESS, REOBSERVE, ADJUST, EXECUTE };
|
||||
PickPhase pick_phase_{PickPhase::ASSESS};
|
||||
|
||||
Reference in New Issue
Block a user