autocore-std 3.3.51

Standard library for AutoCore control programs - shared memory, IPC, and logging utilities
Documentation
// Copyright (C) 2026 Automated Design Corp. All Rights Reserved.
//
//! Test Information System (TIS) producer helpers.
//!
//! Thin, hand-written conveniences over the `tis.*` IPC commands. The
//! per-method `TestManager` codegen targets the same commands for the
//! common writes (`add_cycle`, `add_raw_data`, `update_results`, …); the
//! helpers here cover writes that aren't bound to a single method's
//! generated shape. Today that's attaching **chart region bands** to a
//! recorded cycle's raw trace.

use serde::Serialize;

use crate::CommandClient;

/// A shaded X-range band drawn over a chart view in the HMI, used to
/// indicate a processed / region-of-interest span of a cycle's trace.
///
/// Serializes to the JSON shape the autocore-react `ChartRegion` consumes
/// (`xMin` / `xMax` / `label` / `color` / `borderColor`) — the band spans
/// the full Y height between `x_min` and `x_max` on the view's x scale.
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ChartRegion {
    /// Band start on the X axis, in the view's x-data units.
    pub x_min: f64,
    /// Band end on the X axis.
    pub x_max: f64,
    /// Optional caption drawn at the top-center of the band.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub label: Option<String>,
    /// Optional fill color (any CSS color). HMI applies a default when unset.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub color: Option<String>,
    /// Optional band-edge border color (no border when unset).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub border_color: Option<String>,
}

impl ChartRegion {
    /// A bare band between `x_min` and `x_max` with HMI-default styling.
    pub fn new(x_min: f64, x_max: f64) -> Self {
        Self { x_min, x_max, label: None, color: None, border_color: None }
    }

    /// Builder: set the band caption.
    pub fn with_label(mut self, label: impl Into<String>) -> Self {
        self.label = Some(label.into());
        self
    }

    /// Builder: set the fill color (any CSS color string).
    pub fn with_color(mut self, color: impl Into<String>) -> Self {
        self.color = Some(color.into());
        self
    }

    /// Builder: set the band-edge border color.
    pub fn with_border_color(mut self, color: impl Into<String>) -> Self {
        self.border_color = Some(color.into());
        self
    }
}

/// Set (replacing any prior set) the chart region bands on one recorded
/// cycle's raw trace. The bands are patched into that cycle's raw-data
/// envelope, so the HMI renders them whenever the cycle is viewed — and
/// live-updates if that cycle is on screen when this write lands.
///
/// Decoupled from the trace write: call it any time *after* the cycle's
/// `add_raw_data` / `record_raw_trace` has produced the on-disk file — the
/// same tick, a later tick, or a separate post-processing pass. The trace
/// file must already exist (you can't annotate a trace that hasn't been
/// recorded). Passing an empty slice clears the bands for that cycle.
///
/// `run_id` is optional:
/// - `None` — target the project/method's currently-active test. The server
///   resolves the run from its active-test registry, exactly as
///   `add_raw_data` does. Use this from a live control program.
/// - `Some(run_id)` — target a specific run explicitly, e.g. a
///   post-processing step annotating a run that has already finished.
///
/// Returns the transaction id of the sent command; poll `client` for its
/// response as with any other `tis.*` write.
pub fn set_chart_regions(
    client: &mut CommandClient,
    project_id: &str,
    method_id: &str,
    run_id: Option<&str>,
    name: &str,
    cycle_index: u32,
    regions: &[ChartRegion],
) -> u32 {
    let mut payload = serde_json::json!({
        "project_id":  project_id,
        "method_id":   method_id,
        "name":        name,
        "cycle_index": cycle_index,
        "regions":     regions,
    });
    if let (Some(rid), Some(obj)) = (run_id, payload.as_object_mut()) {
        obj.insert("run_id".to_string(), serde_json::json!(rid));
    }
    client.send("tis.set_chart_regions", payload)
}