forked from M-Labs/thermostat
Compare commits
3 Commits
0fe2d645e2
...
a8465a7e63
Author | SHA1 | Date | |
---|---|---|---|
a8465a7e63 | |||
08e3815225 | |||
59ead548b7 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,2 @@
|
|||||||
target/
|
target/
|
||||||
result
|
result
|
||||||
*.bin
|
|
||||||
|
|
||||||
__pycache__/
|
|
||||||
|
17
README.md
17
README.md
@ -84,7 +84,9 @@ invalidate the first line of input.
|
|||||||
|
|
||||||
### Reading ADC input
|
### Reading ADC input
|
||||||
|
|
||||||
ADC input data is provided in reports. Query for the latest report with the command `report`. See the *Reports* section below.
|
Set report mode to `on` for a continuous stream of input data.
|
||||||
|
|
||||||
|
The scope of this setting is per TCP session.
|
||||||
|
|
||||||
|
|
||||||
### TCP commands
|
### TCP commands
|
||||||
@ -95,11 +97,13 @@ formatted as line-delimited JSON.
|
|||||||
| Syntax | Function |
|
| Syntax | Function |
|
||||||
|----------------------------------|-------------------------------------------------------------------------------|
|
|----------------------------------|-------------------------------------------------------------------------------|
|
||||||
| `report` | Show current input |
|
| `report` | Show current input |
|
||||||
|
| `report mode` | Show current report mode |
|
||||||
|
| `report mode <off/on>` | Set report mode |
|
||||||
| `pwm` | Show current PWM settings |
|
| `pwm` | Show current PWM settings |
|
||||||
| `pwm <0/1> max_i_pos <amp>` | Set maximum positive output current, clamped to [0, 2] |
|
| `pwm <0/1> max_i_pos <amp>` | Set maximum positive output current, clamped at [0, 2] |
|
||||||
| `pwm <0/1> max_i_neg <amp>` | Set maximum negative output current, clamped to [0, 2] |
|
| `pwm <0/1> max_i_neg <amp>` | Set maximum negative output current, clamped at [0, 2] |
|
||||||
| `pwm <0/1> max_v <volt>` | Set maximum output voltage, clamped to [0, 4] |
|
| `pwm <0/1> max_v <volt>` | Set maximum output voltage, clamped at [0, 4] |
|
||||||
| `pwm <0/1> i_set <amp>` | Disengage PID, set fixed output current, clamped to [-2, 2] |
|
| `pwm <0/1> i_set <amp>` | Disengage PID, set fixed output current, clamped at [-2, 2] |
|
||||||
| `pwm <0/1> pid` | Let output current to be controlled by the PID |
|
| `pwm <0/1> pid` | Let output current to be controlled by the PID |
|
||||||
| `center <0/1> <volt>` | Set the MAX1968 0A-centerpoint to the specified fixed voltage |
|
| `center <0/1> <volt>` | Set the MAX1968 0A-centerpoint to the specified fixed voltage |
|
||||||
| `center <0/1> vref` | Set the MAX1968 0A-centerpoint to measure from VREF |
|
| `center <0/1> vref` | Set the MAX1968 0A-centerpoint to measure from VREF |
|
||||||
@ -247,7 +251,8 @@ pwm 0 pid
|
|||||||
|
|
||||||
## Reports
|
## Reports
|
||||||
|
|
||||||
Use the bare `report` command to obtain a single report. Reports are JSON objects
|
Use the bare `report` command to obtain a single report. Enable
|
||||||
|
continuous reporting with `report mode on`. Reports are JSON objects
|
||||||
with the following keys.
|
with the following keys.
|
||||||
|
|
||||||
| Key | Unit | Description |
|
| Key | Unit | Description |
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
name = "thermostat-dev-shell";
|
name = "thermostat-dev-shell";
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
rust llvm
|
rust llvm
|
||||||
openocd dfu-util rlwrap
|
openocd dfu-util
|
||||||
] ++ (with python3Packages; [
|
] ++ (with python3Packages; [
|
||||||
numpy matplotlib
|
numpy matplotlib
|
||||||
]);
|
]);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import socket
|
import socket
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
|
||||||
|
|
||||||
class CommandError(Exception):
|
class CommandError(Exception):
|
||||||
pass
|
pass
|
||||||
@ -127,8 +126,9 @@ class Client:
|
|||||||
'tec_u_meas': 2.5340000000000003,
|
'tec_u_meas': 2.5340000000000003,
|
||||||
'pid_output': 2.067581958092247}
|
'pid_output': 2.067581958092247}
|
||||||
"""
|
"""
|
||||||
|
self._command("report mode", "on")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
self._socket.sendall("report\n".encode('utf-8'))
|
|
||||||
line = self._read_line()
|
line = self._read_line()
|
||||||
if not line:
|
if not line:
|
||||||
break
|
break
|
||||||
@ -136,7 +136,6 @@ class Client:
|
|||||||
yield json.loads(line)
|
yield json.loads(line)
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
pass
|
pass
|
||||||
time.sleep(0.05)
|
|
||||||
|
|
||||||
def set_param(self, topic, channel, field="", value=""):
|
def set_param(self, topic, channel, field="", value=""):
|
||||||
"""Set configuration parameters
|
"""Set configuration parameters
|
||||||
|
@ -22,7 +22,6 @@ use crate::{
|
|||||||
pins::{self, Channel0VRef, Channel1VRef},
|
pins::{self, Channel0VRef, Channel1VRef},
|
||||||
steinhart_hart,
|
steinhart_hart,
|
||||||
};
|
};
|
||||||
use crate::timer::sleep;
|
|
||||||
|
|
||||||
pub enum PinsAdcReadTarget {
|
pub enum PinsAdcReadTarget {
|
||||||
VREF,
|
VREF,
|
||||||
@ -45,11 +44,7 @@ pub const MAX_TEC_V: ElectricPotential = ElectricPotential {
|
|||||||
units: PhantomData,
|
units: PhantomData,
|
||||||
value: 4.0,
|
value: 4.0,
|
||||||
};
|
};
|
||||||
const MAX_TEC_I_DUTY_TO_CURRENT_RATE: ElectricCurrent = ElectricCurrent {
|
|
||||||
dimension: PhantomData,
|
|
||||||
units: PhantomData,
|
|
||||||
value: 1.0 / (10.0 * R_SENSE / 3.3),
|
|
||||||
};
|
|
||||||
// DAC chip outputs 0-5v, which is then passed through a resistor dividor to provide 0-3v range
|
// DAC chip outputs 0-5v, which is then passed through a resistor dividor to provide 0-3v range
|
||||||
const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential {
|
const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential {
|
||||||
dimension: PhantomData,
|
dimension: PhantomData,
|
||||||
@ -274,6 +269,17 @@ impl Channels {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_dac_feedback_until_stable(&mut self, channel: usize, tolerance: ElectricPotential) -> ElectricPotential {
|
||||||
|
let mut prev = self.adc_read(channel, PinsAdcReadTarget::DacVfb, 1);
|
||||||
|
loop {
|
||||||
|
let current = self.adc_read(channel, PinsAdcReadTarget::DacVfb, 1);
|
||||||
|
if (current - prev).abs() < tolerance {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
prev = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Calibrates the DAC output to match vref of the MAX driver to reduce zero-current offset of the MAX driver output.
|
/// Calibrates the DAC output to match vref of the MAX driver to reduce zero-current offset of the MAX driver output.
|
||||||
///
|
///
|
||||||
/// The thermostat DAC applies a control voltage signal to the CTLI pin of MAX driver chip to control its output current.
|
/// The thermostat DAC applies a control voltage signal to the CTLI pin of MAX driver chip to control its output current.
|
||||||
@ -300,7 +306,8 @@ impl Channels {
|
|||||||
let mut start_value = 1;
|
let mut start_value = 1;
|
||||||
let mut best_error = ElectricPotential::new::<volt>(100.0);
|
let mut best_error = ElectricPotential::new::<volt>(100.0);
|
||||||
|
|
||||||
for step in (5..18).rev() {
|
for step in (0..18).rev() {
|
||||||
|
let mut prev_value = start_value;
|
||||||
for value in (start_value..=ad5680::MAX_VALUE).step_by(1 << step) {
|
for value in (start_value..=ad5680::MAX_VALUE).step_by(1 << step) {
|
||||||
match channel {
|
match channel {
|
||||||
0 => {
|
0 => {
|
||||||
@ -311,15 +318,14 @@ impl Channels {
|
|||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
sleep(10);
|
|
||||||
|
|
||||||
let dac_feedback = self.adc_read(channel, PinsAdcReadTarget::DacVfb, 64);
|
let dac_feedback = self.read_dac_feedback_until_stable(channel, ElectricPotential::new::<volt>(0.001));
|
||||||
let error = target_voltage - dac_feedback;
|
let error = target_voltage - dac_feedback;
|
||||||
if error < ElectricPotential::new::<volt>(0.0) {
|
if error < ElectricPotential::new::<volt>(0.0) {
|
||||||
break;
|
break;
|
||||||
} else if error < best_error {
|
} else if error < best_error {
|
||||||
best_error = error;
|
best_error = error;
|
||||||
start_value = value;
|
start_value = prev_value;
|
||||||
|
|
||||||
let vref = (value as f64 / ad5680::MAX_VALUE as f64) * DAC_OUT_V_MAX;
|
let vref = (value as f64 / ad5680::MAX_VALUE as f64) * DAC_OUT_V_MAX;
|
||||||
match channel {
|
match channel {
|
||||||
@ -328,6 +334,8 @@ impl Channels {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prev_value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,19 +422,19 @@ impl Channels {
|
|||||||
pub fn set_max_i_pos(&mut self, channel: usize, max_i_pos: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
|
pub fn set_max_i_pos(&mut self, channel: usize, max_i_pos: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
let max = ElectricCurrent::new::<ampere>(3.0);
|
let max = ElectricCurrent::new::<ampere>(3.0);
|
||||||
let max_i_pos = max_i_pos.min(MAX_TEC_I).max(ElectricCurrent::zero());
|
let max_i_pos = max_i_pos.min(MAX_TEC_I).max(ElectricCurrent::zero());
|
||||||
let duty = (max_i_pos / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
let duty = (max_i_pos / max).get::<ratio>();
|
||||||
let duty = self.set_pwm(channel, PwmPin::MaxIPos, duty);
|
let duty = self.set_pwm(channel, PwmPin::MaxIPos, duty);
|
||||||
self.channel_state(channel).pwm_limits.max_i_pos = max_i_pos.get::<ampere>();
|
self.channel_state(channel).pwm_limits.max_i_pos = max_i_pos.get::<ampere>();
|
||||||
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, max)
|
(duty * max, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_max_i_neg(&mut self, channel: usize, max_i_neg: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
|
pub fn set_max_i_neg(&mut self, channel: usize, max_i_neg: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
let max = ElectricCurrent::new::<ampere>(3.0);
|
let max = ElectricCurrent::new::<ampere>(3.0);
|
||||||
let max_i_neg = max_i_neg.min(MAX_TEC_I).max(ElectricCurrent::zero());
|
let max_i_neg = max_i_neg.min(MAX_TEC_I).max(ElectricCurrent::zero());
|
||||||
let duty = (max_i_neg / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
let duty = (max_i_neg / max).get::<ratio>();
|
||||||
let duty = self.set_pwm(channel, PwmPin::MaxINeg, duty);
|
let duty = self.set_pwm(channel, PwmPin::MaxINeg, duty);
|
||||||
self.channel_state(channel).pwm_limits.max_i_neg = max_i_neg.get::<ampere>();
|
self.channel_state(channel).pwm_limits.max_i_neg = max_i_neg.get::<ampere>();
|
||||||
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, max)
|
(duty * max, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report(&mut self, channel: usize) -> Report {
|
fn report(&mut self, channel: usize) -> Report {
|
||||||
|
@ -22,6 +22,7 @@ use super::{
|
|||||||
config::ChannelConfig,
|
config::ChannelConfig,
|
||||||
dfu,
|
dfu,
|
||||||
flash_store::FlashStore,
|
flash_store::FlashStore,
|
||||||
|
session::Session,
|
||||||
FanCtrl,
|
FanCtrl,
|
||||||
hw_rev::HWRev,
|
hw_rev::HWRev,
|
||||||
};
|
};
|
||||||
@ -86,6 +87,16 @@ fn send_line(socket: &mut TcpSocket, data: &[u8]) -> bool {
|
|||||||
|
|
||||||
impl Handler {
|
impl Handler {
|
||||||
|
|
||||||
|
fn reporting(socket: &mut TcpSocket) -> Result<Handler, Error> {
|
||||||
|
send_line(socket, b"{}");
|
||||||
|
Ok(Handler::Handled)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_report_mode(socket: &mut TcpSocket, session: &Session) -> Result<Handler, Error> {
|
||||||
|
let _ = writeln!(socket, "{{ \"report\": {:?} }}", session.reporting());
|
||||||
|
Ok(Handler::Handled)
|
||||||
|
}
|
||||||
|
|
||||||
fn show_report(socket: &mut TcpSocket, channels: &mut Channels) -> Result<Handler, Error> {
|
fn show_report(socket: &mut TcpSocket, channels: &mut Channels) -> Result<Handler, Error> {
|
||||||
match channels.reports_json() {
|
match channels.reports_json() {
|
||||||
Ok(buf) => {
|
Ok(buf) => {
|
||||||
@ -401,9 +412,11 @@ impl Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_command(command: Command, socket: &mut TcpSocket, channels: &mut Channels, store: &mut FlashStore, ipv4_config: &mut Ipv4Config, fan_ctrl: &mut FanCtrl, hwrev: HWRev) -> Result<Self, Error> {
|
pub fn handle_command(command: Command, socket: &mut TcpSocket, channels: &mut Channels, session: &Session, store: &mut FlashStore, ipv4_config: &mut Ipv4Config, fan_ctrl: &mut FanCtrl, hwrev: HWRev) -> Result<Self, Error> {
|
||||||
match command {
|
match command {
|
||||||
Command::Quit => Ok(Handler::CloseSocket),
|
Command::Quit => Ok(Handler::CloseSocket),
|
||||||
|
Command::Reporting(_reporting) => Handler::reporting(socket),
|
||||||
|
Command::Show(ShowCommand::Reporting) => Handler::show_report_mode(socket, session),
|
||||||
Command::Show(ShowCommand::Input) => Handler::show_report(socket, channels),
|
Command::Show(ShowCommand::Input) => Handler::show_report(socket, channels),
|
||||||
Command::Show(ShowCommand::Pid) => Handler::show_pid(socket, channels),
|
Command::Show(ShowCommand::Pid) => Handler::show_pid(socket, channels),
|
||||||
Command::Show(ShowCommand::Pwm) => Handler::show_pwm(socket, channels),
|
Command::Show(ShowCommand::Pwm) => Handler::show_pwm(socket, channels),
|
||||||
|
@ -96,6 +96,7 @@ pub struct Ipv4Config {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ShowCommand {
|
pub enum ShowCommand {
|
||||||
Input,
|
Input,
|
||||||
|
Reporting,
|
||||||
Pwm,
|
Pwm,
|
||||||
Pid,
|
Pid,
|
||||||
SteinhartHart,
|
SteinhartHart,
|
||||||
@ -147,6 +148,7 @@ pub enum Command {
|
|||||||
Reset,
|
Reset,
|
||||||
Ipv4(Ipv4Config),
|
Ipv4(Ipv4Config),
|
||||||
Show(ShowCommand),
|
Show(ShowCommand),
|
||||||
|
Reporting(bool),
|
||||||
/// PWM parameter setting
|
/// PWM parameter setting
|
||||||
Pwm {
|
Pwm {
|
||||||
channel: usize,
|
channel: usize,
|
||||||
@ -231,6 +233,12 @@ fn float(input: &[u8]) -> IResult<&[u8], Result<f64, Error>> {
|
|||||||
Ok((input, result))
|
Ok((input, result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn off_on(input: &[u8]) -> IResult<&[u8], bool> {
|
||||||
|
alt((value(false, tag("off")),
|
||||||
|
value(true, tag("on"))
|
||||||
|
))(input)
|
||||||
|
}
|
||||||
|
|
||||||
fn channel(input: &[u8]) -> IResult<&[u8], usize> {
|
fn channel(input: &[u8]) -> IResult<&[u8], usize> {
|
||||||
map(one_of("01"), |c| (c as usize) - ('0' as usize))(input)
|
map(one_of("01"), |c| (c as usize) - ('0' as usize))(input)
|
||||||
}
|
}
|
||||||
@ -238,8 +246,24 @@ fn channel(input: &[u8]) -> IResult<&[u8], usize> {
|
|||||||
fn report(input: &[u8]) -> IResult<&[u8], Command> {
|
fn report(input: &[u8]) -> IResult<&[u8], Command> {
|
||||||
preceded(
|
preceded(
|
||||||
tag("report"),
|
tag("report"),
|
||||||
// `report` - Report once
|
alt((
|
||||||
value(Command::Show(ShowCommand::Input), end)
|
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)
|
)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,6 +682,24 @@ mod test {
|
|||||||
assert_eq!(command, Ok(Command::Show(ShowCommand::Input)));
|
assert_eq!(command, Ok(Command::Show(ShowCommand::Input)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_report_mode() {
|
||||||
|
let command = Command::parse(b"report mode");
|
||||||
|
assert_eq!(command, Ok(Command::Show(ShowCommand::Reporting)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_report_mode_on() {
|
||||||
|
let command = Command::parse(b"report mode on");
|
||||||
|
assert_eq!(command, Ok(Command::Reporting(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_report_mode_off() {
|
||||||
|
let command = Command::parse(b"report mode off");
|
||||||
|
assert_eq!(command, Ok(Command::Reporting(false)));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_pwm_i_set() {
|
fn parse_pwm_i_set() {
|
||||||
let command = Command::parse(b"pwm 1 i_set 16383");
|
let command = Command::parse(b"pwm 1 i_set 16383");
|
||||||
|
20
src/main.rs
20
src/main.rs
@ -180,7 +180,10 @@ fn main() -> ! {
|
|||||||
loop {
|
loop {
|
||||||
let mut new_ipv4_config = None;
|
let mut new_ipv4_config = None;
|
||||||
let instant = Instant::from_millis(i64::from(timer::now()));
|
let instant = Instant::from_millis(i64::from(timer::now()));
|
||||||
channels.poll_adc(instant);
|
let updated_channel = channels.poll_adc(instant);
|
||||||
|
if let Some(channel) = updated_channel {
|
||||||
|
server.for_each(|_, session| session.set_report_pending(channel.into()));
|
||||||
|
}
|
||||||
|
|
||||||
fan_ctrl.cycle(channels.current_abs_max_tec_i());
|
fan_ctrl.cycle(channels.current_abs_max_tec_i());
|
||||||
|
|
||||||
@ -213,7 +216,7 @@ fn main() -> ! {
|
|||||||
// Do nothing and feed more data to the line reader in the next loop cycle.
|
// Do nothing and feed more data to the line reader in the next loop cycle.
|
||||||
Ok(SessionInput::Nothing) => {}
|
Ok(SessionInput::Nothing) => {}
|
||||||
Ok(SessionInput::Command(command)) => {
|
Ok(SessionInput::Command(command)) => {
|
||||||
match Handler::handle_command(command, &mut socket, &mut channels, &mut store, &mut ipv4_config, &mut fan_ctrl, hwrev) {
|
match Handler::handle_command(command, &mut socket, &mut channels, session, &mut store, &mut ipv4_config, &mut fan_ctrl, hwrev) {
|
||||||
Ok(Handler::NewIPV4(ip)) => new_ipv4_config = Some(ip),
|
Ok(Handler::NewIPV4(ip)) => new_ipv4_config = Some(ip),
|
||||||
Ok(Handler::Handled) => {},
|
Ok(Handler::Handled) => {},
|
||||||
Ok(Handler::CloseSocket) => socket.close(),
|
Ok(Handler::CloseSocket) => socket.close(),
|
||||||
@ -228,6 +231,19 @@ fn main() -> ! {
|
|||||||
Err(_) =>
|
Err(_) =>
|
||||||
socket.close(),
|
socket.close(),
|
||||||
}
|
}
|
||||||
|
} else if socket.can_send() {
|
||||||
|
if let Some(channel) = session.is_report_pending() {
|
||||||
|
match channels.reports_json() {
|
||||||
|
Ok(buf) => {
|
||||||
|
send_line(&mut socket, &buf[..]);
|
||||||
|
session.mark_report_sent(channel);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("unable to serialize report: {:?}", e);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::command_parser::{Command, Error as ParserError};
|
use super::command_parser::{Command, Error as ParserError};
|
||||||
|
use super::channels::CHANNELS;
|
||||||
|
|
||||||
const MAX_LINE_LEN: usize = 64;
|
const MAX_LINE_LEN: usize = 64;
|
||||||
|
|
||||||
@ -52,6 +53,8 @@ impl From<Result<Command, ParserError>> for SessionInput {
|
|||||||
|
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
reader: LineReader,
|
reader: LineReader,
|
||||||
|
reporting: bool,
|
||||||
|
report_pending: [bool; CHANNELS],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Session {
|
impl Default for Session {
|
||||||
@ -64,11 +67,43 @@ impl Session {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Session {
|
Session {
|
||||||
reader: LineReader::new(),
|
reader: LineReader::new(),
|
||||||
|
reporting: false,
|
||||||
|
report_pending: [false; CHANNELS],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.reader = LineReader::new();
|
self.reader = LineReader::new();
|
||||||
|
self.reporting = false;
|
||||||
|
self.report_pending = [false; CHANNELS];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reporting(&self) -> bool {
|
||||||
|
self.reporting
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_report_pending(&mut self, channel: usize) {
|
||||||
|
if self.reporting {
|
||||||
|
self.report_pending[channel] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_report_pending(&self) -> Option<usize> {
|
||||||
|
if ! self.reporting {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.report_pending.iter()
|
||||||
|
.enumerate()
|
||||||
|
.fold(None, |result, (channel, report_pending)| {
|
||||||
|
result.or_else(|| {
|
||||||
|
if *report_pending { Some(channel) } else { None }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_report_sent(&mut self, channel: usize) {
|
||||||
|
self.report_pending[channel] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn feed(&mut self, buf: &[u8]) -> (usize, SessionInput) {
|
pub fn feed(&mut self, buf: &[u8]) -> (usize, SessionInput) {
|
||||||
@ -79,6 +114,12 @@ impl Session {
|
|||||||
match line {
|
match line {
|
||||||
Some(line) => {
|
Some(line) => {
|
||||||
let command = Command::parse(&line);
|
let command = Command::parse(&line);
|
||||||
|
match command {
|
||||||
|
Ok(Command::Reporting(reporting)) => {
|
||||||
|
self.reporting = reporting;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
return (buf_bytes, command.into());
|
return (buf_bytes, command.into());
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
|
Loading…
Reference in New Issue
Block a user