1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19#[repr(u8)]
20pub enum Intent {
21 Endusersupport = 0,
23 Networkdiag = 1,
25 Crashlogs = 2,
27}
28
29impl Intent {
30 pub fn from_u8(value: u8) -> Option<Self> {
32 match value {
33 0 => Some(Intent::Endusersupport),
34 1 => Some(Intent::Networkdiag),
35 2 => Some(Intent::Crashlogs),
36 _ => None,
37 }
38 }
39
40 pub fn to_u8(self) -> u8 {
42 self as u8
43 }
44}
45
46impl From<Intent> for u8 {
47 fn from(val: Intent) -> Self {
48 val as u8
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
53#[repr(u8)]
54pub enum Status {
55 Success = 0,
57 Exhausted = 1,
59 Nologs = 2,
61 Busy = 3,
63 Denied = 4,
65}
66
67impl Status {
68 pub fn from_u8(value: u8) -> Option<Self> {
70 match value {
71 0 => Some(Status::Success),
72 1 => Some(Status::Exhausted),
73 2 => Some(Status::Nologs),
74 3 => Some(Status::Busy),
75 4 => Some(Status::Denied),
76 _ => None,
77 }
78 }
79
80 pub fn to_u8(self) -> u8 {
82 self as u8
83 }
84}
85
86impl From<Status> for u8 {
87 fn from(val: Status) -> Self {
88 val as u8
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
93#[repr(u8)]
94pub enum TransferProtocol {
95 Responsepayload = 0,
97 Bdx = 1,
99}
100
101impl TransferProtocol {
102 pub fn from_u8(value: u8) -> Option<Self> {
104 match value {
105 0 => Some(TransferProtocol::Responsepayload),
106 1 => Some(TransferProtocol::Bdx),
107 _ => None,
108 }
109 }
110
111 pub fn to_u8(self) -> u8 {
113 self as u8
114 }
115}
116
117impl From<TransferProtocol> for u8 {
118 fn from(val: TransferProtocol) -> Self {
119 val as u8
120 }
121}
122
123pub fn encode_retrieve_logs_request(intent: Intent, requested_protocol: TransferProtocol, transfer_file_designator: String) -> anyhow::Result<Vec<u8>> {
127 let tlv = tlv::TlvItemEnc {
128 tag: 0,
129 value: tlv::TlvItemValueEnc::StructInvisible(vec![
130 (0, tlv::TlvItemValueEnc::UInt8(intent.to_u8())).into(),
131 (1, tlv::TlvItemValueEnc::UInt8(requested_protocol.to_u8())).into(),
132 (2, tlv::TlvItemValueEnc::String(transfer_file_designator)).into(),
133 ]),
134 };
135 Ok(tlv.encode()?)
136}
137
138pub fn get_command_list() -> Vec<(u32, &'static str)> {
141 vec![
142 (0x00, "RetrieveLogsRequest"),
143 ]
144}
145
146pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
147 match cmd_id {
148 0x00 => Some("RetrieveLogsRequest"),
149 _ => None,
150 }
151}
152
153pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
154 match cmd_id {
155 0x00 => Some(vec![
156 crate::clusters::codec::CommandField { tag: 0, name: "intent", kind: crate::clusters::codec::FieldKind::Enum { name: "Intent", variants: &[(0, "Endusersupport"), (1, "Networkdiag"), (2, "Crashlogs")] }, optional: false, nullable: false },
157 crate::clusters::codec::CommandField { tag: 1, name: "requested_protocol", kind: crate::clusters::codec::FieldKind::Enum { name: "TransferProtocol", variants: &[(0, "Responsepayload"), (1, "Bdx")] }, optional: false, nullable: false },
158 crate::clusters::codec::CommandField { tag: 2, name: "transfer_file_designator", kind: crate::clusters::codec::FieldKind::String, optional: true, nullable: false },
159 ]),
160 _ => None,
161 }
162}
163
164pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
165 match cmd_id {
166 0x00 => {
167 let intent = {
168 let n = crate::clusters::codec::json_util::get_u64(args, "intent")?;
169 Intent::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid Intent: {}", n))?
170 };
171 let requested_protocol = {
172 let n = crate::clusters::codec::json_util::get_u64(args, "requested_protocol")?;
173 TransferProtocol::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid TransferProtocol: {}", n))?
174 };
175 let transfer_file_designator = crate::clusters::codec::json_util::get_string(args, "transfer_file_designator")?;
176 encode_retrieve_logs_request(intent, requested_protocol, transfer_file_designator)
177 }
178 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
179 }
180}
181
182#[derive(Debug, serde::Serialize)]
183pub struct RetrieveLogsResponse {
184 pub status: Option<Status>,
185 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
186 pub log_content: Option<Vec<u8>>,
187 pub utc_time_stamp: Option<u64>,
188 pub time_since_boot: Option<u8>,
189}
190
191pub fn decode_retrieve_logs_response(inp: &tlv::TlvItemValue) -> anyhow::Result<RetrieveLogsResponse> {
195 if let tlv::TlvItemValue::List(_fields) = inp {
196 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
197 Ok(RetrieveLogsResponse {
198 status: item.get_int(&[0]).and_then(|v| Status::from_u8(v as u8)),
199 log_content: item.get_octet_string_owned(&[1]),
200 utc_time_stamp: item.get_int(&[2]),
201 time_since_boot: item.get_int(&[3]).map(|v| v as u8),
202 })
203 } else {
204 Err(anyhow::anyhow!("Expected struct fields"))
205 }
206}
207
208pub async fn retrieve_logs_request(conn: &crate::controller::Connection, endpoint: u16, intent: Intent, requested_protocol: TransferProtocol, transfer_file_designator: String) -> anyhow::Result<RetrieveLogsResponse> {
212 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_DIAGNOSTIC_LOGS, crate::clusters::defs::CLUSTER_DIAGNOSTIC_LOGS_CMD_ID_RETRIEVELOGSREQUEST, &encode_retrieve_logs_request(intent, requested_protocol, transfer_file_designator)?).await?;
213 decode_retrieve_logs_response(&tlv)
214}
215