1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
//! This crate contains all the software used to run the sensing rig. It's designed to run on the //! Intel NUC in the rig operator's backpack. There are driver wrappers for each sensor, a //! supervisor that runs each of those in its own thread, plus CLI and web interfaces to control //! everything. //! //! # How to... //! //! ## ... generate this documentation //! //! - Run cargo doc in the NRI repo: //! //! <pre>nri$ cargo doc //! </pre> //! - Now we need the rustdoc command for this crate. //! //! <pre> //! nri$ touch src/main.rs //! nri$ cargo doc -v | grep rustdoc | awk "-FRunning " '{print substr($NF, 2, length($NF)-2)}' > cargo-doc-command //! </pre> //! - Edit <code>cargo-doc-command</code> and add <code>--no-defaults --passes "collapse-docs" --passes "unindent-comments"</code> after <code>src/main.rs</code>. Then run it. //! //! <pre> //! nri$ source cargo-doc-command //! </pre> //! - You can now access the docs at <code>target/doc/nri/index.html</code>. If you want them on the web: copy the docs to the Github Pages repo, commit, and push. //! //! <pre> //! nri$ pushd ../haptics-nri.github.io //! haptics-nri.github.io$ git pull //! haptics-nri.github.io$ popd //! nri$ rsync -a target/doc ../haptics-nri.github.io //! nri$ pushd ../haptics-nri.github.io //! haptics-nri.github.io$ git add doc //! haptics-nri.github.io$ git commit -m "cargo doc" //! haptics-nri.github.io$ git push //! haptics-nri.github.io$ popd //! </pre> //! - The docs are now live (after 30s or so) at http://haptics-nri.github.io/doc/nri. //! //! ## ... lint this code //! //! - The script <code>clippy.sh</code> modifies Cargo.toml and src/main.rs to include [rust-clippy](https://crates.io/crates/clippy), changes the toolchain to nightly (using [multirust](https://github.com/brson/multirust)), runs <code>cargo run</code> to generate all the lint warnings, and then switches everything back. //! //! ## ... set up the wi-fi hotspot //! //! I followed the instructions [here](http://ubuntuhandbook.org/index.php/2014/09/3-ways-create-wifi-hotspot-ubuntu/) to create a Wi-Fi hotspot to which Android devices can connect. Unity's built in network manager can almost, but not quite, do it. You need to create the network in the manager and then go edit the file to change it from Infrastructure Mode to AP Mode (which is not an option in the GUI -- you can select Ad-hoc Mode, but Android won't connect to that). //! //! Shortened instructions: //! //! 1. Click on the network icon in the system tray. Select "Edit connections...". //! 2. Click "Add". //! 3. Choose "Wi-Fi" for the type and click Create. //! 4. Make up an SSID. Leave the type as "Infrastructure" (it doesn't matter, since we'll change //! it manually). Select the wireless card in the "Device MAC address" dropdown. //! 5. Go to the "Wi-Fi Security" tab and choose sane options. //! 6. Go to the "IPv4 Settings" tab and set the Method to "Shared to other computers". //! 7. Click Save. //! 8. Edit the file <code>/etc/NetworkManager/system-connections/$SSID</code> and change //! <code>mode=infrastructure</code> to <code>mode=ap</code>. //! 9. Deactivate and reactivate Wi-Fi. Then you should be able to select the new SSID to //! "connect" (meaning broadcast). And then you should be able to connect from other devices! //! //! Note that (obviously) when the NUC is running as a hotspot, it has no internet connection. I //! tried to get DNS running, but I failed, so for now you have to use an IP address to access the //! NUC. You can see what it chose by running <code>hostname -I</code> -- it seems to like 10.42.0.1. macro_rules! errorln( ($($arg:tt)*) => ( match writeln!(&mut ::std::io::stderr(), $($arg)* ) { Ok(_) => {}, Err(x) => panic!("Unable to write to stderr: {}", x), } ) ); #[macro_use] mod comms; mod cli; mod web; mod structure; mod bluefox; mod optoforce; mod mpmc; use std::io; use std::io::{Write, BufRead}; use std::ascii::AsciiExt; use std::ptr; use std::thread; use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError}; use std::process::Command; use mpmc::MultiSender; use comms::{CmdTo, CmdFrom}; use cli::CLI; use web::Web; use structure::Structure; use bluefox::Bluefox; use optoforce::Optoforce; #[macro_use] extern crate log; extern crate env_logger; macro_rules! rxspawn { ($reply:expr; $($s:ty),*) => { vec![ $( { let (thread_tx, thread_rx) = channel(); let master_tx = $reply.clone(); Service { name: stringify!($s).to_ascii_lowercase(), thread: Some(thread::spawn(move || comms::go::<$s>(thread_rx, master_tx))), tx: thread_tx } } ),* ] }; } /// Service descriptor struct Service { name: String, thread: Option<thread::JoinHandle<()>>, tx: Sender<CmdTo>, } fn send_to(services: &[Service], s: String, cmd: CmdTo) -> bool { match services.iter().position(|x| x.name == s) { Some(i) => { services[i].tx.send(cmd); true }, None => false } } fn start(services: &[Service], s: String) -> bool { send_to(services, s, CmdTo::Start) } fn stop(services: &[Service], s: String) -> bool { send_to(services, s, CmdTo::Stop) } fn stop_all(services: &mut [Service]) { for s in services { s.tx.send(CmdTo::Quit); s.thread.take().map(|t| t.join().unwrap_or_else(|e| errorln!("Failed to join {} thread: {:?}", s.name, e))); } } /// Main function that does everything /// /// TODO actually use the logging infrastructure fn main() { env_logger::init().unwrap(); info!("Hello, world!"); let (reply_tx, reply_rx) = channel(); let mut services = rxspawn!(reply_tx; CLI, Web, Structure, Bluefox, Optoforce); start(&services, "cli".to_string()); loop { match reply_rx.recv() { Ok(cmd) => match cmd { CmdFrom::Start(s, tx) => { tx.send(start(&services, s)); }, CmdFrom::Stop(s, tx) => { tx.send(stop(&services, s)); }, CmdFrom::Quit => { stop_all(&mut services[1..]); break; } }, Err(_) => { stop_all(&mut services[1..]); break; } } } // we can't really join or kill the CLI thread, because it is waiting on stdin // so just exit, and it will be killed }