Skip to main content

tag_attach_cycle/
tag-attach-cycle.rs

1//! `tag-attach-cycle`: repeatedly attach to and detach from a tag.
2//!
3//! Port of `host/commandline/tag-attach-cycle.cc`. Each cycle attaches
4//! (which resets the tag), optionally reads and prints its status, stays
5//! attached for `--on-ms`, detaches, and stays away for `--off-ms`. With
6//! `--cycles 0` it runs until Ctrl-C, which ends the run between steps
7//! rather than killing the process.
8//!
9//! Exits 0 when the cycles complete or Ctrl-C stops them; 1 if an attach
10//! fails.
11//!
12//! Usage: `tag-attach-cycle [-d] [-b BUS:DEVICE] [--on-ms MS] [--off-ms MS]
13//! [--cycles N] [--no-status]`; both intervals default to 5000 ms.
14//!
15//! This is a test of the attach path itself, one of the two legitimate
16//! reasons to attach to a running tag: every cycle resets the tag and ends
17//! whatever it was doing, so it says nothing about how a run behaves
18//! unattended. A status read that fails is reported on stderr and the cycle
19//! continues.
20//!
21//! # Hardware side effects
22//!
23//! One target reset per cycle. A tag past its start time is restarted by
24//! each reset.
25
26use std::process::ExitCode;
27use std::sync::atomic::{AtomicBool, Ordering};
28use std::thread::sleep;
29use std::time::Duration;
30
31use clap::Parser;
32use tag_tools::{BaseArgs, init_logging, select_base, state_name};
33use tagcore::Tag;
34use tagcore::proto::Status;
35
36/// repeatedly attach and detach from a running tag
37#[derive(Parser)]
38#[command(name = "tag-attach-cycle")]
39struct Cli {
40    /// The shared `--debug` and `--base` options.
41    #[command(flatten)]
42    base: BaseArgs,
43
44    /// Milliseconds to remain attached
45    #[arg(long, default_value_t = 5000)]
46    on_ms: u32,
47
48    /// Milliseconds to remain detached
49    #[arg(long, default_value_t = 5000)]
50    off_ms: u32,
51
52    /// Attach/detach cycle count; 0 runs until Ctrl-C
53    #[arg(long, default_value_t = 0)]
54    cycles: u32,
55
56    /// Do not read tag status after attaching
57    #[arg(long)]
58    no_status: bool,
59}
60
61/// Set by Ctrl-C to end the cycling.
62static STOP_REQUESTED: AtomicBool = AtomicBool::new(false);
63
64/// Sleeps for `ms`, in steps of at most 100 ms, returning early on Ctrl-C.
65fn interruptible_sleep(ms: u32) {
66    let mut remaining = u64::from(ms);
67    while !STOP_REQUESTED.load(Ordering::SeqCst) && remaining > 0 {
68        let nap = remaining.min(100);
69        sleep(Duration::from_millis(nap));
70        remaining -= nap;
71    }
72}
73
74/// Prints the tag's state, clock (ms since the Unix epoch) and internal and
75/// external flash page counts for `cycle`, and any firmware debug message on
76/// stderr.
77fn print_status(cycle: u32, status: &Status) {
78    println!(
79        "cycle {cycle}: state={} millis={} internal={} external={}",
80        state_name(status.state),
81        status.millis,
82        status.internal_data_count,
83        status.external_data_count
84    );
85    if !status.debug_message.is_empty() {
86        eprint!("{}", status.debug_message);
87    }
88}
89
90/// Installs the Ctrl-C handler, then runs the attach/detach cycles.
91fn main() -> ExitCode {
92    let _ = ctrlc::set_handler(|| STOP_REQUESTED.store(true, Ordering::SeqCst));
93    let cli = Cli::parse();
94    init_logging(&cli.base);
95
96    let tag = Tag::new();
97    let dev = select_base::<Cli>(&tag, &cli.base);
98
99    let mut cycle: u32 = 1;
100    while !STOP_REQUESTED.load(Ordering::SeqCst) && (cli.cycles == 0 || cycle <= cli.cycles) {
101        println!("cycle {cycle}: attach");
102        if !tag.attach(&dev) {
103            eprintln!("cycle {cycle}: attach failed");
104            return ExitCode::FAILURE;
105        }
106
107        if !cli.no_status {
108            match tag.status() {
109                Ok(status) => print_status(cycle, &status),
110                Err(_) => eprintln!("cycle {cycle}: status failed: {}", tag.debug_message()),
111            }
112        }
113
114        interruptible_sleep(cli.on_ms);
115
116        println!("cycle {cycle}: detach");
117        tag.detach();
118
119        if cli.cycles == 0 || cycle < cli.cycles {
120            interruptible_sleep(cli.off_ms);
121        }
122        cycle = cycle.wrapping_add(1);
123    }
124
125    tag.detach();
126    ExitCode::SUCCESS
127}