/* Copyright 2023 Bruce Merry
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
use serde::Deserialize;
use std::env;
use std::error::Error;
use std::fs;
use std::io::Write;
use std::path::Path;
/// Duplicate of crate::fields::FieldType
#[derive(Deserialize, Debug, Clone)]
enum FieldType {
Charge,
Current,
Energy,
Frequency,
Power,
StateOfCharge,
Temperature,
Time,
Voltage,
Unitless,
}
use FieldType::*;
#[derive(Deserialize, Clone)]
struct Record {
field_type: FieldType,
group: String,
name: String,
id: String,
scale: Option,
offset: Option,
offset2: Option,
reg: Option,
reg2: Option,
}
fn write_fields(w: &mut W, header: &str, records: &[Record]) -> Result<(), Box>
where
W: Write,
{
writeln!(w, "use crate::fields::{{Field, FieldType}};")?;
writeln!(w, "{header}")?;
writeln!(w, "const FIELDS: &[Field] = &[")?;
for record in records.iter() {
let default_scale = match record.field_type {
Charge | Power | StateOfCharge | Unitless => Some(1.0),
Energy | Temperature => Some(0.1),
Frequency => Some(0.01),
Current | Voltage => None,
Time => Some(60.0),
};
let bias = match record.field_type {
Temperature => -100.0,
_ => 0.0,
};
let unit = match record.field_type {
Charge => "Ah",
Current => "A",
Energy => "kWh",
Frequency => "Hz",
Power => "W",
StateOfCharge => "%",
Temperature => "°C",
Time => "s",
Voltage => "V",
Unitless => "",
};
let scale = record.scale.or(default_scale).unwrap();
writeln!(
w,
r#" Field {{
field_type: FieldType::{:?},
group: {:?},
name: {:?},
id: {:?},
scale: {scale:?},
bias: {bias:?},
unit: {unit:?},
}},"#,
record.field_type, record.group, record.name, record.id
)?;
}
writeln!(w, "];")?;
writeln!(w, "#[allow(dead_code)]")?;
writeln!(w, "mod field_idx {{")?;
for (i, record) in records.iter().enumerate() {
writeln!(
w,
" pub const {}: usize = {};",
record.id.to_uppercase(),
i
)?;
}
writeln!(w, "}}")?;
Ok(())
}
fn main() -> Result<(), Box> {
let out_dir = env::var_os("OUT_DIR").unwrap();
let out_path = Path::new(&out_dir);
let pcap_path = out_path.join("pcap_fields.rs");
let modbus_path = out_path.join("modbus_fields.rs");
let mut reader = csv::Reader::from_reader(fs::File::open("fields.csv")?);
let mut pcap_records = vec![];
let mut pcap_offsets = vec![];
let mut modbus_records = vec![];
let mut modbus_regs = vec![];
for result in reader.deserialize() {
let record: Record = result?;
if let Some(offset) = record.offset {
pcap_records.push(record.clone());
let mut offsets = vec![offset];
if let Some(offset2) = record.offset2 {
offsets.push(offset2);
}
pcap_offsets.push(offsets);
}
if let Some(reg) = record.reg {
modbus_records.push(record.clone());
let mut regs = vec![];
if reg >= 0 {
regs.push(reg);
if let Some(reg2) = record.reg2 {
regs.push(reg2);
}
}
modbus_regs.push(regs);
}
}
let mut pcap_writer = fs::File::create(pcap_path)?;
write_fields(
&mut pcap_writer,
"/// Fields found in each packet",
&pcap_records,
)?;
writeln!(&mut pcap_writer, "/// Offsets of fields within packets")?;
writeln!(&mut pcap_writer, "const OFFSETS: &[&[usize]] = &[")?;
for offsets in pcap_offsets.into_iter() {
writeln!(&mut pcap_writer, " &{:?},", offsets.as_slice())?;
}
writeln!(&mut pcap_writer, "];")?;
drop(pcap_writer);
let mut modbus_writer = fs::File::create(modbus_path)?;
write_fields(
&mut modbus_writer,
"/// Fields retrieved by modbus protocol",
&modbus_records,
)?;
writeln!(&mut modbus_writer, "/// Registers corresponding to fields")?;
writeln!(&mut modbus_writer, "const REGISTERS: &[&[u16]] = &[")?;
for regs in modbus_regs.into_iter() {
writeln!(&mut modbus_writer, " &{:?},", regs.as_slice())?;
}
writeln!(&mut modbus_writer, "];")?;
drop(modbus_writer);
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=fields.csv");
Ok(())
}