add instructions on how to make commands

This commit is contained in:
bunnie 2021-03-11 22:54:24 +08:00
parent 685f36af41
commit 4006df3c4f
6 changed files with 94 additions and 63 deletions

View file

@ -12,3 +12,33 @@ Other facilities for handling system-level messages, such as menu configurations
and network events, have yet to be implemented. However, they will likely end up
with a demo in this application first.
To make your own command, copy the `echo.rs` template or snag the one below, and put
it in the services/shellchat/src/cmds/ directory:
```Rust
use crate::{ShellCmdApi, CommonEnv};
use xous::String;
#[derive(Debug)]
pub struct Echo {
}
impl<'a> ShellCmdApi<'a> for Echo {
cmd_api!(echo); // inserts boilerplate for command API
fn process(&mut self, args: String::<1024>, _env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
Ok(Some(rest))
}
}
```
The "verb" is the name of your command, and it is the argument to the `cmd_api!()` macro.
`process()` is your "main" function, and `args` is a string which contains the
rest of the line that was typed into the chat, minus the verb used to identify
your command (the arguments, as they may be, but not tokenized or parsed in
any way). `env` contains some items in a common environment, for example, connections
to the GAM, LLIO server or other resources required by your implementation.
Once you've added your command to the directory, go to the `cmds.rs` file, and follow
the four-step instructions embedded within the file, starting around line 40.

View file

@ -1,10 +1,10 @@
use xous::String;
use core::fmt::Write;
/////////////////////////// Common items to all commands
pub trait ShellCmdApi<'a> {
// user implemented:
// called to process the command with the remainder of the string attached
fn process(&mut self, rest: String::<1024>, env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error>;
fn process(&mut self, args: String::<1024>, env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error>;
// created with cmd_api! macro
// checks if the command matches the current verb in question
@ -28,41 +28,7 @@ macro_rules! cmd_api {
};
}
/// extract the first token, as delimited by spaces
/// modifies the incoming line by removing the token and returning the remainder
/// returns the found token
/// note: we don't have split() because of nostd
pub fn tokenize(line: &mut String::<1024>) -> Option<String::<1024>> {
let mut token = String::<1024>::new();
let mut retline = String::<1024>::new();
let lineiter = line.as_str().unwrap().chars();
let mut foundspace = false;
let mut foundrest = false;
for ch in lineiter {
if ch != ' ' && !foundspace {
token.push(ch).unwrap();
} else if foundspace && foundrest {
retline.push(ch).unwrap();
} else if foundspace && ch != ' ' {
// handle case of multiple spaces in a row
foundrest = true;
retline.push(ch).unwrap();
} else {
foundspace = true;
// consume the space
}
}
line.clear();
write!(line, "{}", retline.as_str().unwrap()).unwrap();
if token.len() > 0 {
Some(token)
} else {
None
}
}
/////////////////////////// Command shell integration
#[derive(Debug)]
pub struct CommonEnv {
llio: xous::CID,
@ -71,18 +37,29 @@ pub struct CommonEnv {
gam: xous::CID,
}
mod echo;
use echo::*;
mod test;
use test::*;
mod sleep;
use sleep::*;
mod sensors;
use sensors::*;
/*
To add a new command:
0. ensure that the command implements the ShellCmdApi (above)
1. mod/use the new command
2. create an entry for the command's storage in the CmdEnv structure
3. initialize the persistant storage here
4. add it to the "commands" array in the dispatch() routine below
Side note: if your command doesn't require persistent storage, you could,
technically, generate the command dynamically every time it's called. Echo
demonstrates this.
*/
///// 1. add your module here, and pull its namespace into the local crate
mod echo; use echo::*;
mod test; use test::*;
mod sleep; use sleep::*;
mod sensors; use sensors::*;
#[derive(Debug)]
pub struct CmdEnv {
common_env: CommonEnv,
///// 2. declare storage for your command here.
test_cmd: Test,
sleep_cmd: Sleep,
sensors_cmd: Sensors,
@ -90,18 +67,6 @@ pub struct CmdEnv {
impl CmdEnv {
pub fn new(gam: xous::CID) -> CmdEnv {
let ticktimer_server_id = xous::SID::from_bytes(b"ticktimer-server").unwrap();
/*
To add a new command:
- ensure that the command implements the ShellCmdApi
- mod/use the new command (above)
- create an entry for the command's storage in the CmdEnv structure
- initialize the persistant storage here
- add it to the "commands" array in the dispatch() routine below
Side note: if your command doesn't require persistent storage, you could,
technically, generate the command dynamically every time it's called. Echo
demonstrates this.
*/
CmdEnv {
common_env: CommonEnv {
llio: xous_names::request_connection_blocking(xous::names::SERVER_NAME_LLIO).expect("CMD: can't connect to LLIO"),
@ -109,6 +74,7 @@ impl CmdEnv {
ticktimer: xous::connect(ticktimer_server_id).unwrap(),
gam,
},
///// 3. initialize your storage, by calling new()
test_cmd: Test::new(),
sleep_cmd: Sleep::new(),
sensors_cmd: Sensors::new(),
@ -120,6 +86,7 @@ impl CmdEnv {
let mut echo_cmd = Echo {}; // this command has no persistent storage, so we can "create" it every time we call dispatch (but it's a zero-cost absraction so this doesn't actually create any instructions)
let commands: &mut [& mut dyn ShellCmdApi] = &mut [
///// 4. add your command to this array, so that it can be looked up and dispatched
&mut echo_cmd,
&mut self.test_cmd,
&mut self.sleep_cmd,
@ -161,4 +128,38 @@ impl CmdEnv {
Ok(None)
}
}
}
}
/// extract the first token, as delimited by spaces
/// modifies the incoming line by removing the token and returning the remainder
/// returns the found token
/// note: we don't have split() because of nostd
pub fn tokenize(line: &mut String::<1024>) -> Option<String::<1024>> {
let mut token = String::<1024>::new();
let mut retline = String::<1024>::new();
let lineiter = line.as_str().unwrap().chars();
let mut foundspace = false;
let mut foundrest = false;
for ch in lineiter {
if ch != ' ' && !foundspace {
token.push(ch).unwrap();
} else if foundspace && foundrest {
retline.push(ch).unwrap();
} else if foundspace && ch != ' ' {
// handle case of multiple spaces in a row
foundrest = true;
retline.push(ch).unwrap();
} else {
foundspace = true;
// consume the space
}
}
line.clear();
write!(line, "{}", retline.as_str().unwrap()).unwrap();
if token.len() > 0 {
Some(token)
} else {
None
}
}

View file

@ -8,7 +8,7 @@ pub struct Echo {
impl<'a> ShellCmdApi<'a> for Echo {
cmd_api!(echo); // inserts boilerplate for command API
fn process(&mut self, rest: String::<1024>, _env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
Ok(Some(rest))
fn process(&mut self, args: String::<1024>, _env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
Ok(Some(args))
}
}

View file

@ -14,7 +14,7 @@ impl Sensors {
impl<'a> ShellCmdApi<'a> for Sensors {
cmd_api!(sensors);
fn process(&mut self, _rest: String::<1024>, env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
fn process(&mut self, _args: String::<1024>, env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
use core::fmt::Write;
let mut ret = String::<1024>::new();

View file

@ -13,7 +13,7 @@ impl Sleep {
impl<'a> ShellCmdApi<'a> for Sleep {
cmd_api!(sleep); // inserts boilerplate for command API
fn process(&mut self, _rest: String::<1024>, env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
fn process(&mut self, _args: String::<1024>, env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
use core::fmt::Write;
let mut ret = String::<1024>::new();

View file

@ -16,7 +16,7 @@ impl Test {
impl<'a> ShellCmdApi<'a> for Test {
cmd_api!(test);
fn process(&mut self, _rest: String::<1024>, _env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
fn process(&mut self, _args: String::<1024>, _env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
use core::fmt::Write;
self.state += 1;