1use clap::{Args, Parser, Subcommand, ValueEnum};
2use clap_num::maybe_hex;
3use std::{path::PathBuf, str::FromStr};
4use zencan_client::common::lss::LssIdentity;
5
6#[derive(Debug, Parser)]
7pub struct Cli {
8 #[command(subcommand)]
9 pub command: Commands,
10}
11
12#[derive(Debug, Subcommand)]
13pub enum Commands {
14 Read(ReadArgs),
16 Write(WriteArgs),
18 Scan,
20 ScanPdoConfig(ScanPdoConfigArgs),
22 Info,
24 LoadConfig(LoadConfigArgs),
26 SaveObjects(SaveObjectsArgs),
28 Nmt(NmtArgs),
30 #[command(subcommand)]
32 Lss(LssCommands),
33}
34
35#[derive(Debug, Args)]
36pub struct ReadArgs {
37 pub node_id: u8,
39 #[clap(value_parser=maybe_hex::<u16>)]
41 pub index: u16,
42 #[clap(value_parser=maybe_hex::<u8>)]
44 pub sub: u8,
45 pub data_type: Option<SdoDataType>,
47}
48
49#[derive(Clone, Copy, Debug, ValueEnum)]
50pub enum SdoDataType {
51 U32,
52 U16,
53 U8,
54 I32,
55 I16,
56 I8,
57 F32,
58 Utf8,
59}
60
61#[derive(Debug, Args)]
62pub struct WriteArgs {
63 pub node_id: u8,
65 #[clap(value_parser=maybe_hex::<u16>)]
67 pub index: u16,
68 #[clap(value_parser=maybe_hex::<u8>)]
70 pub sub: u8,
71 pub data_type: SdoDataType,
73 pub value: String,
75}
76
77#[derive(Debug, Args)]
78pub struct ScanPdoConfigArgs {
79 pub node_id: u8,
80}
81
82#[derive(Debug, Args)]
83pub struct LoadConfigArgs {
84 pub node_id: u8,
86 #[arg(value_hint=clap::ValueHint::FilePath)]
88 pub path: PathBuf,
89}
90
91#[derive(Debug, Args)]
92pub struct SaveObjectsArgs {
93 pub node_id: u8,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq)]
99pub enum NmtNodeArg {
100 All,
101 Specific(u8),
102}
103
104impl NmtNodeArg {
105 pub fn raw(&self) -> u8 {
106 match self {
107 Self::All => 0,
108 Self::Specific(id) => *id,
109 }
110 }
111}
112
113impl FromStr for NmtNodeArg {
114 type Err = &'static str;
115 fn from_str(s: &str) -> Result<Self, Self::Err> {
116 match s.parse::<u8>() {
117 Ok(num) => {
118 if num == 0 {
119 Ok(Self::All)
120 } else if num < 128 {
121 Ok(Self::Specific(num))
122 } else {
123 Err("Node ID must be between 0 and 127")
124 }
125 }
126 Err(_) => {
127 if s == "all" {
128 Ok(Self::All)
129 } else {
130 Err("Must specify a node ID, or 'all' to broadcast")
131 }
132 }
133 }
134 }
135}
136
137#[derive(Debug, Args)]
138pub struct NmtArgs {
139 pub action: NmtAction,
140 pub node: NmtNodeArg,
142}
143
144#[derive(Clone, Copy, Debug, PartialEq, ValueEnum)]
145pub enum NmtAction {
146 ResetApp,
147 ResetComms,
148 Start,
149 Stop,
150}
151
152#[derive(Args, Clone, Copy, Debug)]
153#[group(multiple=true, requires_all=["vendor_id", "product_code", "revision", "serial"])]
154pub struct IdentityArgs {
155 #[clap(value_parser=maybe_hex::<u32>)]
156 #[arg(required = false)]
157 pub vendor_id: u32,
158 #[clap(value_parser=maybe_hex::<u32>)]
160 #[arg(required = false)]
161 pub product_code: u32,
162 #[clap(value_parser=maybe_hex::<u32>)]
164 #[arg(required = false)]
165 pub revision: u32,
166 #[clap(value_parser=maybe_hex::<u32>)]
168 #[arg(required = false)]
169 pub serial: u32,
170}
171
172impl From<IdentityArgs> for LssIdentity {
173 fn from(value: IdentityArgs) -> Self {
174 LssIdentity {
175 vendor_id: value.vendor_id,
176 product_code: value.product_code,
177 revision: value.revision,
178 serial: value.serial,
179 }
180 }
181}
182
183#[derive(Debug, Subcommand)]
184pub enum LssCommands {
185 Activate {
187 #[clap(flatten)]
188 identity: IdentityArgs,
189 },
190 Fastscan {
192 #[arg(default_value = "5")]
194 timeout: u64,
195 },
196 SetNodeId {
197 node_id: u8,
199 #[clap(flatten)]
200 identity: Option<IdentityArgs>,
201 },
202 StoreConfig {
203 #[clap(flatten)]
204 identity: Option<IdentityArgs>,
205 },
206 Global {
208 #[clap(action=clap::ArgAction::Set)]
210 enable: u8,
211 },
212}