Skip to content

Commit

Permalink
fix(fdr2csv): refactor to unify fdr2csv, fix conversion bug (#9324)
Browse files Browse the repository at this point in the history
* feat(fdr2csv): refactor to unify fdr2csv, fix conversion bug

* chore: don't downgrade crate versions

* feat(fdr2csv): add option to get raw file version

* fix(fdr): fixed wrong storage of parameters

* feat(fdr2csv): -o no longer required when -r or -g are present

* fix(fdr2csv): correct description of parameters

---------

Co-authored-by: Andreas Guther <[email protected]>
lukecologne and aguther committed Nov 2, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent c9cfe3c commit 2776100
Showing 23 changed files with 426 additions and 1,185 deletions.
Original file line number Diff line number Diff line change
@@ -140,8 +140,9 @@ void FlightDataRecorder::writeConfiguration() {
// create structure
INIStructure iniStructure;
iniStructure["FLIGHT_DATA_RECORDER"]["ENABLED"] = idIsEnabled->get() == 1 ? "true" : "false";
iniStructure["FLIGHT_DATA_RECORDER"]["MAXIMUM_NUMBER_OF_FILES"] = idMaximumFileCount->get();
iniStructure["FLIGHT_DATA_RECORDER"]["MAXIMUM_NUMBER_OF_ENTRIES_PER_FILE"] = idMaximumSampleCounter->get();
iniStructure["FLIGHT_DATA_RECORDER"]["MAXIMUM_NUMBER_OF_FILES"] = std::to_string(static_cast<int>(idMaximumFileCount->get()));
iniStructure["FLIGHT_DATA_RECORDER"]["MAXIMUM_NUMBER_OF_ENTRIES_PER_FILE"] =
std::to_string(static_cast<int>(idMaximumSampleCounter->get()));

// write file
iniFile.write(iniStructure, true);
Original file line number Diff line number Diff line change
@@ -144,8 +144,9 @@ void FlightDataRecorder::writeConfiguration() {
// create structure
INIStructure iniStructure;
iniStructure["FLIGHT_DATA_RECORDER"]["ENABLED"] = idIsEnabled->get() == 1 ? "true" : "false";
iniStructure["FLIGHT_DATA_RECORDER"]["MAXIMUM_NUMBER_OF_FILES"] = idMaximumFileCount->get();
iniStructure["FLIGHT_DATA_RECORDER"]["MAXIMUM_NUMBER_OF_ENTRIES_PER_FILE"] = idMaximumSampleCounter->get();
iniStructure["FLIGHT_DATA_RECORDER"]["MAXIMUM_NUMBER_OF_FILES"] = std::to_string(static_cast<int>(idMaximumFileCount->get()));
iniStructure["FLIGHT_DATA_RECORDER"]["MAXIMUM_NUMBER_OF_ENTRIES_PER_FILE"] =
std::to_string(static_cast<int>(idMaximumSampleCounter->get()));

// write file
iniFile.write(iniStructure, true);
31 changes: 9 additions & 22 deletions tools/fdr2csv/a32nx/Cargo.lock → tools/fdr2csv/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "fdr2csv_common"
name = "fdr2csv"
version = "0.1.0"
authors = ["FlyByWire Simulations"]
edition = "2021"
7 changes: 7 additions & 0 deletions tools/fdr2csv/a320_wrapper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "../../fbw-a32nx/src/wasm/fbw_a320/src/model/AutopilotLaws_types.h"
#include "../../fbw-a32nx/src/wasm/fbw_a320/src/model/AutopilotStateMachine_types.h"
#include "../../fbw-a32nx/src/wasm/fbw_a320/src/model/Autothrust_types.h"
#include "../../fbw-a32nx/src/wasm/fbw_a320/src/model/ElacComputer_types.h"
#include "../../fbw-a32nx/src/wasm/fbw_a320/src/model/FacComputer_types.h"
#include "../../fbw-a32nx/src/wasm/fbw_a320/src/model/SecComputer_types.h"
#include "../../fbw-a32nx/src/wasm/fbw_a320/src/recording/RecordingDataTypes.h"
22 changes: 0 additions & 22 deletions tools/fdr2csv/a32nx/Cargo.toml

This file was deleted.

45 changes: 0 additions & 45 deletions tools/fdr2csv/a32nx/build.rs

This file was deleted.

216 changes: 0 additions & 216 deletions tools/fdr2csv/a32nx/src/main.rs

This file was deleted.

7 changes: 0 additions & 7 deletions tools/fdr2csv/a32nx/wrapper.hpp

This file was deleted.

8 changes: 8 additions & 0 deletions tools/fdr2csv/a380_wrapper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "../../fbw-a380x/src/wasm/fbw_a380/src/interface/FuelSystemData.h"
#include "../../fbw-a380x/src/wasm/fbw_a380/src/model/A380PrimComputer_types.h"
#include "../../fbw-a380x/src/wasm/fbw_a380/src/model/A380SecComputer_types.h"
#include "../../fbw-a380x/src/wasm/fbw_a380/src/model/AutopilotLaws_types.h"
#include "../../fbw-a380x/src/wasm/fbw_a380/src/model/AutopilotStateMachine_types.h"
#include "../../fbw-a380x/src/wasm/fbw_a380/src/model/Autothrust_types.h"
#include "../../fbw-a380x/src/wasm/fbw_a380/src/model/FacComputer_types.h"
#include "../../fbw-a380x/src/wasm/fbw_a380/src/recording/RecordingDataTypes.h"
610 changes: 0 additions & 610 deletions tools/fdr2csv/a380x/Cargo.lock

This file was deleted.

22 changes: 0 additions & 22 deletions tools/fdr2csv/a380x/Cargo.toml

This file was deleted.

220 changes: 0 additions & 220 deletions tools/fdr2csv/a380x/src/main.rs

This file was deleted.

8 changes: 0 additions & 8 deletions tools/fdr2csv/a380x/wrapper.hpp

This file was deleted.

26 changes: 22 additions & 4 deletions tools/fdr2csv/a380x/build.rs → tools/fdr2csv/build.rs
Original file line number Diff line number Diff line change
@@ -23,10 +23,24 @@ fn main() {
// The bindgen::Builder is the main entry point
// to bindgen, and lets you build up options for
// the resulting bindings.
let bindings = bindgen::Builder::default()
let bindings_320 = bindgen::Builder::default()
// The input header we would like to generate
// bindings for.
.header("wrapper.hpp")
.header("a320_wrapper.hpp")
.clang_arg("-std=c++20")
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.parse_callbacks(Box::new(CustomDeriveCallback::new()))
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");

let bindings_380 = bindgen::Builder::default()
// The input header we would like to generate
// bindings for.
.header("a380_wrapper.hpp")
.clang_arg("-std=c++20")
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
@@ -39,7 +53,11 @@ fn main() {

// Write the bindings to the $OUT_DIR/bindings.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
bindings_320
.write_to_file(out_path.join("bindings_320.rs"))
.expect("Couldn't write bindings!");

bindings_380
.write_to_file(out_path.join("bindings_380.rs"))
.expect("Couldn't write bindings!");
}
2 changes: 0 additions & 2 deletions tools/fdr2csv/common/src/lib.rs

This file was deleted.

93 changes: 93 additions & 0 deletions tools/fdr2csv/src/a320.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use crate::{
a320_headers::{
ap_laws_output, ap_sm_output, athr_out, base_elac_analog_outputs,
base_elac_discrete_outputs, base_elac_out_bus, base_fac_analog_outputs, base_fac_bus,
base_fac_discrete_outputs, base_sec_analog_outputs, base_sec_discrete_outputs,
base_sec_out_bus, AircraftSpecificData, BaseData,
},
read_bytes,
};
use serde::Serialize;
use std::io::{prelude::*, Error};

pub const INTERFACE_VERSION: u64 = 3200001;

// A single FDR record
#[derive(Serialize, Default)]
pub struct FdrData {
base: BaseData,
specific: AircraftSpecificData,
elac_1: ElacData,
elac_2: ElacData,
sec_1: SecData,
sec_2: SecData,
sec_3: SecData,
fac_1: FacData,
fac_2: FacData,
ap_sm: ap_sm_output,
ap_law: ap_laws_output,
athr: athr_out,
}

#[derive(Serialize, Default)]
struct ElacData {
bus_outputs: base_elac_out_bus,
discrete_outputs: base_elac_discrete_outputs,
analog_outputs: base_elac_analog_outputs,
}

#[derive(Serialize, Default)]
struct SecData {
bus_outputs: base_sec_out_bus,
discrete_outputs: base_sec_discrete_outputs,
analog_outputs: base_sec_analog_outputs,
}

#[derive(Serialize, Default)]
struct FacData {
bus_outputs: base_fac_bus,
discrete_outputs: base_fac_discrete_outputs,
analog_outputs: base_fac_analog_outputs,
}

// These are helper functions to read in a whole FDR record.
pub fn read_record(reader: &mut impl Read) -> Result<FdrData, Error> {
Ok(FdrData {
base: read_bytes::<BaseData>(reader)?,
specific: read_bytes::<AircraftSpecificData>(reader)?,
elac_1: read_elac(reader)?,
elac_2: read_elac(reader)?,
sec_1: read_sec(reader)?,
sec_2: read_sec(reader)?,
sec_3: read_sec(reader)?,
fac_1: read_fac(reader)?,
fac_2: read_fac(reader)?,
ap_sm: read_bytes::<ap_sm_output>(reader)?,
ap_law: read_bytes::<ap_laws_output>(reader)?,
athr: read_bytes::<athr_out>(reader)?,
})
}

fn read_elac(reader: &mut impl Read) -> Result<ElacData, Error> {
Ok(ElacData {
bus_outputs: read_bytes::<base_elac_out_bus>(reader)?,
discrete_outputs: read_bytes::<base_elac_discrete_outputs>(reader)?,
analog_outputs: read_bytes::<base_elac_analog_outputs>(reader)?,
})
}

fn read_sec(reader: &mut impl Read) -> Result<SecData, Error> {
Ok(SecData {
bus_outputs: read_bytes::<base_sec_out_bus>(reader)?,
discrete_outputs: read_bytes::<base_sec_discrete_outputs>(reader)?,
analog_outputs: read_bytes::<base_sec_analog_outputs>(reader)?,
})
}

fn read_fac(reader: &mut impl Read) -> Result<FacData, Error> {
Ok(FacData {
bus_outputs: read_bytes::<base_fac_bus>(reader)?,
discrete_outputs: read_bytes::<base_fac_discrete_outputs>(reader)?,
analog_outputs: read_bytes::<base_fac_analog_outputs>(reader)?,
})
}
Original file line number Diff line number Diff line change
@@ -7,4 +7,4 @@ use serde::Serialize;

use bytemuck::AnyBitPattern;

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
include!(concat!(env!("OUT_DIR"), "/bindings_320.rs"));
98 changes: 98 additions & 0 deletions tools/fdr2csv/src/a380.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use crate::{
a380_headers::{
ap_laws_output, ap_sm_output, athr_out, base_fac_analog_outputs, base_fac_bus,
base_fac_discrete_outputs, base_prim_analog_outputs, base_prim_discrete_outputs,
base_prim_out_bus, base_sec_analog_outputs, base_sec_discrete_outputs, base_sec_out_bus,
AircraftSpecificData, BaseData, FuelSystemData,
},
read_bytes,
};
use serde::Serialize;
use std::io::{prelude::*, Error};

pub const INTERFACE_VERSION: u64 = 3800001;
pub const INTERFACE_MIN_VERSION: u64 = 3800000;

// A single FDR record
#[derive(Serialize, Default)]
pub struct FdrData {
base: BaseData,
specific: AircraftSpecificData,
prim_1: PrimData,
prim_2: PrimData,
prim_3: PrimData,
sec_1: SecData,
sec_2: SecData,
sec_3: SecData,
fac_1: FacData,
fac_2: FacData,
ap_sm: ap_sm_output,
ap_law: ap_laws_output,
athr: athr_out,
fuel: FuelSystemData,
}

#[derive(Serialize, Default)]
struct PrimData {
bus_outputs: base_prim_out_bus,
discrete_outputs: base_prim_discrete_outputs,
analog_outputs: base_prim_analog_outputs,
}

#[derive(Serialize, Default)]
struct SecData {
bus_outputs: base_sec_out_bus,
discrete_outputs: base_sec_discrete_outputs,
analog_outputs: base_sec_analog_outputs,
}

#[derive(Serialize, Default)]
struct FacData {
bus_outputs: base_fac_bus,
discrete_outputs: base_fac_discrete_outputs,
analog_outputs: base_fac_analog_outputs,
}

// These are helper functions to read in a whole FDR record.
pub fn read_record(reader: &mut impl Read) -> Result<FdrData, Error> {
Ok(FdrData {
base: read_bytes::<BaseData>(reader)?,
specific: read_bytes::<AircraftSpecificData>(reader)?,
prim_1: read_prim(reader)?,
prim_2: read_prim(reader)?,
prim_3: read_prim(reader)?,
sec_1: read_sec(reader)?,
sec_2: read_sec(reader)?,
sec_3: read_sec(reader)?,
fac_1: read_fac(reader)?,
fac_2: read_fac(reader)?,
ap_sm: read_bytes::<ap_sm_output>(reader)?,
ap_law: read_bytes::<ap_laws_output>(reader)?,
athr: read_bytes::<athr_out>(reader)?,
fuel: read_bytes::<FuelSystemData>(reader)?,
})
}

fn read_prim(reader: &mut impl Read) -> Result<PrimData, Error> {
Ok(PrimData {
bus_outputs: read_bytes::<base_prim_out_bus>(reader)?,
discrete_outputs: read_bytes::<base_prim_discrete_outputs>(reader)?,
analog_outputs: read_bytes::<base_prim_analog_outputs>(reader)?,
})
}

fn read_sec(reader: &mut impl Read) -> Result<SecData, Error> {
Ok(SecData {
bus_outputs: read_bytes::<base_sec_out_bus>(reader)?,
discrete_outputs: read_bytes::<base_sec_discrete_outputs>(reader)?,
analog_outputs: read_bytes::<base_sec_analog_outputs>(reader)?,
})
}

fn read_fac(reader: &mut impl Read) -> Result<FacData, Error> {
Ok(FacData {
bus_outputs: read_bytes::<base_fac_bus>(reader)?,
discrete_outputs: read_bytes::<base_fac_discrete_outputs>(reader)?,
analog_outputs: read_bytes::<base_fac_analog_outputs>(reader)?,
})
}
Original file line number Diff line number Diff line change
@@ -7,4 +7,4 @@ use serde::Serialize;

use bytemuck::AnyBitPattern;

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
include!(concat!(env!("OUT_DIR"), "/bindings_380.rs"));
File renamed without changes.
File renamed without changes.
180 changes: 180 additions & 0 deletions tools/fdr2csv/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
use bytemuck::AnyBitPattern;
use clap::Parser;
use csv::WriterBuilder;
use flate2::bufread::GzDecoder;
use std::{
fs::{File, OpenOptions},
io::{prelude::*, BufReader, BufWriter, Error, ErrorKind},
mem,
};

mod a320;
mod a320_headers;
mod a380;
mod a380_headers;
mod csv_header_serializer;
mod error;

#[derive(Debug)]
enum AircraftType {
A320,
A380,
}

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Input file
#[arg(short, long)]
input: String,
/// Output file
#[arg(short, long, required_unless_present_any(["get_input_file_version", "get_raw_input_file_version"]))]
output: Option<String>,
/// Delimiter
#[arg(short, long, default_value = ",")]
delimiter: char,
/// Input file is not compressed
#[arg(short, long, default_value_t = false)]
no_compression: bool,
/// Print struct size
#[arg(short, long, default_value_t = false)]
print_struct_size: bool,
/// Print interface version and aircraft type of input file
#[arg(short, long, default_value_t = false)]
get_input_file_version: bool,
/// Print raw interface version of input file
#[arg(short = 'r', long, default_value_t = false)]
get_raw_input_file_version: bool,
}

// Read number of bytes specified by the size of T from the binary file
pub fn read_bytes<T: AnyBitPattern>(reader: &mut impl Read) -> Result<T, Error> {
let size = mem::size_of::<T>();

// allocate the buffer that will hold the value read from the binary
let mut buf = vec![0u8; size];

// now read from the reader into the buffer
reader.read_exact(&mut buf)?;

// If the read was successful, reinterpret the bytes as the struct, and return
let res = bytemuck::from_bytes::<T>(buf.as_slice());

Ok(*res)
}

fn main() -> Result<(), std::io::Error> {
// Parse CLI arguments
let args = Args::parse();

// Open the input file
let in_file = File::open(args.input.trim())
.map_err(|e| std::io::Error::new(e.kind(), "Failed to open input file!"))?;

// Create Gzip Reader
let mut reader: Box<dyn Read> = if args.no_compression {
Box::new(BufReader::new(in_file))
} else {
Box::new(GzDecoder::new(BufReader::new(in_file)))
};

// Read file version
let file_format_version = read_bytes::<u64>(&mut reader)?;
let aircraft_type = if file_format_version > a380::INTERFACE_MIN_VERSION {
AircraftType::A380
} else {
AircraftType::A320
};

let aircraft_interface_version = match aircraft_type {
AircraftType::A320 => a320::INTERFACE_VERSION,
AircraftType::A380 => a380::INTERFACE_VERSION,
};

// Print or check file version
if args.get_input_file_version {
println!(
"Aircraft Type is {:?}, Interface version is {}",
aircraft_type, file_format_version
);
return Ok(());
} else if args.get_raw_input_file_version {
println!("{}", file_format_version);
return Ok(());
} else if aircraft_interface_version != file_format_version {
return Err(std::io::Error::new(
ErrorKind::InvalidInput,
format!(
"Mismatch between converter and file version (expected {aircraft_interface_version}, got {file_format_version})",
),
));
}

// Print info on conversion start
println!(
"Converting from '{}' to '{}' for aircraft type '{:?}' with interface version '{}' and delimiter '{}'",
args.input, args.output.clone().unwrap(), aircraft_type, file_format_version, args.delimiter
);

// Open or create output file in truncate mode
let out_file = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(args.output.clone().unwrap().trim())
.map_err(|e| std::io::Error::new(e.kind(), "Failed to open output file!"))?;

let mut buf_writer = BufWriter::new(out_file);

let mut counter = 0;

// Generate and write the header
let header = match aircraft_type {
AircraftType::A320 => {
csv_header_serializer::to_string(&a320::FdrData::default(), args.delimiter)
}
AircraftType::A380 => {
csv_header_serializer::to_string(&a380::FdrData::default(), args.delimiter)
}
}
.map_err(|_| std::io::Error::new(ErrorKind::Other, "Failed to generate header."))?;

buf_writer.write(header.as_bytes())?;

// Create the CSV writer, and serialize the file.
let mut writer = WriterBuilder::new()
.delimiter(args.delimiter as u8)
.has_headers(false)
.from_writer(buf_writer);

match aircraft_type {
AircraftType::A320 => {
while let Ok(fdr_data) = a320::read_record(&mut reader) {
writer.serialize(&fdr_data)?;

counter += 1;

if counter % 1000 == 0 {
print!("Processed {counter} entries...\r");
std::io::stdout().flush()?;
}
}
}
AircraftType::A380 => {
while let Ok(fdr_data) = a380::read_record(&mut reader) {
writer.serialize(&fdr_data)?;

counter += 1;

if counter % 1000 == 0 {
print!("Processed {counter} entries...\r");
std::io::stdout().flush()?;
}
}
}
}

println!("Processed {counter} entries...");

Result::Ok(())
}

0 comments on commit 2776100

Please sign in to comment.