Compare commits

..

3 Commits

Author SHA1 Message Date
0940520ded README: Document swap status in report 2024-09-23 18:04:06 +08:00
2c6b8e8186 add swap status to the report
Signed-off-by: Egor Savkin <es@m-labs.hk>
2024-09-23 18:04:06 +08:00
8f8baa0cee Add command for flipping output polarity
Effectively switches all values related to the directionality of current
through the TEC, including current maximums. The swapped status is
stored in the flash store.

To swap current directions, use the command "pwm <ch> polarity_swapped
<bool>", where <bool> is true when TEC current direction is reversed
from the front panel markings.

This is needed for IDC cable connections, since the IDC connector and
front panel connectors have flipped polarities.
2024-09-23 18:04:04 +08:00
9 changed files with 245 additions and 145 deletions

View File

@ -84,7 +84,9 @@ invalidate the first line of 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
@ -102,7 +104,7 @@ formatted as line-delimited JSON.
| `pwm <0/1> max_i_neg <amp>` | Set maximum negative output current |
| `pwm <0/1> max_v <volt>` | Set maximum output voltage |
| `pwm <0/1> i_set <amp>` | Disengage PID, set fixed output current |
| `pwm <0/1> polarity <normal/reversed>` | Set output current polarity, with 'normal' being the front panel polarity |
| `pwm <0/1> polarity_swapped <false/true>` | Swap output current polarity on channel |
| `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> vref` | Set the MAX1968 0A-centerpoint to measure from VREF |
@ -185,8 +187,6 @@ postfilter rate can be tuned with the `postfilter` command.
When using a TEC module with the Thermostat, the Thermostat expects the thermal load (where the thermistor is located) to cool down with a positive software current set point, and heat up with a negative current set point.
If the Thermostat is used for temperature control with the Sinara 5432 DAC "Zotino", and is connected via an IDC cable, the TEC polarity may need to be reversed with the `pwm <ch> polarity reversed` TCP command.
Testing heat flow direction with a low set current is recommended before installation of the TEC module.
### Limits
@ -252,7 +252,8 @@ pwm 0 pid
## 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.
| Key | Unit | Description |
@ -263,6 +264,7 @@ with the following keys.
| `sens` | Ohms | Thermistor resistance derived from `adc` |
| `temperature` | Degrees Celsius | Steinhart-Hart conversion result derived from `sens` |
| `pid_engaged` | Boolean | `true` if in closed-loop mode |
| `current_swapped` | Boolean | `true` if TEC current direction is swapped relative to front panel |
| `i_set` | Amperes | TEC output current |
| `dac_value` | Volts | AD5680 output derived from `i_set` |
| `dac_feedback` | Volts | ADC measurement of the AD5680 output |

View File

@ -1,7 +1,6 @@
import socket
import json
import logging
import time
class CommandError(Exception):
pass
@ -57,14 +56,12 @@ class Client:
'max_i_neg': {'max': 3.0, 'value': 3.0},
'max_v': {'max': 5.988, 'value': 5.988},
'max_i_pos': {'max': 3.0, 'value': 3.0}},
'polarity': 'normal',
{'channel': 1,
'center': 'vref',
'i_set': {'max': 2.9802790335151985, 'value': -0.02002179650216762},
'max_i_neg': {'max': 3.0, 'value': 3.0},
'max_v': {'max': 5.988, 'value': 5.988},
'max_i_pos': {'max': 3.0, 'value': 3.0}}
'polarity': 'normal',
]
"""
return self._get_conf("pwm")
@ -129,8 +126,9 @@ class Client:
'tec_u_meas': 2.5340000000000003,
'pid_output': 2.067581958092247}
"""
self._command("report mode", "on")
while True:
self._socket.sendall("report\n".encode('utf-8'))
line = self._read_line()
if not line:
break
@ -138,7 +136,6 @@ class Client:
yield json.loads(line)
except json.decoder.JSONDecodeError:
pass
time.sleep(0.05)
def set_param(self, topic, channel, field="", value=""):
"""Set configuration parameters

View File

@ -17,7 +17,7 @@ use crate::{
ad7172,
pid,
steinhart_hart as sh,
command_parser::{CenterPoint, Polarity},
command_parser::CenterPoint,
};
const R_INNER: f64 = 2.0 * 5100.0;
@ -35,7 +35,7 @@ pub struct ChannelState {
pub pid_engaged: bool,
pub pid: pid::Controller,
pub sh: sh::Parameters,
pub polarity: Polarity,
pub polarity_swapped: bool,
}
impl ChannelState {
@ -52,7 +52,7 @@ impl ChannelState {
pid_engaged: false,
pid: pid::Controller::new(pid::Parameters::default()),
sh: sh::Parameters::default(),
polarity: Polarity::Normal,
polarity_swapped: false,
}
}

View File

@ -17,7 +17,7 @@ use crate::{
ad7172,
channel::{Channel, Channel0, Channel1},
channel_state::ChannelState,
command_parser::{CenterPoint, PwmPin, Polarity},
command_parser::{CenterPoint, PwmPin},
command_handler::JsonBuffer,
pins::{self, Channel0VRef, Channel1VRef},
steinhart_hart,
@ -45,11 +45,7 @@ pub const MAX_TEC_V: ElectricPotential = ElectricPotential {
units: PhantomData,
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
const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential {
dimension: PhantomData,
@ -159,9 +155,10 @@ impl Channels {
pub fn set_i(&mut self, channel: usize, i_set: ElectricCurrent) -> ElectricCurrent {
let i_set = i_set.min(MAX_TEC_I).max(-MAX_TEC_I);
self.channel_state(channel).i_set = i_set;
let negate = match self.channel_state(channel).polarity {
Polarity::Normal => 1.0,
Polarity::Reversed => -1.0,
let negate = if self.channel_state(channel).polarity_swapped {
-1.0
} else {
1.0
};
let vref_meas = match channel.into() {
0 => self.channel0.vref_meas,
@ -305,6 +302,7 @@ impl Channels {
let mut best_error = ElectricPotential::new::<volt>(100.0);
for step in (5..18).rev() {
let mut prev_value = start_value;
for value in (start_value..=ad5680::MAX_VALUE).step_by(1 << step) {
match channel {
0 => {
@ -323,7 +321,7 @@ impl Channels {
break;
} else if error < best_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;
match channel {
@ -332,6 +330,8 @@ impl Channels {
_ => unreachable!(),
}
}
prev_value = value;
}
}
@ -390,27 +390,32 @@ impl Channels {
}
pub fn get_max_i_pos(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
let duty = match self.channel_state(channel).polarity {
Polarity::Normal => self.get_pwm(channel, PwmPin::MaxIPos),
Polarity::Reversed => self.get_pwm(channel, PwmPin::MaxINeg),
let max = ElectricCurrent::new::<ampere>(3.0);
let duty = if self.channel_state(channel).polarity_swapped {
self.get_pwm(channel, PwmPin::MaxINeg)
} else {
self.get_pwm(channel, PwmPin::MaxIPos)
};
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, MAX_TEC_I)
(duty * max, MAX_TEC_I)
}
pub fn get_max_i_neg(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
let duty = match self.channel_state(channel).polarity {
Polarity::Normal => self.get_pwm(channel, PwmPin::MaxINeg),
Polarity::Reversed => self.get_pwm(channel, PwmPin::MaxIPos),
let max = ElectricCurrent::new::<ampere>(3.0);
let duty = if self.channel_state(channel).polarity_swapped {
self.get_pwm(channel, PwmPin::MaxIPos)
} else {
self.get_pwm(channel, PwmPin::MaxINeg)
};
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, MAX_TEC_I)
(duty * max, MAX_TEC_I)
}
// Get current passing through TEC
pub fn get_tec_i(&mut self, channel: usize) -> ElectricCurrent {
let tec_i = (self.adc_read(channel, PinsAdcReadTarget::ITec, 16) - self.adc_read(channel, PinsAdcReadTarget::VREF, 16)) / ElectricalResistance::new::<ohm>(0.4);
match self.channel_state(channel).polarity {
Polarity::Normal => tec_i,
Polarity::Reversed => -tec_i,
if self.channel_state(channel).polarity_swapped {
-tec_i
} else {
tec_i
}
}
@ -455,31 +460,33 @@ impl Channels {
pub fn set_max_i_pos(&mut self, channel: usize, max_i_pos: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
let max = ElectricCurrent::new::<ampere>(3.0);
let duty = (max_i_pos.min(MAX_TEC_I).max(ElectricCurrent::zero()) / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>();
let duty = match self.channel_state(channel).polarity {
Polarity::Normal => self.set_pwm(channel, PwmPin::MaxIPos, duty),
Polarity::Reversed => self.set_pwm(channel, PwmPin::MaxINeg, duty),
let duty = (max_i_pos.min(MAX_TEC_I).max(ElectricCurrent::zero()) / max).get::<ratio>();
let duty = if self.channel_state(channel).polarity_swapped {
self.set_pwm(channel, PwmPin::MaxINeg, duty)
} else {
self.set_pwm(channel, PwmPin::MaxIPos, duty)
};
(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) {
let max = ElectricCurrent::new::<ampere>(3.0);
let duty = (max_i_neg.min(MAX_TEC_I).max(ElectricCurrent::zero()) / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>();
let duty = match self.channel_state(channel).polarity {
Polarity::Normal => self.set_pwm(channel, PwmPin::MaxINeg, duty),
Polarity::Reversed => self.set_pwm(channel, PwmPin::MaxIPos, duty),
let duty = (max_i_neg.min(MAX_TEC_I).max(ElectricCurrent::zero()) / max).get::<ratio>();
let duty = if self.channel_state(channel).polarity_swapped {
self.set_pwm(channel, PwmPin::MaxIPos, duty)
} else {
self.set_pwm(channel, PwmPin::MaxINeg, duty)
};
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, max)
(duty * max, max)
}
pub fn set_polarity(&mut self, channel: usize, polarity: Polarity) {
if self.channel_state(channel).polarity != polarity {
pub fn swap_polarity(&mut self, channel: usize, swapped: bool) {
if self.channel_state(channel).polarity_swapped != swapped {
let i_set = self.channel_state(channel).i_set;
let max_i_pos = self.get_max_i_pos(channel).0;
let max_i_neg = self.get_max_i_neg(channel).0;
self.channel_state(channel).polarity = polarity;
self.channel_state(channel).polarity_swapped = swapped;
self.set_i(channel, i_set);
self.set_max_i_pos(channel, max_i_pos);
self.set_max_i_neg(channel, max_i_neg);
@ -502,6 +509,7 @@ impl Channels {
temperature: state.get_temperature()
.map(|temperature| temperature.get::<degree_celsius>()),
pid_engaged: state.pid_engaged,
current_swapped: state.polarity_swapped,
i_set,
dac_value,
dac_feedback: self.adc_read(channel, PinsAdcReadTarget::DacVfb, 1),
@ -545,7 +553,6 @@ impl Channels {
max_v: self.get_max_v(channel).into(),
max_i_pos: self.get_max_i_pos(channel).into(),
max_i_neg: self.get_max_i_neg(channel).into(),
polarity: PolarityJson(self.channel_state(channel).polarity.clone()),
}
}
@ -601,6 +608,7 @@ pub struct Report {
sens: Option<ElectricalResistance>,
temperature: Option<f64>,
pid_engaged: bool,
current_swapped: bool,
i_set: ElectricCurrent,
dac_value: ElectricPotential,
dac_feedback: ElectricPotential,
@ -627,21 +635,6 @@ impl Serialize for CenterPointJson {
}
}
pub struct PolarityJson(Polarity);
// used in JSON encoding, not for config
impl Serialize for PolarityJson {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(match self.0 {
Polarity::Normal => "normal",
Polarity::Reversed => "reversed",
})
}
}
#[derive(Serialize)]
pub struct PwmSummaryField<T: Serialize> {
value: T,
@ -662,7 +655,6 @@ pub struct PwmSummary {
max_v: PwmSummaryField<ElectricPotential>,
max_i_pos: PwmSummaryField<ElectricCurrent>,
max_i_neg: PwmSummaryField<ElectricCurrent>,
polarity: PolarityJson,
}
#[derive(Serialize)]

View File

@ -11,8 +11,7 @@ use super::{
CenterPoint,
PidParameter,
PwmPin,
ShParameter,
Polarity
ShParameter
},
ad7172,
CHANNEL_CONFIG_KEY,
@ -23,6 +22,7 @@ use super::{
config::ChannelConfig,
dfu,
flash_store::FlashStore,
session::Session,
FanCtrl,
hw_rev::HWRev,
};
@ -87,6 +87,16 @@ fn send_line(socket: &mut TcpSocket, data: &[u8]) -> bool {
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> {
match channels.reports_json() {
Ok(buf) => {
@ -171,8 +181,8 @@ impl Handler {
Ok(Handler::Handled)
}
fn set_polarity(socket: &mut TcpSocket, channels: &mut Channels, channel: usize, polarity: Polarity) -> Result<Handler, Error> {
channels.set_polarity(channel, polarity);
fn swap_polarity (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, swapped: bool) -> Result<Handler, Error> {
channels.swap_polarity(channel, swapped);
send_line(socket, b"{}");
Ok(Handler::Handled)
}
@ -408,9 +418,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 {
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::Pid) => Handler::show_pid(socket, channels),
Command::Show(ShowCommand::Pwm) => Handler::show_pwm(socket, channels),
@ -418,7 +430,7 @@ impl Handler {
Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, channels),
Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config),
Command::PwmPid { channel } => Handler::engage_pid(socket, channels, channel),
Command::PwmPolarity { channel, polarity } => Handler::set_polarity(socket, channels, channel, polarity),
Command::PwmPolaritySwapped { channel, swapped } => Handler::swap_polarity(socket, channels, channel, swapped),
Command::Pwm { channel, pin, value } => Handler::set_pwm(socket, channels, channel, pin, value),
Command::CenterPoint { channel, center } => Handler::set_center_point(socket, channels, channel, center),
Command::Pid { channel, parameter, value } => Handler::set_pid(socket, channels, channel, parameter, value),

View File

@ -96,6 +96,7 @@ pub struct Ipv4Config {
#[derive(Debug, Clone, PartialEq)]
pub enum ShowCommand {
Input,
Reporting,
Pwm,
Pid,
SteinhartHart,
@ -135,12 +136,6 @@ pub enum CenterPoint {
Override(f32),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Polarity {
Normal,
Reversed,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Command {
Quit,
@ -153,6 +148,7 @@ pub enum Command {
Reset,
Ipv4(Ipv4Config),
Show(ShowCommand),
Reporting(bool),
/// PWM parameter setting
Pwm {
channel: usize,
@ -163,9 +159,9 @@ pub enum Command {
PwmPid {
channel: usize,
},
PwmPolarity {
PwmPolaritySwapped {
channel: usize,
polarity: Polarity,
swapped: bool,
},
CenterPoint {
channel: usize,
@ -241,6 +237,18 @@ fn float(input: &[u8]) -> IResult<&[u8], Result<f64, Error>> {
Ok((input, result))
}
fn off_on(input: &[u8]) -> IResult<&[u8], bool> {
alt((value(false, tag("off")),
value(true, tag("on"))
))(input)
}
fn boolean(input: &[u8]) -> IResult<&[u8], bool> {
alt((value(false, tag("false")),
value(true, tag("true"))
))(input)
}
fn channel(input: &[u8]) -> IResult<&[u8], usize> {
map(one_of("01"), |c| (c as usize) - ('0' as usize))(input)
}
@ -248,8 +256,24 @@ fn channel(input: &[u8]) -> IResult<&[u8], usize> {
fn report(input: &[u8]) -> IResult<&[u8], Command> {
preceded(
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)
}
@ -307,14 +331,12 @@ fn pwm_pid(input: &[u8]) -> IResult<&[u8], ()> {
value((), tag("pid"))(input)
}
fn pwm_polarity(input: &[u8]) -> IResult<&[u8], Polarity> {
fn pwm_polarity_swapped(input: &[u8]) -> IResult<&[u8], bool> {
preceded(
tag("polarity"),
tag("polarity_swapped"),
preceded(
whitespace,
alt((value(Polarity::Normal, tag("normal")),
value(Polarity::Reversed, tag("reversed"))
))
boolean,
)
)(input)
}
@ -332,8 +354,8 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
Ok((input, Ok(Command::PwmPid { channel })))
},
|input| {
let (input, polarity) = pwm_polarity(input)?;
Ok((input, Ok(Command::PwmPolarity { channel, polarity })))
let (input, swapped) = pwm_polarity_swapped(input)?;
Ok((input, Ok(Command::PwmPolaritySwapped { channel, swapped })))
},
|input| {
let (input, config) = pwm_setup(input)?;
@ -684,6 +706,24 @@ mod test {
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]
fn parse_pwm_i_set() {
let command = Command::parse(b"pwm 1 i_set 16383");

View File

@ -8,7 +8,7 @@ use uom::si::{
use crate::{
ad7172::PostFilter,
channels::Channels,
command_parser::{CenterPoint, Polarity},
command_parser::CenterPoint,
pid,
steinhart_hart,
};
@ -20,7 +20,7 @@ pub struct ChannelConfig {
pid_target: f32,
pid_engaged: bool,
i_set: ElectricCurrent,
polarity: Polarity,
polarity_swapped: bool,
sh: steinhart_hart::Parameters,
pwm: PwmLimits,
/// uses variant `PostFilter::Invalid` instead of `None` to save space
@ -47,7 +47,7 @@ impl ChannelConfig {
pid_target: state.pid.target as f32,
pid_engaged: state.pid_engaged,
i_set,
polarity: state.polarity.clone(),
polarity_swapped: state.polarity_swapped,
sh: state.sh.clone(),
pwm,
adc_postfilter,
@ -70,7 +70,7 @@ impl ChannelConfig {
};
let _ = channels.adc.set_postfilter(channel as u8, adc_postfilter);
let _ = channels.set_i(channel, self.i_set);
channels.set_polarity(channel, self.polarity.clone());
channels.swap_polarity(channel, self.polarity_swapped);
}
}

View File

@ -58,7 +58,7 @@ mod hw_rev;
const HSE: MegaHertz = MegaHertz(8);
#[cfg(not(feature = "semihosting"))]
const WATCHDOG_INTERVAL: u32 = 1_000;
const WATCHDOG_INTERVAL: u32 = 2_000;
#[cfg(feature = "semihosting")]
const WATCHDOG_INTERVAL: u32 = 30_000;
@ -180,7 +180,10 @@ fn main() -> ! {
loop {
let mut new_ipv4_config = None;
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());
@ -213,7 +216,7 @@ fn main() -> ! {
// Do nothing and feed more data to the line reader in the next loop cycle.
Ok(SessionInput::Nothing) => {}
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::Handled) => {},
Ok(Handler::CloseSocket) => socket.close(),
@ -228,6 +231,19 @@ fn main() -> ! {
Err(_) =>
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 {

View File

@ -1,4 +1,5 @@
use super::command_parser::{Command, Error as ParserError};
use super::channels::CHANNELS;
const MAX_LINE_LEN: usize = 64;
@ -52,6 +53,8 @@ impl From<Result<Command, ParserError>> for SessionInput {
pub struct Session {
reader: LineReader,
reporting: bool,
report_pending: [bool; CHANNELS],
}
impl Default for Session {
@ -64,11 +67,43 @@ impl Session {
pub fn new() -> Self {
Session {
reader: LineReader::new(),
reporting: false,
report_pending: [false; CHANNELS],
}
}
pub fn reset(&mut self) {
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) {
@ -79,6 +114,12 @@ impl Session {
match line {
Some(line) => {
let command = Command::parse(&line);
match command {
Ok(Command::Reporting(reporting)) => {
self.reporting = reporting;
}
_ => {}
}
return (buf_bytes, command.into());
}
None => {}