如何在同一资源上链接两个期货而无需提前定义每个方法组合?

时间:2017-11-03 20:12:06

标签: rust

我正在编写代码来引导并使用SIM800L调制解调器连接到2G / 3G网络。这个调制解调器与一个串行通道接口,我在这个项目之外将它复用到4个通道(数据,文本接口,控制接口,状态消息)。

为了引导它,我需要运行一系列顺序命令。此序列根据调制解调器的输出而变化(SIM卡是否锁定?SIM需要解锁什么样的信息?我们正在使用什么类型的APN?我们想要什么样的网络选择?)。我最初认为这对于futures来说是一个完美的应用程序,因为每个单独的操作在闲置时间方面成本非常高(AT+COPS,其中一个命令,最多需要10秒才能返回)。

我正在做类似这样的事情,虽然它编译并且似乎按顺序执行命令,但第三个操作是空的。我的问题是双重的:为什么命令运行不会在最后的未来结果中弹出,是否有更强大的方法来做这样的事情?

#![feature(conservative_impl_trait)]

extern crate futures;
extern crate tokio_core;

use std::sync::{Arc, Mutex};
use futures::{future, Future};
use tokio_core::reactor::Core;
use futures::sync::oneshot;
use std::thread;
use std::io;
use std::time::Duration;

pub struct Channel {
    operations: Arc<Mutex<Vec<String>>>,
}

impl Channel {
    pub fn ops(&mut self) -> Box<Future<Item = Vec<String>, Error = io::Error>> {
        println!("{:?}", self.operations);
        let ops = Arc::clone(&self.operations);
        let ops = ops.lock().unwrap();
        future::ok::<Vec<String>, io::Error>(ops.to_vec()).boxed()
    }

    pub fn run(&mut self, command: &str) -> Box<Future<Item = Vec<String>, Error = io::Error>> {
        let (tx, rx) = oneshot::channel::<Vec<String>>();

        let ops = Arc::clone(&self.operations);
        let str_cmd = String::from(command);
        thread::spawn(move || {
            thread::sleep(Duration::new(0, 10000));

            let mut ops = ops.lock().unwrap();
            ops.push(str_cmd.clone());
            println!("Pushing op: {}", str_cmd.clone());
            tx.send(vec!["OK".to_string()])
        });

        rx.map_err(|_| io::Error::new(io::ErrorKind::NotFound, "Test"))
            .boxed()
    }
}

pub struct Channels {
    inner_object: Arc<Mutex<Channel>>,
}

impl Channels {
    pub fn one(&self, cmd: &str) -> Box<Future<Item = Vec<String>, Error = io::Error>> {
        let v = Arc::clone(&self.inner_object);
        let mut v = v.lock().unwrap();
        v.run(&cmd)
    }

    pub fn ops(&self) -> Box<Future<Item = Vec<String>, Error = io::Error>> {
        let v = Arc::clone(&self.inner_object);
        let mut v = v.lock().unwrap();
        v.ops()
    }

    pub fn run_command(&self) -> Box<Future<Item = (), Error = io::Error>> {
        let a = self.one("AT+CMEE=2");
        let b = self.one("AT+CREG=0");
        let c = self.ops();
        Box::new(a.and_then(|result_1| {
            assert_eq!(result_1, vec![String::from("OK")]);
            b.and_then(|result_2| {
                assert_eq!(result_2, vec![String::from("OK")]);
                c.map(move |ops| {
                    assert_eq!(
                        ops.as_slice(),
                        ["AT+CMEE=2".to_string(), "AT+CREG=0".to_string()]
                    );
                })
            })
        }))
    }
}

fn main() {
    let mut core = Core::new().expect("Core should be created");
    let channels = Channels {
        inner_object: Arc::new(Mutex::new(Channel {
            operations: Arc::new(Mutex::new(vec![])),
        })),
    };
    let result = core.run(channels.run_command()).expect("Should've worked");

    println!("{:?}", result);
}

playground

1 个答案:

答案 0 :(得分:1)

  

为什么命令运行不会在最后的结果中弹出

因为您没有按照这种方式对操作进行排序:

let a = self.one("AT+CMEE=2");
let b = self.one("AT+CREG=0");
let c = self.ops();

立即构建:

  • ab - 承诺在回复之前睡一会儿
  • c - 在向量中获取操作的承诺

在创建c的时间点,睡眠尚未终止,因此没有执行任何操作,因此向量将为空。

Future::and_then旨在用于定义顺序操作。在您的情况下,这很复杂,因为您希望在self闭包的主体中使用and_then。您可以克隆Arc<Channel>并使用它。

你会注意到我做了很多简化:

  • 返回String而不是Vec<String>
  • 删除未使用的mut限定符和Mutex
  • 直接返回操作Vec
extern crate futures;
extern crate tokio_core;

use std::sync::{Arc, Mutex};
use futures::Future;
use tokio_core::reactor::Core;
use futures::sync::oneshot;
use std::thread;
use std::io;
use std::time::Duration;

pub struct Channel {
    operations: Arc<Mutex<Vec<String>>>,
}

impl Channel {
    fn ops(&self) -> Vec<String> {
        self.operations.lock().unwrap().clone()
    }

    fn command(&self, command: &str) -> Box<Future<Item = String, Error = io::Error>> {
        let (tx, rx) = oneshot::channel();

        let ops = Arc::clone(&self.operations);
        let str_cmd = String::from(command);

        thread::spawn(move || {
            thread::sleep(Duration::new(0, 10000));

            println!("Pushing op: {}", str_cmd);
            ops.lock().unwrap().push(str_cmd);

            tx.send("OK".to_string())
        });

        Box::new(rx.map_err(|_| io::Error::new(io::ErrorKind::NotFound, "Test")))
    }
}

struct Channels {
    data: Arc<Channel>,
}

impl Channels {
    fn run_command(&self) -> Box<Future<Item = (), Error = io::Error>> {
        let d2 = Arc::clone(&self.data);
        let d3 = Arc::clone(&self.data);

        Box::new(
            self.data
                .command("AT+CMEE=2")
                .and_then(move |cmee_answer| {
                    assert_eq!(cmee_answer, "OK"); // This should be checked in `command` and be a specific Error
                    d2.command("AT+CREG=0")
                })
                .map(move |creg_answer| {
                    assert_eq!(creg_answer, "OK"); // This should be checked in `command` and be a specific Error
                    let ops = d3.ops();
                    assert_eq!(ops, ["AT+CMEE=2", "AT+CREG=0"])
                }),
        )
    }
}

fn main() {
    let mut core = Core::new().expect("Core should be created");
    let channels = Channels {
        data: Arc::new(Channel {
            operations: Arc::new(Mutex::new(vec![])),
        }),
    };
    let result = core.run(channels.run_command()).expect("Should've worked");

    println!("{:?}", result);
}

然而,这不是我通常用期货看到的代码类型。许多期货取代&self而不是self。让我们看看它的样子:

extern crate futures;
extern crate tokio_core;

use std::sync::{Arc, Mutex};
use futures::Future;
use tokio_core::reactor::Core;
use futures::sync::oneshot;
use std::thread;
use std::io;
use std::time::Duration;

#[derive(Clone)]
pub struct Channel {
    operations: Arc<Mutex<Vec<String>>>,
}

impl Channel {
    fn ops(&self) -> Arc<Mutex<Vec<String>>> {
        Arc::clone(&self.operations)
    }

    fn command(self, command: &str) -> Box<Future<Item = (Self, String), Error = io::Error>> {
        let (tx, rx) = oneshot::channel();
        let str_cmd = String::from(command);

        thread::spawn(move || {
            thread::sleep(Duration::new(0, 10000));

            println!("Pushing op: {}", str_cmd);
            self.operations.lock().unwrap().push(str_cmd);

            tx.send((self, "OK".to_string()))
        });

        Box::new(rx.map_err(|_| io::Error::new(io::ErrorKind::NotFound, "Test")))
    }
}

struct Channels {
    data: Channel,
}

impl Channels {
    fn run_command(self) -> Box<Future<Item = (), Error = io::Error>> {
        Box::new(
            self.data
                .clone()
                .command("AT+CMEE=2")
                .and_then(|(channel, cmee_answer)| {
                    assert_eq!(cmee_answer, "OK");
                    channel.command("AT+CREG=0")
                })
                .map(|(channel, creg_answer)| {
                    assert_eq!(creg_answer, "OK");
                    let ops = channel.ops();
                    let ops = ops.lock().unwrap();
                    assert_eq!(*ops, ["AT+CMEE=2", "AT+CREG=0"]);
                }),
        )
    }
}

fn main() {
    let mut core = Core::new().expect("Core should be created");
    let channels = Channels {
        data: Channel {
            operations: Arc::new(Mutex::new(vec![])),
        },
    };
    let result = core.run(channels.run_command()).expect("Should've worked");

    println!("{:?}", result);
}