mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-04 04:27:34 +00:00
add instructions on how to make commands
This commit is contained in:
parent
685f36af41
commit
4006df3c4f
6 changed files with 94 additions and 63 deletions
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue