huawei_modem/at.rs
1//! Types for dealing with AT commands and replies.
2use crate::error_codes::CmsError;
3use std::fmt;
4use crate::errors::{HuaweiError, HuaweiResult};
5/// An AT result code, which indicates the completion of a command.
6#[derive(Fail, Debug, Clone, PartialEq, Eq, is_enum_variant)]
7pub enum AtResultCode {
8 /// Command executed without failure.
9 #[fail(display = "A command is executed, and there is no error.")]
10 Ok,
11 /// Connection established.
12 #[fail(display = "A connection is established.")]
13 Connect,
14 /// Incoming call.
15 #[fail(display = "An incoming call is originated.")]
16 Ring,
17 /// Connection terminated.
18 #[fail(display = "A connection is terminated.")]
19 NoCarrier,
20 /// Generic error (rather unhelpful).
21 #[fail(display = "A generic error occurred.")]
22 Error,
23 /// CME error (= generic error), with annoyingly opaque error code (will be fixed).
24 ///
25 /// There is a list of CME errors that I should really get around to
26 /// making into an `enum`. However, that's annoying, so I haven't done
27 /// it yet.
28 #[fail(display = "An error occurred: code {}", _0)]
29 CmeError(u32),
30 /// Typed CMS error (= SMS-related error) that uses one of the
31 /// available error codes.
32 #[fail(display = "An SMS-related error occurred: {}", _0)]
33 CmsError(#[cause] CmsError),
34 /// CMS error given as string, because of modem configuration.
35 ///
36 /// There's probably some way to get modems to report errors as a numeric
37 /// error code, so you can make use of the `enum`. However, I don't know
38 /// of one.
39 #[fail(display = "An unknown SMS-related error occurred: {}", _0)]
40 CmsErrorString(String),
41 /// Unknown CMS error code.
42 #[fail(display = "An unknown SMS-related error occurred: code {}", _0)]
43 CmsErrorUnknown(u32),
44 /// No dialtone.
45 #[fail(display = "There is no dialtone.")]
46 NoDialtone,
47 /// Recipient busy.
48 #[fail(display = "Recipient is busy.")]
49 Busy,
50 /// No answer.
51 #[fail(display = "No reply (timeout occurred).")]
52 NoAnswer,
53 /// Command not supported.
54 #[fail(display = "Command not supported.")]
55 CommandNotSupported,
56 /// Too many parameters.
57 #[fail(display = "Too many parameters.")]
58 TooManyParameters
59}
60/// Any of a set of types used in AT commands.
61#[derive(Debug, Clone, PartialEq, Eq, is_enum_variant)]
62pub enum AtValue {
63 /// A string-type value - text surrounded by "quotation marks".
64 String(String),
65 /// An integer.
66 Integer(u32),
67 /// A range of integers.
68 Range((u32, u32)),
69 /// Some untyped value - usually 'bareword' strings, i.e. strings that aren't
70 /// surrounded in "quotation marks".
71 Unknown(String),
72 /// An empty value, corresponding to nothing at all.
73 Empty,
74 /// A bracketed array.
75 BracketedArray(Vec<AtValue>),
76 /// A non-bracketed array.
77 Array(Vec<AtValue>)
78}
79macro_rules! at_value_impl {
80 ($atv:ident, $($var:ident, $refmeth:ident, $mutmeth:ident, $asmeth:ident, $ty:ty),*) => {
81 /// This `impl` block provides methods to extract various types
82 /// out of an `AtValue`. If the value is not of the desired type,
83 /// `HuaweiError::TypeMismatch` is returned.
84 ///
85 /// - `as_x` methods take `self`, and return either the type or an error.
86 /// - `get_x` methods take `&self`, and return a `&` reference.
87 /// - `get_x_mut` methods take `&mut self`, and return a `&mut` reference.
88 impl $atv {
89 $(
90 pub fn $refmeth(&self) -> HuaweiResult<&$ty> {
91 if let $atv::$var(ref i) = *self {
92 Ok(i)
93 }
94 else {
95 Err(HuaweiError::TypeMismatch)
96 }
97 }
98 pub fn $mutmeth(&mut self) -> HuaweiResult<&mut $ty> {
99 if let $atv::$var(ref mut i) = *self {
100 Ok(i)
101 }
102 else {
103 Err(HuaweiError::TypeMismatch)
104 }
105 }
106 pub fn $asmeth(self) -> HuaweiResult<$ty> {
107 if let $atv::$var(i) = self {
108 Ok(i)
109 }
110 else {
111 Err(HuaweiError::TypeMismatch)
112 }
113 }
114 )*
115 }
116 }
117}
118at_value_impl!(AtValue,
119 String, get_string, get_string_mut, as_string, String,
120 Integer, get_integer, get_integer_mut, as_integer, u32,
121 Range, get_range, get_range_mut, as_range, (u32, u32),
122 Unknown, get_unknown, get_unknown_mut, as_unknown, String,
123 BracketedArray, get_bracketed_array, get_bracketed_array_mut, as_bracketed_array, Vec<AtValue>,
124 Array, get_array, get_array_mut, as_array, Vec<AtValue>);
125/// Writes the `AtValue` out, as it would appear on the command line.
126///
127/// This `impl` is directly used for formatting `AtValue`s when making
128/// AT commands.
129impl fmt::Display for AtValue {
130 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131 use self::AtValue::*;
132 match *self {
133 String(ref st) => write!(f, "\"{}\"", st)?,
134 Integer(i) => write!(f, "{}", i)?,
135 Range((a, b)) => write!(f, "{}-{}", a, b)?,
136 Unknown(ref st) => write!(f, "{}", st)?,
137 Empty => {},
138 BracketedArray(ref val) => {
139 write!(f, "(")?;
140 for (i, val) in val.iter().enumerate() {
141 let c = if i == 0 { "" } else { "," };
142 write!(f, "{}{}", c, val)?;
143 }
144 write!(f, ")")?;
145 },
146 Array(ref val) => {
147 for (i, val) in val.iter().enumerate() {
148 let c = if i == 0 { "" } else { "," };
149 write!(f, "{}{}", c, val)?;
150 }
151 }
152 }
153 Ok(())
154 }
155}
156/// One of possibly many response lines to an AT command.
157///
158/// One `AtResponse` always corresponds to one line of text.
159#[derive(Debug, Clone, PartialEq, Eq, is_enum_variant)]
160pub enum AtResponse {
161 /// An information response issued as a result of a command.
162 ///
163 /// Corresponds to '<param>: <response>'.
164 InformationResponse {
165 param: String,
166 response: AtValue
167 },
168 /// An AT result code, indicating the completion of a command.
169 ResultCode(AtResultCode),
170 /// Some other unknown response.
171 Unknown(String)
172}
173/// The complete set of responses to an issued AT command.
174#[derive(Debug, Clone)]
175pub struct AtResponsePacket {
176 /// The various `AtResponses` issued.
177 ///
178 /// Note that this will only contain 'expected' `InformationResponse`s,
179 /// as well as any `Unknown` responses. 'Expected' values are values
180 /// that were expected as a result of the command issued - for more
181 /// information, see the `AtCommand` documentation.
182 pub responses: Vec<AtResponse>,
183 /// The final result code for this command.
184 pub status: AtResultCode
185}
186impl AtResponsePacket {
187 /// Extracts the value of an `InformationResponse` that has a given `resp`
188 /// as its `param`, if such a response exists.
189 ///
190 /// Also invokes `self.assert_ok()?`, to verify that the response was successful.
191 pub fn extract_named_response_opt(&self, resp: &str) -> HuaweiResult<Option<&AtValue>> {
192 self.assert_ok()?;
193 for r in self.responses.iter() {
194 if let AtResponse::InformationResponse { ref param, ref response } = *r {
195 if resp == param {
196 return Ok(Some(response));
197 }
198 }
199 }
200 Ok(None)
201 }
202 /// Like `extract_named_response_opt`, but fails with a `HuaweiError::ExpectedResponse` if the
203 /// named response doesn't actually exist.
204 pub fn extract_named_response(&self, resp: &str) -> HuaweiResult<&AtValue> {
205 match self.extract_named_response_opt(resp)? {
206 Some(val) => Ok(val),
207 None => Err(HuaweiError::ExpectedResponse(resp.into()))
208 }
209 }
210 /// Returns `HuaweiError::AtError(self.status.clone())` if the status code was not `Ok`.
211 pub fn assert_ok(&self) -> HuaweiResult<()> {
212 if self.status.is_ok() {
213 Ok(())
214 }
215 else {
216 Err(HuaweiError::AtError(self.status.clone()))
217 }
218 }
219}
220impl AtCommand {
221 /// Get the set of 'expected' `InformationResponse`s for this command.
222 ///
223 /// This is used by the library to filter out URCs (Unsolicited Response Codes) - basically,
224 /// commands only get `InformationResponse`s that match their `expected()` array, so we can
225 /// filter all of the other responses out and assume that they're URCs.
226 ///
227 /// - For `Equals`, `Read`, and `Test`, this is the value of `vec![param]`.
228 /// - For `Execute` and `Basic`, this is the value of `vec![command]`.
229 /// - For `Text`, this is the value of `expected`.
230 pub fn expected(&self) -> Vec<String> {
231 match *self {
232 AtCommand::Equals { ref param, .. } => vec![param.clone()],
233 AtCommand::Execute { ref command } => vec![command.clone()],
234 AtCommand::Read { ref param } => vec![param.clone()],
235 AtCommand::Test { ref param } => vec![param.clone()],
236 AtCommand::Basic { ref command, .. } => vec![command.clone()],
237 AtCommand::Text { ref expected, .. } => expected.clone(),
238 }
239 }
240}
241/// An AT command.
242#[derive(Debug, Clone, PartialEq, Eq, is_enum_variant)]
243pub enum AtCommand {
244 /// Either execute a non-basic command named `param` with `value` as
245 /// argument, or set the current value of `param` to `value`.
246 ///
247 /// Corresponds to `AT<param>=<value>`.
248 Equals {
249 param: String,
250 value: AtValue,
251 },
252 /// Execute a non-basic command, with the name of `command`.
253 ///
254 /// Corresponds to `AT<command>`.
255 Execute {
256 command: String
257 },
258 /// Read the current value of `param`.
259 ///
260 /// Corresponds to `AT<param>?`.
261 Read {
262 param: String
263 },
264 /// Return the available value range of `param`.
265 ///
266 /// Corresponds to `AT<param>=?'.
267 Test {
268 param: String
269 },
270 /// Execute a basic command, where `command` indicates a single letter (A-Z)
271 /// or the & symbol and a single letter, with an optional number parameter.
272 ///
273 /// Corresponds to `AT<command>[<number>]`.
274 Basic {
275 command: String,
276 number: Option<usize>
277 },
278 /// Just send some raw text.
279 Text {
280 text: String,
281 /// The set of 'expected' `InformationResponse`s to this command.
282 expected: Vec<String>
283 }
284}
285/// Writes the `AtCommand` out, as it would appear on the command line.
286impl fmt::Display for AtCommand {
287 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
288 use self::AtCommand::*;
289 match *self {
290 Equals { ref param, ref value } => write!(f, "AT{}={}", param, value)?,
291 Execute { ref command } => write!(f, "AT{}", command)?,
292 Read { ref param } => write!(f, "AT{}?", param)?,
293 Test { ref param } => write!(f, "AT{}=?", param)?,
294 Basic { ref command, ref number } => {
295 write!(f, "AT{}", command)?;
296 if let Some(n) = *number {
297 write!(f, "{}", n)?;
298 }
299 },
300 Text { ref text, .. } => write!(f, "{}", text)?
301 }
302 Ok(())
303 }
304}