Add MAC pause/unpause

Also adds associated error variants `CannotPause` and `BadResponse`, an
associated helper function `Error::bad_response`, and the internal
helper function `transact_expecting` which shortens function bodies for
functions implementing simple handshake-like commands, like `mac
resume`.
This commit is contained in:
Leonora Tindall 2019-12-31 14:48:51 -08:00
parent fa31aab5cf
commit d42b0ec6b3
Signed by: nora
GPG Key ID: 7A8B52EC67E09AAF
2 changed files with 62 additions and 3 deletions

View File

@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Rn2903::system_version_bytes()` - `Rn2903::system_version_bytes()`
- `Rn2903::system_factory_reset()` - `Rn2903::system_factory_reset()`
- `Rn2903::system_module_reset()` - `Rn2903::system_module_reset()`
- `Rn2903::mac_pause()` and `::mac_resume()`
- `BadResponse` and `CannotPause` error variants
### Changed ### Changed

View File

@ -36,6 +36,19 @@ quick_error! {
display("Could not verify version string. Expected a RN2903 firmware revision, got '{}'", display("Could not verify version string. Expected a RN2903 firmware revision, got '{}'",
version) version)
} }
/// The device returned a response that doesn't make sense, given the command that
/// was issued.
BadResponse(expected: String, response: String) {
description("bad response from module")
display("Received a bad response from the module. Expected '{}', got '{}'.",
expected, response)
}
/// The device is operating in a mode which prevents MAC functionality from being
/// paused, but a pause was requested.
CannotPause {
description("the LoRaWAN MAC cannot be paused")
display("The LoRaWAN MAC cannot be paused right now, but a pause was requested.")
}
/// The program has become disconnected from the RN2903 module due to an I/O /// The program has become disconnected from the RN2903 module due to an I/O
/// error. It is possible the device was physically disconnected, or that the /// error. It is possible the device was physically disconnected, or that the
/// host operating system closed the serial port for some reason. /// host operating system closed the serial port for some reason.
@ -47,6 +60,12 @@ quick_error! {
} }
} }
impl Error {
fn bad_response<S: Into<String>, T: Into<String>>(expected: S, response: T) -> Self {
Self::BadResponse(expected.into(), response.into())
}
}
/// Universal `Result` wrapper for the RN2903 interface. /// Universal `Result` wrapper for the RN2903 interface.
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
@ -133,9 +152,7 @@ pub struct Rn2903 {
/// # Meta (type) Functions /// # Meta (type) Functions
/// ///
/// These functions deal with the type `Rn2903`, providing ways to create and manipulate /// These functions deal with the type `Rn2903`, providing ways to create and manipulate
/// the structure itself. Aside from performing validation of the device on the other side /// the structure itself.
/// of the serial link, these functions do not communicate with the module.
///
/// ## Creating an `Rn2903` /// ## Creating an `Rn2903`
/// There are several ways to create a `Rn2903` wrapper for an RN2903 serial connection. /// There are several ways to create a `Rn2903` wrapper for an RN2903 serial connection.
/// `::new_at()` is the recommended method, but `::new()` can be useful if the platform /// `::new_at()` is the recommended method, but `::new()` can be useful if the platform
@ -220,6 +237,18 @@ impl Rn2903 {
self.read_line() self.read_line()
} }
/// Convenience function for situations where only one response is expected according
/// to the module's documentation. Receiving another response means something wacky
/// is going on.
fn transact_expecting(&mut self, command: &[u8], expectation: &[u8]) -> Result<()> {
let bytes = self.transact(command)?;
if bytes != expectation {
Err(Error::bad_response(bytes_to_string(expectation), bytes_to_string(&bytes)))
} else {
Ok(())
}
}
/// Writes the specified command to the module, adding a CRLF and flushing the buffer. /// Writes the specified command to the module, adding a CRLF and flushing the buffer.
/// ///
/// Using [`::transact()`](#method.transact) is preferred. /// Using [`::transact()`](#method.transact) is preferred.
@ -310,3 +339,31 @@ impl Rn2903 {
self.transact(b"sys factoryRESET") self.transact(b"sys factoryRESET")
} }
} }
/// # MAC API Functions
impl Rn2903 {
/// Pauses the LoRaWAN MAC functionality on the device, returning the number of
/// milliseconds for which the MAC can remain paused without affecting LoRaWAN
/// functionality.
///
/// This command can fail with `CannotPause`, meaning the device is operating in a
/// mode (like LoRaWAN Class C mode) in which pausing the MAC for any period of time
/// would result in degraded service.
pub fn mac_pause(&mut self) -> Result<u32> {
let val = bytes_to_string(&self.transact(b"mac pause")?);
let ms: u32 = match val.parse() {
Ok(v) => v,
Err(_) => return Err(Error::bad_response("<integer>", val))
};
if ms == 0 {
Err(Error::CannotPause)
} else {
Ok(ms)
}
}
/// Resumes LoRaWAN MAC functionality on the device after being paused.
pub fn mac_resume(&mut self) -> Result<()> {
self.transact_expecting(b"mac resume", b"ok")
}
}