You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

429 lines
16 KiB

//! Modem management.
use huawei_modem::{HuaweiModem, cmd};
use futures::{self, Future, Stream, Poll, Async, IntoFuture};
use tokio_core::reactor::Handle;
use futures::sync::mpsc::{UnboundedSender, UnboundedReceiver};
use huawei_modem::at::AtResponse;
use crate::comm::{ModemCommand, ContactFactoryCommand, ControlBotCommand, InitParameters};
use tokio_timer::{Delay, Interval, Timeout};
use std::time::{Instant, Duration};
use crate::store::Store;
use huawei_modem::cmd::sms::SmsMessage;
use huawei_modem::pdu::{Pdu, PduAddress};
use huawei_modem::gsm_encoding::GsmMessageData;
use failure::Error;
use crate::util::{self, Result};
use std::mem;
macro_rules! command_timeout {
($self:ident, $fut:expr) => {{
let tx = $self.int_tx.clone();
let timeout_ms = $self.cmd_timeout_ms;
Timeout::new($fut, Duration::from_millis(timeout_ms as _))
.map_err(move |_| {
tx.unbounded_send(ModemCommand::CommandTimeout)
.unwrap();
format_err!("Modem command timeout reached")
})
}}
}
enum ModemInner {
Uninitialized,
Disabled,
Waiting(Delay),
Initializing(Box<dyn Future<Item = HuaweiModem, Error = Error>>),
Running {
modem: HuaweiModem,
urc_rx: UnboundedReceiver<AtResponse>
},
}
impl ModemInner {
fn init_future(path: &str, hdl: &Handle, timeout_ms: u32) -> Box<dyn Future<Item = HuaweiModem, Error = Error>> {
info!("Initializing modem {}", path);
let modem = HuaweiModem::new_from_path(path, hdl);
let fut = modem.into_future()
.map_err(|e| Error::from(e))
.and_then(move |mut modem| {
info!("Configuring modem settings");
cmd::sms::set_sms_textmode(&mut modem, false)
.map_err(|e| Error::from(e))
.join(cmd::sms::set_new_message_indications(&mut modem,
cmd::sms::NewMessageNotification::SendDirectlyOrBuffer,
cmd::sms::NewMessageStorage::StoreAndNotify)
.map_err(|e| Error::from(e)))
.then(move |res| {
if let Err(e) = res {
warn!("Failed to set +CNMI: {}", e);
}
Ok(modem)
})
});
Box::new(Timeout::new(fut, Duration::from_millis(timeout_ms as _)).map_err(|e| {
if let Some(e) = e.into_inner() {
e
}
else {
format_err!("Modem initialization timeout reached")
}
}))
}
fn make_delay(delay_ms: u32) -> Delay {
Delay::new(Instant::now() + Duration::from_millis(delay_ms as _))
}
pub fn report_error(&mut self, e: Error, delay_ms: u32) {
error!("Modem error: {}", e);
*self = ModemInner::Waiting(Self::make_delay(delay_ms));
}
pub fn get_urc_rx(&mut self) -> Option<&mut UnboundedReceiver<AtResponse>> {
if let ModemInner::Running { ref mut urc_rx, .. } = *self {
Some(urc_rx)
}
else {
None
}
}
pub fn get_modem(&mut self) -> Result<&mut HuaweiModem> {
if let ModemInner::Running { ref mut modem, .. } = *self {
Ok(modem)
}
else {
Err(format_err!("Modem is not initialized"))
}
}
// return value: whether or not the modem was just freshly reinitialized
pub fn poll(&mut self, modem_path: &Option<String>, hdl: &Handle, delay_ms: u32, timeout_ms: u32) -> bool {
use self::ModemInner::*;
loop {
match mem::replace(self, Uninitialized) {
Uninitialized => {
if let Some(ref path) = modem_path {
*self = Initializing(Self::init_future(path, hdl, timeout_ms));
}
else {
info!("Modem is disabled");
*self = Disabled;
break;
}
},
Waiting(mut delay) => {
match delay.poll() {
Ok(Async::Ready(_)) => {
*self = Uninitialized;
},
Ok(Async::NotReady) => {
*self = Waiting(delay);
break;
},
Err(e) => {
error!("Modem delay timer failed: {}", e);
*self = Waiting(Self::make_delay(delay_ms));
}
}
},
Initializing(mut fut) => {
match fut.poll() {
Ok(Async::Ready(mut modem)) => {
let urc_rx = modem.take_urc_rx().unwrap();
info!("Modem initialized!");
*self = Running {
modem, urc_rx
};
return true;
},
Ok(Async::NotReady) => {
*self = Initializing(fut);
break;
},
Err(e) => {
error!("Modem initialization failed: {}", e);
*self = Waiting(Self::make_delay(delay_ms));
}
}
},
x => {
*self = x;
break;
}
}
}
false
}
}
macro_rules! get_modem {
($self:ident, $desc:expr) => {
match $self.inner.get_modem() {
Ok(m) => m,
Err(e) => {
warn!("Modem operation failed: {}", e);
let err = format!("{} failed: {}", $desc, e);
$self.cb_tx.unbounded_send(ControlBotCommand::ReportFailure(err))
.unwrap();
return;
}
}
}
}
pub struct ModemManager {
inner: ModemInner,
store: Store,
handle: Handle,
modem_path: Option<String>,
delay_ms: u32,
timeout_ms: u32,
cmd_timeout_ms: u32,
rx: UnboundedReceiver<ModemCommand>,
cf_tx: UnboundedSender<ContactFactoryCommand>,
int_tx: UnboundedSender<ModemCommand>,
cb_tx: UnboundedSender<ControlBotCommand>,
}
impl Future for ModemManager {
type Item = ();
type Error = Error;
fn poll(&mut self) -> Poll<(), Error> {
self.poll_modem();
while let Async::Ready(msg) = self.rx.poll().unwrap() {
use self::ModemCommand::*;
let msg = msg.expect("rx stopped producing");
match msg {
DoCmgl => self.cmgl(),
CmglComplete(msgs) => self.cmgl_complete(msgs)?,
CmglFailed(e) => self.cmgl_failed(e),
SendMessage(addr, msg) => self.send_message(addr, msg),
RequestCsq => self.request_csq(),
RequestReg => self.request_reg(),
ForceReinit => self.reinit_modem(),
UpdatePath(p) => self.update_path(p),
CommandTimeout => self.command_timeout(),
MakeContact(a) => self.make_contact(a)?
}
}
Ok(Async::NotReady)
}
}
impl ModemManager {
fn poll_modem(&mut self) {
if self.inner.poll(&self.modem_path, &self.handle, self.delay_ms, self.timeout_ms) {
self.cmgl();
}
if let Err(e) = self.poll_urc_rx() {
self.report_modem_error(e);
}
}
fn make_contact(&mut self, addr: PduAddress) -> Result<()> {
if self.store.get_recipient_by_addr_opt(&addr)?.is_none() {
let nick = util::make_nick_for_address(&addr);
self.store.store_recipient(&addr, &nick)?;
info!("Creating new SMS recipient for {} (nick {})", addr, nick);
self.cf_tx.unbounded_send(ContactFactoryCommand::SetupContact(addr.clone()))
.unwrap();
self.cf_tx.unbounded_send(ContactFactoryCommand::ProcessMessages).unwrap();
}
Ok(())
}
fn update_path(&mut self, path: Option<String>) {
info!("Updating modem path to {:?}", path);
self.modem_path = path;
self.reinit_modem();
}
fn reinit_modem(&mut self) {
self.inner.report_error(format_err!("Reinitialization requested"), 0);
self.poll_modem();
}
fn command_timeout(&mut self) {
self.inner.report_error(format_err!("Command timed out"), 0);
self.poll_modem();
}
fn report_modem_error(&mut self, err: Error) {
self.inner.report_error(err, self.delay_ms);
self.poll_modem();
}
fn poll_urc_rx(&mut self) -> Result<()> {
let mut do_cmgl = false;
if let Some(urc_rx) = self.inner.get_urc_rx() {
while let Async::Ready(urc) = urc_rx.poll().unwrap() {
let urc = urc.ok_or(format_err!("urc_rx stopped producing"))?;
trace!("received URC: {:?}", urc);
if let AtResponse::InformationResponse { param, .. } = urc {
if param == "+CMTI" {
debug!("received CMTI indication");
do_cmgl = true;
}
}
}
}
if do_cmgl {
self.cmgl();
}
Ok(())
}
pub fn new<T>(p: InitParameters<T>) -> Self {
let modem_path = p.cfg.modem.modem_path.clone();
let handle = p.hdl.clone();
let cs = p.cfg.modem.cmgl_secs;
let delay_ms = p.cfg.modem.restart_delay_ms.unwrap_or(5000);
let timeout_ms = p.cfg.modem.restart_timeout_ms.unwrap_or(30000);
let cmd_timeout_ms = p.cfg.modem.command_timeout_ms.unwrap_or(30000);
let rx = p.cm.modem_rx.take().unwrap();
let int_tx = p.cm.modem_tx.clone();
let cf_tx = p.cm.cf_tx.clone();
let cb_tx = p.cm.cb_tx.clone();
int_tx.unbounded_send(ModemCommand::DoCmgl).unwrap();
let cs = cs.unwrap_or(30);
let int_tx_timer = p.cm.modem_tx.clone();
let timer = Interval::new(Instant::now(), Duration::new(cs as _, 0))
.map_err(|e| {
error!("CMGL timer failed: {}", e);
panic!("timer failed!");
}).for_each(move |_| {
trace!("CMGL timer triggered.");
int_tx_timer.unbounded_send(ModemCommand::DoCmgl).unwrap();
Ok(())
});
p.hdl.spawn(timer);
let store = p.store;
let inner = ModemInner::Uninitialized;
Self {
rx, store, cf_tx, handle, int_tx, cb_tx, inner, modem_path, delay_ms, timeout_ms, cmd_timeout_ms
}
}
fn request_reg(&mut self) {
let tx = self.cb_tx.clone();
let mut modem = get_modem!(self, "Getting registration");
let fut = command_timeout!(self, cmd::network::get_registration(&mut modem))
.then(move |res| {
match res {
Ok(res) => {
let res = format!("Registration state: \x02{}\x0f", res);
tx.unbounded_send(ControlBotCommand::CommandResponse(res)).unwrap();
},
Err(e) => warn!("Error getting registration: {}", e)
}
Ok(())
});
self.handle.spawn(fut);
}
fn request_csq(&mut self) {
let tx = self.cb_tx.clone();
let mut modem = get_modem!(self, "Getting signal quality");
let fut = command_timeout!(self, cmd::network::get_signal_quality(&mut modem))
.then(move |res| {
match res {
Ok(res) => {
let res = format!("RSSI: \x02{}\x0f | BER: \x02{}\x0f", res.rssi, res.ber);
tx.unbounded_send(ControlBotCommand::CommandResponse(res)).unwrap();
}
Err(e) => warn!("Error getting signal quality: {}", e)
}
Ok(())
});
self.handle.spawn(fut);
}
fn cmgl_complete(&mut self, msgs: Vec<SmsMessage>) -> Result<()> {
use huawei_modem::cmd::sms::{MessageStatus, DeletionOptions};
debug!("+CMGL complete");
trace!("messages received from CMGL: {:?}", msgs);
for msg in msgs {
trace!("processing message: {:?}", msg);
if msg.status != MessageStatus::ReceivedUnread {
continue;
}
let data = msg.pdu.get_message_data();
let csms_data = match data.decode_message() {
Ok(m) => {
m.udh
.and_then(|x| x.get_concatenated_sms_data())
.map(|x| x.reference as i32)
},
Err(e) => {
// The error is reported when the message is sent
// via the ContactManager, not here.
debug!("Error decoding message - but it'll be reported later: {:?}", e);
None
}
};
if let Some(d) = csms_data {
trace!("Message is concatenated: {:?}", d);
}
let addr = msg.pdu.originating_address;
self.store.store_sms_message(&addr, &msg.raw_pdu, csms_data)?;
}
self.cf_tx.unbounded_send(ContactFactoryCommand::ProcessMessages).unwrap();
let mut modem = match self.inner.get_modem() {
Ok(m) => m,
Err(e) => {
warn!("Failed to delete messages: {}", e);
return Ok(());
}
};
let fut = command_timeout!(self, cmd::sms::del_sms_pdu(&mut modem, DeletionOptions::DeleteReadAndOutgoing))
.map_err(|e| {
warn!("Failed to delete messages: {}", e);
});
self.handle.spawn(fut);
Ok(())
}
fn cmgl_failed(&mut self, e: Error) {
self.report_modem_error(format_err!("+CMGL failed: {}", e));
}
fn send_message(&mut self, addr: PduAddress, msg: String) {
let data = GsmMessageData::encode_message(&msg);
let parts = data.len();
let mut modem = get_modem!(self, "Sending message");
debug!("Sending {}-part message to {}...", parts, addr);
trace!("Message content: {}", msg);
let mut futs = vec![];
for (i, part) in data.into_iter().enumerate() {
debug!("Sending part {}/{} of message to {}...", i+1, parts, addr);
let pdu = Pdu::make_simple_message(addr.clone(), part);
trace!("PDU: {:?}", pdu);
futs.push(cmd::sms::send_sms_pdu(&mut modem, &pdu)
.map_err(|e| e.into()));
}
let a1 = addr.clone();
let cb_tx = self.cb_tx.clone();
let fut = futures::future::join_all(futs)
.map(move |res| {
info!("Message to {} sent!", a1);
debug!("Message ids: {:?}", res);
}).map_err(move |e: ::failure::Error| {
// FIXME: retrying?
warn!("Failed to send message to {}: {}", addr, e);
let emsg = format!("Failed to send message to {}: {}", addr, e);
cb_tx.unbounded_send(ControlBotCommand::ReportFailure(emsg))
.unwrap();
});
self.handle.spawn(fut);
}
fn cmgl(&mut self) {
use huawei_modem::cmd::sms::MessageStatus;
if let Ok(mut modem) = self.inner.get_modem() {
let tx = self.int_tx.clone();
let fut = command_timeout!(self, cmd::sms::list_sms_pdu(&mut modem, MessageStatus::All))
.then(move |results| {
match results {
Ok(results) => {
tx.unbounded_send(
ModemCommand::CmglComplete(results)).unwrap();
},
Err(e) => {
tx.unbounded_send(
ModemCommand::CmglFailed(e)).unwrap();
}
}
let res: ::std::result::Result<(), ()> = Ok(());
res
});
self.handle.spawn(fut);
}
else {
debug!("+CMGL failed due to uninitialized modem");
}
}
}