Compare commits

..

No commits in common. "d8e590ee9f4eb2ff49a21c8376516bcb03948737" and "680193b34b6dc24689e623dde8336f296ea4b189" have entirely different histories.

9 changed files with 182 additions and 164 deletions

View File

@ -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
@ -92,43 +94,42 @@ ADC input data is provided in reports. Query for the latest report with the comm
Send commands as simple text string terminated by `\n`. Responses are Send commands as simple text string terminated by `\n`. Responses are
formatted as line-delimited JSON. 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` | Show current report mode |
| `report mode <off/on>` | Set 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 | | `pwm <0/1> max_i_pos <amp>` | Set maximum positive output current |
| `pwm <0/1> max_i_neg <amp>` | Set maximum negative output current | | `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> max_v <volt>` | Set maximum output voltage |
| `pwm <0/1> i_set <amp>` | Disengage PID, set fixed output current | | `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> 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 | | `pid` | Show PID configuration |
| `pid` | Show PID configuration | | `pid <0/1> target <deg_celsius>` | Set the PID controller target temperature |
| `pid <0/1> target <deg_celsius>` | Set the PID controller target temperature | | `pid <0/1> kp <value>` | Set proportional gain |
| `pid <0/1> kp <value>` | Set proportional gain | | `pid <0/1> ki <value>` | Set integral gain |
| `pid <0/1> ki <value>` | Set integral gain | | `pid <0/1> kd <value>` | Set differential gain |
| `pid <0/1> kd <value>` | Set differential gain | | `pid <0/1> output_min <amp>` | Set mininum output |
| `pid <0/1> output_min <amp>` | Set mininum output | | `pid <0/1> output_max <amp>` | Set maximum output |
| `pid <0/1> output_max <amp>` | Set maximum output | | `s-h` | Show Steinhart-Hart equation parameters |
| `s-h` | Show Steinhart-Hart equation parameters | | `s-h <0/1> <t0/b/r0> <value>` | Set Steinhart-Hart parameter for a channel |
| `s-h <0/1> <t0/b/r0> <value>` | Set Steinhart-Hart parameter for a channel | | `postfilter` | Show postfilter settings |
| `postfilter` | Show postfilter settings | | `postfilter <0/1> off` | Disable postfilter |
| `postfilter <0/1> off` | Disable postfilter | | `postfilter <0/1> rate <rate>` | Set postfilter output data rate |
| `postfilter <0/1> rate <rate>` | Set postfilter output data rate | | `load [0/1]` | Restore configuration for channel all/0/1 from flash |
| `load [0/1]` | Restore configuration for channel all/0/1 from flash | | `save [0/1]` | Save configuration for channel all/0/1 to flash |
| `save [0/1]` | Save configuration for channel all/0/1 to flash | | `reset` | Reset the device |
| `reset` | Reset the device | | `dfu` | Reset device and enters USB device firmware update (DFU) mode |
| `dfu` | Reset device and enters USB device firmware update (DFU) mode | | `ipv4 <X.X.X.X/L> [Y.Y.Y.Y]` | Configure IPv4 address, netmask length, and optional default gateway |
| `ipv4 <X.X.X.X/L> [Y.Y.Y.Y]` | Configure IPv4 address, netmask length, and optional default gateway | | `fan` | Show current fan settings and sensors' measurements |
| `fan` | Show current fan settings and sensors' measurements | | `fan <value>` | Set fan power with values from 1 to 100 |
| `fan <value>` | Set fan power with values from 1 to 100 | | `fan auto` | Enable automatic fan speed control |
| `fan auto` | Enable automatic fan speed control | | `fcurve <a> <b> <c>` | Set fan controller curve coefficients (see *Fan control* section) |
| `fcurve <a> <b> <c>` | Set fan controller curve coefficients (see *Fan control* section) | | `fcurve default` | Set fan controller curve coefficients to defaults (see *Fan control* section) |
| `fcurve default` | Set fan controller curve coefficients to defaults (see *Fan control* section) | | `hwrev` | Show hardware revision, and settings related to it |
| `hwrev` | Show hardware revision, and settings related to it |
## USB ## USB
@ -185,8 +186,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. 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. Testing heat flow direction with a low set current is recommended before installation of the TEC module.
### Limits ### Limits
@ -252,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 |

View File

@ -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
@ -57,14 +56,12 @@ class Client:
'max_i_neg': {'max': 3.0, 'value': 3.0}, 'max_i_neg': {'max': 3.0, 'value': 3.0},
'max_v': {'max': 5.988, 'value': 5.988}, 'max_v': {'max': 5.988, 'value': 5.988},
'max_i_pos': {'max': 3.0, 'value': 3.0}}, 'max_i_pos': {'max': 3.0, 'value': 3.0}},
'polarity': 'normal',
{'channel': 1, {'channel': 1,
'center': 'vref', 'center': 'vref',
'i_set': {'max': 2.9802790335151985, 'value': -0.02002179650216762}, 'i_set': {'max': 2.9802790335151985, 'value': -0.02002179650216762},
'max_i_neg': {'max': 3.0, 'value': 3.0}, 'max_i_neg': {'max': 3.0, 'value': 3.0},
'max_v': {'max': 5.988, 'value': 5.988}, 'max_v': {'max': 5.988, 'value': 5.988},
'max_i_pos': {'max': 3.0, 'value': 3.0}} 'max_i_pos': {'max': 3.0, 'value': 3.0}}
'polarity': 'normal',
] ]
""" """
return self._get_conf("pwm") return self._get_conf("pwm")
@ -129,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
@ -138,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

View File

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

View File

@ -17,7 +17,7 @@ use crate::{
ad7172, ad7172,
channel::{Channel, Channel0, Channel1}, channel::{Channel, Channel0, Channel1},
channel_state::ChannelState, channel_state::ChannelState,
command_parser::{CenterPoint, PwmPin, Polarity}, command_parser::{CenterPoint, PwmPin},
command_handler::JsonBuffer, command_handler::JsonBuffer,
pins::{self, Channel0VRef, Channel1VRef}, pins::{self, Channel0VRef, Channel1VRef},
steinhart_hart, steinhart_hart,
@ -45,11 +45,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,
@ -158,11 +154,6 @@ impl Channels {
pub fn set_i(&mut self, channel: usize, i_set: ElectricCurrent) -> ElectricCurrent { 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); 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 vref_meas = match channel.into() { let vref_meas = match channel.into() {
0 => self.channel0.vref_meas, 0 => self.channel0.vref_meas,
1 => self.channel1.vref_meas, 1 => self.channel1.vref_meas,
@ -170,9 +161,10 @@ impl Channels {
}; };
let center_point = vref_meas; let center_point = vref_meas;
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE); let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
let voltage = negate * i_set * 10.0 * r_sense + center_point; let voltage = i_set * 10.0 * r_sense + center_point;
let voltage = self.set_dac(channel, voltage); let voltage = self.set_dac(channel, voltage);
let i_set = negate * (voltage - center_point) / (10.0 * r_sense); let i_set = (voltage - center_point) / (10.0 * r_sense);
self.channel_state(channel).i_set = i_set;
i_set i_set
} }
@ -390,28 +382,20 @@ impl Channels {
} }
pub fn get_max_i_pos(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) { pub fn get_max_i_pos(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
let duty = match self.channel_state(channel).polarity { let max = ElectricCurrent::new::<ampere>(3.0);
Polarity::Normal => self.get_pwm(channel, PwmPin::MaxIPos), let duty = self.get_pwm(channel, PwmPin::MaxIPos);
Polarity::Reversed => self.get_pwm(channel, PwmPin::MaxINeg), (duty * max, MAX_TEC_I)
};
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, MAX_TEC_I)
} }
pub fn get_max_i_neg(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) { pub fn get_max_i_neg(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
let duty = match self.channel_state(channel).polarity { let max = ElectricCurrent::new::<ampere>(3.0);
Polarity::Normal => self.get_pwm(channel, PwmPin::MaxINeg), let duty = self.get_pwm(channel, PwmPin::MaxINeg);
Polarity::Reversed => self.get_pwm(channel, PwmPin::MaxIPos), (duty * max, MAX_TEC_I)
};
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, MAX_TEC_I)
} }
// Get current passing through TEC // Get current passing through TEC
pub fn get_tec_i(&mut self, channel: usize) -> ElectricCurrent { 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); (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,
}
} }
// Get voltage across TEC // Get voltage across TEC
@ -455,35 +439,16 @@ 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 duty = (max_i_pos.min(MAX_TEC_I).max(ElectricCurrent::zero()) / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>(); let duty = (max_i_pos.min(MAX_TEC_I).max(ElectricCurrent::zero()) / max).get::<ratio>();
let duty = match self.channel_state(channel).polarity { let duty = self.set_pwm(channel, PwmPin::MaxIPos, duty);
Polarity::Normal => self.set_pwm(channel, PwmPin::MaxIPos, duty), (duty * max, max)
Polarity::Reversed => self.set_pwm(channel, PwmPin::MaxINeg, duty),
};
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, 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 duty = (max_i_neg.min(MAX_TEC_I).max(ElectricCurrent::zero()) / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>(); let duty = (max_i_neg.min(MAX_TEC_I).max(ElectricCurrent::zero()) / max).get::<ratio>();
let duty = match self.channel_state(channel).polarity { let duty = self.set_pwm(channel, PwmPin::MaxINeg, duty);
Polarity::Normal => self.set_pwm(channel, PwmPin::MaxINeg, duty), (duty * max, max)
Polarity::Reversed => self.set_pwm(channel, PwmPin::MaxIPos, duty),
};
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, max)
}
pub fn set_polarity(&mut self, channel: usize, polarity: Polarity) {
if self.channel_state(channel).polarity != polarity {
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.set_i(channel, i_set);
self.set_max_i_pos(channel, max_i_pos);
self.set_max_i_neg(channel, max_i_neg);
}
} }
fn report(&mut self, channel: usize) -> Report { fn report(&mut self, channel: usize) -> Report {
@ -545,7 +510,6 @@ impl Channels {
max_v: self.get_max_v(channel).into(), max_v: self.get_max_v(channel).into(),
max_i_pos: self.get_max_i_pos(channel).into(), max_i_pos: self.get_max_i_pos(channel).into(),
max_i_neg: self.get_max_i_neg(channel).into(), max_i_neg: self.get_max_i_neg(channel).into(),
polarity: PolarityJson(self.channel_state(channel).polarity.clone()),
} }
} }
@ -627,21 +591,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)] #[derive(Serialize)]
pub struct PwmSummaryField<T: Serialize> { pub struct PwmSummaryField<T: Serialize> {
value: T, value: T,
@ -662,7 +611,6 @@ pub struct PwmSummary {
max_v: PwmSummaryField<ElectricPotential>, max_v: PwmSummaryField<ElectricPotential>,
max_i_pos: PwmSummaryField<ElectricCurrent>, max_i_pos: PwmSummaryField<ElectricCurrent>,
max_i_neg: PwmSummaryField<ElectricCurrent>, max_i_neg: PwmSummaryField<ElectricCurrent>,
polarity: PolarityJson,
} }
#[derive(Serialize)] #[derive(Serialize)]

View File

@ -11,8 +11,7 @@ use super::{
CenterPoint, CenterPoint,
PidParameter, PidParameter,
PwmPin, PwmPin,
ShParameter, ShParameter
Polarity
}, },
ad7172, ad7172,
CHANNEL_CONFIG_KEY, CHANNEL_CONFIG_KEY,
@ -23,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,
}; };
@ -87,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) => {
@ -171,12 +181,6 @@ impl Handler {
Ok(Handler::Handled) Ok(Handler::Handled)
} }
fn set_polarity(socket: &mut TcpSocket, channels: &mut Channels, channel: usize, polarity: Polarity) -> Result<Handler, Error> {
channels.set_polarity(channel, polarity);
send_line(socket, b"{}");
Ok(Handler::Handled)
}
fn set_pwm (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, pin: PwmPin, value: f64) -> Result<Handler, Error> { fn set_pwm (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, pin: PwmPin, value: f64) -> Result<Handler, Error> {
match pin { match pin {
PwmPin::ISet => { PwmPin::ISet => {
@ -408,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),
@ -418,7 +424,6 @@ impl Handler {
Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, channels), Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, channels),
Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config), Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config),
Command::PwmPid { channel } => Handler::engage_pid(socket, channels, channel), Command::PwmPid { channel } => Handler::engage_pid(socket, channels, channel),
Command::PwmPolarity { channel, polarity } => Handler::set_polarity(socket, channels, channel, polarity),
Command::Pwm { channel, pin, value } => Handler::set_pwm(socket, channels, channel, pin, value), 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::CenterPoint { channel, center } => Handler::set_center_point(socket, channels, channel, center),
Command::Pid { channel, parameter, value } => Handler::set_pid(socket, channels, channel, parameter, value), 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)] #[derive(Debug, Clone, PartialEq)]
pub enum ShowCommand { pub enum ShowCommand {
Input, Input,
Reporting,
Pwm, Pwm,
Pid, Pid,
SteinhartHart, SteinhartHart,
@ -135,12 +136,6 @@ pub enum CenterPoint {
Override(f32), Override(f32),
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Polarity {
Normal,
Reversed,
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Command { pub enum Command {
Quit, Quit,
@ -153,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,
@ -163,10 +159,6 @@ pub enum Command {
PwmPid { PwmPid {
channel: usize, channel: usize,
}, },
PwmPolarity {
channel: usize,
polarity: Polarity,
},
CenterPoint { CenterPoint {
channel: usize, channel: usize,
center: CenterPoint, center: CenterPoint,
@ -241,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)
} }
@ -248,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)
} }
@ -307,18 +321,6 @@ fn pwm_pid(input: &[u8]) -> IResult<&[u8], ()> {
value((), tag("pid"))(input) value((), tag("pid"))(input)
} }
fn pwm_polarity(input: &[u8]) -> IResult<&[u8], Polarity> {
preceded(
tag("polarity"),
preceded(
whitespace,
alt((value(Polarity::Normal, tag("normal")),
value(Polarity::Reversed, tag("reversed"))
))
)
)(input)
}
fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> { fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("pwm")(input)?; let (input, _) = tag("pwm")(input)?;
alt(( alt((
@ -331,10 +333,6 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, ()) = pwm_pid(input)?; let (input, ()) = pwm_pid(input)?;
Ok((input, Ok(Command::PwmPid { channel }))) Ok((input, Ok(Command::PwmPid { channel })))
}, },
|input| {
let (input, polarity) = pwm_polarity(input)?;
Ok((input, Ok(Command::PwmPolarity { channel, polarity })))
},
|input| { |input| {
let (input, config) = pwm_setup(input)?; let (input, config) = pwm_setup(input)?;
match config { match config {
@ -684,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");

View File

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

View File

@ -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 {

View File

@ -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 => {}