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 Sync(SyncArgs),
35}
36
37#[derive(Debug, Args)]
38pub struct ReadArgs {
39 pub node_id: u8,
41 #[clap(value_parser=maybe_hex::<u16>)]
43 pub index: u16,
44 #[clap(value_parser=maybe_hex::<u8>)]
46 pub sub: u8,
47 pub data_type: Option<SdoDataType>,
49}
50
51#[derive(Clone, Copy, Debug, ValueEnum)]
52pub enum SdoDataType {
53 U32,
54 U16,
55 U8,
56 I32,
57 I16,
58 I8,
59 F32,
60 Utf8,
61}
62
63#[derive(Debug, Args)]
64pub struct WriteArgs {
65 pub node_id: u8,
67 #[clap(value_parser=maybe_hex::<u16>)]
69 pub index: u16,
70 #[clap(value_parser=maybe_hex::<u8>)]
72 pub sub: u8,
73 pub data_type: SdoDataType,
75 pub value: String,
77}
78
79#[derive(Debug, Args)]
80pub struct ScanPdoConfigArgs {
81 pub node_id: u8,
82}
83
84#[derive(Debug, Args)]
85pub struct LoadConfigArgs {
86 pub node_id: u8,
88 #[arg(value_hint=clap::ValueHint::FilePath)]
90 pub path: PathBuf,
91}
92
93#[derive(Debug, Args)]
94pub struct SaveObjectsArgs {
95 pub node_id: u8,
97}
98
99#[derive(Debug, Args)]
100pub struct SyncArgs {
101 pub count: Option<u8>,
103}
104
105#[derive(Debug, Clone, Copy, PartialEq)]
107pub enum NmtNodeArg {
108 All,
109 Specific(u8),
110}
111
112impl NmtNodeArg {
113 pub fn raw(&self) -> u8 {
114 match self {
115 Self::All => 0,
116 Self::Specific(id) => *id,
117 }
118 }
119}
120
121impl FromStr for NmtNodeArg {
122 type Err = &'static str;
123 fn from_str(s: &str) -> Result<Self, Self::Err> {
124 match s.parse::<u8>() {
125 Ok(num) => {
126 if num == 0 {
127 Ok(Self::All)
128 } else if num < 128 {
129 Ok(Self::Specific(num))
130 } else {
131 Err("Node ID must be between 0 and 127")
132 }
133 }
134 Err(_) => {
135 if s == "all" {
136 Ok(Self::All)
137 } else {
138 Err("Must specify a node ID, or 'all' to broadcast")
139 }
140 }
141 }
142 }
143}
144
145#[derive(Debug, Args)]
146pub struct NmtArgs {
147 pub action: NmtAction,
148 pub node: NmtNodeArg,
150}
151
152#[derive(Clone, Copy, Debug, PartialEq, ValueEnum)]
153pub enum NmtAction {
154 ResetApp,
155 ResetComms,
156 Start,
157 Stop,
158}
159
160#[derive(Args, Clone, Copy, Debug)]
161#[group(multiple=true, requires_all=["vendor_id", "product_code", "revision", "serial"])]
162pub struct IdentityArgs {
163 #[clap(value_parser=maybe_hex::<u32>)]
164 #[arg(required = false)]
165 pub vendor_id: u32,
166 #[clap(value_parser=maybe_hex::<u32>)]
168 #[arg(required = false)]
169 pub product_code: u32,
170 #[clap(value_parser=maybe_hex::<u32>)]
172 #[arg(required = false)]
173 pub revision: u32,
174 #[clap(value_parser=maybe_hex::<u32>)]
176 #[arg(required = false)]
177 pub serial: u32,
178}
179
180impl From<IdentityArgs> for LssIdentity {
181 fn from(value: IdentityArgs) -> Self {
182 LssIdentity {
183 vendor_id: value.vendor_id,
184 product_code: value.product_code,
185 revision: value.revision,
186 serial: value.serial,
187 }
188 }
189}
190
191#[derive(Debug, Subcommand)]
192pub enum LssCommands {
193 Activate {
195 #[clap(flatten)]
196 identity: IdentityArgs,
197 },
198 Fastscan {
200 #[arg(default_value = "5")]
202 timeout: u64,
203 },
204 SetNodeId {
205 node_id: u8,
207 #[clap(flatten)]
208 identity: Option<IdentityArgs>,
209 },
210 StoreConfig {
211 #[clap(flatten)]
212 identity: Option<IdentityArgs>,
213 },
214 Global {
216 #[clap(action=clap::ArgAction::Set)]
218 enable: u8,
219 },
220}