forked from M-Labs/thermostat
pwm: export summary as json
This commit is contained in:
parent
5521563c91
commit
62d89a68a1
|
@ -1,4 +1,4 @@
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Serializer};
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
use stm32f4xx_hal::hal;
|
use stm32f4xx_hal::hal;
|
||||||
use uom::si::{
|
use uom::si::{
|
||||||
|
@ -438,9 +438,22 @@ impl Channels {
|
||||||
pid_output,
|
pid_output,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pwm_summary(&mut self, channel: usize) -> PwmSummary {
|
||||||
|
PwmSummary {
|
||||||
|
channel,
|
||||||
|
center: CenterPointJson(self.channel_state(channel).center.clone()),
|
||||||
|
i_set: self.get_i(channel).into(),
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
type JsonBuffer = heapless::Vec<u8, heapless::consts::U360>;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
pub struct Report {
|
pub struct Report {
|
||||||
channel: usize,
|
channel: usize,
|
||||||
time: i64,
|
time: i64,
|
||||||
|
@ -457,14 +470,58 @@ pub struct Report {
|
||||||
pid_output: Option<ElectricCurrent>,
|
pid_output: Option<ElectricCurrent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type JsonBuffer = heapless::Vec<u8, heapless::consts::U360>;
|
|
||||||
|
|
||||||
impl Report {
|
impl Report {
|
||||||
pub fn to_json(&self) -> Result<JsonBuffer, serde_json_core::ser::Error> {
|
pub fn to_json(&self) -> Result<JsonBuffer, serde_json_core::ser::Error> {
|
||||||
serde_json_core::to_vec(self)
|
serde_json_core::to_vec(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CenterPointJson(CenterPoint);
|
||||||
|
|
||||||
|
// used in JSON encoding, not for config
|
||||||
|
impl Serialize for CenterPointJson {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
match self.0 {
|
||||||
|
CenterPoint::Vref =>
|
||||||
|
serializer.serialize_str("vref"),
|
||||||
|
CenterPoint::Override(vref) =>
|
||||||
|
serializer.serialize_f32(vref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct PwmSummaryField<T: Serialize> {
|
||||||
|
value: T,
|
||||||
|
max: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Serialize> From<(T, T)> for PwmSummaryField<T> {
|
||||||
|
fn from((value, max): (T, T)) -> Self {
|
||||||
|
PwmSummaryField { value, max }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct PwmSummary {
|
||||||
|
channel: usize,
|
||||||
|
center: CenterPointJson,
|
||||||
|
i_set: PwmSummaryField<ElectricCurrent>,
|
||||||
|
max_v: PwmSummaryField<ElectricPotential>,
|
||||||
|
max_i_pos: PwmSummaryField<ElectricCurrent>,
|
||||||
|
max_i_neg: PwmSummaryField<ElectricCurrent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PwmSummary {
|
||||||
|
pub fn to_json(&self) -> Result<JsonBuffer, serde_json_core::ser::Error> {
|
||||||
|
serde_json_core::to_vec(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -491,4 +548,34 @@ mod test {
|
||||||
assert_eq!(buf[0], b'{');
|
assert_eq!(buf[0], b'{');
|
||||||
assert_eq!(buf[buf.len() - 1], b'}');
|
assert_eq!(buf[buf.len() - 1], b'}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwm_summary_to_json() {
|
||||||
|
let value = 1.0 / 1.1;
|
||||||
|
let max = 5.0 / 1.1;
|
||||||
|
|
||||||
|
let pwm_summary = PwmSummary {
|
||||||
|
channel: 0,
|
||||||
|
center: CenterPoint::Vref,
|
||||||
|
i_set: PwmSummaryField {
|
||||||
|
value: ElectricCurrent::new::<ampere>(value),
|
||||||
|
max: ElectricCurrent::new::<ampere>(max),
|
||||||
|
},
|
||||||
|
max_v: PwmSummaryField {
|
||||||
|
value: ElectricPotential::new::<volt>(value),
|
||||||
|
max: ElectricPotential::new::<volt>(max),
|
||||||
|
},
|
||||||
|
max_i_pos: PwmSummaryField {
|
||||||
|
value: ElectricCurrent::new::<ampere>(value),
|
||||||
|
max: ElectricCurrent::new::<ampere>(max),
|
||||||
|
},
|
||||||
|
max_i_neg: PwmSummaryField {
|
||||||
|
value: ElectricCurrent::new::<ampere>(value),
|
||||||
|
max: ElectricCurrent::new::<ampere>(max),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let buf = pwm_summary.to_json().unwrap();
|
||||||
|
assert_eq!(buf[0], b'{');
|
||||||
|
assert_eq!(buf[buf.len() - 1], b'}');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
101
src/main.rs
101
src/main.rs
|
@ -56,7 +56,7 @@ use server::Server;
|
||||||
mod session;
|
mod session;
|
||||||
use session::{Session, SessionOutput};
|
use session::{Session, SessionOutput};
|
||||||
mod command_parser;
|
mod command_parser;
|
||||||
use command_parser::{CenterPoint, Command, ShowCommand, PwmPin};
|
use command_parser::{Command, ShowCommand, PwmPin};
|
||||||
mod timer;
|
mod timer;
|
||||||
mod pid;
|
mod pid;
|
||||||
mod steinhart_hart;
|
mod steinhart_hart;
|
||||||
|
@ -80,36 +80,42 @@ pub const EEPROM_SIZE: usize = 128;
|
||||||
const TCP_PORT: u16 = 23;
|
const TCP_PORT: u16 = 23;
|
||||||
|
|
||||||
|
|
||||||
fn report_to(channel: usize, channels: &mut Channels, socket: &mut TcpSocket) -> bool {
|
fn send_line(socket: &mut TcpSocket, data: &[u8]) -> bool {
|
||||||
let send_free = socket.send_capacity() - socket.send_queue();
|
let send_free = socket.send_capacity() - socket.send_queue();
|
||||||
match channels.report(channel).to_json() {
|
if data.len() > send_free + 1 {
|
||||||
Ok(buf) if buf.len() > send_free + 1 => {
|
|
||||||
// Not enough buffer space, skip report for now
|
// Not enough buffer space, skip report for now
|
||||||
warn!(
|
warn!(
|
||||||
"TCP socket has only {}/{} needed {}",
|
"TCP socket has only {}/{} needed {}",
|
||||||
send_free + 1, socket.send_capacity(), buf.len(),
|
send_free + 1, socket.send_capacity(), data.len(),
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
Ok(buf) => {
|
match socket.send_slice(&data) {
|
||||||
match socket.send_slice(&buf) {
|
Ok(sent) if sent == data.len() => {
|
||||||
Ok(sent) if sent == buf.len() => {
|
|
||||||
let _ = socket.send_slice(b"\n");
|
let _ = socket.send_slice(b"\n");
|
||||||
// success
|
// success
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
Ok(sent) =>
|
Ok(sent) =>
|
||||||
warn!("sent only {}/{} bytes of report", sent, buf.len()),
|
warn!("sent only {}/{} bytes", sent, data.len()),
|
||||||
Err(e) =>
|
Err(e) =>
|
||||||
error!("error sending report: {:?}", e),
|
error!("error sending line: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) =>
|
|
||||||
error!("unable to serialize report: {:?}", e),
|
|
||||||
}
|
|
||||||
// not success
|
// not success
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn report_to(channel: usize, channels: &mut Channels, socket: &mut TcpSocket) -> bool {
|
||||||
|
match channels.report(channel).to_json() {
|
||||||
|
Ok(buf) =>
|
||||||
|
send_line(socket, &buf[..]),
|
||||||
|
Err(e) => {
|
||||||
|
error!("unable to serialize report: {:?}", e);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialization and main loop
|
/// Initialization and main loop
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
#[entry]
|
#[entry]
|
||||||
|
@ -207,26 +213,9 @@ fn main() -> ! {
|
||||||
}
|
}
|
||||||
Command::Show(ShowCommand::Pid) => {
|
Command::Show(ShowCommand::Pid) => {
|
||||||
for channel in 0..CHANNELS {
|
for channel in 0..CHANNELS {
|
||||||
let send_free = socket.send_capacity() - socket.send_queue();
|
|
||||||
match channels.channel_state(channel).pid.summary(channel).to_json() {
|
match channels.channel_state(channel).pid.summary(channel).to_json() {
|
||||||
Ok(buf) if buf.len() > send_free + 1 => {
|
|
||||||
// Not enough buffer space, skip report for now
|
|
||||||
warn!(
|
|
||||||
"TCP socket has only {}/{} needed {}",
|
|
||||||
send_free + 1, socket.send_capacity(), buf.len(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(buf) => {
|
Ok(buf) => {
|
||||||
match socket.send_slice(&buf) {
|
send_line(&mut socket, &buf);
|
||||||
Ok(sent) if sent == buf.len() => {
|
|
||||||
let _ = socket.send_slice(b"\n");
|
|
||||||
// success
|
|
||||||
}
|
|
||||||
Ok(sent) =>
|
|
||||||
warn!("sent only {}/{} bytes of summary", sent, buf.len()),
|
|
||||||
Err(e) =>
|
|
||||||
error!("error sending summary: {:?}", e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) =>
|
Err(e) =>
|
||||||
error!("unable to serialize pid summary: {:?}", e),
|
error!("unable to serialize pid summary: {:?}", e),
|
||||||
|
@ -235,52 +224,14 @@ fn main() -> ! {
|
||||||
}
|
}
|
||||||
Command::Show(ShowCommand::Pwm) => {
|
Command::Show(ShowCommand::Pwm) => {
|
||||||
for channel in 0..CHANNELS {
|
for channel in 0..CHANNELS {
|
||||||
let i_set = channels.get_i(channel);
|
match channels.pwm_summary(channel).to_json() {
|
||||||
let state = channels.channel_state(channel);
|
Ok(buf) => {
|
||||||
let _ = writeln!(
|
send_line(&mut socket, &buf);
|
||||||
socket, "channel {}: PID={}",
|
|
||||||
channel,
|
|
||||||
if state.pid_engaged { "engaged" } else { "disengaged" }
|
|
||||||
);
|
|
||||||
let _ = write!(
|
|
||||||
socket, "- i_set={:.3} / {:.3} ",
|
|
||||||
i_set.0.into_format_args(ampere, Abbreviation),
|
|
||||||
i_set.1.into_format_args(ampere, Abbreviation),
|
|
||||||
);
|
|
||||||
match state.center {
|
|
||||||
CenterPoint::Vref => {
|
|
||||||
let _ = writeln!(
|
|
||||||
socket, "center=vref vref={:.3}",
|
|
||||||
state.vref.into_format_args(volt, Abbreviation),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
CenterPoint::Override(volts) => {
|
Err(e) =>
|
||||||
let _ = writeln!(
|
error!("unable to serialize pwm summary: {:?}", e),
|
||||||
socket, "center={:.3} V",
|
|
||||||
volts,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let max_v = channels.get_max_v(channel);
|
|
||||||
let _ = writeln!(
|
|
||||||
socket, "- max_v={:.3} / {:.3}",
|
|
||||||
max_v.0.into_format_args(volt, Abbreviation),
|
|
||||||
max_v.1.into_format_args(volt, Abbreviation),
|
|
||||||
);
|
|
||||||
let max_i_pos = channels.get_max_i_pos(channel);
|
|
||||||
let _ = writeln!(
|
|
||||||
socket, "- max_i_pos={:.3} / {:.3}",
|
|
||||||
max_i_pos.0.into_format_args(ampere, Abbreviation),
|
|
||||||
max_i_pos.1.into_format_args(ampere, Abbreviation),
|
|
||||||
);
|
|
||||||
let max_i_neg = channels.get_max_i_neg(channel);
|
|
||||||
let _ = writeln!(
|
|
||||||
socket, "- max_i_neg={:.3} / {:.3}",
|
|
||||||
max_i_neg.0.into_format_args(ampere, Abbreviation),
|
|
||||||
max_i_neg.1.into_format_args(ampere, Abbreviation),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let _ = writeln!(socket, "");
|
|
||||||
}
|
}
|
||||||
Command::Show(ShowCommand::SteinhartHart) => {
|
Command::Show(ShowCommand::SteinhartHart) => {
|
||||||
for channel in 0..CHANNELS {
|
for channel in 0..CHANNELS {
|
||||||
|
|
|
@ -96,7 +96,7 @@ impl Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type JsonBuffer = heapless::Vec<u8, heapless::consts::U240>;
|
type JsonBuffer = heapless::Vec<u8, heapless::consts::U360>;
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Summary {
|
pub struct Summary {
|
||||||
|
|
Loading…
Reference in New Issue