export { Protocol, Protocols };
import { OccupantBodyPart } from "./occupant.mjs";
import { WorkflowOccupant } from "./workflow_occupant.mjs";
import { Structure } from "./structure.mjs";
import { Regulation } from "./regulations.mjs";
import { CrashTest } from "./crash_tests.mjs";
import { AssessmentType } from "./assessment_types.mjs";
import { AssessmentDatums } from "../post/this/assessment_datums.mjs";
import { find_all_json_files, WriteJSON } from "./file_helper.mjs";
import { JSPath } from "./path.mjs";
import { ProtocolVehicle } from "./vehicle.mjs";
class Protocol {
/**
* Note you can create a protocol with no assessment_datums (e.g. empty array)
* which is useful when we do not need this data (e.g. PRIMER gui)
* @param {string} regulation Regulation, e.g. Regulation.CNCAP
* @param {string} crash_test Crash test, e.g. CrashTest.ODB
* @param {string} version Version, e.g. "7.1.2"
* @param {ProtocolVehicle} vehicle class containing info about valid occupant types
* @param {AssessmentDatums[]} [assessment_datums = []] Array of AssessmentDatums instances
* @example
* let protocol = new ProtocolAssessment(Regulation.EuroNCAP, CrashTest.ODB, "7.1.2", []);
*/
constructor(regulation, crash_test, version, vehicle, assessment_datums = []) {
this.regulation = regulation;
this.crash_test = crash_test;
this.version = version;
this.vehicle = vehicle;
this.assessment_datums = assessment_datums;
}
/* Instance property getter and setters */
/**
* Protocol regulation
* @type {string}
*/
get regulation() {
return this._regulation;
}
set regulation(new_regulation) {
if (Regulation.GetAll().indexOf(new_regulation) == -1) {
throw new Error(`Invalid version: ${new_regulation}`);
}
this._regulation = new_regulation;
}
/**
* Protocol crash test
* @type {string}
*/
get crash_test() {
return this._crash_test;
}
set crash_test(new_crash_test) {
if (CrashTest.GetAll().indexOf(new_crash_test) == -1) {
throw new Error(`Invalid crash_test: ${new_crash_test}`);
}
this._crash_test = new_crash_test;
}
/**
* Protocol version
* @type {string}
*/
get version() {
return this._version;
}
set version(new_version) {
this._version = new_version.toString();
}
/**
* Array of AssessmentDatums instances
* @type {AssessmentDatums[]}
*/
get assessment_datums() {
return this._assessment_datums;
}
set assessment_datums(new_assessment_datums) {
if (!(new_assessment_datums instanceof Array)) {
throw new Error("assessment_datums must be an Array");
}
for (let new_assessment_datum of new_assessment_datums) {
if (!(new_assessment_datum instanceof AssessmentDatums)) {
throw new Error("assessment_datums must be an array of AssessmentDatums instances");
}
}
this._assessment_datums = new_assessment_datums;
}
/**
* Create a Protocol instance with the default AssessmentDatums for
* the given regulation, test and year
* @param {string} regulation Protocol regulation, e.g. Regulastion.CNCAP
* @param {string} crash_test Protocol crash test, e.g. CrashTest.ODB
* @param {string} version Version, e.g. "7.1.2"
* @returns {Protocol}
* @example
* let p = Protocol.CreateDefaultProtocol(Regulation.CNCAP, CrashTest.ODB, "7.1.2");
*/
static CreateDefaultProtocol(regulation, crash_test, version) {
let vehicle = Protocols.GetProtocolVehicle(regulation, crash_test, version);
let assessment_datums = Protocol.CreateDefaultAssessmentDatums(regulation, crash_test, version);
return new Protocol(regulation, crash_test, version, vehicle, assessment_datums);
}
/**
* Create AssessmentDatums for the given regulation, test and year
* These are read from the default.json file in the datums/[regulation]/[crash_test]/[assessment] directory
* @param {string} regulation Protocol regulation, e.g. Regulation.CNCAP
* @param {string} crash_test Protocol crash test, e.g. CrashTest.ODB
* @param {string} version Protocol version, e.g. "7.1.2"
* @returns {AssessmentDatums[]}
* @example
* let a = Protocol.CreateDefaultAssessmentDatums(Regulation.CNCAP, CrashTest.ODB, "7.1.2");
*/
static CreateDefaultAssessmentDatums(regulation, crash_test, version) {
/* Create the AssessmentDatums based on the protocol/test/version */
/** @type {AssessmentDatums[]} */
let assessment_datums = [];
for (let assessment_type of AssessmentType.GetAll()) {
let assessment_datum = AssessmentDatums.ReadFromFile(
regulation,
crash_test,
version,
assessment_type,
"default"
);
/* It is expected that some of the assessment types won't have datums, so only add to
* the list if a datum file was found */
if (assessment_datum) {
assessment_datums.push(assessment_datum);
}
}
if (assessment_datums.length == 0) {
let datums_dir = JSPath.GetDatumsDirectory();
WarningMessage(
`Could not find any assessment datums for ${regulation} ${crash_test} ${version} in datums directory: ${datums_dir}`
);
}
return assessment_datums;
}
/**
* this constructs a protocol object from a filepath
* @param {string} filepath to a protocol json file
* @returns {?Protocol}
*
*/
static FromJSONFile(filepath) {
Message(`Reading protocol: ${filepath}`);
let f = new File(filepath, File.READ);
let text = f.ReadAll();
f.Close();
let o = JSON.parse(text);
//extract base filename from path
let match = filepath.match(/([^\\/]*)\.json$/i);
if (match != null) {
o.name = match[1]; //first capture group
} else {
Message(`failed to extract protocol from ${filepath}`);
return null;
}
return Protocol.FromJSON(o);
}
/**
* construct a Protocol from JSON
* @param {Object} json JSON of Protocol
* @returns {?Protocol}
*/
static FromJSON(json) {
//first check the input object contains valid keys
let expected_keys = ["regulation", "crash_test", "version", "vehicle"];
for (let key of expected_keys) {
if (!(key in json)) {
throw new Error(`${key} is not a key in the json object passed to Protocol.FromJSON(json)`);
}
}
//set undefined optional keys to empty array
let optional_keys = ["drive_side", "description"];
for (let key of optional_keys) {
if (!(key in json)) {
json[key] = [];
}
}
//then construct Occupant object. Note setters check they are valid
try {
let vehicle = ProtocolVehicle.FromJSON(json.vehicle);
let assessment_datums = Protocol.CreateDefaultAssessmentDatums(
json.regulation,
json.crash_test,
json.version
);
return new Protocol(json.regulation, json.crash_test, json.version, vehicle, assessment_datums);
} catch (error) {
Message(`${error} so skipping parsing JSON for this protocol.`);
return null;
}
}
/**
* Returns the versions we support for a regulation and crash test
* (Most recent version first)
* @param {string} regulation Regulation, e.g. Regulation.CNCAP
* @param {string} crash_test Crash test, e.g. CrashTest.ODB
* @returns {string[]}
* @example
* let versions = Protocol.Versions(Regulation.CNCAP, CrashTest.ODB);
*/
static Versions(regulation, crash_test) {
let protocols = Protocols.GetOnly(regulation, crash_test, "ALL");
let versions = [];
for (let protocol of protocols) {
versions.push(protocol.version.toString());
}
/* If it gets here we support the crash test, but we haven't added it to the switch
* statements in this function so write an error message to flag that it needs to be added. */
if (versions.length == 0) {
ErrorMessage(`No protocol versions found for ${regulation} ${crash_test}.`);
return [];
}
return versions.sort();
}
/* Instance methods */
/**
* Returns the assessment types we support for the protocol
* and the given occupant and body part type
* @param {WorkflowOccupant} occupant Occupant
* @param {string} body_part_type Occupant body part type
* @returns {string[]}
* @example
* let assessment_types = p.OccupantAssessmentTypes(o, OccupantBodyPart.NECK);
*/
OccupantAssessmentTypes(occupant, body_part_type) {
/* Return assessment types based on regulation, crash test and version */
let crash_tests = Regulation.CrashTests(this.regulation);
for (let crash_test of crash_tests) {
if (this.crash_test != crash_test) continue;
switch (this.regulation) {
case Regulation.CNCAP:
switch (crash_test) {
case CrashTest.ODB:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [
AssessmentType.HEAD_HIC,
AssessmentType.HEAD_THREE_MS_EXCEEDENCE,
AssessmentType.HEAD_PEAK_ACCELERATION
];
case OccupantBodyPart.NECK:
/* Assessments are different for the front and rear occupants */
if (occupant.GetRow() == WorkflowOccupant.FRONT) {
return [
AssessmentType.NECK_SHEAR_EXCEEDENCE,
AssessmentType.NECK_TENSION_EXCEEDENCE,
AssessmentType.NECK_EXTENSION
];
} else if (occupant.GetRow() == WorkflowOccupant.REAR) {
return [
AssessmentType.NECK_SHEAR,
AssessmentType.NECK_AXIAL,
AssessmentType.NECK_EXTENSION_REAR_PASSENGER
];
} else {
return [];
}
case OccupantBodyPart.CHEST:
/* Assessments are different for the front and rear occupants */
if (occupant.GetRow() == WorkflowOccupant.FRONT) {
return [
AssessmentType.CHEST_COMPRESSION,
AssessmentType.CHEST_THREE_MS_EXCEEDENCE
];
} else if (occupant.GetRow() == WorkflowOccupant.REAR) {
return [
AssessmentType.CHEST_COMPRESSION_REAR_PASSENGER,
AssessmentType.CHEST_THREE_MS_EXCEEDENCE
];
} else {
return [];
}
case OccupantBodyPart.FEMUR:
if (occupant.GetRow() == WorkflowOccupant.FRONT) {
return [
AssessmentType.LEFT_FEMUR_COMPRESSION_EXCEEDENCE,
AssessmentType.RIGHT_FEMUR_COMPRESSION_EXCEEDENCE
];
} else {
return [];
}
case OccupantBodyPart.KNEE:
if (occupant.GetRow() == WorkflowOccupant.FRONT) {
return [
AssessmentType.LEFT_KNEE_DISPLACEMENT,
AssessmentType.RIGHT_KNEE_DISPLACEMENT
];
} else {
return [];
}
case OccupantBodyPart.TIBIA:
if (occupant.GetRow() == WorkflowOccupant.FRONT) {
return [
AssessmentType.LEFT_TIBIA_COMPRESSION,
AssessmentType.RIGHT_TIBIA_COMPRESSION,
AssessmentType.LEFT_TIBIA_INDEX,
AssessmentType.RIGHT_TIBIA_INDEX
];
} else {
return [];
}
default:
return [];
}
case CrashTest.MPDB:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [
AssessmentType.HEAD_HIC,
AssessmentType.HEAD_THREE_MS_EXCEEDENCE,
AssessmentType.HEAD_PEAK_ACCELERATION
];
case OccupantBodyPart.NECK:
if (occupant.position == WorkflowOccupant.DRIVER) {
return [
AssessmentType.NECK_SHEAR,
AssessmentType.NECK_AXIAL,
AssessmentType.NECK_EXTENSION
];
} else {
return [
AssessmentType.NECK_SHEAR_PASSENGER,
AssessmentType.NECK_AXIAL_PASSENGER,
AssessmentType.NECK_EXTENSION_PASSENGER
];
}
case OccupantBodyPart.ABDOMEN:
if (occupant.position == WorkflowOccupant.DRIVER) {
return [
AssessmentType.ABDOMEN_COMPRESSION_LEFT,
AssessmentType.ABDOMEN_COMPRESSION_RIGHT
];
} else {
return [];
}
case OccupantBodyPart.CHEST:
if (occupant.position == WorkflowOccupant.DRIVER) {
return [
AssessmentType.CHEST_COMPRESSION_LEFT,
AssessmentType.CHEST_COMPRESSION_RIGHT
];
} else {
return [
AssessmentType.CHEST_COMPRESSION_PASSENGER,
AssessmentType.CHEST_VISCOUS_CRITERION
];
}
case OccupantBodyPart.PELVIS:
if (occupant.position == WorkflowOccupant.DRIVER) {
return [
AssessmentType.ACETABULAR_FORCE_LEFT,
AssessmentType.ACETABULAR_FORCE_RIGHT
];
} else {
return [];
}
case OccupantBodyPart.FEMUR:
if (occupant.position == WorkflowOccupant.DRIVER) {
return [
AssessmentType.LEFT_FEMUR_COMPRESSION_EXCEEDENCE,
AssessmentType.RIGHT_FEMUR_COMPRESSION_EXCEEDENCE
];
} else {
return [AssessmentType.LEFT_FEMUR_AXIAL, AssessmentType.RIGHT_FEMUR_AXIAL];
}
case OccupantBodyPart.KNEE:
if (occupant.position == WorkflowOccupant.DRIVER) {
return [
AssessmentType.LEFT_KNEE_DISPLACEMENT,
AssessmentType.RIGHT_KNEE_DISPLACEMENT
];
} else {
return [];
}
case OccupantBodyPart.TIBIA:
if (occupant.position == WorkflowOccupant.DRIVER) {
return [
AssessmentType.LEFT_TIBIA_COMPRESSION,
AssessmentType.RIGHT_TIBIA_COMPRESSION,
AssessmentType.LEFT_TIBIA_INDEX,
AssessmentType.RIGHT_TIBIA_INDEX
];
} else {
return [];
}
default:
return [];
}
}
break;
case Regulation.EuroNCAP:
switch (crash_test) {
case CrashTest.ODB:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [
AssessmentType.HEAD_HIC,
AssessmentType.HEAD_THREE_MS_EXCEEDENCE,
AssessmentType.HEAD_PEAK_ACCELERATION
];
case OccupantBodyPart.NECK:
return [
AssessmentType.NECK_SHEAR_EXCEEDENCE,
AssessmentType.NECK_TENSION_EXCEEDENCE,
AssessmentType.NECK_EXTENSION
];
case OccupantBodyPart.CHEST:
return [AssessmentType.CHEST_COMPRESSION, AssessmentType.CHEST_VISCOUS_CRITERION];
case OccupantBodyPart.FEMUR:
return [
AssessmentType.LEFT_FEMUR_COMPRESSION_EXCEEDENCE,
AssessmentType.RIGHT_FEMUR_COMPRESSION_EXCEEDENCE
];
case OccupantBodyPart.KNEE:
return [
AssessmentType.LEFT_KNEE_DISPLACEMENT,
AssessmentType.RIGHT_KNEE_DISPLACEMENT
];
case OccupantBodyPart.TIBIA:
return [
AssessmentType.LEFT_TIBIA_COMPRESSION,
AssessmentType.RIGHT_TIBIA_COMPRESSION,
AssessmentType.LEFT_TIBIA_INDEX,
AssessmentType.RIGHT_TIBIA_INDEX
];
default:
return [];
}
case CrashTest.FFB:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [
AssessmentType.HEAD_HIC,
AssessmentType.HEAD_THREE_MS_EXCEEDENCE,
AssessmentType.HEAD_PEAK_ACCELERATION
];
case OccupantBodyPart.NECK:
return [
AssessmentType.NECK_SHEAR,
AssessmentType.NECK_AXIAL,
AssessmentType.NECK_EXTENSION
];
case OccupantBodyPart.CHEST:
return [AssessmentType.CHEST_COMPRESSION, AssessmentType.CHEST_VISCOUS_CRITERION];
case OccupantBodyPart.FEMUR:
return [AssessmentType.LEFT_FEMUR_AXIAL, AssessmentType.RIGHT_FEMUR_AXIAL];
default:
return [];
}
case CrashTest.MPDB:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [
AssessmentType.HEAD_HIC,
AssessmentType.HEAD_THREE_MS_EXCEEDENCE,
AssessmentType.HEAD_PEAK_ACCELERATION
];
case OccupantBodyPart.NECK:
if (occupant.position == WorkflowOccupant.DRIVER) {
return [
AssessmentType.NECK_SHEAR,
AssessmentType.NECK_AXIAL,
AssessmentType.NECK_EXTENSION
];
} else {
return [
AssessmentType.NECK_SHEAR_EXCEEDENCE,
AssessmentType.NECK_TENSION_EXCEEDENCE,
AssessmentType.NECK_EXTENSION
];
}
case OccupantBodyPart.ABDOMEN:
if (occupant.position == WorkflowOccupant.DRIVER) {
return [
AssessmentType.ABDOMEN_COMPRESSION_LEFT,
AssessmentType.ABDOMEN_COMPRESSION_RIGHT
];
} else {
return [];
}
case OccupantBodyPart.CHEST:
if (occupant.position == WorkflowOccupant.DRIVER) {
return [
AssessmentType.CHEST_COMPRESSION_LEFT,
AssessmentType.CHEST_COMPRESSION_RIGHT
];
} else {
return [
AssessmentType.CHEST_COMPRESSION,
AssessmentType.CHEST_VISCOUS_CRITERION
];
}
case OccupantBodyPart.PELVIS:
if (occupant.position == WorkflowOccupant.DRIVER) {
return [
AssessmentType.ACETABULAR_FORCE_LEFT,
AssessmentType.ACETABULAR_FORCE_RIGHT
];
} else {
return [];
}
case OccupantBodyPart.FEMUR:
return [
AssessmentType.LEFT_FEMUR_COMPRESSION_EXCEEDENCE,
AssessmentType.RIGHT_FEMUR_COMPRESSION_EXCEEDENCE
];
case OccupantBodyPart.KNEE:
return [
AssessmentType.LEFT_KNEE_DISPLACEMENT,
AssessmentType.RIGHT_KNEE_DISPLACEMENT
];
case OccupantBodyPart.TIBIA:
return [
AssessmentType.LEFT_TIBIA_COMPRESSION,
AssessmentType.RIGHT_TIBIA_COMPRESSION,
AssessmentType.LEFT_TIBIA_INDEX,
AssessmentType.RIGHT_TIBIA_INDEX
];
default:
return [];
}
case CrashTest.FAR_SIDE:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [AssessmentType.HEAD_HIC, AssessmentType.HEAD_THREE_MS_EXCEEDENCE];
case OccupantBodyPart.NECK:
return [
AssessmentType.NECK_UPPER_EXTENSION,
AssessmentType.NECK_LOWER_EXTENSION,
AssessmentType.NECK_UPPER_FLEXION,
AssessmentType.NECK_LOWER_FLEXION,
AssessmentType.NECK_UPPER_AXIAL,
AssessmentType.NECK_LOWER_AXIAL
];
case OccupantBodyPart.CHEST:
return [AssessmentType.CHEST_COMPRESSION];
case OccupantBodyPart.ABDOMEN:
return [AssessmentType.ABDOMEN_COMPRESSION];
case OccupantBodyPart.LUMBAR:
return [
AssessmentType.LUMBAR_SHEAR,
AssessmentType.LUMBAR_AXIAL,
AssessmentType.LUMBAR_TORSION
];
case OccupantBodyPart.PELVIS:
return [AssessmentType.PELVIS_FORCE];
default:
return [];
}
case CrashTest.MDB:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [AssessmentType.HEAD_HIC, AssessmentType.HEAD_THREE_MS_EXCEEDENCE];
case OccupantBodyPart.SHOULDER:
return [AssessmentType.SHOULDER_LATERAL_FORCES];
case OccupantBodyPart.CHEST:
return [AssessmentType.CHEST_COMPRESSION, AssessmentType.CHEST_VISCOUS_CRITERION];
case OccupantBodyPart.ABDOMEN:
return [
AssessmentType.ABDOMEN_COMPRESSION,
AssessmentType.ABDOMEN_VISCOUS_CRITERION
];
case OccupantBodyPart.PELVIS:
return [AssessmentType.PELVIS_FORCE];
default:
return [];
}
case CrashTest.SIDE_POLE:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [AssessmentType.HEAD_HIC, AssessmentType.HEAD_PEAK_ACCELERATION];
case OccupantBodyPart.SHOULDER:
return [AssessmentType.SHOULDER_LATERAL_FORCES];
case OccupantBodyPart.CHEST:
return [AssessmentType.CHEST_COMPRESSION, AssessmentType.CHEST_VISCOUS_CRITERION];
case OccupantBodyPart.ABDOMEN:
return [
AssessmentType.ABDOMEN_COMPRESSION,
AssessmentType.ABDOMEN_VISCOUS_CRITERION
];
case OccupantBodyPart.PELVIS:
return [AssessmentType.PELVIS_FORCE];
default:
return [];
}
}
break;
case Regulation.IIHS:
switch (crash_test) {
case CrashTest.ODB:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [AssessmentType.HEAD_HIC];
case OccupantBodyPart.NECK:
return [
AssessmentType.NECK_SHEAR_EXCEEDENCE,
AssessmentType.NECK_TENSION_EXCEEDENCE,
AssessmentType.NECK_COMPRESSION_EXCEEDENCE,
AssessmentType.NECK_AXIAL,
AssessmentType.NECK_NIJ
];
case OccupantBodyPart.CHEST:
return [
AssessmentType.CHEST_COMPRESSION,
AssessmentType.CHEST_COMPRESSION_RATE,
AssessmentType.CHEST_THREE_MS_EXCEEDENCE,
AssessmentType.CHEST_VISCOUS_CRITERION
];
case OccupantBodyPart.FEMUR:
return [
AssessmentType.LEFT_FEMUR_AXIAL,
AssessmentType.RIGHT_FEMUR_AXIAL,
AssessmentType.LEFT_FEMUR_COMPRESSION_EXCEEDENCE,
AssessmentType.RIGHT_FEMUR_COMPRESSION_EXCEEDENCE
];
case OccupantBodyPart.KNEE:
return [
AssessmentType.LEFT_KNEE_DISPLACEMENT,
AssessmentType.RIGHT_KNEE_DISPLACEMENT
];
case OccupantBodyPart.TIBIA:
return [
AssessmentType.LEFT_TIBIA_COMPRESSION,
AssessmentType.RIGHT_TIBIA_COMPRESSION,
AssessmentType.LEFT_TIBIA_INDEX,
AssessmentType.RIGHT_TIBIA_INDEX
];
case OccupantBodyPart.FOOT:
return [
AssessmentType.LEFT_FOOT_ACCELERATION,
AssessmentType.RIGHT_FOOT_ACCELERATION
];
default:
return [];
}
case CrashTest.MDB:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [AssessmentType.HEAD_HIC];
case OccupantBodyPart.NECK:
switch (this.version) {
case "2021":
return [AssessmentType.NECK_AXIAL];
case "2016":
return [
AssessmentType.NECK_AXIAL,
AssessmentType.NECK_TENSION_EXCEEDENCE,
AssessmentType.NECK_COMPRESSION_EXCEEDENCE,
AssessmentType.NECK_SHEAR_EXCEEDENCE
];
default:
return [];
}
case OccupantBodyPart.SHOULDER:
return [
AssessmentType.SHOULDER_DEFLECTION,
AssessmentType.SHOULDER_DEFLECTION_RATE,
AssessmentType.SHOULDER_VISCOUS_CRITERION
];
case OccupantBodyPart.CHEST:
return [
AssessmentType.CHEST_COMPRESSION,
AssessmentType.CHEST_COMPRESSION_RATE,
AssessmentType.CHEST_VISCOUS_CRITERION
];
case OccupantBodyPart.PELVIS:
switch (this.version) {
case "2021":
return [AssessmentType.PELVIS_FORCE];
case "2016":
return [
AssessmentType.ACETABULAR_FORCE,
AssessmentType.ILIUM_FORCE,
AssessmentType.PELVIS_FORCE
];
default:
return [];
}
default:
return [];
}
case CrashTest.SOB:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [AssessmentType.HEAD_HIC];
case OccupantBodyPart.NECK:
return [
AssessmentType.NECK_SHEAR_EXCEEDENCE,
AssessmentType.NECK_TENSION_EXCEEDENCE,
AssessmentType.NECK_COMPRESSION_EXCEEDENCE,
AssessmentType.NECK_AXIAL,
AssessmentType.NECK_NIJ
];
case OccupantBodyPart.CHEST:
return [
AssessmentType.CHEST_COMPRESSION,
AssessmentType.CHEST_COMPRESSION_RATE,
AssessmentType.CHEST_THREE_MS_EXCEEDENCE,
AssessmentType.CHEST_VISCOUS_CRITERION
];
case OccupantBodyPart.FEMUR:
return [
AssessmentType.LEFT_FEMUR_AXIAL,
AssessmentType.RIGHT_FEMUR_AXIAL,
AssessmentType.LEFT_FEMUR_COMPRESSION_VS_IMPULSE,
AssessmentType.RIGHT_FEMUR_COMPRESSION_VS_IMPULSE
];
case OccupantBodyPart.KNEE:
return [
AssessmentType.LEFT_KNEE_DISPLACEMENT,
AssessmentType.RIGHT_KNEE_DISPLACEMENT
];
case OccupantBodyPart.TIBIA:
return [
AssessmentType.LEFT_TIBIA_COMPRESSION,
AssessmentType.RIGHT_TIBIA_COMPRESSION,
AssessmentType.LEFT_TIBIA_INDEX,
AssessmentType.RIGHT_TIBIA_INDEX
];
case OccupantBodyPart.FOOT:
return [
AssessmentType.LEFT_FOOT_ACCELERATION,
AssessmentType.RIGHT_FOOT_ACCELERATION
];
default:
return [];
}
}
break;
case Regulation.USNCAP:
switch (crash_test) {
case CrashTest.FFB:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [AssessmentType.HEAD_HIC];
case OccupantBodyPart.NECK:
return [
AssessmentType.NECK_COMPRESSION_EXCEEDENCE,
AssessmentType.NECK_TENSION_EXCEEDENCE,
AssessmentType.NECK_NIJ
];
case OccupantBodyPart.CHEST:
return [AssessmentType.CHEST_COMPRESSION];
case OccupantBodyPart.FEMUR:
return [AssessmentType.LEFT_FEMUR_FORCE, AssessmentType.RIGHT_FEMUR_FORCE];
default:
return [];
}
case CrashTest.MDB:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [AssessmentType.HEAD_HIC];
case OccupantBodyPart.CHEST:
return [AssessmentType.CHEST_COMPRESSION];
case OccupantBodyPart.ABDOMEN:
return [AssessmentType.ABDOMEN_FORCE];
case OccupantBodyPart.PELVIS:
return [AssessmentType.PELVIS_FORCE];
default:
return [];
}
case CrashTest.SIDE_POLE:
switch (body_part_type) {
case OccupantBodyPart.HEAD:
return [AssessmentType.HEAD_HIC];
case OccupantBodyPart.PELVIS:
return [AssessmentType.PELVIS_FORCE];
default:
return [];
}
}
break;
}
/* If it gets here we support the crash test, but we haven't added it to the switch
* statements in this function so write an error message to flag that it needs to be added. */
ErrorMessage(
`Crash test ${crash_test} needs to be added to <protocol.OccupantAssessmentTypes> for ${this.regulation}`
);
}
return [];
}
/**
* Returns the assessment types we support for the protocol
* and the given structure
* @param {Structure} structure Structure
* @returns {string[]}
* @example
* let assessment_types = p.StructureAssessmentTypes(s);
*/
StructureAssessmentTypes(structure) {
/* Return assessment types based on regulation, crash test and version */
let crash_tests = Regulation.CrashTests(this.regulation);
let structure_type = structure.component_type;
for (let crash_test of crash_tests) {
if (this.crash_test != crash_test) continue;
switch (this.regulation) {
case Regulation.CNCAP:
switch (crash_test) {
case CrashTest.ODB:
switch (structure_type) {
case Structure.A_PILLAR:
return [AssessmentType.A_PILLAR_FORE_AFT_INTRUSION];
case Structure.ACCELERATOR_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.BRAKE_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.CLUTCH_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.STEERING_COLUMN:
return [
AssessmentType.STEERING_COLUMN_FORE_AFT_INTRUSION,
AssessmentType.STEERING_COLUMN_VERTICAL_INTRUSION
];
default:
return [];
}
case CrashTest.MPDB:
switch (structure_type) {
case Structure.A_PILLAR:
return [AssessmentType.A_PILLAR_FORE_AFT_INTRUSION];
case Structure.ACCELERATOR_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.BRAKE_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.STEERING_COLUMN:
return [AssessmentType.STEERING_COLUMN_ALL_INTRUSION];
default:
return [];
}
}
break;
case Regulation.EuroNCAP:
switch (crash_test) {
case CrashTest.ODB:
switch (structure_type) {
case Structure.A_PILLAR:
return [AssessmentType.A_PILLAR_FORE_AFT_INTRUSION];
case Structure.ACCELERATOR_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.BRAKE_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.CLUTCH_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.STEERING_COLUMN:
return [AssessmentType.STEERING_COLUMN_ALL_INTRUSION];
default:
return [];
}
case CrashTest.FFB:
switch (structure_type) {
case Structure.STEERING_COLUMN:
return [AssessmentType.STEERING_COLUMN_ALL_INTRUSION];
default:
return [];
}
case CrashTest.MPDB:
switch (structure_type) {
case Structure.A_PILLAR:
return [AssessmentType.A_PILLAR_FORE_AFT_INTRUSION];
case Structure.ACCELERATOR_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.BRAKE_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.STEERING_COLUMN:
return [AssessmentType.STEERING_COLUMN_ALL_INTRUSION];
default:
return [];
}
case CrashTest.FAR_SIDE:
switch (structure_type) {
case Structure.HEAD_EXCURSION:
return [AssessmentType.HEAD_EXCURSION];
default:
return [];
}
case CrashTest.MDB:
return [];
case CrashTest.SIDE_POLE:
return [];
}
break;
case Regulation.IIHS:
switch (crash_test) {
case CrashTest.ODB:
switch (structure_type) {
case Structure.BRAKE_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_LATERAL_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.CLUTCH_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_LATERAL_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.DRIVER_LEFT_TOEPAN:
return [AssessmentType.DRIVER_LEFT_TOEPAN_ALL_INTRUSION];
case Structure.DRIVER_CENTRE_TOEPAN:
return [AssessmentType.DRIVER_CENTRE_TOEPAN_ALL_INTRUSION];
case Structure.DRIVER_RIGHT_TOEPAN:
return [AssessmentType.DRIVER_RIGHT_TOEPAN_ALL_INTRUSION];
case Structure.DOOR:
return [AssessmentType.DOOR_FORE_AFT_INTRUSION];
case Structure.DRIVER_FOOTREST:
return [AssessmentType.DRIVER_FOOTREST_ALL_INTRUSION];
case Structure.LEFT_INSTRUMENT_PANEL:
return [AssessmentType.LEFT_INSTRUMENT_PANEL_FORE_AFT_INTRUSION];
case Structure.RIGHT_INSTRUMENT_PANEL:
return [AssessmentType.RIGHT_INSTRUMENT_PANEL_FORE_AFT_INTRUSION];
default:
return [];
}
case CrashTest.MDB:
switch (structure_type) {
case Structure.B_PILLAR:
return [AssessmentType.B_PILLAR_INTRUSION];
default:
return [];
}
case CrashTest.SOB:
switch (structure_type) {
case Structure.BRAKE_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_LATERAL_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.CLUTCH_PEDAL:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_LATERAL_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.PARKING_BRAKE:
return [
AssessmentType.PEDAL_FORE_AFT_INTRUSION,
AssessmentType.PEDAL_LATERAL_INTRUSION,
AssessmentType.PEDAL_VERTICAL_INTRUSION
];
case Structure.DRIVER_LEFT_TOEPAN:
return [AssessmentType.DRIVER_LEFT_TOEPAN_ALL_INTRUSION];
case Structure.DRIVER_CENTRE_TOEPAN:
return [AssessmentType.DRIVER_CENTRE_TOEPAN_ALL_INTRUSION];
case Structure.DRIVER_RIGHT_TOEPAN:
return [AssessmentType.DRIVER_RIGHT_TOEPAN_ALL_INTRUSION];
case Structure.PASSENGER_LEFT_TOEPAN:
return [AssessmentType.PASSENGER_LEFT_TOEPAN_ALL_INTRUSION];
case Structure.PASSENGER_CENTRE_TOEPAN:
return [AssessmentType.PASSENGER_CENTRE_TOEPAN_ALL_INTRUSION];
case Structure.PASSENGER_RIGHT_TOEPAN:
return [AssessmentType.PASSENGER_RIGHT_TOEPAN_ALL_INTRUSION];
case Structure.DOOR:
return [AssessmentType.DOOR_FORE_AFT_INTRUSION];
case Structure.DRIVER_FOOTREST:
return [AssessmentType.DRIVER_FOOTREST_ALL_INTRUSION];
case Structure.PASSENGER_FOOTREST:
return [AssessmentType.PASSENGER_FOOTREST_ALL_INTRUSION];
case Structure.LEFT_INSTRUMENT_PANEL:
return [AssessmentType.LEFT_INSTRUMENT_PANEL_FORE_AFT_INTRUSION];
case Structure.RIGHT_INSTRUMENT_PANEL:
return [AssessmentType.RIGHT_INSTRUMENT_PANEL_FORE_AFT_INTRUSION];
case Structure.STEERING_COLUMN:
return [AssessmentType.STEERING_COLUMN_FORE_AFT_INTRUSION];
case Structure.DRIVER_UPPER_DASH:
return [AssessmentType.DRIVER_UPPER_DASH_ALL_INTRUSION];
case Structure.PASSENGER_UPPER_DASH:
return [AssessmentType.PASSENGER_UPPER_DASH_ALL_INTRUSION];
case Structure.PASSENGER_LEFT_LOWER_DASH:
return [AssessmentType.PASSENGER_LEFT_LOWER_DASH_ALL_INTRUSION];
case Structure.PASSENGER_RIGHT_LOWER_DASH:
return [AssessmentType.PASSENGER_RIGHT_LOWER_DASH_ALL_INTRUSION];
case Structure.PASSENGER_CENTRE_DASH:
return [AssessmentType.PASSENGER_CENTRE_DASH_ALL_INTRUSION];
case Structure.DRIVER_UPPER_HINGE_1:
return [AssessmentType.DRIVER_UPPER_HINGE_1_ALL_INTRUSION];
case Structure.DRIVER_UPPER_HINGE_2:
return [AssessmentType.DRIVER_UPPER_HINGE_2_ALL_INTRUSION];
case Structure.DRIVER_UPPER_HINGE_3:
return [AssessmentType.DRIVER_UPPER_HINGE_3_ALL_INTRUSION];
case Structure.DRIVER_LOWER_HINGE_1:
return [AssessmentType.DRIVER_LOWER_HINGE_1_ALL_INTRUSION];
case Structure.DRIVER_LOWER_HINGE_2:
return [AssessmentType.DRIVER_LOWER_HINGE_2_ALL_INTRUSION];
case Structure.DRIVER_LOWER_HINGE_3:
return [AssessmentType.DRIVER_LOWER_HINGE_3_ALL_INTRUSION];
case Structure.PASSENGER_LOWER_HINGE_1:
return [AssessmentType.PASSENGER_LOWER_HINGE_1_ALL_INTRUSION];
case Structure.PASSENGER_LOWER_HINGE_2:
return [AssessmentType.PASSENGER_LOWER_HINGE_2_ALL_INTRUSION];
case Structure.PASSENGER_LOWER_HINGE_3:
return [AssessmentType.PASSENGER_LOWER_HINGE_3_ALL_INTRUSION];
case Structure.PASSENGER_UPPER_HINGE_1:
return [AssessmentType.PASSENGER_UPPER_HINGE_1_ALL_INTRUSION];
case Structure.PASSENGER_UPPER_HINGE_2:
return [AssessmentType.PASSENGER_UPPER_HINGE_2_ALL_INTRUSION];
case Structure.PASSENGER_UPPER_HINGE_3:
return [AssessmentType.PASSENGER_UPPER_HINGE_3_ALL_INTRUSION];
case Structure.DRIVER_ROCKER_PANEL_1:
return [AssessmentType.DRIVER_ROCKER_PANEL_1_LATERAL_INTRUSION];
case Structure.DRIVER_ROCKER_PANEL_2:
return [AssessmentType.DRIVER_ROCKER_PANEL_2_LATERAL_INTRUSION];
case Structure.DRIVER_ROCKER_PANEL_3:
return [AssessmentType.DRIVER_ROCKER_PANEL_3_LATERAL_INTRUSION];
case Structure.PASSENGER_ROCKER_PANEL_1:
return [AssessmentType.PASSENGER_ROCKER_PANEL_1_LATERAL_INTRUSION];
case Structure.PASSENGER_ROCKER_PANEL_2:
return [AssessmentType.PASSENGER_ROCKER_PANEL_2_LATERAL_INTRUSION];
case Structure.PASSENGER_ROCKER_PANEL_3:
return [AssessmentType.PASSENGER_ROCKER_PANEL_3_LATERAL_INTRUSION];
default:
return [];
}
}
break;
case Regulation.USNCAP:
switch (crash_test) {
case CrashTest.FFB:
return [];
case CrashTest.MDB:
return [];
case CrashTest.SIDE_POLE:
return [];
}
break;
}
/* If it gets here we support the crash test, but we haven't added it to the switch
* statements in this function so write an error message to flag that it needs to be added. */
ErrorMessage(
`Crash test ${crash_test} needs to be added to <protocol.StructureAssessmentTypes> for ${this.regulation}`
);
}
return [];
}
/**
* @typedef {Object} ViscousCriterionConstants
* @property {number} A Viscous Criterion Constant A (for m/s)
* @property {number} B Viscous Criterion Constant B (for m/s)
*/
/**
* Returns the Viscous Criterion Constants
* @returns {?ViscousCriterionConstants}
*/
GetViscousCriterionConstants() {
/* These values are taken from the T/HIS manual Appendix E.3
*
* A = 1.3 for frontal, 1.0 side impacts
* B = 0.229 for frontal, 0.14 side impacts
*/
switch (this.crash_test) {
case CrashTest.ODB:
case CrashTest.FFB:
case CrashTest.MPDB:
return {
A: 1.3,
B: 0.229
};
case CrashTest.MDB:
case CrashTest.SIDE_POLE:
case CrashTest.SOB:
case CrashTest.FAR_SIDE:
return {
A: 1.0,
B: 0.14
};
default:
ErrorMessage(
`Unknown crash test ${this.crash_test} in <Protocol>.GetViscousCriterion(). Returning null.`
);
return null;
}
}
/**
* Returns the datums for the given assessment
* @param {string} assessment Assessment type, e.g. AssessmentType.NECK_AXIAL
* @returns {?AssessmentDatums}
* @example
* let assessment_datums = p.GetDatumsByAssessment(AssessmentType.NECK_EXTENSION);
* for (let datum of assessment_datums.datums)
* {
* datum.Plot();
* }
*/
GetDatumsByAssessment(assessment) {
for (let assessment_datums of this.assessment_datums) {
if (assessment_datums.assessment == assessment) {
return assessment_datums;
}
}
return null;
}
/**
* Returns the HIC window (in seconds) for the protocol and the given occupant
* @param {WorkflowOccupant} occupant Workflow occupant
* @returns {number}
* @example
* // Get the HIC window to use for Protocol p and occupant o
* let hic_window = p.HICWindow(o);
*/
HICWindow(occupant) {
if (!occupant) {
throw new Error("No occupant specified in <protocol.HICWindow>");
}
if (this.regulation == Regulation.CNCAP) {
if (this.crash_test == CrashTest.ODB) {
if (occupant.position == WorkflowOccupant.DRIVER) {
return 0.036;
} else if (occupant.position == WorkflowOccupant.FRONT_PASSENGER) {
return 0.036;
} else {
return 0.015;
}
} else if (this.crash_test == CrashTest.MPDB) {
return 0.015;
}
} else if (this.regulation == Regulation.EuroNCAP) {
if (
this.crash_test == CrashTest.FAR_SIDE ||
this.crash_test == CrashTest.FFB ||
this.crash_test == CrashTest.MPDB ||
this.crash_test == CrashTest.MDB ||
this.crash_test == CrashTest.ODB ||
this.crash_test == CrashTest.SIDE_POLE
) {
return 0.015;
}
} else if (this.regulation == Regulation.IIHS) {
if (
this.crash_test == CrashTest.MDB ||
this.crash_test == CrashTest.ODB ||
this.crash_test == CrashTest.SOB
) {
return 0.015;
}
} else if (this.regulation == Regulation.USNCAP) {
if (this.crash_test == CrashTest.FFB) {
return 0.036;
} else if (this.crash_test == CrashTest.MDB || this.crash_test == CrashTest.SIDE_POLE) {
return 0.015;
}
}
/* Get here and we don't know what to do, so return a default of 0.015*/
ErrorMessage(
`Unknown regulation ${this.regulation} and crash test ${this.crash_test} in <protocol.HICWindow>. Returning 0.015 seconds.`
);
return 0.015;
}
/**
* String representation
* @returns {string}
*/
toString() {
return `${this.regulation} ${this.crash_test} ${this.version}`;
}
}
class Protocols {
/**
* Class to store supported protocols with getters for
* protocols based on crash test, regulation and occupant position/side. It is not instantiable. It behaves like a singleton
* where the files should only be imported once.
*
* if we want to reimport the json files we simply need to call Protocols.importAllOccupantJSONFiles()
* again and it will overwrite this._protocols property returned by OccupantVersion.protocols
*/
/**
* Initialises the Protocols class for the specified Workflows definitions directory
* @example
* Protocols.Initialise();
*/
static Initialise() {
if (!this._protocols) Protocols._importAllProtocolJSONFiles();
}
/**
* Returns an array of all the Protocol classes.
* @returns {Protocol[]}
* @example
* let protocols = Protocols.GetAll();
*/
static GetAll() {
return this._protocols;
}
/**
* TODO this will supercede the versions and structure types once fully implemented
* this class method imports all the protocol json files in the protocols directory
* and returns an array of protocol objects.
* TODO implement FromJSONFile
*
* IMPORTANT we ideally want to ensure that this is only called once
* as it is relatively expensive finding and reading lots of JSON files
*/
static _importAllProtocolJSONFiles() {
this._protocols = [];
var protocol_directory = JSPath.GetProtocolsDirectory();
Message("protocol_directory = " + protocol_directory);
var file_list = find_all_json_files(protocol_directory);
//Message(file_list.join("\n"));
if (file_list.length == 0) {
throw new Error(`Failed to parse protocol data from ${protocol_directory}`);
}
for (let filepath of file_list) {
let protocol;
try {
if (/\.json$/i.test(filepath)) {
protocol = Protocol.FromJSONFile(filepath);
if (protocol) this._protocols.push(protocol);
else throw new Error(`Failed to parse protocol data from ${filepath}`);
// TODO write check for protocols (or add check to FromJSONFile and throw errors)
// Message("writing out new protocol format");
// let re = /(.*[\\\/])(.*?\.json)/;
// let match = filepath.match(re);
// let new_filepath = `${match[1]}New/${match[2]}`;
// WriteJSON(
// new_filepath,
// protocol.vehicle.ToJSON(protocol.regulation, protocol.crash_test, protocol.version)
// );
}
} catch (error) {
ErrorMessage(`${error} so skipping file ${filepath}`);
}
}
}
// /* Static methods */
/**
* Returns an array of all the protocol regulations
* @returns {string[]}
* @example
* let regulations = Protocols.GetAllRegulations();
*/
static GetAllRegulations() {
let protocols = Protocols.GetAll();
let regulations = [];
for (let protocol of protocols) {
if (regulations.indexOf(protocol.regulation) == -1) {
regulations.push(protocol.regulation);
}
}
//TODO: SORT alphabetically?
return regulations.sort();
}
/**
* Returns an array of all the supported crash test types
* @returns {string[]}
* @example
* let crash_tests = Protocols.GetAllCrashTests();
*/
static GetAllCrashTests() {
let protocols = Protocols.GetAll();
let crash_tests = [];
for (let protocol of protocols) {
if (crash_tests.indexOf(protocol.crash_test) == -1) {
crash_tests.push(protocol.crash_test);
}
}
//TODO: SORT alphabetically?
return crash_tests.sort();
}
/**
* Returns an array of only the protocols matching the crash test type, regulation body and verion
* Note that passing 'ALL' (or invalid value) for any parameter means that the filter will not be applied for that parameter
* @param {string} [regulation = 'ALL'] regulation body e.g. EuroNCAP, IIHS...
* @param {string} [crash_test = 'ALL'] type e.g. ODB, Side Pole...
* @param {string} [version = 'ALL'] regulation vesion
* @returns {Protocol[]}
* @example
* let protocols = Protocols.GetOnly();
*/
static GetOnly(regulation = "ALL", crash_test = "ALL", version = "ALL") {
let protocols = Protocols.GetAll();
let temp_protocols = [];
if (!/ALL/i.test(crash_test)) {
for (let protocol of protocols) {
//Message(`protocol.crash_test ${protocol.crash_test}`);
if (protocol.crash_test == crash_test) {
temp_protocols.push(protocol);
}
}
if (temp_protocols.length == 0) {
Message(`Crash test ${crash_test} was not found in any protocols so ignoring this filter`);
crash_test = "ALL";
return [];
} else {
protocols = temp_protocols;
}
temp_protocols = [];
}
if (!/ALL/i.test(regulation)) {
for (let protocol of protocols) {
if (protocol.regulation == regulation) {
temp_protocols.push(protocol);
}
}
if (temp_protocols.length == 0) {
Message(`Regulation ${regulation} was not found in any protocols so ignoring this filter`);
regulation = "ALL";
return [];
} else {
protocols = temp_protocols;
}
temp_protocols = [];
}
if (!/ALL/i.test(version)) {
for (let protocol of protocols) {
if (protocol.version == version) {
temp_protocols.push(protocol);
}
}
if (temp_protocols.length == 0) {
Message(`Version ${version} was not found in any protocols so ignoring this filter`);
return [];
} else {
protocols = temp_protocols;
}
temp_protocols = [];
}
return protocols;
}
/**
* Returns a single ProtocolVehicle for the specified crash test type, regulation body and version.
* Returns null if none or more than one matching ProtocolVehicle found.
* @param {string} regulation regulation body e.g. EuroNCAP, IIHS, ...
* @param {string} crash_test Crash test e.g. ODB, Side Pole, ...
* @param {string} version Protocol version e.g. 2022, XVIII, ...
* @returns {?ProtocolVehicle}
* @example
* let vehicle = Protocols.GetProtocolVehicle("EuroNCAP", "ODB", "2017");
*/
static GetProtocolVehicle(regulation, crash_test, version) {
let protocols = Protocols.GetOnly(regulation, crash_test, version);
if (protocols.length == 0) {
ErrorMessage(
`Tried to get protocol vehicle for ${regulation} ${crash_test} ${version} which is unsupported.`
);
return null;
} else if (protocols.length > 1) {
WarningMessage(
`Multiple (${protocols.length}) protocols found for ${regulation} ${crash_test} ${version}. The first one found will be used, but please ensure the protocol jsons are unique. `
);
}
return protocols[0].vehicle;
}
}