// module: TRUE
import { JSPath } from "../shared/path.mjs";
import { Regulation } from "../shared/regulations.mjs";
import { CrashTest } from "../shared/crash_tests.mjs";
import { Protocol } from "../shared/protocols.mjs";
import { ProtocolAssessment } from "./automotive_assessments.mjs";
import { AssessmentType } from "../shared/assessment_types.mjs";
import { WorkflowOccupant } from "../shared/workflow_occupant.mjs";
/* TODO TODO TODO
* Questions for Chris Archer:
*
* 1. At the moment, I don't think we identify which occupant versions are appropriate
* for each protocol (e.g. Euro NCAP MPDB needs THOR 50M and HIII 50M), so we're not
* filtering in the PRIMER GUI accordingly. Rationale? When it comes to REPORTER, I
* think we'll need some way of checking that correct user data is in place, and I
* think that that would start by looking at correct occupant types, and then
* correct assessment types. We can check assessment types via
* protocolassessment.UniqueOccupantAssessmentTypes, but I can't find an existing way of
* checking occupants are valid. Solution: new protocol.OccupantVersions method.
* 2. In PRIMER, there doesn't seem to be a way of selecting the regulation version.
* I think we'll need this?
* /
/* Lists of all of the available parameters to check against */
let regulations = Regulation.GetAll();
let crash_tests = CrashTest.GetAll();
let assessment_types = AssessmentType.GetAll();
let positions = WorkflowOccupant.Positions();
let front_rears = WorkflowOccupant.FrontRear();
let sides = WorkflowOccupant.Sides();
/** Checks whether image filenames in REPORTER template are valid
* @param {string} output_dir REPORTER output directory for images etc.
*/
export function check_report_contents(f_json_name, output_dir) {
let reporter_data = {};
reporter_data.jobs = [];
let f_contents_name = `${output_dir}/report_contents.lst`;
if (!File.Exists(f_contents_name)) {
ErrorMessage(`Report contents list does not exist: ${f_contents_name}`);
return;
}
if (!File.IsReadable(f_contents_name)) {
ErrorMessage(`Report contents list could not be opened for reading: ${f_contents_name}`);
return;
}
let f_contents = new File(f_contents_name, File.READ);
let line;
let line_count = 0;
Message(`Reading report contents list: ${f_contents_name}`);
while ((line = f_contents.ReadLongLine()) != undefined) {
line_count++;
Message(`Processing line: ${line}`);
/* Expect each line to contain a path to an image filename in the form:
*
* ${output_dir}/${regulation}~${crash_test}~${version}~${occupants_str}~${assessment_type}.png
*
* Extract image filename (strip preceding path and trailing ".png"), leaving:
*
* ${regulation}~${crash_test}~${version}~${occupants_str}~${assessment_type}
*/
let re_filename = /(.*)[\\\/]([^~]*)~([^~]*)~([^~]*)~([^~]*)~([^~]*)\.(.*)/;
let match;
if ((match = line.match(re_filename))) {
var regulation = match[2];
var crash_test = match[3];
var version = match[4];
var occupants_str = match[5];
var assessment_type = match[6];
} else {
ErrorMessage(`Unexpected filename composition at line ${line_count}. Skipping.`);
continue;
}
// let image_filename = line.substring(line.lastIndexOf("/") + 1, line.length - 4);
// /* Split the filename into its components */
// let fields = image_filename.split("~");
// if (fields.length != 5) {
// ErrorMessage(`Unexpected filename composition at line ${line_count}. Skipping.`);
// continue;
// }
// /* Expected filename components */
// let regulation = fields[0];
// let crash_test = fields[1];
// let version = fields[2];
// let occupants_str = fields[3];
// let assessment_type = fields[4];
/* Check that filename components are valid */
if (!regulations.includes(regulation)) {
ErrorMessage(`Unexpected regulation "${regulation}" in filename at line ${line_count}. Skipping.`);
continue;
}
if (!crash_tests.includes(crash_test)) {
ErrorMessage(`Unexpected crash_test "${crash_test}" in filename at line ${line_count}. Skipping.`);
continue;
}
let versions = Protocol.Versions(regulation, crash_test);
if (!versions.includes(version)) {
ErrorMessage(`Unexpected version "${version}" in filename at line ${line_count}. Skipping.`);
continue;
}
if (!assessment_types.includes(assessment_type)) {
ErrorMessage(
`Unexpected assessment_type "${assessment_type}" in filename at line ${line_count}. Skipping.`
);
continue;
}
/* create a protocol with the default datums based on the regulation, crash test and version */
let protocol = ProtocolAssessment.CreateDefaultProtocol(regulation, crash_test, version);
/* The <occupants_str> needs further parsing. Multiple occupants are separated by underscores:
*
* ${occupant_1}_${occupant_2}_${occupant_3}
*
* And each occupant in turn is separated by hyphens into position, front_rear and side
* according to the WorkflowOccupant.toString() method:
*
* ${this.position}-${this.front_rear}-${this.side}
*/
let valid = true;
let occupants = occupants_str.split("_");
for (let i = 0; i < occupants.length; i++) {
let occupant_fields = occupants[i].split("-");
if (occupant_fields.length != 3) {
ErrorMessage(`Unexpected occupant composition ("${occupants[i]}") at line ${line_count}. Skipping.`);
valid = false;
continue;
}
/* Expected occupant components */
let position = occupant_fields[0];
let front_rear = occupant_fields[1];
let side = occupant_fields[2];
/* Check that occupant components are valid */
if (!positions.includes(position)) {
ErrorMessage(`Unexpected position "${position}" in filename at line ${line_count}. Skipping.`);
valid = false;
continue;
}
if (!front_rears.includes(front_rear)) {
ErrorMessage(`Unexpected front_rear "${front_rear}" in filename at line ${line_count}. Skipping.`);
valid = false;
continue;
}
if (!sides.includes(side)) {
ErrorMessage(`Unexpected side "${side}" in filename at line ${line_count}. Skipping.`);
valid = false;
continue;
}
/* Create new WorkflowOccupant from parsed data, but use placeholder version and body_parts for now */
/*occupant = new WorkflowOccupant(WorkflowOccupant.HUMANETICS_HIII_50M_V1_5, position, side, front_rear, [
OccupantBodyPart.CHEST
]);*/
/* This should really be a class, perhaps a parent class of WorkflowOccupant
* with just position and front_rear, and a separate array of valid occupant versions */
occupants[i] = { position: position, front_rear: front_rear };
//COMMENT RB: Do we want this to be (or look like) user_data?
//if the occupant null then it is 'latent' and needs to be defined.
//this should
}
if (!valid) {
ErrorMessage(`Unexpected occupant_str composition at line ${line_count}. Skipping.`);
continue;
}
/* Here, we assemble an object of REPORTER "jobs", each of which is a unique combination of
* crash test, regulation and version. Within each job, we create occupant variants.
* Occupant variants determine which curves are plotted e.g. Driver only, or Driver and
* Passenger together. We will have to run through the T/HIS graph plotting process once
* for each occupant variant within each job. Each occupant variant contains a list of its
* occupants. Each occupant has properties for the expected position, front_rear, and a
* list of the valid occupant versions for that occupant.
*
* All of this should probably be handled in a class.
*/
let found_reg_crash_ver = false;
for (let job of reporter_data.jobs) {
/* If we find an existing job that matches our requirements, add to it */
if (regulation == job.regulation && crash_test == job.crash_test && version == job.version) {
let found_occupants_str = false;
for (let occ_var of job.occupant_variants) {
/* If we find an existing matching occupant variant, add to it */
if (occupants_str == occ_var.occupants_str) {
/* If everything matches, must be an image duplicate in the report */
if (occ_var.assessment_types.includes(assessment_type)) {
WarningMessage(`Found duplicate image filename at line ${line_count}.`);
/* Else add to the assessment types processed for this occupant variant */
} else {
occ_var.assessment_types.push(assessment_type);
}
found_occupants_str = true;
}
}
/* If a matching occupant variant can't be found, add a new one */
if (!found_occupants_str) {
job.occupant_variants.push({
occupants_str: occupants_str,
occupants: occupants,
assessment_types: [assessment_type]
});
}
found_reg_crash_ver = true;
}
}
/* If a matching job can't be found, add a new one */
if (!found_reg_crash_ver) {
reporter_data.jobs.push({
regulation: regulation,
crash_test: crash_test,
version: version,
occupant_variants: [
{
occupants_str: occupants_str,
occupants: occupants,
assessment_types: [assessment_type]
}
]
});
}
}
f_contents.Close();
/* Write the job data as a JSON file that will be picked up by T/HIS */
let json_str = JSON.stringify(reporter_data);
let f_out_json_name = `${output_dir}/report_contents.json`;
let f_json = new File(f_out_json_name, File.WRITE);
f_json.Writeln(json_str);
f_json.Close();
/* Now check whether user data contains everything we need for our jobs */
/* Workflow class doesn't work for REPORTER yet so hard-wire JSON.parse for now */
f_json = new File(f_json_name, File.READ);
json_str = "";
while ((line = f_json.ReadLongLine()) != undefined) {
json_str = json_str + line;
}
f_json.Close();
let user_data = JSON.parse(json_str).workflows[0].data;
/* Whenever we find missing data, we will add it to a list */
let missing_data = [];
for (let job of reporter_data.jobs) {
if (user_data.crash_test != job.crash_test) {
missing_data.push(`Crash test: ${job.crash_test}`);
}
if (!user_data.regulations.includes(job.regulation)) {
missing_data.push(`Regulation: ${job.regulation}`);
}
/* Loop through all of the job's occupants and check that there is a matching occupant in
* the user data.
*/
for (let occ_var of job.occupant_variants) {
for (let job_occ of occ_var.occupants) {
let occ_match = false;
for (let user_occ of user_data.occupants) {
if (job_occ.position == user_occ.position && job_occ.front_rear == user_occ.front_rear) {
occ_match = true;
}
}
if (!occ_match) {
missing_data.push(
`Occupant: ${job_occ.position}-${job_occ.front_rear} [${job_occ.valid_occ_vers.join(", ")}]`
);
}
}
}
}
if (missing_data.length > 0) {
ErrorMessage(`Some of the user inputs required for this report are missing (see list below).`);
for (let msg of missing_data) {
Message(msg);
}
}
}