forked from M-Labs/thermostat
Compare commits
1 Commits
master
...
command_pa
Author | SHA1 | Date | |
---|---|---|---|
8d41b67bcf |
@ -1,18 +1,8 @@
|
|||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::num::ParseIntError;
|
use core::num::ParseIntError;
|
||||||
use core::str::{from_utf8, Utf8Error};
|
use core::str::{from_utf8, Utf8Error, SplitAsciiWhitespace};
|
||||||
use nom::{
|
use nom::error::ErrorKind;
|
||||||
IResult,
|
use num_traits::ParseFloatError;
|
||||||
branch::alt,
|
|
||||||
bytes::complete::{is_a, tag, take_while1},
|
|
||||||
character::{is_digit, complete::{char, one_of}},
|
|
||||||
combinator::{complete, map, opt, value},
|
|
||||||
sequence::preceded,
|
|
||||||
multi::{fold_many0, fold_many1},
|
|
||||||
error::ErrorKind,
|
|
||||||
Needed,
|
|
||||||
};
|
|
||||||
use num_traits::{Num, ParseFloatError};
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
|
||||||
@ -193,425 +183,76 @@ pub enum Command {
|
|||||||
ShowHWRev,
|
ShowHWRev,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(input: &[u8]) -> IResult<&[u8], ()> {
|
fn load(tokens: &mut SplitAsciiWhitespace) -> Result<Command, Error> {
|
||||||
complete(
|
tokens.next();
|
||||||
fold_many0(
|
Err(Error::Incomplete)
|
||||||
one_of("\r\n\t "),
|
|
||||||
(), |(), _| ()
|
|
||||||
)
|
|
||||||
)(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn whitespace(input: &[u8]) -> IResult<&[u8], ()> {
|
fn save(_tokens: &mut SplitAsciiWhitespace) -> Result<Command, Error> {
|
||||||
fold_many1(char(' '), (), |(), _| ())(input)
|
Err(Error::Incomplete)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unsigned(input: &[u8]) -> IResult<&[u8], Result<u32, Error>> {
|
fn ipv4(_tokens: &mut SplitAsciiWhitespace) -> Result<Command, Error> {
|
||||||
take_while1(is_digit)(input)
|
Err(Error::Incomplete)
|
||||||
.map(|(input, digits)| {
|
|
||||||
let result =
|
|
||||||
from_utf8(digits)
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
.and_then(|digits| u32::from_str_radix(digits, 10)
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
);
|
|
||||||
(input, result)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn float(input: &[u8]) -> IResult<&[u8], Result<f64, Error>> {
|
fn report(_tokens: &mut SplitAsciiWhitespace) -> Result<Command, Error> {
|
||||||
let (input, sign) = opt(is_a("-"))(input)?;
|
Err(Error::Incomplete)
|
||||||
let negative = sign.is_some();
|
|
||||||
let (input, digits) = take_while1(|c| is_digit(c) || c == '.' as u8)(input)?;
|
|
||||||
let result =
|
|
||||||
from_utf8(digits)
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
.and_then(|digits| f64::from_str_radix(digits, 10)
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
)
|
|
||||||
.map(|result: f64| if negative { -result } else { result });
|
|
||||||
Ok((input, result))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn off_on(input: &[u8]) -> IResult<&[u8], bool> {
|
fn pwm(_tokens: &mut SplitAsciiWhitespace) -> Result<Command, Error> {
|
||||||
alt((value(false, tag("off")),
|
Err(Error::Incomplete)
|
||||||
value(true, tag("on"))
|
|
||||||
))(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn channel(input: &[u8]) -> IResult<&[u8], usize> {
|
fn center(_tokens: &mut SplitAsciiWhitespace) -> Result<Command, Error> {
|
||||||
map(one_of("01"), |c| (c as usize) - ('0' as usize))(input)
|
Err(Error::Incomplete)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report(input: &[u8]) -> IResult<&[u8], Command> {
|
fn pid(_tokens: &mut SplitAsciiWhitespace) -> Result<Command, Error> {
|
||||||
preceded(
|
Err(Error::Incomplete)
|
||||||
tag("report"),
|
|
||||||
alt((
|
|
||||||
preceded(
|
|
||||||
whitespace,
|
|
||||||
preceded(
|
|
||||||
tag("mode"),
|
|
||||||
alt((
|
|
||||||
preceded(
|
|
||||||
whitespace,
|
|
||||||
// `report mode <on | off>` - Switch repoting mode
|
|
||||||
map(off_on, Command::Reporting)
|
|
||||||
),
|
|
||||||
// `report mode` - Show current reporting state
|
|
||||||
value(Command::Show(ShowCommand::Reporting), end)
|
|
||||||
))
|
|
||||||
)),
|
|
||||||
// `report` - Report once
|
|
||||||
value(Command::Show(ShowCommand::Input), end)
|
|
||||||
))
|
|
||||||
)(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, f64), Error>> {
|
fn s_h(_tokens: &mut SplitAsciiWhitespace) -> Result<Command, Error> {
|
||||||
let result_with_pin = |pin: PwmPin|
|
Err(Error::Incomplete)
|
||||||
move |result: Result<f64, Error>|
|
|
||||||
result.map(|value| (pin, value));
|
|
||||||
|
|
||||||
alt((
|
|
||||||
map(
|
|
||||||
preceded(
|
|
||||||
tag("i_set"),
|
|
||||||
preceded(
|
|
||||||
whitespace,
|
|
||||||
float
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result_with_pin(PwmPin::ISet)
|
|
||||||
),
|
|
||||||
map(
|
|
||||||
preceded(
|
|
||||||
tag("max_i_pos"),
|
|
||||||
preceded(
|
|
||||||
whitespace,
|
|
||||||
float
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result_with_pin(PwmPin::MaxIPos)
|
|
||||||
),
|
|
||||||
map(
|
|
||||||
preceded(
|
|
||||||
tag("max_i_neg"),
|
|
||||||
preceded(
|
|
||||||
whitespace,
|
|
||||||
float
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result_with_pin(PwmPin::MaxINeg)
|
|
||||||
),
|
|
||||||
map(
|
|
||||||
preceded(
|
|
||||||
tag("max_v"),
|
|
||||||
preceded(
|
|
||||||
whitespace,
|
|
||||||
float
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result_with_pin(PwmPin::MaxV)
|
|
||||||
))
|
|
||||||
)(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `pwm <0-1> pid` - Set PWM to be controlled by PID
|
fn postfilter(_tokens: &mut SplitAsciiWhitespace) -> Result<Command, Error> {
|
||||||
fn pwm_pid(input: &[u8]) -> IResult<&[u8], ()> {
|
Err(Error::Incomplete)
|
||||||
value((), tag("pid"))(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
fn fan(_tokens: &mut SplitAsciiWhitespace) -> Result<Command, Error> {
|
||||||
let (input, _) = tag("pwm")(input)?;
|
Err(Error::Incomplete)
|
||||||
alt((
|
|
||||||
|input| {
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, channel) = channel(input)?;
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, result) = alt((
|
|
||||||
|input| {
|
|
||||||
let (input, ()) = pwm_pid(input)?;
|
|
||||||
Ok((input, Ok(Command::PwmPid { channel })))
|
|
||||||
},
|
|
||||||
|input| {
|
|
||||||
let (input, config) = pwm_setup(input)?;
|
|
||||||
match config {
|
|
||||||
Ok((pin, value)) =>
|
|
||||||
Ok((input, Ok(Command::Pwm { channel, pin, value }))),
|
|
||||||
Err(e) =>
|
|
||||||
Ok((input, Err(e))),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
))(input)?;
|
|
||||||
end(input)?;
|
|
||||||
Ok((input, result))
|
|
||||||
},
|
|
||||||
value(Ok(Command::Show(ShowCommand::Pwm)), end)
|
|
||||||
))(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn center_point(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
fn fcurve(_tokens: &mut SplitAsciiWhitespace) -> Result<Command, Error> {
|
||||||
let (input, _) = tag("center")(input)?;
|
Err(Error::Incomplete)
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, channel) = channel(input)?;
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, center) = alt((
|
|
||||||
value(Ok(CenterPoint::Vref), tag("vref")),
|
|
||||||
|input| {
|
|
||||||
let (input, value) = float(input)?;
|
|
||||||
Ok((input, value.map(|value| CenterPoint::Override(value as f32))))
|
|
||||||
}
|
|
||||||
))(input)?;
|
|
||||||
end(input)?;
|
|
||||||
Ok((input, center.map(|center| Command::CenterPoint {
|
|
||||||
channel,
|
|
||||||
center,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `pid <0-1> <parameter> <value>`
|
|
||||||
fn pid_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|
||||||
let (input, channel) = channel(input)?;
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, parameter) =
|
|
||||||
alt((value(PidParameter::Target, tag("target")),
|
|
||||||
value(PidParameter::KP, tag("kp")),
|
|
||||||
value(PidParameter::KI, tag("ki")),
|
|
||||||
value(PidParameter::KD, tag("kd")),
|
|
||||||
value(PidParameter::OutputMin, tag("output_min")),
|
|
||||||
value(PidParameter::OutputMax, tag("output_max")),
|
|
||||||
))(input)?;
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, value) = float(input)?;
|
|
||||||
let result = value
|
|
||||||
.map(|value| Command::Pid { channel, parameter, value });
|
|
||||||
Ok((input, result))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `pid` | `pid <pid_parameter>`
|
|
||||||
fn pid(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|
||||||
let (input, _) = tag("pid")(input)?;
|
|
||||||
alt((
|
|
||||||
preceded(
|
|
||||||
whitespace,
|
|
||||||
pid_parameter
|
|
||||||
),
|
|
||||||
value(Ok(Command::Show(ShowCommand::Pid)), end)
|
|
||||||
))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `s-h <0-1> <parameter> <value>`
|
|
||||||
fn steinhart_hart_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|
||||||
let (input, channel) = channel(input)?;
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, parameter) =
|
|
||||||
alt((value(ShParameter::T0, tag("t0")),
|
|
||||||
value(ShParameter::B, tag("b")),
|
|
||||||
value(ShParameter::R0, tag("r0"))
|
|
||||||
))(input)?;
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, value) = float(input)?;
|
|
||||||
let result = value
|
|
||||||
.map(|value| Command::SteinhartHart { channel, parameter, value });
|
|
||||||
Ok((input, result))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `s-h` | `s-h <steinhart_hart_parameter>`
|
|
||||||
fn steinhart_hart(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|
||||||
let (input, _) = tag("s-h")(input)?;
|
|
||||||
alt((
|
|
||||||
preceded(
|
|
||||||
whitespace,
|
|
||||||
steinhart_hart_parameter
|
|
||||||
),
|
|
||||||
value(Ok(Command::Show(ShowCommand::SteinhartHart)), end)
|
|
||||||
))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postfilter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|
||||||
let (input, _) = tag("postfilter")(input)?;
|
|
||||||
alt((
|
|
||||||
preceded(
|
|
||||||
whitespace,
|
|
||||||
|input| {
|
|
||||||
let (input, channel) = channel(input)?;
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
alt((
|
|
||||||
value(Ok(Command::PostFilter {
|
|
||||||
channel,
|
|
||||||
rate: None,
|
|
||||||
}), tag("off")),
|
|
||||||
move |input| {
|
|
||||||
let (input, _) = tag("rate")(input)?;
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, rate) = float(input)?;
|
|
||||||
let result = rate
|
|
||||||
.map(|rate| Command::PostFilter {
|
|
||||||
channel,
|
|
||||||
rate: Some(rate as f32),
|
|
||||||
});
|
|
||||||
Ok((input, result))
|
|
||||||
}
|
|
||||||
))(input)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
value(Ok(Command::Show(ShowCommand::PostFilter)), end)
|
|
||||||
))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|
||||||
let (input, _) = tag("load")(input)?;
|
|
||||||
let (input, channel) = alt((
|
|
||||||
|input| {
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, channel) = channel(input)?;
|
|
||||||
let (input, _) = end(input)?;
|
|
||||||
Ok((input, Some(channel)))
|
|
||||||
},
|
|
||||||
value(None, end)
|
|
||||||
))(input)?;
|
|
||||||
|
|
||||||
let result = Ok(Command::Load { channel });
|
|
||||||
Ok((input, result))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|
||||||
let (input, _) = tag("save")(input)?;
|
|
||||||
let (input, channel) = alt((
|
|
||||||
|input| {
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, channel) = channel(input)?;
|
|
||||||
let (input, _) = end(input)?;
|
|
||||||
Ok((input, Some(channel)))
|
|
||||||
},
|
|
||||||
value(None, end)
|
|
||||||
))(input)?;
|
|
||||||
|
|
||||||
let result = Ok(Command::Save { channel });
|
|
||||||
Ok((input, result))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ipv4_addr(input: &[u8]) -> IResult<&[u8], Result<[u8; 4], Error>> {
|
|
||||||
let (input, a) = unsigned(input)?;
|
|
||||||
let (input, _) = tag(".")(input)?;
|
|
||||||
let (input, b) = unsigned(input)?;
|
|
||||||
let (input, _) = tag(".")(input)?;
|
|
||||||
let (input, c) = unsigned(input)?;
|
|
||||||
let (input, _) = tag(".")(input)?;
|
|
||||||
let (input, d) = unsigned(input)?;
|
|
||||||
let address = move || Ok([a? as u8, b? as u8, c? as u8, d? as u8]);
|
|
||||||
Ok((input, address()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ipv4(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|
||||||
let (input, _) = tag("ipv4")(input)?;
|
|
||||||
alt((
|
|
||||||
|input| {
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, address) = ipv4_addr(input)?;
|
|
||||||
let (input, _) = tag("/")(input)?;
|
|
||||||
let (input, mask_len) = unsigned(input)?;
|
|
||||||
let (input, gateway) = alt((
|
|
||||||
|input| {
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, gateway) = ipv4_addr(input)?;
|
|
||||||
Ok((input, gateway.map(Some)))
|
|
||||||
},
|
|
||||||
value(Ok(None), end),
|
|
||||||
))(input)?;
|
|
||||||
|
|
||||||
let result = move || {
|
|
||||||
Ok(Command::Ipv4(Ipv4Config {
|
|
||||||
address: address?,
|
|
||||||
mask_len: mask_len? as u8,
|
|
||||||
gateway: gateway?,
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
Ok((input, result()))
|
|
||||||
},
|
|
||||||
value(Ok(Command::Show(ShowCommand::Ipv4)), end),
|
|
||||||
))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fan(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|
||||||
let (input, _) = tag("fan")(input)?;
|
|
||||||
alt((
|
|
||||||
|input| {
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
|
|
||||||
let (input, result) = alt((
|
|
||||||
|input| {
|
|
||||||
let (input, _) = tag("auto")(input)?;
|
|
||||||
Ok((input, Ok(Command::FanAuto)))
|
|
||||||
},
|
|
||||||
|input| {
|
|
||||||
let (input, value) = unsigned(input)?;
|
|
||||||
Ok((input, Ok(Command::FanSet { fan_pwm: value.unwrap_or(0)})))
|
|
||||||
},
|
|
||||||
))(input)?;
|
|
||||||
Ok((input, result))
|
|
||||||
},
|
|
||||||
value(Ok(Command::ShowFan), end)
|
|
||||||
))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fan_curve(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|
||||||
let (input, _) = tag("fcurve")(input)?;
|
|
||||||
alt((
|
|
||||||
|input| {
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, result) = alt((
|
|
||||||
|input| {
|
|
||||||
let (input, _) = tag("default")(input)?;
|
|
||||||
Ok((input, Ok(Command::FanCurveDefaults)))
|
|
||||||
},
|
|
||||||
|input| {
|
|
||||||
let (input, k_a) = float(input)?;
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, k_b) = float(input)?;
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, k_c) = float(input)?;
|
|
||||||
if k_a.is_ok() && k_b.is_ok() && k_c.is_ok() {
|
|
||||||
Ok((input, Ok(Command::FanCurve { k_a: k_a.unwrap() as f32, k_b: k_b.unwrap() as f32, k_c: k_c.unwrap() as f32 })))
|
|
||||||
} else {
|
|
||||||
Err(nom::Err::Incomplete(Needed::Size(3)))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
))(input)?;
|
|
||||||
Ok((input, result))
|
|
||||||
},
|
|
||||||
value(Err(Error::Incomplete), end)
|
|
||||||
))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|
||||||
alt((value(Ok(Command::Quit), tag("quit")),
|
|
||||||
load,
|
|
||||||
save,
|
|
||||||
value(Ok(Command::Reset), tag("reset")),
|
|
||||||
ipv4,
|
|
||||||
map(report, Ok),
|
|
||||||
pwm,
|
|
||||||
center_point,
|
|
||||||
pid,
|
|
||||||
steinhart_hart,
|
|
||||||
postfilter,
|
|
||||||
value(Ok(Command::Dfu), tag("dfu")),
|
|
||||||
fan,
|
|
||||||
fan_curve,
|
|
||||||
value(Ok(Command::ShowHWRev), tag("hwrev")),
|
|
||||||
))(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
pub fn parse(input: &[u8]) -> Result<Self, Error> {
|
pub fn parse(input: &[u8]) -> Result<Self, Error> {
|
||||||
match command(input) {
|
let mut tokens = from_utf8(input).unwrap().split_ascii_whitespace();
|
||||||
Ok((input_remain, result)) if input_remain.len() == 0 =>
|
match tokens.next() {
|
||||||
result,
|
Some(keyword) => {
|
||||||
Ok((input_remain, _)) =>
|
match keyword {
|
||||||
Err(Error::UnexpectedInput(input_remain[0])),
|
"quit" => Ok(Command::Quit),
|
||||||
Err(e) =>
|
"load" => load(&mut tokens),
|
||||||
Err(e.into()),
|
"save" => save(&mut tokens),
|
||||||
|
"reset" => Ok(Command::Reset),
|
||||||
|
"ipv4" => ipv4(&mut tokens),
|
||||||
|
"report" => report(&mut tokens),
|
||||||
|
"pwm" => pwm(&mut tokens),
|
||||||
|
"center" => center(&mut tokens),
|
||||||
|
"pid" => pid(&mut tokens),
|
||||||
|
"s-h" => s_h(&mut tokens),
|
||||||
|
"postfilter" => postfilter(&mut tokens),
|
||||||
|
"dfu" => Ok(Command::Dfu),
|
||||||
|
"fan" => fan(&mut tokens),
|
||||||
|
"fcurve" => fcurve(&mut tokens),
|
||||||
|
"hwrev" => Ok(Command::ShowHWRev),
|
||||||
|
_ => Err(Error::Incomplete),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Err(Error::Incomplete),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user