use std::fs; use std::io::{Seek, Write}; use std::path::Path; use chrono::{DateTime, Datelike, Utc}; use serde::{Deserialize, Serialize}; use gps_parser; use ina3221; #[derive(Serialize, Deserialize, Debug)] struct Sample { value: f64, unit: String, } #[derive(Serialize, Deserialize, Debug)] struct CvsData { lat: f64, lon: f64, timestamp: String, speed: Sample, rpm: Sample, current: Sample, voltage: Sample, } #[derive(serde::Deserialize)] struct Opts { log_path: String, i2c_dev: String, i2c_slave_address: u16, gps_dev: String, shunt_resistor: f64, } fn read_opts(path: &str) -> Opts { let opts_str = fs::read_to_string(path).expect("Unable to read log file"); return serde_json::from_str(&opts_str).unwrap(); } fn update_date(datetime: DateTime, year: i32, month: u32, day: u32) -> DateTime { let mut res = datetime; res = match res.with_year(year) { Some(value) => value, _none => datetime, }; res = match res.with_month(month) { Some(value) => value, _none => datetime, }; res = match res.with_day(day) { Some(value) => value, _none => datetime, }; return res; } fn log_sample(log_path: &str, sample: CvsData) { let path = Path::new(log_path); let mut log_json: Vec = Vec::new(); let mut file: fs::File; if path.exists() { file = fs::OpenOptions::new().read(true).write(true).create(true).truncate(false).append(false).open(log_path).unwrap(); let log_str = fs::read_to_string(path).expect("Unable to read log file"); log_json = serde_json::from_str(&log_str).unwrap(); file.set_len(0).unwrap(); file.rewind().unwrap(); } else { file = fs::File::create(log_path).expect("Unable to create file"); } log_json.push(sample); let j = match serde_json::to_string(&log_json) { Ok(value) => value, Err(error) => panic!("Unable to make JSON from struct {:?}", error), }; file.write_all(j.as_bytes()).expect("Unable to write JSON string to file"); } fn main() { let opts = read_opts("/etc/cvs-monitor/config.json"); let path = Path::new(&opts.log_path); if !path.exists() { match fs::create_dir(&opts.log_path) { Ok(()) => {}, Err(err) => panic!("Can't create dir \"{}\" ({})", opts.log_path, err), }; } let now = Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true); let log_file: String = opts.log_path + &now + ".json"; let mut gps_parser = gps_parser::GpsParser::new(&opts.gps_dev); let mut ina = ina3221::Ina3221::new(opts.i2c_slave_address, &opts.i2c_dev, opts.shunt_resistor); loop { let speed = Sample { value: 0.0, unit: "km/h".to_string(), }; let rpm = Sample { value: 0.0, unit: "T/min".to_string(), }; let current = Sample { value: 0.0, unit: "A".to_string(), }; let voltage = Sample { value: 0.0, unit: "V".to_string(), }; let mut sample = CvsData { lat: 0.0, lon: 0.0, timestamp: Utc::now().to_rfc3339(), speed, rpm, current, voltage, }; let gps = match gps_parser.parse_nmea() { Ok(value) => value, Err(_) => continue }; sample.lat = gps.lat; sample.lon = gps.lon; sample.speed.value = gps.speed; sample.rpm.value = sample.speed.value * 1000.0 / (60.0 * 2.2); let mut tmp = gps.timestamp; tmp = update_date(tmp, Utc::now().year(), Utc::now().month(), Utc::now().day()); sample.timestamp = tmp.to_rfc3339(); let current = match ina.current(ina3221::Channel::One) { Ok(value) => value, Err(_error) => 0.0, }; sample.current.value = current; let voltage = match ina.bus_voltage(ina3221::Channel::One) { Ok(value) => value, Err(_error) => 0.0, }; sample.voltage.value = voltage; log_sample(&log_file, sample); } }