import { Measurement } from "../../shared/measurement.mjs";
import { OccupantBodyPart, OccupantEntity } from "../../shared/occupant.mjs";
import { WorkflowOccupant } from "../../shared/workflow_occupant.mjs";
import { THisHelper } from "./this.mjs";
import { WorkflowUnits } from "../../../../modules/units.mjs";
import { Protocol, Protocols } from "../../shared/protocols.mjs";
import { ProtocolVehicle } from "../../shared/vehicle.mjs";
import { AssessmentType } from "../../shared/assessment_types.mjs";
import { AssessmentDatums } from "./assessment_datums.mjs";
import { Structure } from "../../shared/structure.mjs";
import { OccupantSupplier, OccupantProduct, OccupantPhysiology } from "../../shared/occupant.mjs";
import { BaseEntity } from "../../shared/base.mjs";
export {
ProtocolAssessment,
DoOccupantAssessmentOccupantData,
DoStructureAssessmentStructureData,
DoAssessmentOptions
};
/**
* Class to do assessments for a protocol in T/HIS<br><br>
* It extends the Protocol class and adds the assessment functions<br><br>
* It is separated like this because it uses T/HIS JS-API functions
* that if they were in the Protocol class would stop it from
* working in PRIMER.
* @extends Protocol
* @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 vehicle = Protocols.GetProtocolVehicle(regulation, crash_test, version);
* let protocol = new ProtocolAssessment(Regulation.CNCAP, CrashTest.ODB, "7.1.2", vehicle, []);
*/
class ProtocolAssessment extends Protocol {
constructor(regulation, crash_test, version, vehicle, assessment_datums) {
super(regulation, crash_test, version, vehicle, assessment_datums);
}
/**
* Create a ProtocolAssessment instance with the default Vehicle and
* AssessmentDatums for the given regulation, test and year
* @param {string} regulation Protocol regulation
* @param {string} crash_test Protocol test
* @param {string} version Version, e.g. "7.1.2"
* @returns {ProtocolAssessment}
* @example
* let p = ProtocolAssessment.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 = super.CreateDefaultAssessmentDatums(regulation, crash_test, version);
return new ProtocolAssessment(regulation, crash_test, version, vehicle, assessment_datums);
}
/* Instance methods */
/**
* Do calculations on an OccupantBodyPart
* @param {DoOccupantAssessmentOccupantData[]} occupants_data Array of DoOccupantAssessmentOccupantData instances
* @param {string[]} assessment_types Array of assessment types, e.g. AssessmentType.NECK_AXIAL
* @param {DoAssessmentOptions} [options=null] Options
* @returns {DoAssessmentResults}
* @example
* let vehicle = Protocols.GetProtocolVehicle(regulation, crash_test, version);
* let p = new ProtocolAssessment(Regulation.CNCAP, CrashTest.ODB, "7.1.2", vehicle, []);
* let m = Model.GetFromID(1);
* let o = new WorkflowOccupant(WorkflowOccupant.HUMANETICS_HIII_50M_V1_5,
* WorkflowOccupant.DRIVER,
* WorkflowOccupant.LEFT,
* WorkflowOccupant.FRONT,
* []);
* let occupants_data = [new DoOccupantAssessmentOccupantData(o, m, Workflow.UNIT_SYSTEM_U2)];
* let assessment_types = [AssessmentType.NECK_AXIAL, AssessmentType.NECK_SHEAR];
* let options = new DoAssessmentOptions("separate_pages");
*
* let assessment = p.DoOccupantAssessment(occupants_data,
* assessment_types,
* options);
*/
DoOccupantAssessment(occupants_data, assessment_types, options = null) {
if (!(occupants_data instanceof Array)) {
throw new Error(`<occupants_data> must be an array in <ProtocolAssessment.DoOccupantAssessment>`);
}
for (let occupant_data of occupants_data) {
if (!(occupant_data instanceof DoOccupantAssessmentOccupantData)) {
throw new Error(
"Not all of the items in the <occupants_data> array are DoOccupantAssessmentOccupantData instances in <ProtocolAssessment.DoOccupantAssessment>"
);
}
}
/* Object to return values from assessments */
let results = new DoAssessmentResults();
/* Check that there are models to extract data from */
for (let occupant_data of occupants_data) {
let model = Model.GetFromID(occupant_data.model_id);
if (model == null) {
ErrorMessage(
`Model ${occupant_data.model_id} for ${occupant_data.occupant} not found in <ProtocolAssessment.DoOccupantAssessment>`
);
return results;
}
}
/* Blank all the curves and datums */
if (options.blank_all) {
THisHelper.BlankAllCurves();
THisHelper.BlankAllDatums();
}
/* Get the first graph to plot on */
let graph_id = options.first_graph_id;
let graph_ids = [];
/* Multiple assessments are done on the head acceleration curve (HIC, 3ms, peak)
* Initialise an array to flag whether it has been read in for each occupant */
let read_head_curve_acceleration = [];
for (let i = 0; i < occupants_data.length; i++) {
read_head_curve_acceleration[i] = false;
}
/* Do each assessment for this protocol and occupant */
for (let assessment_type of assessment_types) {
/* Create new graph if required */
if (graph_id > Graph.Total()) {
if (graph_id >= THisHelper.MAX_GRAPHS) {
WarningMessage(
`Unable to do any more assessments, the graph limit of ${THisHelper.MAX_GRAPHS} has been reached`
);
break;
}
new Graph();
}
/* REPORTER uses same graph over and over so blank curves and datums */
if (options.graph_layout == DoAssessmentOptions.GRAPH_LAYOUT_REPORTER) {
THisHelper.BlankAllCurves();
THisHelper.BlankAllDatums();
}
/* Flag to say whether to add a graph for this assessment
* For most assessments this is true, but for the head acceleration
* where one curve is used for multiple assessments we only want to
* add it once */
let add_graph = true;
/* Get the datums */
let assessment_datums = this.GetDatumsByAssessment(assessment_type);
/** @type {ReadAssessmentOutput} */
let output = null;
let occupant_index = -1;
let occupants_str = "";
for (let occupant_data of occupants_data) {
++occupant_index;
let occupant = occupant_data.occupant;
let model = Model.GetFromID(occupant_data.model_id);
let unit_system = occupant_data.unit_system;
/* Concatenated list of occupants used for REPORTER image filenames */
if (occupant_index > 0) occupants_str += `_`;
occupants_str += `${occupant}`;
/* Only do the assessment if it's applicable to this occupant */
let occupant_assessment_types = this.UniqueOccupantAssessmentTypes(
[occupant],
OccupantBodyPart.Types()
);
if (occupant_assessment_types.indexOf(assessment_type) == -1) continue;
/* Get options for head acceleration assessments so they can all be done on one curve. */
let head_acceleration_options = {};
for (let assessment_type of assessment_types) {
if (assessment_type == AssessmentType.HEAD_HIC) {
head_acceleration_options.hic = true;
head_acceleration_options.hic_window =
this.HICWindow(occupant) * WorkflowUnits.TimeToSecondsFactor(unit_system);
} else if (assessment_type == AssessmentType.HEAD_THREE_MS_EXCEEDENCE) {
head_acceleration_options.three_ms = true;
} else if (assessment_type == AssessmentType.HEAD_PEAK_ACCELERATION) {
head_acceleration_options.peak_acceleration = true;
}
}
/* HEAD */
if (
assessment_type == AssessmentType.HEAD_HIC ||
assessment_type == AssessmentType.HEAD_THREE_MS_EXCEEDENCE ||
assessment_type == AssessmentType.HEAD_PEAK_ACCELERATION
) {
if (!read_head_curve_acceleration[occupant_index]) {
/* Read in the head acceleration */
output = this.ReadHeadAcceleration(
model,
occupant,
unit_system,
occupant_index,
head_acceleration_options
);
/* Flag it so we don't read in the acceleration curve for this occupant again */
read_head_curve_acceleration[occupant_index] = true;
} else {
/* No need to add a graph again - the assessment has already been carried out */
add_graph = false;
}
/* NECK */
} else if (
assessment_type == AssessmentType.NECK_EXTENSION ||
assessment_type == AssessmentType.NECK_EXTENSION_PASSENGER ||
assessment_type == AssessmentType.NECK_EXTENSION_REAR_PASSENGER
) {
output = this.ReadNeckBending(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.NECK_SHEAR_EXCEEDENCE) {
output = this.ReadNeckShearExceedence(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.NECK_TENSION_EXCEEDENCE) {
output = this.ReadNeckTensionExceedence(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.NECK_COMPRESSION_EXCEEDENCE) {
output = this.ReadNeckCompressionExceedence(model, occupant, unit_system, occupant_index);
} else if (
assessment_type == AssessmentType.NECK_SHEAR ||
assessment_type == AssessmentType.NECK_SHEAR_PASSENGER
) {
output = this.ReadNeckShear(model, occupant, unit_system, occupant_index);
} else if (
assessment_type == AssessmentType.NECK_AXIAL ||
assessment_type == AssessmentType.NECK_AXIAL_PASSENGER
) {
output = this.ReadNeckAxial(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.NECK_NIJ) {
output = this.ReadNeckNIJ(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.NECK_UPPER_AXIAL) {
output = this.ReadNeckUpperAxial(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.NECK_LOWER_AXIAL) {
output = this.ReadNeckLowerAxial(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.NECK_UPPER_FLEXION) {
output = this.ReadNeckUpperFlexion(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.NECK_LOWER_FLEXION) {
output = this.ReadNeckLowerFlexion(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.NECK_UPPER_EXTENSION) {
output = this.ReadNeckUpperExtension(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.NECK_LOWER_EXTENSION) {
output = this.ReadNeckLowerExtension(model, occupant, unit_system, occupant_index);
/* CHEST */
} else if (
assessment_type == AssessmentType.CHEST_COMPRESSION ||
assessment_type == AssessmentType.CHEST_COMPRESSION_PASSENGER ||
assessment_type == AssessmentType.CHEST_COMPRESSION_REAR_PASSENGER
) {
output = this.ReadChestCompression(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.CHEST_COMPRESSION_LEFT) {
output = this.ReadChestCompression(model, occupant, unit_system, occupant_index, "Left");
} else if (assessment_type == AssessmentType.CHEST_COMPRESSION_RIGHT) {
output = this.ReadChestCompression(model, occupant, unit_system, occupant_index, "Right");
} else if (assessment_type == AssessmentType.CHEST_COMPRESSION_RATE) {
output = this.ReadChestCompressionRate(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.CHEST_THREE_MS_EXCEEDENCE) {
output = this.ReadChestAcceleration(model, occupant, unit_system, occupant_index, {
three_ms: true
});
} else if (assessment_type == AssessmentType.CHEST_VISCOUS_CRITERION) {
output = this.ReadChestViscousCriterion(model, occupant, unit_system, occupant_index);
/* SHOULDER */
} else if (assessment_type == AssessmentType.SHOULDER_DEFLECTION) {
output = this.ReadShoulderDeflection(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.SHOULDER_DEFLECTION_RATE) {
output = this.ReadShoulderDeflectionRate(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.SHOULDER_VISCOUS_CRITERION) {
output = this.ReadShoulderViscousCriterion(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.SHOULDER_LATERAL_FORCES) {
output = this.ReadShoulderLateralForces(model, occupant, unit_system, occupant_index);
/* ABDOMEN */
} else if (assessment_type == AssessmentType.ABDOMEN_COMPRESSION) {
output = this.ReadAbdomenCompression(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.ABDOMEN_COMPRESSION_LEFT) {
output = this.ReadAbdomenCompression(model, occupant, unit_system, occupant_index, "Left");
} else if (assessment_type == AssessmentType.ABDOMEN_COMPRESSION_RIGHT) {
output = this.ReadAbdomenCompression(model, occupant, unit_system, occupant_index, "Right");
} else if (assessment_type == AssessmentType.ABDOMEN_VISCOUS_CRITERION) {
output = this.ReadAbdomenViscousCriterion(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.ABDOMEN_FORCE) {
output = this.ReadAbdomenForce(model, occupant, unit_system, occupant_index);
/* LUMBAR */
} else if (assessment_type == AssessmentType.LUMBAR_SHEAR) {
output = this.ReadLumbarShear(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.LUMBAR_AXIAL) {
output = this.ReadLumbarAxial(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.LUMBAR_TORSION) {
output = this.ReadLumbarTorsion(model, occupant, unit_system, occupant_index);
/* PELVIS */
} else if (assessment_type == AssessmentType.ACETABULAR_FORCE) {
output = this.ReadAcetabularForce(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.ACETABULAR_FORCE_LEFT) {
output = this.ReadAcetabularForce(model, occupant, unit_system, occupant_index, "Left");
} else if (assessment_type == AssessmentType.ACETABULAR_FORCE_RIGHT) {
output = this.ReadAcetabularForce(model, occupant, unit_system, occupant_index, "Right");
} else if (assessment_type == AssessmentType.ILIUM_FORCE) {
output = this.ReadIliumForce(model, occupant, unit_system, occupant_index);
} else if (assessment_type == AssessmentType.PELVIS_FORCE) {
output = this.ReadPelvisForce(model, occupant, unit_system, occupant_index);
/* FEMUR */
} else if (assessment_type == AssessmentType.LEFT_FEMUR_AXIAL) {
output = this.ReadFemurAxial(model, occupant, unit_system, occupant_index, "Left");
} else if (assessment_type == AssessmentType.RIGHT_FEMUR_AXIAL) {
output = this.ReadFemurAxial(model, occupant, unit_system, occupant_index, "Right");
} else if (assessment_type == AssessmentType.LEFT_FEMUR_FORCE) {
output = this.ReadFemurForce(model, occupant, unit_system, occupant_index, "Left");
} else if (assessment_type == AssessmentType.RIGHT_FEMUR_FORCE) {
output = this.ReadFemurForce(model, occupant, unit_system, occupant_index, "Right");
} else if (assessment_type == AssessmentType.LEFT_FEMUR_COMPRESSION_EXCEEDENCE) {
output = this.ReadFemurCompressionExceedence(model, occupant, unit_system, occupant_index, "Left");
} else if (assessment_type == AssessmentType.RIGHT_FEMUR_COMPRESSION_EXCEEDENCE) {
output = this.ReadFemurCompressionExceedence(model, occupant, unit_system, occupant_index, "Right");
} else if (assessment_type == AssessmentType.LEFT_FEMUR_COMPRESSION_VS_IMPULSE) {
output = this.ReadFemurCompressionVsImpulse(model, occupant, unit_system, occupant_index, "Left");
} else if (assessment_type == AssessmentType.RIGHT_FEMUR_COMPRESSION_VS_IMPULSE) {
output = this.ReadFemurCompressionVsImpulse(model, occupant, unit_system, occupant_index, "Right");
/* KNEE */
} else if (assessment_type == AssessmentType.LEFT_KNEE_DISPLACEMENT) {
output = this.ReadKneeDisplacement(model, occupant, unit_system, occupant_index, "Left");
} else if (assessment_type == AssessmentType.RIGHT_KNEE_DISPLACEMENT) {
output = this.ReadKneeDisplacement(model, occupant, unit_system, occupant_index, "Right");
/* TIBIA */
} else if (assessment_type == AssessmentType.LEFT_TIBIA_COMPRESSION) {
output = this.ReadTibiaCompression(model, occupant, unit_system, occupant_index, "Left");
} else if (assessment_type == AssessmentType.RIGHT_TIBIA_COMPRESSION) {
output = this.ReadTibiaCompression(model, occupant, unit_system, occupant_index, "Right");
} else if (assessment_type == AssessmentType.LEFT_TIBIA_INDEX) {
output = this.ReadTibiaIndex(model, occupant, unit_system, occupant_index, "Left");
} else if (assessment_type == AssessmentType.RIGHT_TIBIA_INDEX) {
output = this.ReadTibiaIndex(model, occupant, unit_system, occupant_index, "Right");
/* FOOT */
} else if (assessment_type == AssessmentType.LEFT_FOOT_ACCELERATION) {
output = this.ReadFootAcceleration(model, occupant, unit_system, occupant_index, "Left");
} else if (assessment_type == AssessmentType.RIGHT_FOOT_ACCELERATION) {
output = this.ReadFootAcceleration(model, occupant, unit_system, occupant_index, "Right");
} else {
ErrorMessage(
`Unknown assessment type '${assessment_type}' in <ProtocolAssessment.DoOccupantAssessment>`
);
}
/* Add curves to graph, extend datums to last curve point and output values */
if (output) {
for (let curve of output.curves) {
curve.AddToGraph(graph_id);
if (assessment_datums) {
let p = curve.GetPoint(curve.npoints);
assessment_datums.ExtendLastYValueToX(p[0]);
}
}
THisHelper.SetGraphTitle(graph_id, output.graph_title);
results.outputs.push(output);
} else {
if (add_graph) {
WarningMessage(
`No output from assessment type '${assessment_type}' for occupant M${model.id} '${occupant}'`
);
}
}
}
/* Plot the datums and scale the graph */
if (add_graph) {
graph_ids.push(graph_id);
if (assessment_datums) assessment_datums.Plot(graph_id);
THisHelper.ScaleGraph(graph_id, assessment_datums);
/* Special logic for the Femur compression v impulse graphs so the graph
* always starts x=0.0 and ends at x=10.0 */
if (
assessment_type == AssessmentType.LEFT_FEMUR_COMPRESSION_VS_IMPULSE ||
assessment_type == AssessmentType.RIGHT_FEMUR_COMPRESSION_VS_IMPULSE
) {
let graph = Graph.GetFromID(graph_id);
if (graph) {
if (graph.xmin > 0.0) graph.xmin = 0.0;
if (graph.xmax < 10.0) graph.xmax = 10.0;
}
}
if (options.graph_layout != DoAssessmentOptions.GRAPH_LAYOUT_REPORTER) {
++graph_id;
}
}
/* Capture an image */
if (options.graph_layout == DoAssessmentOptions.GRAPH_LAYOUT_REPORTER) {
Plot();
/* Assemble a descriptive filename for the images used by REPORTER */
let at = assessment_type;
let output_dir = options.output_dir;
let fname = `${output_dir}/${this.regulation}~${this.crash_test}~${this.version}~${occupants_str}~${at}.png`;
THisHelper.CaptureImage(fname);
}
}
/* Layout the graphs and plot them */
results.last_page_id = this.ApplyOptionsToGraphs(options, graph_ids);
Plot();
/* Return the last graph id used */
results.last_graph_id = Math.max(...graph_ids);
return results;
}
/**
* Do calculations on a Structure
* @param {DoStructureAssessmentStructureData[]} structures_data Array of DoStructureAssessmentStructureData instances
* @param {string[]} assessment_types Array of assessment types, e.g. AssessmentType.DOOR_INTRUSION
* @param {DoAssessmentOptions} [options=null] Options
* @returns {DoAssessmentResults}
* @example
* let p = new ProtocolAssessment(Regulation.CNCAP, CrashTest.ODB, "7.1.2", []);
* let m = Model.GetFromID(1);
* let s = new Structure(Structure.A_PILLAR, []);
* let structures_data = [new DoStructureAssessmentStructureData(s, m, Workflow.UNIT_SYSTEM_U2)];
* let assessment_types = [AssessmentType.A_PILLAR_INTRUSION];
* let options = new DoAssessmentOptions("separate_pages");
*
* let assessment = p.DoStructureAssessment(structures_data,
* assessment_types,
* options);
*/
DoStructureAssessment(structures_data, assessment_types, options = null) {
if (!(structures_data instanceof Array)) {
throw new Error(`<structures_data> must be an array in <ProtocolAssessment.DoStructureAssessment>`);
}
for (let structure_data of structures_data) {
if (!(structure_data instanceof DoStructureAssessmentStructureData)) {
throw new Error(
"Not all of the items in the <structures_data> array are DoStructureAssessmentStructureData instances in <ProtocolAssessment.DoStructureAssessment>"
);
}
}
/* Object to return values from assessments */
let results = new DoAssessmentResults();
/* Check that there are models to extract data from */
for (let structure_data of structures_data) {
let model = Model.GetFromID(structure_data.model_id);
if (model == null) {
ErrorMessage(
`Model ${structure_data.model_id} for ${structure_data.structure} not found in <ProtocolAssessment.DoStructureAssessment>`
);
return results;
}
}
/* Blank all the curves and datums */
if (options.blank_all) {
THisHelper.BlankAllCurves();
THisHelper.BlankAllDatums();
}
/* Get the first graph to plot on */
let graph_id = options.first_graph_id;
let graph_ids = [];
/* Do each assessment for this protocol and occupant */
for (let assessment_type of assessment_types) {
/* Create new graph if required */
if (graph_id > Graph.Total()) {
if (graph_id >= THisHelper.MAX_GRAPHS) {
WarningMessage(
`Unable to do any more assessments, the graph limit of ${THisHelper.MAX_GRAPHS} has been reached`
);
break;
}
new Graph();
}
/* REPORTER uses same graph over and over so blank curves and datums */
if (options.graph_layout == DoAssessmentOptions.GRAPH_LAYOUT_REPORTER) {
THisHelper.BlankAllCurves();
THisHelper.BlankAllDatums();
}
/* Flag to say whether to add a graph for this assessment
* For most assessments this is true, but for the head acceleration
* where one curve is used for multiple assessments we only want to
* add it once */
let add_graph = true;
/* Get the datums */
let assessment_datums = this.GetDatumsByAssessment(assessment_type);
/** @type {ReadStructureOutput} */
let output = null;
let structure_index = -1;
let structures_str = "";
for (let structure_data of structures_data) {
++structure_index;
let structure = structure_data.structure;
let model = Model.GetFromID(structure_data.model_id);
let unit_system = structure_data.unit_system;
/* Concatenated list of structures used for REPORTER image filenames */
/* TODO currently assuming only one Structure per image, so not using this.
* Need to consider: will people want to plot comparisons of different structures? */
if (structure_index > 0) structures_str += `_`;
structures_str += `${structure}`;
/* Only do the assessment if it's applicable to this structure */
let structure_assessment_types = this.StructureAssessmentTypes(structure);
if (structure_assessment_types.indexOf(assessment_type) == -1) continue;
if (assessment_type == AssessmentType.PEDAL_VERTICAL_INTRUSION) {
output = this.ReadVerticalIntrusion(model, structure, unit_system, structure_index, "Pedal");
} else if (assessment_type == AssessmentType.PEDAL_LATERAL_INTRUSION) {
output = this.ReadLateralIntrusion(model, structure, unit_system, structure_index, "Pedal");
} else if (assessment_type == AssessmentType.PEDAL_FORE_AFT_INTRUSION) {
output = this.ReadForeAftIntrusion(model, structure, unit_system, structure_index, "Pedal");
} else if (
assessment_type == AssessmentType.STEERING_COLUMN_LATERAL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_ROCKER_PANEL_1_LATERAL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_ROCKER_PANEL_2_LATERAL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_ROCKER_PANEL_3_LATERAL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_ROCKER_PANEL_1_LATERAL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_ROCKER_PANEL_2_LATERAL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_ROCKER_PANEL_3_LATERAL_INTRUSION
) {
output = this.ReadLateralIntrusion(model, structure, unit_system, structure_index);
} else if (
assessment_type == AssessmentType.A_PILLAR_FORE_AFT_INTRUSION ||
assessment_type == AssessmentType.STEERING_COLUMN_FORE_AFT_INTRUSION ||
assessment_type == AssessmentType.LEFT_INSTRUMENT_PANEL_FORE_AFT_INTRUSION ||
assessment_type == AssessmentType.RIGHT_INSTRUMENT_PANEL_FORE_AFT_INTRUSION ||
assessment_type == AssessmentType.DOOR_FORE_AFT_INTRUSION
) {
output = this.ReadForeAftIntrusion(model, structure, unit_system, structure_index);
} else if (
assessment_type == AssessmentType.STEERING_COLUMN_ALL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_LEFT_TOEPAN_ALL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_CENTRE_TOEPAN_ALL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_RIGHT_TOEPAN_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_LEFT_TOEPAN_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_CENTRE_TOEPAN_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_RIGHT_TOEPAN_ALL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_FOOTREST_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_FOOTREST_ALL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_LOWER_HINGE_1_ALL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_LOWER_HINGE_2_ALL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_LOWER_HINGE_3_ALL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_UPPER_HINGE_1_ALL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_UPPER_HINGE_2_ALL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_UPPER_HINGE_3_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_LOWER_HINGE_1_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_LOWER_HINGE_2_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_LOWER_HINGE_3_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_UPPER_HINGE_1_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_UPPER_HINGE_2_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_UPPER_HINGE_3_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_LEFT_LOWER_DASH_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_RIGHT_LOWER_DASH_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_CENTRE_DASH_ALL_INTRUSION ||
assessment_type == AssessmentType.DRIVER_UPPER_DASH_ALL_INTRUSION ||
assessment_type == AssessmentType.PASSENGER_UPPER_DASH_ALL_INTRUSION
) {
output = this.ReadAllIntrusion(model, structure, unit_system, structure_index);
} else {
ErrorMessage(
`Unknown assessment type '${assessment_type}' in <ProtocolAssessment.DoStructureAssessment>`
);
}
/* Add curves to graph, extend datums to last curve point and output values */
if (output) {
for (let curve of output.curves) {
curve.AddToGraph(graph_id);
if (assessment_datums) {
let p = curve.GetPoint(curve.npoints);
assessment_datums.ExtendLastYValueToX(p[0]);
}
}
THisHelper.SetGraphTitle(graph_id, output.graph_title);
results.outputs.push(output);
} else {
if (add_graph) {
WarningMessage(
`No output from assessment type '${assessment_type}' for structure M${model.id} '${structure}'`
);
}
}
}
/* Plot the datums and scale the graph */
if (add_graph) {
graph_ids.push(graph_id);
if (assessment_datums) assessment_datums.Plot(graph_id);
THisHelper.ScaleGraph(graph_id, assessment_datums);
if (options.graph_layout != DoAssessmentOptions.GRAPH_LAYOUT_REPORTER) {
++graph_id;
}
}
/* Capture an image */
if (options.graph_layout == DoAssessmentOptions.GRAPH_LAYOUT_REPORTER) {
Plot();
/* Assemble a descriptive filename for the images used by REPORTER */
let at = assessment_type;
let output_dir = options.output_dir;
/* TODO revisit this and consider how to support multiple structures in one graph */
//let fname = `${output_dir}/${this.regulation}~${this.crash_test}~${this.version}~${structures_str}~${at}.png`;
let fname = `${output_dir}/${this.regulation}~${this.crash_test}~${this.version}~Structure~${at}.png`;
THisHelper.CaptureImage(fname);
}
}
/* Layout the graphs and plot them */
results.last_page_id = this.ApplyOptionsToGraphs(options, graph_ids);
Plot();
/* Return the last graph id used */
results.last_graph_id = Math.max(...graph_ids);
return results;
}
/**
* Apply the selected options to the graphs
* @param {DoAssessmentOptions} options Options to specify what to do for the graphs
* @param {number[]} graph_ids Graph ids to apply options to
* @return {number} The last page id used
*/
ApplyOptionsToGraphs(options, graph_ids) {
/* Set the layout */
let last_page = 1;
if (options.graph_layout == DoAssessmentOptions.GRAPH_LAYOUT_SAME_PAGE) {
THisHelper.PutGraphsOnPage(graph_ids, options.first_page_id, options.remove_existing_graphs);
last_page = options.first_page_id;
} else if (options.graph_layout == DoAssessmentOptions.GRAPH_LAYOUT_SEPARATE_PAGES) {
last_page = THisHelper.PutGraphsOnSeparatePages(graph_ids, options.first_page_id);
if (Page.ReturnGraphs(options.first_page_id).length > 0) Page.SetActivePage(options.first_page_id);
} else if (options.graph_layout == DoAssessmentOptions.GRAPH_LAYOUT_REPORTER) {
/* Do nothing for REPORTER (images already captured) */
} else {
ErrorMessage(`Invalid graph_layout ${options.graph_layout} in <ProtocolAssessment.ApplyOptionsToGraphs>`);
}
return last_page;
}
/**
* Returns a list of the assessment types for the given occupants and body parts
* @param {WorkflowOccupant[]} occupants WorkflowOccupant instances
* @param {string[]} body_part_types Body part types
* @returns {string[]}
*/
UniqueOccupantAssessmentTypes(occupants, body_part_types) {
let assessment_types = new Set();
for (let body_part_type of body_part_types) {
for (let occupant of occupants) {
let at = this.OccupantAssessmentTypes(occupant, body_part_type.toLowerCase());
for (let a of at) {
assessment_types.add(a);
}
}
}
return [...assessment_types];
}
/**
* Returns a list of the assessment types for the given structures
* @param {Structure[]} structures Structure instances
* @returns {string[]}
*/
UniqueStructureAssessmentTypes(structures) {
let assessment_types = new Set();
for (let structure of structures) {
let at = this.StructureAssessmentTypes(structure);
for (let a of at) {
assessment_types.add(a);
}
}
return [...assessment_types];
}
/**
* Returns a colour to use for a curve by index
* @param {number} index
* @returns {number}
*/
GetCurveColourByIndex(index) {
let colors = [Colour.BLACK, Colour.BLUE, Colour.MAGENTA, Colour.CYAN];
return colors[index % colors.length];
}
/**
* Reads head accelerations from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {object} [options] Options
* @return {?ReadAssessmentOutput}
* @example
* let curve = p.ReadHeadAcceleration(model, occupant, unit_system);
*/
ReadHeadAcceleration(model, occupant, unit_system, occupant_index, options) {
/* Graph title - also used in curve labels */
let graph_title = `Head Acceleration Magnitude`;
/* Get the raw measurements from the head */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.HEAD);
/* All the occupants have nodes in the head to read the accelerations,
* so no need for occupant specific logic here */
let curve_x = raw_measurements.GetCurve(Measurement.X_ACCELERATION);
let curve_y = raw_measurements.GetCurve(Measurement.Y_ACCELERATION);
let curve_z = raw_measurements.GetCurve(Measurement.Z_ACCELERATION);
if (!curve_x) {
WarningMessage(
`Unable to read x acceleration from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadHeadAcceleration>`
);
return null;
}
if (!curve_y) {
WarningMessage(
`Unable to read y acceleration from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadHeadAcceleration>`
);
return null;
}
if (!curve_z) {
WarningMessage(
`Unable to read z acceleration from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadHeadAcceleration>`
);
return null;
}
/* Convert acceleration to g */
let curve_x_g = Operate.Div(curve_x, WorkflowUnits.GravityConstant(unit_system));
let curve_y_g = Operate.Div(curve_y, WorkflowUnits.GravityConstant(unit_system));
let curve_z_g = Operate.Div(curve_z, WorkflowUnits.GravityConstant(unit_system));
curve_x_g.RemoveFromGraph();
curve_y_g.RemoveFromGraph();
curve_z_g.RemoveFromGraph();
/* Convert time to seconds */
let curve_x_g_s = Operate.Dix(curve_x_g, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_y_g_s = Operate.Dix(curve_y_g, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_z_g_s = Operate.Dix(curve_z_g, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_x_g_s.RemoveFromGraph();
curve_y_g_s.RemoveFromGraph();
curve_z_g_s.RemoveFromGraph();
/* Filter with C1000 */
let curve_x_c1000 = Operate.C1000(curve_x_g_s, 0.00001);
let curve_y_c1000 = Operate.C1000(curve_y_g_s, 0.00001);
let curve_z_c1000 = Operate.C1000(curve_z_g_s, 0.00001);
curve_x_c1000.RemoveFromGraph();
curve_y_c1000.RemoveFromGraph();
curve_z_c1000.RemoveFromGraph();
/* Vector combine */
let curve_vec = Operate.Vec(curve_x_c1000, curve_y_c1000, curve_z_c1000);
curve_vec.RemoveFromGraph();
/* Convert back to model time */
curve_vec = Operate.Mux(curve_vec, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_vec.RemoveFromGraph();
/* Values to extract from curve */
let values = [];
/* Do a HIC calculation */
if (options && options.hic) {
values.push(new ReadAssessmentValue(`HIC`, Operate.Hic(curve_vec, options.hic_window, 1.0)));
values.push(new ReadAssessmentValue(`HIC window`, options.hic_window, `s`));
curve_vec.RemoveFromGraph();
}
/* Do a three ms calculation */
if (options && options.three_ms) {
Operate.Tms(curve_vec, 0.003 * WorkflowUnits.TimeToSecondsFactor(unit_system));
values.push(new ReadAssessmentValue(`TMS`, curve_vec.tms, `g`));
values.push(new ReadAssessmentValue(`Start of TMS window`, curve_vec.tms_tmin, `s`));
values.push(new ReadAssessmentValue(`End of TMS window`, curve_vec.tms_tmax, `s`));
curve_vec.RemoveFromGraph();
}
/* Get the peak acceleration */
if (options && options.peak_acceleration) {
values.push(new ReadAssessmentValue(`Peak acceleration`, curve_vec.ymax, `g`));
}
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_vec,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Acceleration (g)"
);
THisHelper.SetLineStyle(curve_vec, this.GetCurveColourByIndex(occupant_index));
return new ReadAssessmentOutput(
graph_title,
[curve_vec],
model.id,
occupant.position,
OccupantBodyPart.HEAD,
values
);
}
/**
* Reads neck shear forces from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckShear(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Neck Shear`;
/* Get the raw measurements from the neck */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.NECK);
/* Process them to get shear force */
switch (occupant.GetEntityTypeFromTag(OccupantEntity.NECK_LOADCELL)) {
case BaseEntity.XSECTION:
let shear = raw_measurements.GetCurve(Measurement.SHEAR);
if (!shear) {
WarningMessage(
`Unable to read shear force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadNeckShear>`
);
return null;
}
/* Convert forces kN */
let curve_shear_kn = Operate.Div(shear, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_shear_kn.RemoveFromGraph();
/* Convert time to seconds */
let curve_shear_s = Operate.Dix(curve_shear_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_shear_s.RemoveFromGraph();
/* Filter with C1000 */
let curve_shear_c1000 = Operate.C1000(curve_shear_s, 0.00001);
curve_shear_c1000.RemoveFromGraph();
/* Convert back to model time */
let curve_shear = Operate.Mux(curve_shear_c1000, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_shear.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_shear,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_shear], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max shear`, curve_shear.ymax, `kN`),
new ReadAssessmentValue(`min shear`, curve_shear.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_shear],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckShear>`);
return null;
}
}
/**
* Reads neck axial forces from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckAxial(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Neck Axial`;
/* Get the raw measurements from the neck */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.NECK);
/* Process them to get axial force */
switch (occupant.GetEntityTypeFromTag(OccupantEntity.NECK_LOADCELL)) {
case BaseEntity.XSECTION:
let axial = raw_measurements.GetCurve(Measurement.AXIAL);
if (!axial) {
WarningMessage(
`Unable to read axial force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadNeckAxial>`
);
return null;
}
/* Convert forces kN */
let curve_axial_kn = Operate.Div(axial, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_axial_kn.RemoveFromGraph();
/* Convert time to seconds */
let curve_axial_s = Operate.Dix(curve_axial_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_axial_s.RemoveFromGraph();
/* Filter with C1000 */
let curve_axial_c1000 = Operate.C1000(curve_axial_s, 0.00001);
curve_axial_c1000.RemoveFromGraph();
/* Convert back to model time */
let curve_axial = Operate.Mux(curve_axial_c1000, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_axial.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_axial,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_axial], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max axial`, curve_axial.ymax, `kN`),
new ReadAssessmentValue(`min axial`, curve_axial.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_axial],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckAxial>`);
return null;
}
}
/**
* Reads neck lower axial forces from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckLowerAxial(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Lower Neck Axial`;
/* Get the raw measurements from the neck */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.NECK);
/* Process them to get axial force */
switch (occupant.product) {
case OccupantProduct.WSID:
let lower_axial = raw_measurements.GetCurve(Measurement.LOWER_AXIAL);
if (!lower_axial) {
WarningMessage(
`Unable to read lower axial force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadNeckLowerAxial>`
);
return null;
}
/* Convert forces kN */
let curve_axial_kn = Operate.Div(lower_axial, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_axial_kn.RemoveFromGraph();
/* Convert time to seconds */
let curve_axial_s = Operate.Dix(curve_axial_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_axial_s.RemoveFromGraph();
/* Filter with C600 */
let curve_axial_c600 = Operate.C600(curve_axial_s, 0.00001);
curve_axial_c600.RemoveFromGraph();
/* Convert back to model time */
let curve_axial = Operate.Mux(curve_axial_c600, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_axial.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_axial,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_axial], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max lower axial`, curve_axial.ymax, `kN`),
new ReadAssessmentValue(`min lower axial`, curve_axial.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_axial],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckLowerAxial>`);
return null;
}
}
/**
* Reads neck upper axial forces from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckUpperAxial(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Upper Neck Axial`;
/* Get the raw measurements from the neck */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.NECK);
/* Process them to get axial force */
switch (occupant.product) {
case OccupantProduct.WSID:
let upper_axial = raw_measurements.GetCurve(Measurement.UPPER_AXIAL);
if (!upper_axial) {
WarningMessage(
`Unable to read upper axial force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadNeckUpperAxial>`
);
return null;
}
/* Convert forces kN */
let curve_axial_kn = Operate.Div(upper_axial, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_axial_kn.RemoveFromGraph();
/* Convert time to seconds */
let curve_axial_s = Operate.Dix(curve_axial_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_axial_s.RemoveFromGraph();
/* Filter with C600 */
let curve_axial_c600 = Operate.C600(curve_axial_s, 0.00001);
curve_axial_c600.RemoveFromGraph();
/* Convert back to model time */
let curve_axial = Operate.Mux(curve_axial_c600, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_axial.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_axial,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_axial], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max upper axial`, curve_axial.ymax, `kN`),
new ReadAssessmentValue(`min upper axial`, curve_axial.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_axial],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckUpperAxial>`);
return null;
}
}
/**
* Reads neck bending from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckBending(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Neck Extension Bending Moment`;
/* Get the raw measurements from the neck */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.NECK);
/* Process them to get bending moment */
switch (occupant.GetEntityTypeFromTag(OccupantEntity.NECK_LOADCELL)) {
case BaseEntity.XSECTION:
let bending = raw_measurements.GetCurve(Measurement.BENDING);
if (!bending) {
WarningMessage(
`Unable to read bending moment from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadNeckBending>`
);
return null;
}
/* BENDING MOMENT */
/* Convert forces Nm */
let curve_bending_nm = Operate.Div(bending, WorkflowUnits.MomentToNewtonMetreFactor(unit_system));
curve_bending_nm.RemoveFromGraph();
/* Convert time to seconds */
let curve_bending_s = Operate.Dix(curve_bending_nm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_bending_s.RemoveFromGraph();
/* Filter with C600 */
let curve_bending_c600 = Operate.C600(curve_bending_s, 0.00001);
curve_bending_c600.RemoveFromGraph();
/* SHEAR */
let shear_output = this.ReadNeckShear(model, occupant, unit_system, occupant_index);
if (!shear_output) return null;
if (shear_output.curves.length == 0) return null;
let curve_shear = shear_output.curves[0];
/* Transpose the bending moment to the occupital condoyle moment
* Not required for the THOR occupant.
*
* TODO - check which other occupants it applies to
*/
/** @type {Curve} */
let curve_bending_adj = null;
if (occupant.product == OccupantProduct.THOR) {
curve_bending_adj = curve_bending_c600;
} else {
let curve_shear_adj = Operate.Mul(curve_shear, 17.78);
curve_bending_adj = Operate.Sub(curve_bending_c600, curve_shear_adj);
curve_shear_adj.RemoveFromGraph();
}
curve_bending_adj.RemoveFromGraph();
/* Convert back to model time */
let curve_bending = Operate.Mux(curve_bending_adj, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_bending.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_bending,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Bending Moment (Nm)"
);
THisHelper.SetLineStyle([curve_bending], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max extension`, curve_bending.ymax, `Nm`),
new ReadAssessmentValue(`min extension`, curve_bending.ymin, `Nm`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_bending],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckBending>`);
return null;
}
}
/**
* Reads neck lower extension moment from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckLowerExtension(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Lower Neck Lateral Extension`;
/* Get the raw measurements from the neck */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.NECK);
/* Process them to get bending moment */
switch (occupant.product) {
case OccupantProduct.WSID:
let lower_extension = raw_measurements.GetCurve(Measurement.LOWER_EXTENSION);
if (!lower_extension) {
WarningMessage(
`Unable to read lower extension from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadNeckLowerExtension>`
);
return null;
}
/* Convert forces Nm */
let curve_extension_nm = Operate.Div(
lower_extension,
WorkflowUnits.MomentToNewtonMetreFactor(unit_system)
);
curve_extension_nm.RemoveFromGraph();
/* Convert time to seconds */
let curve_extension_s = Operate.Dix(curve_extension_nm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_extension_s.RemoveFromGraph();
/* Filter with C600 */
let curve_extension_c600 = Operate.C600(curve_extension_s, 0.00001);
curve_extension_c600.RemoveFromGraph();
/* Convert back to model time */
let curve_extension = Operate.Mux(curve_extension_c600, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_extension.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_extension,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Extension (Nm)"
);
THisHelper.SetLineStyle([curve_extension], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max lower extension`, curve_extension.ymax, `Nm`),
new ReadAssessmentValue(`min lower extension`, curve_extension.ymin, `Nm`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_extension],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckLowerExtension>`);
return null;
}
}
/**
* Reads neck upper extension moment from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckUpperExtension(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Upper Neck Lateral Extension`;
/* Get the raw measurements from the neck */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.NECK);
/* Process them to get bending moment */
switch (occupant.product) {
case OccupantProduct.WSID:
let upper_extension = raw_measurements.GetCurve(Measurement.UPPER_EXTENSION);
if (!upper_extension) {
WarningMessage(
`Unable to read upper extension from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadNeckUpperExtension>`
);
return null;
}
/* Convert forces Nm */
let curve_extension_nm = Operate.Div(
upper_extension,
WorkflowUnits.MomentToNewtonMetreFactor(unit_system)
);
curve_extension_nm.RemoveFromGraph();
/* Convert time to seconds */
let curve_extension_s = Operate.Dix(curve_extension_nm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_extension_s.RemoveFromGraph();
/* Filter with C600 */
let curve_extension_c600 = Operate.C600(curve_extension_s, 0.00001);
curve_extension_c600.RemoveFromGraph();
/* Convert back to model time */
let curve_extension = Operate.Mux(curve_extension_c600, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_extension.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_extension,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Extension (Nm)"
);
THisHelper.SetLineStyle([curve_extension], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max upper extension`, curve_extension.ymax, `Nm`),
new ReadAssessmentValue(`min upper extension`, curve_extension.ymin, `Nm`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_extension],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckUpperExtension>`);
return null;
}
}
/**
* Reads neck lower flexion moment from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckLowerFlexion(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Lower Neck Lateral Flexion`;
/* Get the raw measurements from the neck */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.NECK);
/* Process them to get bending moment */
switch (occupant.product) {
case OccupantProduct.WSID:
let lower_flexion = raw_measurements.GetCurve(Measurement.LOWER_FLEXION);
if (!lower_flexion) {
WarningMessage(
`Unable to read lower flexion from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadNeckLowerFlexion>`
);
return null;
}
/* Convert forces Nm */
let curve_flexion_nm = Operate.Div(lower_flexion, WorkflowUnits.MomentToNewtonMetreFactor(unit_system));
curve_flexion_nm.RemoveFromGraph();
/* Convert time to seconds */
let curve_flexion_s = Operate.Dix(curve_flexion_nm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_flexion_s.RemoveFromGraph();
/* Filter with C600 */
let curve_flexion_c600 = Operate.C600(curve_flexion_s, 0.00001);
curve_flexion_c600.RemoveFromGraph();
/* Convert back to model time */
let curve_flexion = Operate.Mux(curve_flexion_c600, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_flexion.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_flexion,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Flexion (Nm)"
);
THisHelper.SetLineStyle([curve_flexion], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max lower flexion`, curve_flexion.ymax, `Nm`),
new ReadAssessmentValue(`min lower flexion`, curve_flexion.ymin, `Nm`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_flexion],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckLowerFlexion>`);
return null;
}
}
/**
* Reads neck upper flexion moment from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckUpperFlexion(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Upper Neck Lateral Flexion`;
/* Get the raw measurements from the neck */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.NECK);
/* Process them to get bending moment */
switch (occupant.product) {
case OccupantProduct.WSID:
let upper_flexion = raw_measurements.GetCurve(Measurement.UPPER_FLEXION);
if (!upper_flexion) {
WarningMessage(
`Unable to read upper flexion from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadNeckUpperFlexion>`
);
return null;
}
/* Convert forces Nm */
let curve_flexion_nm = Operate.Div(upper_flexion, WorkflowUnits.MomentToNewtonMetreFactor(unit_system));
curve_flexion_nm.RemoveFromGraph();
/* Convert time to seconds */
let curve_flexion_s = Operate.Dix(curve_flexion_nm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_flexion_s.RemoveFromGraph();
/* Filter with C600 */
let curve_flexion_c600 = Operate.C600(curve_flexion_s, 0.00001);
curve_flexion_c600.RemoveFromGraph();
/* Convert back to model time */
let curve_flexion = Operate.Mux(curve_flexion_c600, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_flexion.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_flexion,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Flexion (Nm)"
);
THisHelper.SetLineStyle([curve_flexion], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max upper flexion`, curve_flexion.ymax, `Nm`),
new ReadAssessmentValue(`min upper flexion`, curve_flexion.ymin, `Nm`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_flexion],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckUpperFlexion>`);
return null;
}
}
/**
* Reads neck shear force exceedence from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckShearExceedence(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Neck Shear Exceedence`;
switch (occupant.GetEntityTypeFromTag(OccupantEntity.NECK_LOADCELL)) {
case BaseEntity.XSECTION:
let shear_output = this.ReadNeckShear(model, occupant, unit_system, occupant_index);
if (!shear_output) return null;
if (shear_output.curves.length == 0) return null;
let curve_shear = shear_output.curves[0];
/* Take the absolute values in case of -ve loads */
let curve_shear_abs = Operate.Abs(curve_shear);
curve_shear_abs.RemoveFromGraph();
let curve_shear_exc = Operate.Exc(curve_shear_abs, "positive");
curve_shear_exc.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_shear_exc,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_shear_exc], this.GetCurveColourByIndex(occupant_index));
let values = [];
return new ReadAssessmentOutput(
graph_title,
[curve_shear_exc],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckShearExceedence>`);
return null;
}
}
/**
* Reads neck tension force exceedence from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckTensionExceedence(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Neck Tension Exceedence`;
switch (occupant.GetEntityTypeFromTag(OccupantEntity.NECK_LOADCELL)) {
case BaseEntity.XSECTION:
let axial_output = this.ReadNeckAxial(model, occupant, unit_system, occupant_index);
if (!axial_output) return null;
if (axial_output.curves.length == 0) return null;
let curve_axial = axial_output.curves[0];
let curve_tension_exc = Operate.Exc(curve_axial, "positive");
curve_tension_exc.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_tension_exc,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_tension_exc], this.GetCurveColourByIndex(occupant_index));
let values = [];
return new ReadAssessmentOutput(
graph_title,
[curve_tension_exc],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckTensionExceedence>`);
return null;
}
}
/**
* Reads neck compression force exceedence from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckCompressionExceedence(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Neck Compression Exceedence`;
switch (occupant.GetEntityTypeFromTag(OccupantEntity.NECK_LOADCELL)) {
case BaseEntity.XSECTION:
let axial_output = this.ReadNeckAxial(model, occupant, unit_system, occupant_index);
if (!axial_output) return null;
if (axial_output.curves.length == 0) return null;
let curve_axial = axial_output.curves[0];
let curve_compression_exc = Operate.Exc(curve_axial, "negative");
curve_compression_exc.RemoveFromGraph();
let curve_compression_exc_abs = Operate.Abs(curve_compression_exc);
curve_compression_exc_abs.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_compression_exc_abs,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_compression_exc_abs], this.GetCurveColourByIndex(occupant_index));
let values = [];
return new ReadAssessmentOutput(
graph_title,
[curve_compression_exc_abs],
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckCompressionExceedence>`);
return null;
}
}
/**
* Reads Neck Injury Criteria (NIJ) from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadNeckNIJ(model, occupant, unit_system, occupant_index) {
switch (occupant.GetEntityTypeFromTag(OccupantEntity.NECK_LOADCELL)) {
case BaseEntity.XSECTION:
let shear_output = this.ReadNeckShear(model, occupant, unit_system, occupant_index);
if (!shear_output) return null;
if (shear_output.curves.length == 0) return null;
let curve_shear = shear_output.curves[0];
let axial_output = this.ReadNeckAxial(model, occupant, unit_system, occupant_index);
if (!axial_output) return null;
if (axial_output.curves.length == 0) return null;
let curve_axial = axial_output.curves[0];
let bending_output = this.ReadNeckBending(model, occupant, unit_system, occupant_index);
if (!bending_output) return null;
if (bending_output.curves.length == 0) return null;
let curve_bending = bending_output.curves[0];
/* Regularise the curves so they have the same number of points */
let max_points = Math.max(curve_shear.npoints, curve_axial.npoints, curve_bending.npoints);
let last_point = curve_shear.GetPoint(curve_shear.npoints);
let last_time = last_point[0];
let dt = last_time / max_points;
let curve_shear_reg = Operate.Reg(curve_shear, dt);
curve_shear_reg.RemoveFromGraph();
last_point = curve_axial.GetPoint(curve_axial.npoints);
last_time = last_point[0];
dt = last_time / max_points;
let curve_axial_reg = Operate.Reg(curve_axial, dt);
curve_axial_reg.RemoveFromGraph();
last_point = curve_bending.GetPoint(curve_bending.npoints);
last_time = last_point[0];
dt = last_time / max_points;
let curve_bending_reg = Operate.Reg(curve_bending, dt);
curve_bending_reg.RemoveFromGraph();
/* Get the critical loads for this occupant */
let nij_critical_loads = occupant.GetNIJCriticalLoads();
/* Calculate the NIJ */
let curves_nij = Operate.Nij(
curve_shear_reg,
curve_axial_reg,
curve_bending_reg,
nij_critical_loads.tension,
nij_critical_loads.compression,
nij_critical_loads.flexion,
nij_critical_loads.extension,
0.0
);
for (let c of curves_nij) {
c.RemoveFromGraph();
}
/* Set the labels and line style */
for (let i = 0; i < curves_nij.length; i++) {
let label = "";
if (i == 0) label = `${occupant} NIJ - Tension-Extension`;
if (i == 1) label = `${occupant} NIJ - Tension-Flexion`;
if (i == 2) label = `${occupant} NIJ - Compression-Extension`;
if (i == 3) label = `${occupant} NIJ - Compression-Extension`;
THisHelper.SetCurveLabels(
curves_nij[i],
label,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"NIJ"
);
}
THisHelper.SetLineStyle(
[curves_nij[0], curves_nij[1]],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH
);
THisHelper.SetLineStyle([curves_nij[2], curves_nij[3]], this.GetCurveColourByIndex(occupant_index));
let nij_max = 0.0;
for (let curve_nij of curves_nij) {
nij_max = Math.max(nij_max, curve_nij.ymax);
}
let values = [new ReadAssessmentValue(`NIJ max`, nij_max)];
return new ReadAssessmentOutput(
`NIJ`,
curves_nij,
model.id,
occupant.position,
OccupantBodyPart.NECK,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadNeckNIJ>`);
return null;
}
}
/**
* Reads chest compression from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {string} [side] "Left" or "Right" side
* @returns {?ReadAssessmentOutput}
*/
ReadChestCompression(model, occupant, unit_system, occupant_index, side) {
/* Graph title - also used in curve labels */
let graph_title = `Chest Compression`;
/* Get the raw measurements from the chest */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.CHEST);
/* Process them to get compression */
let values = [];
switch (occupant.product) {
case OccupantProduct.HIII:
let rotation = raw_measurements.GetCurve(Measurement.ROTATION);
if (!rotation) {
WarningMessage(
`Unable to read rotations from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
/* The dummy manuals say that a negative rotation indicates compression, however testing has shown
* that in some versions it is actually the other way around. If the maximum absolute value is +ve
* then multiply the rotation by -1 so the conversion to compression works correctly. */
let rmax = rotation.ymax;
let rmin = rotation.ymin;
if (Math.abs(rmax) > Math.abs(rmin)) {
rotation = Operate.Mul(rotation, -1);
rotation.RemoveFromGraph();
}
/* Convert rotation to deflection */
let rotation_factors = occupant.GetChestRotationFactors();
if (!rotation_factors) return null;
let curve_disp_mm = null;
if (rotation_factors.type == "linear") {
curve_disp_mm = Operate.Mul(rotation, rotation_factors.values[0]);
} else if (rotation_factors.type == "third_order") {
let curve_id = Curve.FirstFreeID();
curve_disp_mm = new Curve(curve_id);
let n = rotation.npoints;
for (let i = 1; i <= n; ++i) {
let p_rot = rotation.GetPoint(i);
let time = p_rot[0];
let com =
p_rot[1] * rotation_factors.values[0] +
p_rot[1] * rotation_factors.values[1] +
p_rot[1] * rotation_factors.values[2];
curve_disp_mm.AddPoint(time, com);
}
} else {
ErrorMessage(
`Unknown rotation factor type ${rotation_factors.type} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
curve_disp_mm.RemoveFromGraph();
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp_mm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Filter with C180 */
let curve_disp_c180 = Operate.C180(curve_disp_s, 0.00001);
curve_disp_c180.RemoveFromGraph();
/* Convert back to model time */
let curve_disp = Operate.Mux(curve_disp_c180, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_disp,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle([curve_disp], this.GetCurveColourByIndex(occupant_index));
values = [new ReadAssessmentValue(`max chest compression`, curve_disp.ymax, `mm`)];
return new ReadAssessmentOutput(
graph_title,
[curve_disp],
model.id,
occupant.position,
OccupantBodyPart.CHEST,
values
);
case OccupantProduct.WSID:
let upper_rib_rotation = raw_measurements.GetCurve(Measurement.UPPER_ROTATION);
let mid_rib_rotation = raw_measurements.GetCurve(Measurement.MID_ROTATION);
let lower_rib_rotation = raw_measurements.GetCurve(Measurement.LOWER_ROTATION);
let upper_rib_translation = raw_measurements.GetCurve(Measurement.UPPER_TRANSLATION);
let mid_rib_translation = raw_measurements.GetCurve(Measurement.MID_TRANSLATION);
let lower_rib_translation = raw_measurements.GetCurve(Measurement.LOWER_TRANSLATION);
if (!upper_rib_rotation) {
WarningMessage(
`Unable to read upper rib rotation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!mid_rib_rotation) {
WarningMessage(
`Unable to read mid rib rotation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!lower_rib_rotation) {
WarningMessage(
`Unable to read lower rib rotation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!upper_rib_translation) {
WarningMessage(
`Unable to read upper rib translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!mid_rib_translation) {
WarningMessage(
`Unable to read mid rib translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!lower_rib_translation) {
WarningMessage(
`Unable to read lower rib translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
/* Assess each rib */
/** @type {Curve} */
let curve_upper_rib_disp = null;
/** @type {Curve} */
let curve_mid_rib_disp = null;
/** @type {Curve} */
let curve_bottom_rib_disp = null;
for (let rib = 0; rib < 3; rib++) {
/** @type {Curve} */
let curve_disp = null;
/** @type {Curve} */
let curve_rot = null;
let irtracc_length = 0;
if (rib == 0) {
curve_disp = upper_rib_translation;
curve_rot = upper_rib_rotation;
irtracc_length = occupant.upper_rib_irtracc_length;
} else if (rib == 1) {
curve_disp = mid_rib_translation;
curve_rot = mid_rib_rotation;
irtracc_length = occupant.mid_rib_irtracc_length;
} else if (rib == 2) {
curve_disp = lower_rib_translation;
curve_rot = lower_rib_rotation;
irtracc_length = occupant.bottom_rib_irtracc_length;
}
/* Add IR-TRACC length */
let curve_total_disp = Operate.Add(curve_disp, irtracc_length);
curve_total_disp.RemoveFromGraph();
/* Convert length to mm */
let curve_disp_mm = Operate.Div(
curve_total_disp,
WorkflowUnits.LengthToMillimetresFactor(unit_system)
);
curve_disp_mm.RemoveFromGraph();
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp_mm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
let curve_rot_s = Operate.Dix(curve_rot, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_rot_s.RemoveFromGraph();
/* Filter with C180 */
let curve_disp_c180 = Operate.C180(curve_disp_s, 0.00001);
curve_disp_c180.RemoveFromGraph();
let curve_rot_c180 = Operate.C180(curve_rot_s, 0.00001);
curve_rot_c180.RemoveFromGraph();
/* Calculate the lateral displacement */
let curve_rot_sin_theta = Operate.Sin(curve_rot_c180);
curve_rot_sin_theta.RemoveFromGraph();
let curve_lat_disp = Operate.Mul(curve_disp_c180, curve_rot_sin_theta);
curve_lat_disp.RemoveFromGraph();
/* Assign displacement to the correct curve and calculate Viscous Criterion */
if (rib == 0) {
curve_upper_rib_disp = curve_lat_disp;
THisHelper.SetCurveLabels(
curve_upper_rib_disp,
`${occupant} Upper Rib Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle([curve_upper_rib_disp], this.GetCurveColourByIndex(occupant_index));
} else if (rib == 1) {
curve_mid_rib_disp = curve_lat_disp;
THisHelper.SetCurveLabels(
curve_mid_rib_disp,
`${occupant} Middle Rib Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle(
[curve_mid_rib_disp],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH
);
} else if (rib == 2) {
curve_bottom_rib_disp = curve_lat_disp;
THisHelper.SetCurveLabels(
curve_bottom_rib_disp,
`${occupant} Bottom Rib Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle(
[curve_bottom_rib_disp],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
}
}
values = [
new ReadAssessmentValue(`upper rib displacement`, curve_upper_rib_disp.ymax, `mm`),
new ReadAssessmentValue(`middle rib displacement`, curve_mid_rib_disp.ymax, `mm`),
new ReadAssessmentValue(`lower rib displacement`, curve_bottom_rib_disp.ymax, `mm`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_upper_rib_disp, curve_mid_rib_disp, curve_bottom_rib_disp],
model.id,
occupant.position,
OccupantBodyPart.CHEST,
values
);
case OccupantProduct.ES2RE:
let upper_translation = raw_measurements.GetCurve(Measurement.UPPER_TRANSLATION);
let mid_translation = raw_measurements.GetCurve(Measurement.MID_TRANSLATION);
let lower_translation = raw_measurements.GetCurve(Measurement.LOWER_TRANSLATION);
if (!upper_translation) {
WarningMessage(
`Unable to read upper translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!mid_translation) {
WarningMessage(
`Unable to read mid translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!lower_translation) {
WarningMessage(
`Unable to read lower translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
/** @type {Curve} */
let curve_upper_disp = null;
/** @type {Curve} */
let curve_mid_disp = null;
/** @type {Curve} */
let curve_bottom_disp = null;
/* Assess each sensor */
for (let sensor = 0; sensor < 3; sensor++) {
let curve_disp = null;
if (sensor == 0) {
curve_disp = upper_translation;
} else if (sensor == 1) {
curve_disp = mid_translation;
} else if (sensor == 2) {
curve_disp = lower_translation;
}
/* Convert length to mm */
let curve_disp_mm = Operate.Div(curve_disp, WorkflowUnits.LengthToMillimetresFactor(unit_system));
curve_disp_mm.RemoveFromGraph();
/* Compression is negative */
curve_disp_mm = Operate.Mul(curve_disp_mm, -1.0);
curve_disp_mm.RemoveFromGraph();
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp_mm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Filter with C180 */
let curve_disp_c180 = Operate.C180(curve_disp_s, 0.00001);
curve_disp_c180.RemoveFromGraph();
/* Convert back to model time */
curve_disp_c180 = Operate.Mux(curve_disp_c180, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_c180.RemoveFromGraph();
/* Assign displacement to the correct curve and calculate Viscous Criterion */
if (sensor == 0) {
curve_upper_disp = curve_disp_c180;
THisHelper.SetCurveLabels(
curve_upper_disp,
`${occupant} Upper Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle([curve_upper_disp], this.GetCurveColourByIndex(occupant_index));
} else if (sensor == 1) {
curve_mid_disp = curve_disp_c180;
THisHelper.SetCurveLabels(
curve_mid_disp,
`${occupant} Middle Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle(
[curve_mid_disp],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH
);
} else if (sensor == 2) {
curve_bottom_disp = curve_disp_c180;
THisHelper.SetCurveLabels(
curve_bottom_disp,
`${occupant} Bottom Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle(
[curve_bottom_disp],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
}
}
values = [
new ReadAssessmentValue(`upper displacement`, curve_upper_disp.ymax, `mm`),
new ReadAssessmentValue(`middle displacement`, curve_mid_disp.ymax, `mm`),
new ReadAssessmentValue(`lower displacement`, curve_bottom_disp.ymax, `mm`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_upper_disp, curve_mid_disp, curve_bottom_disp],
model.id,
occupant.position,
OccupantBodyPart.CHEST,
values
);
case OccupantProduct.SID2:
let upper_thorax_translation = raw_measurements.GetCurve(Measurement.UPPER_TRANSLATION);
let mid_thorax_translation = raw_measurements.GetCurve(Measurement.MID_TRANSLATION);
let lower_thorax_translation = raw_measurements.GetCurve(Measurement.LOWER_TRANSLATION);
if (!upper_thorax_translation) {
WarningMessage(
`Unable to read upper thorax translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!mid_thorax_translation) {
WarningMessage(
`Unable to read mid thorax translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!lower_thorax_translation) {
WarningMessage(
`Unable to read lower thorax translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
/** @type {Curve} */
let curve_upper_thorax_disp = null;
/** @type {Curve} */
let curve_mid_thorax_disp = null;
/** @type {Curve} */
let curve_bottom_thorax_disp = null;
/* Assess each sensor */
for (let sensor = 0; sensor < 3; sensor++) {
let curve_disp = null;
if (sensor == 0) {
curve_disp = upper_thorax_translation;
} else if (sensor == 1) {
curve_disp = mid_thorax_translation;
} else if (sensor == 2) {
curve_disp = lower_thorax_translation;
}
/* Convert length to mm */
let curve_disp_mm = Operate.Div(curve_disp, WorkflowUnits.LengthToMillimetresFactor(unit_system));
curve_disp_mm.RemoveFromGraph();
/* Compression is negative */
curve_disp_mm = Operate.Mul(curve_disp_mm, -1.0);
curve_disp_mm.RemoveFromGraph();
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp_mm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Filter with C180 */
let curve_disp_c180 = Operate.C180(curve_disp_s, 0.00001);
curve_disp_c180.RemoveFromGraph();
/* Convert back to model time */
curve_disp_c180 = Operate.Mux(curve_disp_c180, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_c180.RemoveFromGraph();
/* Assign displacement to the correct curve and calculate Viscous Criterion */
if (sensor == 0) {
curve_upper_thorax_disp = curve_disp_c180;
THisHelper.SetCurveLabels(
curve_upper_thorax_disp,
`${occupant} Upper Thorax Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle([curve_upper_thorax_disp], this.GetCurveColourByIndex(occupant_index));
} else if (sensor == 1) {
curve_mid_thorax_disp = curve_disp_c180;
THisHelper.SetCurveLabels(
curve_mid_thorax_disp,
`${occupant} Middle Thorax Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle(
[curve_mid_thorax_disp],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH
);
} else if (sensor == 2) {
curve_bottom_thorax_disp = curve_disp_c180;
THisHelper.SetCurveLabels(
curve_bottom_thorax_disp,
`${occupant} Bottom Thorax Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle(
[curve_bottom_thorax_disp],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
}
}
values = [
new ReadAssessmentValue(`upper thorax displacement`, curve_upper_thorax_disp.ymax, `mm`),
new ReadAssessmentValue(`middle thorax displacement`, curve_mid_thorax_disp.ymax, `mm`),
new ReadAssessmentValue(`lower thorax displacement`, curve_bottom_thorax_disp.ymax, `mm`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_upper_thorax_disp, curve_mid_thorax_disp, curve_bottom_thorax_disp],
model.id,
occupant.position,
OccupantBodyPart.CHEST,
values
);
case OccupantProduct.THOR:
/** @type {Curve} */
let upper_x1 = null;
/** @type {Curve} */
let upper_y1 = null;
/** @type {Curve} */
let upper_z1 = null;
/** @type {Curve} */
let lower_x1 = null;
/** @type {Curve} */
let lower_y1 = null;
/** @type {Curve} */
let lower_z1 = null;
/** @type {Curve} */
let upper_x2 = null;
/** @type {Curve} */
let upper_y2 = null;
/** @type {Curve} */
let upper_z2 = null;
/** @type {Curve} */
let lower_x2 = null;
/** @type {Curve} */
let lower_y2 = null;
/** @type {Curve} */
let lower_z2 = null;
if (side == "Left") {
upper_x1 = raw_measurements.GetCurve(Measurement.UPPER_LEFT_COMPRESSION_X_1);
upper_y1 = raw_measurements.GetCurve(Measurement.UPPER_LEFT_COMPRESSION_Y_1);
upper_z1 = raw_measurements.GetCurve(Measurement.UPPER_LEFT_COMPRESSION_Z_1);
lower_x1 = raw_measurements.GetCurve(Measurement.LOWER_LEFT_COMPRESSION_X_1);
lower_y1 = raw_measurements.GetCurve(Measurement.LOWER_LEFT_COMPRESSION_Y_1);
lower_z1 = raw_measurements.GetCurve(Measurement.LOWER_LEFT_COMPRESSION_Z_1);
upper_x2 = raw_measurements.GetCurve(Measurement.UPPER_LEFT_COMPRESSION_X_2);
upper_y2 = raw_measurements.GetCurve(Measurement.UPPER_LEFT_COMPRESSION_Y_2);
upper_z2 = raw_measurements.GetCurve(Measurement.UPPER_LEFT_COMPRESSION_Z_2);
lower_x2 = raw_measurements.GetCurve(Measurement.LOWER_LEFT_COMPRESSION_X_2);
lower_y2 = raw_measurements.GetCurve(Measurement.LOWER_LEFT_COMPRESSION_Y_2);
lower_z2 = raw_measurements.GetCurve(Measurement.LOWER_LEFT_COMPRESSION_Z_2);
} else if (side == "Right") {
upper_x1 = raw_measurements.GetCurve(Measurement.UPPER_RIGHT_COMPRESSION_X_1);
upper_y1 = raw_measurements.GetCurve(Measurement.UPPER_RIGHT_COMPRESSION_Y_1);
upper_z1 = raw_measurements.GetCurve(Measurement.UPPER_RIGHT_COMPRESSION_Z_1);
lower_x1 = raw_measurements.GetCurve(Measurement.LOWER_RIGHT_COMPRESSION_X_1);
lower_y1 = raw_measurements.GetCurve(Measurement.LOWER_RIGHT_COMPRESSION_Y_1);
lower_z1 = raw_measurements.GetCurve(Measurement.LOWER_RIGHT_COMPRESSION_Z_1);
upper_x2 = raw_measurements.GetCurve(Measurement.UPPER_RIGHT_COMPRESSION_X_2);
upper_y2 = raw_measurements.GetCurve(Measurement.UPPER_RIGHT_COMPRESSION_Y_2);
upper_z2 = raw_measurements.GetCurve(Measurement.UPPER_RIGHT_COMPRESSION_Z_2);
lower_x2 = raw_measurements.GetCurve(Measurement.LOWER_RIGHT_COMPRESSION_X_2);
lower_y2 = raw_measurements.GetCurve(Measurement.LOWER_RIGHT_COMPRESSION_Y_2);
lower_z2 = raw_measurements.GetCurve(Measurement.LOWER_RIGHT_COMPRESSION_Z_2);
} else {
ErrorMessage(`Invalid side '${side}' in <ProtocolAssessment.ReadChestCompression>`);
return null;
}
if (!upper_x1) {
WarningMessage(
`Unable to read upper ${side} x1 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!upper_y1) {
WarningMessage(
`Unable to read upper ${side} y1 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!upper_z1) {
WarningMessage(
`Unable to read upper ${side} z1 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!lower_x1) {
WarningMessage(
`Unable to read lower ${side} x1 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!lower_y1) {
WarningMessage(
`Unable to read lower ${side} y1 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!lower_z1) {
WarningMessage(
`Unable to read lower ${side} z1 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!upper_x2) {
WarningMessage(
`Unable to read upper ${side} x2 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!upper_y2) {
WarningMessage(
`Unable to read upper ${side} y2 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!upper_z2) {
WarningMessage(
`Unable to read upper ${side} z2 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!lower_x2) {
WarningMessage(
`Unable to read lower ${side} x2 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!lower_y2) {
WarningMessage(
`Unable to read lower ${side} y2 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
if (!lower_z2) {
WarningMessage(
`Unable to read lower ${side} z2 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestCompression>`
);
return null;
}
/* Get the difference between each node */
let curve_upper_diff_x = Operate.Sub(upper_x1, upper_x2);
let curve_upper_diff_y = Operate.Sub(upper_y1, upper_y2);
let curve_upper_diff_z = Operate.Sub(upper_z1, upper_z2);
curve_upper_diff_x.RemoveFromGraph();
curve_upper_diff_y.RemoveFromGraph();
curve_upper_diff_z.RemoveFromGraph();
let curve_lower_diff_x = Operate.Sub(lower_x1, lower_x2);
let curve_lower_diff_y = Operate.Sub(lower_y1, lower_y2);
let curve_lower_diff_z = Operate.Sub(lower_z1, lower_z2);
curve_lower_diff_x.RemoveFromGraph();
curve_lower_diff_y.RemoveFromGraph();
curve_lower_diff_z.RemoveFromGraph();
/* Get resultant */
let curve_upper_vec = Operate.Vec(curve_upper_diff_x, curve_upper_diff_y, curve_upper_diff_z);
let curve_lower_vec = Operate.Vec(curve_lower_diff_x, curve_lower_diff_y, curve_lower_diff_z);
curve_upper_vec.RemoveFromGraph();
curve_lower_vec.RemoveFromGraph();
/* Convert displacement to mm */
let curve_upper_mm = Operate.Div(curve_upper_vec, WorkflowUnits.LengthToMillimetresFactor(unit_system));
let curve_lower_mm = Operate.Div(curve_lower_vec, WorkflowUnits.LengthToMillimetresFactor(unit_system));
curve_upper_mm.RemoveFromGraph();
curve_lower_mm.RemoveFromGraph();
/* Convert time to seconds */
let curve_upper_s = Operate.Dix(curve_upper_mm, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_lower_s = Operate.Dix(curve_lower_mm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_upper_s.RemoveFromGraph();
curve_lower_s.RemoveFromGraph();
/* Filter with C180 */
let curve_upper_c180 = Operate.C180(curve_upper_s, 0.00001);
let curve_lower_c180 = Operate.C180(curve_lower_s, 0.00001);
curve_upper_c180.RemoveFromGraph();
curve_lower_c180.RemoveFromGraph();
/* Convert back to model time */
let curve_upper = Operate.Mux(curve_upper_c180, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_lower = Operate.Mux(curve_lower_c180, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_upper.RemoveFromGraph();
curve_lower.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_upper,
`${occupant} Chest Upper ${side} Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle([curve_upper], this.GetCurveColourByIndex(occupant_index));
THisHelper.SetCurveLabels(
curve_lower,
`${occupant} Chest Lower ${side} Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle([curve_lower], this.GetCurveColourByIndex(occupant_index), LineStyle.DASH);
values = [
new ReadAssessmentValue(`${side} upper max compression`, curve_upper.ymax, `mm`),
new ReadAssessmentValue(`${side} lower max compression`, curve_lower.ymax, `mm`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_upper, curve_lower],
model.id,
occupant.position,
OccupantBodyPart.CHEST,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadChestCompression>`);
return null;
}
}
/**
* Reads chest compression rate from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadChestCompressionRate(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Chest Compression Rate`;
/* Get the compression output */
let compression_output = this.ReadChestCompression(model, occupant, unit_system, occupant_index);
if (!compression_output) return null;
let values = [];
switch (occupant.product) {
case OccupantProduct.HIII:
if (compression_output.curves.length == 0) return null;
let curve_disp = compression_output.curves[0];
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Convert from mm to metres */
let curve_disp_m = Operate.Div(curve_disp_s, 1000.0);
curve_disp_m.RemoveFromGraph();
/* Differentiate to get deflection rate in m/s */
let curve_disp_rate = Operate.Dif(curve_disp_m);
curve_disp_rate.RemoveFromGraph();
/* Convert back to model time */
curve_disp_rate = Operate.Mux(curve_disp_rate, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_rate.RemoveFromGraph();
THisHelper.SetCurveLabels(
curve_disp_rate,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression Rate (m/s)"
);
THisHelper.SetLineStyle([curve_disp_rate], this.GetCurveColourByIndex(occupant_index));
values = [new ReadAssessmentValue(`max chest compression rate`, curve_disp_rate.ymax, `m/s`)];
return new ReadAssessmentOutput(
graph_title,
[curve_disp_rate],
model.id,
occupant.position,
OccupantBodyPart.CHEST,
values
);
case OccupantProduct.WSID:
case OccupantProduct.ES2RE:
case OccupantProduct.SID2:
if (compression_output.curves.length < 3) return null;
/** @type {Curve} */
let curve_upper_disp_rate = null;
/** @type {Curve} */
let curve_mid_disp_rate = null;
/** @type {Curve} */
let curve_bottom_disp_rate = null;
/* Assess each rib */
for (let rib = 0; rib < 3; rib++) {
let curve_disp = compression_output.curves[rib];
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Convert from mm to metres */
let curve_disp_m = Operate.Div(curve_disp_s, 1000.0);
curve_disp_m.RemoveFromGraph();
/* Differentiate to get deflection rate in m/s */
let curve_disp_rate = Operate.Dif(curve_disp_m);
curve_disp_rate.RemoveFromGraph();
/* Convert back to model time */
curve_disp_rate = Operate.Mux(curve_disp_rate, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_rate.RemoveFromGraph();
/* Assign to the correct curve */
if (rib == 0) {
curve_upper_disp_rate = curve_disp_rate;
THisHelper.SetCurveLabels(
curve_upper_disp_rate,
`${occupant} Upper Compression Rate`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression Rate (m/s)"
);
THisHelper.SetLineStyle([curve_disp_rate], this.GetCurveColourByIndex(occupant_index));
} else if (rib == 1) {
curve_mid_disp_rate = curve_disp_rate;
THisHelper.SetCurveLabels(
curve_mid_disp_rate,
`${occupant} Middle Compression Rate`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression Rate (m/s)"
);
THisHelper.SetLineStyle(
[curve_disp_rate],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH
);
} else if (rib == 2) {
curve_bottom_disp_rate = curve_disp_rate;
THisHelper.SetCurveLabels(
curve_bottom_disp_rate,
`${occupant} Bottom Compression Rate`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression Rate (m/s)"
);
THisHelper.SetLineStyle(
[curve_disp_rate],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
}
}
values = [
new ReadAssessmentValue(`upper displacement rate`, curve_upper_disp_rate.ymax, `m/s`),
new ReadAssessmentValue(`middle displacement rate`, curve_mid_disp_rate.ymax, `m/s`),
new ReadAssessmentValue(`lower displacement rate`, curve_bottom_disp_rate.ymax, `m/s`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_upper_disp_rate, curve_mid_disp_rate, curve_bottom_disp_rate],
model.id,
occupant.position,
OccupantBodyPart.CHEST,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadChestCompressionRate>`);
return null;
}
}
/**
* Reads chest viscous criterion from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadChestViscousCriterion(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Chest Viscous Criterion`;
/* Get the compression output */
let compression_output = this.ReadChestCompression(model, occupant, unit_system, occupant_index);
if (!compression_output) return null;
/* Viscous Criterion constants */
let viscous_criterion_constants = this.GetViscousCriterionConstants();
if (!viscous_criterion_constants) return null;
let values = [];
switch (occupant.product) {
case OccupantProduct.HIII:
if (compression_output.curves.length == 0) return null;
let curve_disp = compression_output.curves[0];
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Convert from mm to metres */
let curve_disp_m = Operate.Div(curve_disp_s, 1000.0);
curve_disp_m.RemoveFromGraph();
let curve_viscous_criterion = Operate.Vc(
curve_disp_m,
viscous_criterion_constants.A,
viscous_criterion_constants.B,
"ECER95"
);
curve_viscous_criterion.RemoveFromGraph();
/* Convert back to model time */
curve_viscous_criterion = Operate.Mux(
curve_viscous_criterion,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_viscous_criterion.RemoveFromGraph();
THisHelper.SetCurveLabels(
curve_viscous_criterion,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Velocity (m/s)"
);
THisHelper.SetLineStyle([curve_viscous_criterion], this.GetCurveColourByIndex(occupant_index));
values = [new ReadAssessmentValue(`max viscous criterion`, curve_viscous_criterion.ymax, `m/s`)];
return new ReadAssessmentOutput(
graph_title,
[curve_viscous_criterion],
model.id,
occupant.position,
OccupantBodyPart.CHEST,
values
);
case OccupantProduct.WSID:
case OccupantProduct.ES2RE:
case OccupantProduct.SID2:
if (compression_output.curves.length < 3) return null;
/** @type {Curve} */
let curve_upper_vc = null;
/** @type {Curve} */
let curve_mid_vc = null;
/** @type {Curve} */
let curve_bottom_vc = null;
/* Assess each rib */
for (let rib = 0; rib < 3; rib++) {
let curve_disp = compression_output.curves[rib];
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Convert from mm to metres */
let curve_disp_m = Operate.Div(curve_disp_s, 1000.0);
curve_disp_m.RemoveFromGraph();
let curve_viscous_criterion = Operate.Vc(
curve_disp_m,
viscous_criterion_constants.A,
viscous_criterion_constants.B,
"ECER95"
);
curve_viscous_criterion.RemoveFromGraph();
/* Convert back to model time */
curve_viscous_criterion = Operate.Mux(
curve_viscous_criterion,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_viscous_criterion.RemoveFromGraph();
/* Assign displacement to the correct curve and calculate Viscous Criterion */
if (rib == 0) {
curve_upper_vc = curve_viscous_criterion;
THisHelper.SetCurveLabels(
curve_upper_vc,
`${occupant} Upper Chest Viscous Criterion`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Velocity (m/s)"
);
THisHelper.SetLineStyle([curve_viscous_criterion], this.GetCurveColourByIndex(occupant_index));
} else if (rib == 1) {
curve_mid_vc = curve_viscous_criterion;
THisHelper.SetCurveLabels(
curve_mid_vc,
`${occupant} Middle Chest Viscous Criterion`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Velocity (m/s)"
);
THisHelper.SetLineStyle(
[curve_viscous_criterion],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH
);
} else if (rib == 2) {
curve_bottom_vc = curve_viscous_criterion;
THisHelper.SetCurveLabels(
curve_bottom_vc,
`${occupant} Bottom Chest Viscous Criterion`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Velocity (m/s)"
);
THisHelper.SetLineStyle(
[curve_viscous_criterion],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
}
}
values = [
new ReadAssessmentValue(`upper viscous criterion`, curve_upper_vc.ymax, `m/s`),
new ReadAssessmentValue(`middle viscous criterion`, curve_mid_vc.ymax, `m/s`),
new ReadAssessmentValue(`lower viscous criterion`, curve_bottom_vc.ymax, `m/s`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_upper_vc, curve_mid_vc, curve_bottom_vc],
model.id,
occupant.position,
OccupantBodyPart.CHEST,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadChestViscousCriterion>`);
return null;
}
}
/**
* Reads chest acceleration from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {object} [options] Options
* @returns {?ReadAssessmentOutput}
*/
ReadChestAcceleration(model, occupant, unit_system, occupant_index, options) {
/* Graph title - also used in curve labels */
let graph_title = `Chest Acceleration`;
/* Get the raw measurements from the chest */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.CHEST);
/* Process them to get compression */
switch (`${occupant.product}${occupant.physiology}`) {
case `${OccupantProduct.HIII}${OccupantPhysiology.M50}`:
let x_acceleration = raw_measurements.GetCurve(Measurement.X_ACCELERATION);
let y_acceleration = raw_measurements.GetCurve(Measurement.Y_ACCELERATION);
let z_acceleration = raw_measurements.GetCurve(Measurement.Z_ACCELERATION);
if (!x_acceleration) {
WarningMessage(
`Unable to read x acceleration from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestAcceleration>`
);
return null;
}
if (!y_acceleration) {
WarningMessage(
`Unable to read y acceleration from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestAcceleration>`
);
return null;
}
if (!z_acceleration) {
WarningMessage(
`Unable to read z acceleration from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadChestAcceleration>`
);
return null;
}
/* Convert acceleration to g */
let curve_x_g = Operate.Div(x_acceleration, WorkflowUnits.GravityConstant(unit_system));
curve_x_g.RemoveFromGraph();
let curve_y_g = Operate.Div(y_acceleration, WorkflowUnits.GravityConstant(unit_system));
curve_y_g.RemoveFromGraph();
let curve_z_g = Operate.Div(z_acceleration, WorkflowUnits.GravityConstant(unit_system));
curve_z_g.RemoveFromGraph();
/* Convert time to seconds */
let curve_x_g_s = Operate.Dix(curve_x_g, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_x_g_s.RemoveFromGraph();
let curve_y_g_s = Operate.Dix(curve_y_g, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_y_g_s.RemoveFromGraph();
let curve_z_g_s = Operate.Dix(curve_z_g, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_z_g_s.RemoveFromGraph();
/* Filter with C1000 */
let curve_x_c1000 = Operate.C1000(curve_x_g_s, 0.00001);
curve_x_c1000.RemoveFromGraph();
let curve_y_c1000 = Operate.C1000(curve_y_g_s, 0.00001);
curve_y_c1000.RemoveFromGraph();
let curve_z_c1000 = Operate.C1000(curve_z_g_s, 0.00001);
curve_z_c1000.RemoveFromGraph();
/* Vector combine */
let curve_vec = Operate.Vec(curve_x_c1000, curve_y_c1000, curve_z_c1000);
curve_vec.RemoveFromGraph();
/* Convert back to model time */
let curve_acceleration = Operate.Mux(curve_vec, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_acceleration.RemoveFromGraph();
let values = [];
/* Do a three ms calculation */
if (options && options.three_ms) {
Operate.Tms(curve_acceleration, 0.003 * WorkflowUnits.TimeToSecondsFactor(unit_system));
values = [
new ReadAssessmentValue(`TMS`, curve_acceleration.tms, `g`),
new ReadAssessmentValue(`Start of TMS window`, curve_acceleration.tms_tmin, `s`),
new ReadAssessmentValue(`End of TMS window`, curve_acceleration.tms_tmax, `s`)
];
curve_acceleration.RemoveFromGraph();
}
THisHelper.SetCurveLabels(
curve_acceleration,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Acceleration (g)"
);
THisHelper.SetLineStyle([curve_acceleration], this.GetCurveColourByIndex(occupant_index));
return new ReadAssessmentOutput(
graph_title,
[curve_acceleration],
model.id,
occupant.position,
OccupantBodyPart.CHEST,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadChestAcceleration>`);
return null;
}
}
/**
* Reads shoulder deflection from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadShoulderDeflection(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Shoulder Compression`;
/* Get the raw measurements from the shoulder */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.SHOULDER);
/* Process them to get deflections */
switch (occupant.product) {
case OccupantProduct.SID2:
let deflection = raw_measurements.GetCurve(Measurement.DEFLECTION);
if (!deflection) {
WarningMessage(
`Unable to read deflection from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadShoulderDeflection>`
);
return null;
}
/* Convert length to mm */
let curve_disp_mm = Operate.Div(deflection, WorkflowUnits.LengthToMillimetresFactor(unit_system));
curve_disp_mm.RemoveFromGraph();
/* Compression is negative */
curve_disp_mm = Operate.Mul(curve_disp_mm, -1.0);
curve_disp_mm.RemoveFromGraph();
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp_mm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Filter with C180 */
let curve_disp_c180 = Operate.C180(curve_disp_s, 0.00001);
curve_disp_c180.RemoveFromGraph();
/* Convert back to model time */
let curve_disp = Operate.Mux(curve_disp_c180, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_disp,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle([curve_disp], this.GetCurveColourByIndex(occupant_index));
let values = [new ReadAssessmentValue(`max compression`, curve_disp.ymax, `mm`)];
return new ReadAssessmentOutput(
graph_title,
[curve_disp],
model.id,
occupant.position,
OccupantBodyPart.SHOULDER,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadShoulderDeflection>`);
return null;
}
}
/**
* Reads shoulder deflection rate from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadShoulderDeflectionRate(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Shoulder Compression Rate`;
/* Get the deflection output */
let deflection_output = this.ReadShoulderDeflection(model, occupant, unit_system, occupant_index);
if (!deflection_output) return null;
switch (occupant.product) {
case OccupantProduct.SID2:
if (deflection_output.curves.length == 0) return null;
let curve_disp = deflection_output.curves[0];
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Convert from mm to metres */
let curve_disp_m = Operate.Div(curve_disp_s, 1000.0);
curve_disp_m.RemoveFromGraph();
/* Differentiate to get deflection rate in m/s */
let curve_disp_rate = Operate.Dif(curve_disp_m);
curve_disp_rate.RemoveFromGraph();
/* Convert back to model time */
curve_disp_rate = Operate.Mux(curve_disp_rate, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_rate.RemoveFromGraph();
THisHelper.SetCurveLabels(
curve_disp_rate,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression Rate (m/s)"
);
THisHelper.SetLineStyle([curve_disp_rate], this.GetCurveColourByIndex(occupant_index));
let values = [new ReadAssessmentValue(`max compression rate`, curve_disp_rate.ymax, `m/s`)];
return new ReadAssessmentOutput(
graph_title,
[curve_disp_rate],
model.id,
occupant.position,
OccupantBodyPart.SHOULDER,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadShoulderCompressionRate>`);
return null;
}
}
/**
* Reads shoulder viscous criterion from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadShoulderViscousCriterion(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Shoulder Viscous Criterion`;
/* Get the deflection output */
let deflection_output = this.ReadShoulderDeflection(model, occupant, unit_system, occupant_index);
if (!deflection_output) return null;
/* Viscous Criterion constants */
let viscous_criterion_constants = this.GetViscousCriterionConstants();
if (!viscous_criterion_constants) return null;
switch (occupant.product) {
case OccupantProduct.SID2:
if (deflection_output.curves.length == 0) return null;
let curve_disp = deflection_output.curves[0];
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Convert from mm to metres */
let curve_disp_m = Operate.Div(curve_disp_s, 1000.0);
curve_disp_m.RemoveFromGraph();
let curve_viscous_criterion = Operate.Vc(
curve_disp_m,
viscous_criterion_constants.A,
viscous_criterion_constants.B,
"ECER95"
);
curve_viscous_criterion.RemoveFromGraph();
/* Convert back to model time */
curve_viscous_criterion = Operate.Mux(
curve_viscous_criterion,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_viscous_criterion.RemoveFromGraph();
THisHelper.SetCurveLabels(
curve_viscous_criterion,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Velocity (m/s)"
);
THisHelper.SetLineStyle([curve_viscous_criterion], this.GetCurveColourByIndex(occupant_index));
let values = [new ReadAssessmentValue(`max viscous criterion`, curve_viscous_criterion.ymax, `m/s`)];
return new ReadAssessmentOutput(
graph_title,
[curve_viscous_criterion],
model.id,
occupant.position,
OccupantBodyPart.SHOULDER,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadShoulderViscousCriterion>`);
return null;
}
}
/**
* Reads shoulder lateral forces from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadShoulderLateralForces(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Shoulder Lateral Force`;
/* Get the raw measurements from the shoulder */
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.SHOULDER);
/* Process them to get deflections */
switch (occupant.product) {
case OccupantProduct.WSID:
let left_force = raw_measurements.GetCurve(Measurement.LEFT_FORCE);
let right_force = raw_measurements.GetCurve(Measurement.RIGHT_FORCE);
if (!left_force) {
WarningMessage(
`Unable to read left force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadShoulderLateralForces>`
);
return null;
}
if (!right_force) {
WarningMessage(
`Unable to read right force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadShoulderLateralForces>`
);
return null;
}
/** @type {Curve} */
let curve_left_force = null;
/** @type {Curve} */
let curve_right_force = null;
for (let side = 0; side < 2; side++) {
/** @type {Curve} */
let curve_force = null;
if (side == 0) {
curve_force = left_force;
} else if (side == 1) {
curve_force = right_force;
}
/* Convert force to kN */
let curve_force_kn = Operate.Div(curve_force, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_force_kn.RemoveFromGraph();
/* Convert time to seconds */
let curve_force_s = Operate.Dix(curve_force_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_force_s.RemoveFromGraph();
/* Filter with C600 */
let curve_force_c600 = Operate.C600(curve_force_s, 0.00001);
curve_force_c600.RemoveFromGraph();
/* Convert back to model time */
let curve_lateral_force = Operate.Mux(
curve_force_c600,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_lateral_force.RemoveFromGraph();
/* Set the labels and line style */
if (side == 0) {
curve_left_force = curve_lateral_force;
THisHelper.SetCurveLabels(
curve_left_force,
`${occupant} Left Shoulder Lateral Force`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compressive Force (kN)"
);
THisHelper.SetLineStyle([curve_left_force], this.GetCurveColourByIndex(occupant_index));
} else if (side == 1) {
curve_right_force = curve_lateral_force;
THisHelper.SetCurveLabels(
curve_right_force,
`${occupant} Right Shoulder Lateral Force`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compressive Force (kN)"
);
THisHelper.SetLineStyle(
[curve_right_force],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
}
}
let values = [
new ReadAssessmentValue(`max left force`, curve_left_force.ymax, `kN`),
new ReadAssessmentValue(`max right force`, curve_right_force.ymax, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_left_force, curve_right_force],
model.id,
occupant.position,
OccupantBodyPart.SHOULDER,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadShoulderLateralForces>`);
return null;
}
}
/**
* Reads abdomen compression from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {string} [side] "Left" or "Right" side
* @returns {?ReadAssessmentOutput}
*/
ReadAbdomenCompression(model, occupant, unit_system, occupant_index, side) {
/* Graph title - also used in curve labels */
let graph_title = `Abdomen Compression`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.ABDOMEN);
/* Process them to get compression */
switch (occupant.product) {
case OccupantProduct.WSID: {
let upper_rotation = raw_measurements.GetCurve(Measurement.UPPER_ROTATION);
let lower_rotation = raw_measurements.GetCurve(Measurement.LOWER_ROTATION);
let upper_translation = raw_measurements.GetCurve(Measurement.UPPER_TRANSLATION);
let lower_translation = raw_measurements.GetCurve(Measurement.LOWER_TRANSLATION);
if (!upper_rotation) {
WarningMessage(
`Unable to read upper abdomen rotation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
if (!lower_rotation) {
WarningMessage(
`Unable to read lower abdomen rotation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
if (!upper_translation) {
WarningMessage(
`Unable to read upper abdomen translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
if (!lower_translation) {
WarningMessage(
`Unable to read lower abdomen translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
/* Assess upper and lower abdomen */
/** @type {Curve} */
let curve_upper_disp = null;
/** @type {Curve} */
let curve_bottom_disp = null;
for (let i = 0; i < 2; i++) {
/** @type {Curve} */
let curve_disp = null;
/** @type {Curve} */
let curve_rot = null;
let irtracc_length = 0;
if (i == 0) {
curve_disp = upper_translation;
curve_rot = upper_rotation;
irtracc_length = occupant.upper_abdomen_irtracc_length;
} else if (i == 1) {
curve_disp = lower_translation;
curve_rot = lower_rotation;
irtracc_length = occupant.bottom_abdomen_irtracc_length;
}
/* Add IR-TRACC length */
let curve_total_disp = Operate.Add(curve_disp, irtracc_length);
curve_total_disp.RemoveFromGraph();
/* Convert length to mm */
let curve_disp_mm = Operate.Div(
curve_total_disp,
WorkflowUnits.LengthToMillimetresFactor(unit_system)
);
curve_disp_mm.RemoveFromGraph();
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp_mm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
let curve_rot_s = Operate.Dix(curve_rot, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_rot_s.RemoveFromGraph();
/* Filter with C180 */
let curve_disp_c180 = Operate.C180(curve_disp_s, 0.00001);
curve_disp_c180.RemoveFromGraph();
let curve_rot_c180 = Operate.C180(curve_rot_s, 0.00001);
curve_rot_c180.RemoveFromGraph();
/* Calculate the lateral displacement */
let curve_rot_sin_theta = Operate.Sin(curve_rot_c180);
curve_rot_sin_theta.RemoveFromGraph();
let curve_lat_disp = Operate.Mul(curve_disp_c180, curve_rot_sin_theta);
curve_lat_disp.RemoveFromGraph();
/* Assign displacement to the correct curve and calculate Viscous Criterion */
if (i == 0) {
curve_upper_disp = curve_lat_disp;
THisHelper.SetCurveLabels(
curve_upper_disp,
`${occupant} Upper Abdomen Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle([curve_upper_disp], this.GetCurveColourByIndex(occupant_index));
} else if (i == 1) {
curve_bottom_disp = curve_lat_disp;
THisHelper.SetCurveLabels(
curve_bottom_disp,
`${occupant} Bottom Abdomen Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle(
[curve_bottom_disp],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
}
}
let values = [
new ReadAssessmentValue(`upper abdomen displacement`, curve_upper_disp.ymax, `kN`),
new ReadAssessmentValue(`lower abdomen displacement`, curve_bottom_disp.ymax, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_upper_disp, curve_bottom_disp],
model.id,
occupant.position,
OccupantBodyPart.ABDOMEN,
values
);
}
case OccupantProduct.SID2: {
let upper_translation = raw_measurements.GetCurve(Measurement.UPPER_TRANSLATION);
let lower_translation = raw_measurements.GetCurve(Measurement.LOWER_TRANSLATION);
if (!upper_translation) {
WarningMessage(
`Unable to read upper abdomen translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
if (!lower_translation) {
WarningMessage(
`Unable to read lower abdomen translation from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
/** @type {Curve} */
let curve_upper_disp = null;
/** @type {Curve} */
let curve_bottom_disp = null;
/* Assess upper and lower abdomen */
for (let i = 0; i < 2; i++) {
let curve_disp = null;
if (i == 0) {
curve_disp = upper_translation;
} else if (i == 1) {
curve_disp = lower_translation;
}
/* Convert length to mm */
let curve_disp_mm = Operate.Div(curve_disp, WorkflowUnits.LengthToMillimetresFactor(unit_system));
curve_disp_mm.RemoveFromGraph();
/* Compression is negative */
curve_disp_mm = Operate.Mul(curve_disp_mm, -1.0);
curve_disp_mm.RemoveFromGraph();
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp_mm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Filter with C180 */
let curve_disp_c180 = Operate.C180(curve_disp_s, 0.00001);
curve_disp_c180.RemoveFromGraph();
/* Convert back to model time */
curve_disp_c180 = Operate.Mux(curve_disp_c180, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_c180.RemoveFromGraph();
/* Assign displacement to the correct curve and calculate Viscous Criterion */
if (i == 0) {
curve_upper_disp = curve_disp_c180;
THisHelper.SetCurveLabels(
curve_upper_disp,
`${occupant} Upper Abdomen Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle([curve_upper_disp], this.GetCurveColourByIndex(occupant_index));
} else if (i == 1) {
curve_bottom_disp = curve_disp_c180;
THisHelper.SetCurveLabels(
curve_bottom_disp,
`${occupant} Bottom Abdomen Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle(
[curve_bottom_disp],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
}
}
let values = [
new ReadAssessmentValue(`upper abdomen displacement`, curve_upper_disp.ymax, `mm`),
new ReadAssessmentValue(`lower abdomen displacement`, curve_bottom_disp.ymax, `mm`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_upper_disp, curve_bottom_disp],
model.id,
occupant.position,
OccupantBodyPart.ABDOMEN,
values
);
}
case OccupantProduct.THOR:
/** @type {Curve} */
let x1 = null;
/** @type {Curve} */
let y1 = null;
/** @type {Curve} */
let z1 = null;
/** @type {Curve} */
let x2 = null;
/** @type {Curve} */
let y2 = null;
/** @type {Curve} */
let z2 = null;
if (side == "Left") {
x1 = raw_measurements.GetCurve(Measurement.LEFT_COMPRESSION_X_1);
y1 = raw_measurements.GetCurve(Measurement.LEFT_COMPRESSION_Y_1);
z1 = raw_measurements.GetCurve(Measurement.LEFT_COMPRESSION_Z_1);
x2 = raw_measurements.GetCurve(Measurement.LEFT_COMPRESSION_X_2);
y2 = raw_measurements.GetCurve(Measurement.LEFT_COMPRESSION_Y_2);
z2 = raw_measurements.GetCurve(Measurement.LEFT_COMPRESSION_Z_2);
} else if (side == "Right") {
x1 = raw_measurements.GetCurve(Measurement.RIGHT_COMPRESSION_X_1);
y1 = raw_measurements.GetCurve(Measurement.RIGHT_COMPRESSION_Y_1);
z1 = raw_measurements.GetCurve(Measurement.RIGHT_COMPRESSION_Z_1);
x2 = raw_measurements.GetCurve(Measurement.RIGHT_COMPRESSION_X_2);
y2 = raw_measurements.GetCurve(Measurement.RIGHT_COMPRESSION_Y_2);
z2 = raw_measurements.GetCurve(Measurement.RIGHT_COMPRESSION_Z_2);
} else {
ErrorMessage(`Invalid side '${side}' in <ProtocolAssessment.ReadAbdomenCompression>`);
return null;
}
if (!x1) {
WarningMessage(
`Unable to read ${side} x1 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
if (!y1) {
WarningMessage(
`Unable to read ${side} y1 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
if (!z1) {
WarningMessage(
`Unable to read ${side} z1 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
if (!x2) {
WarningMessage(
`Unable to read ${side} x2 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
if (!y2) {
WarningMessage(
`Unable to read ${side} y2 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
if (!z2) {
WarningMessage(
`Unable to read ${side} z2 displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenCompression>`
);
return null;
}
/* Get the difference between each node */
let curve_diff_x = Operate.Sub(x1, x2);
let curve_diff_y = Operate.Sub(y1, y2);
let curve_diff_z = Operate.Sub(z1, z2);
curve_diff_x.RemoveFromGraph();
curve_diff_y.RemoveFromGraph();
curve_diff_z.RemoveFromGraph();
/* Get resultant */
let curve_vec = Operate.Vec(curve_diff_x, curve_diff_y, curve_diff_z);
curve_vec.RemoveFromGraph();
/* Convert displacement to mm */
let curve_mm = Operate.Div(curve_vec, WorkflowUnits.LengthToMillimetresFactor(unit_system));
curve_mm.RemoveFromGraph();
/* Convert time to seconds */
let curve_s = Operate.Dix(curve_mm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_s.RemoveFromGraph();
/* Filter with C180 */
let curve_c180 = Operate.C180(curve_s, 0.00001);
curve_c180.RemoveFromGraph();
/* Convert back to model time */
let curve_disp = Operate.Mux(curve_c180, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_disp,
`${occupant} Abdomen ${side} Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (mm)"
);
THisHelper.SetLineStyle([curve_disp], this.GetCurveColourByIndex(occupant_index));
let values = [new ReadAssessmentValue(`${side} max compression`, curve_disp.ymax, `mm`)];
return new ReadAssessmentOutput(
`${side} ${graph_title}`,
[curve_disp],
model.id,
occupant.position,
OccupantBodyPart.ABDOMEN,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadAbdomenCompression>`);
return null;
}
}
/**
* Reads abdomen viscous criterion from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadAbdomenViscousCriterion(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Abdomen Viscous Criterion`;
/* Get the compression output */
let compression_output = this.ReadAbdomenCompression(model, occupant, unit_system, occupant_index);
if (!compression_output) return null;
/* Viscous Criterion constants */
let viscous_criterion_constants = this.GetViscousCriterionConstants();
if (!viscous_criterion_constants) return null;
switch (occupant.product) {
case OccupantProduct.SID2:
case OccupantProduct.WSID:
if (compression_output.curves.length < 2) return null;
/** @type {Curve} */
let curve_upper_vc = null;
/** @type {Curve} */
let curve_bottom_vc = null;
/* Assess upper and lower abdomen */
for (let i = 0; i < 2; i++) {
let curve_disp = compression_output.curves[i];
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Convert from mm to metres */
let curve_disp_m = Operate.Div(curve_disp_s, 1000.0);
curve_disp_m.RemoveFromGraph();
let curve_viscous_criterion = Operate.Vc(
curve_disp_m,
viscous_criterion_constants.A,
viscous_criterion_constants.B,
"ECER95"
);
curve_viscous_criterion.RemoveFromGraph();
/* Convert back to model time */
curve_viscous_criterion = Operate.Mux(
curve_viscous_criterion,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_viscous_criterion.RemoveFromGraph();
/* Assign displacement to the correct curve and calculate Viscous Criterion */
if (i == 0) {
curve_upper_vc = curve_viscous_criterion;
THisHelper.SetCurveLabels(
curve_upper_vc,
`${occupant} Upper Abdomen Viscous Criterion`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Velocity (m/s)"
);
THisHelper.SetLineStyle([curve_viscous_criterion], this.GetCurveColourByIndex(occupant_index));
} else if (i == 1) {
curve_bottom_vc = curve_viscous_criterion;
THisHelper.SetCurveLabels(
curve_bottom_vc,
`${occupant} Bottom Abdomen Viscous Criterion`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Velocity (m/s)"
);
THisHelper.SetLineStyle(
[curve_viscous_criterion],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
}
}
let values = [
new ReadAssessmentValue(`upper viscous criterion`, curve_upper_vc.ymax, `m/s`),
new ReadAssessmentValue(`lower viscous criterion`, curve_bottom_vc.ymax, `m/s`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_upper_vc, curve_bottom_vc],
model.id,
occupant.position,
OccupantBodyPart.ABDOMEN,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadAbdomenViscousCriterion>`);
return null;
}
}
/**
* Reads abdomen forces from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadAbdomenForce(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Abdomen Force`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.ABDOMEN);
/* Process them to get force */
switch (occupant.product) {
case OccupantProduct.ES2RE:
let front_force = raw_measurements.GetCurve(Measurement.FRONT_FORCE);
let mid_force = raw_measurements.GetCurve(Measurement.MID_FORCE);
let back_force = raw_measurements.GetCurve(Measurement.BACK_FORCE);
if (!front_force) {
WarningMessage(
`Unable to read front abdomen force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenForce>`
);
return null;
}
if (!mid_force) {
WarningMessage(
`Unable to read mid abdomen force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenForce>`
);
return null;
}
if (!back_force) {
WarningMessage(
`Unable to read back abdomen force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAbdomenForce>`
);
return null;
}
/* Convert time to seconds */
let curve_front_s = Operate.Dix(front_force, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_mid_s = Operate.Dix(mid_force, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_back_s = Operate.Dix(back_force, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_front_s.RemoveFromGraph();
curve_mid_s.RemoveFromGraph();
curve_back_s.RemoveFromGraph();
/* Filter with C600 */
let curve_front_c600 = Operate.C600(curve_front_s, 0.00001);
let curve_mid_c600 = Operate.C600(curve_mid_s, 0.00001);
let curve_back_c600 = Operate.C600(curve_back_s, 0.00001);
curve_front_c600.RemoveFromGraph();
curve_mid_c600.RemoveFromGraph();
curve_back_c600.RemoveFromGraph();
/* Convert to kN */
let curve_front_kn = Operate.Div(curve_front_c600, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
let curve_mid_kn = Operate.Div(curve_mid_c600, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
let curve_back_kn = Operate.Div(curve_back_c600, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_front_kn.RemoveFromGraph();
curve_mid_kn.RemoveFromGraph();
curve_back_kn.RemoveFromGraph();
/* Convert back to model time */
curve_front_kn = Operate.Mux(curve_front_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_mid_kn = Operate.Mux(curve_mid_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_back_kn = Operate.Mux(curve_back_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_front_kn.RemoveFromGraph();
curve_mid_kn.RemoveFromGraph();
curve_back_kn.RemoveFromGraph();
/* Sum all the forces */
let curve_front_add_mid_kn = Operate.Add(curve_front_kn, curve_mid_kn);
curve_front_add_mid_kn.RemoveFromGraph();
let curve_force = Operate.Add(curve_front_add_mid_kn, curve_back_kn);
THisHelper.SetCurveLabels(
curve_force,
`${occupant} Resultant Abdomen Force`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetCurveLabels(
curve_front_kn,
`${occupant} Front Abdomen Force`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetCurveLabels(
curve_mid_kn,
`${occupant} Middle Abdomen Force`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetCurveLabels(
curve_back_kn,
`${occupant} Back Abdomen Force`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_force], this.GetCurveColourByIndex(occupant_index));
THisHelper.SetLineStyle([curve_front_kn], this.GetCurveColourByIndex(occupant_index), LineStyle.DASH);
THisHelper.SetLineStyle([curve_mid_kn], this.GetCurveColourByIndex(occupant_index), LineStyle.DASH2);
THisHelper.SetLineStyle([curve_back_kn], this.GetCurveColourByIndex(occupant_index), LineStyle.DASH3);
let values = [
new ReadAssessmentValue(`max abdomen force`, curve_force.ymax, `kN`),
new ReadAssessmentValue(`min abdomen force`, curve_force.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_force, curve_front_kn, curve_mid_kn, curve_back_kn],
model.id,
occupant.position,
OccupantBodyPart.ABDOMEN,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadAbdomenForce>`);
return null;
}
}
/**
* Reads lumbar shear force from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadLumbarShear(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Lumbar Shear`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.LUMBAR);
switch (occupant.product) {
case OccupantProduct.WSID:
let shear = raw_measurements.GetCurve(Measurement.SHEAR);
if (!shear) {
WarningMessage(
`Unable to read shear force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadLumbarShear>`
);
return null;
}
/* Convert time to seconds */
let curve_shear_s = Operate.Dix(shear, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_shear_s.RemoveFromGraph();
/* Filter with C600 */
let curve_shear_c600 = Operate.C600(curve_shear_s, 0.00001);
curve_shear_c600.RemoveFromGraph();
/* Convert to kN */
let curve_shear_kn = Operate.Div(curve_shear_c600, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_shear_kn.RemoveFromGraph();
/* Convert back to model time */
curve_shear_kn = Operate.Mux(curve_shear_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_shear_kn.RemoveFromGraph();
THisHelper.SetCurveLabels(
curve_shear_kn,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_shear_kn], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max shear`, curve_shear_kn.ymax, `kN`),
new ReadAssessmentValue(`min shear`, curve_shear_kn.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_shear_kn],
model.id,
occupant.position,
OccupantBodyPart.LUMBAR,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadLumbarShear>`);
return null;
}
}
/**
* Reads lumbar axial force from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadLumbarAxial(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Lumbar Axial`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.LUMBAR);
switch (occupant.product) {
case OccupantProduct.WSID:
let axial = raw_measurements.GetCurve(Measurement.AXIAL);
if (!axial) {
WarningMessage(
`Unable to read axial force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadLumbarAxial>`
);
return null;
}
/* Convert time to seconds */
let curve_axial_s = Operate.Dix(axial, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_axial_s.RemoveFromGraph();
/* Filter with C600 */
let curve_axial_c600 = Operate.C600(curve_axial_s, 0.00001);
curve_axial_c600.RemoveFromGraph();
/* Convert to kN */
let curve_axial_kn = Operate.Div(curve_axial_c600, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_axial_kn.RemoveFromGraph();
/* Convert back to model time */
curve_axial_kn = Operate.Mux(curve_axial_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_axial_kn.RemoveFromGraph();
THisHelper.SetCurveLabels(
curve_axial_kn,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_axial_kn], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max axial`, curve_axial_kn.ymax, `kN`),
new ReadAssessmentValue(`min axial`, curve_axial_kn.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_axial_kn],
model.id,
occupant.position,
OccupantBodyPart.LUMBAR,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadLumbarAxial>`);
return null;
}
}
/**
* Reads lumbar torsion from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadLumbarTorsion(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Lumbar Torsion`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.LUMBAR);
switch (occupant.product) {
case OccupantProduct.WSID:
let torsion = raw_measurements.GetCurve(Measurement.TORSION);
if (!torsion) {
WarningMessage(
`Unable to read torsion from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadLumbarTorsion>`
);
return null;
}
/* Convert time to seconds */
let curve_torsion_s = Operate.Dix(torsion, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_torsion_s.RemoveFromGraph();
/* Filter with C600 */
let curve_torsion_c600 = Operate.C600(curve_torsion_s, 0.00001);
curve_torsion_c600.RemoveFromGraph();
/* Convert to Nm */
let curve_torsion_nm = Operate.Div(
curve_torsion_c600,
WorkflowUnits.MomentToNewtonMetreFactor(unit_system)
);
curve_torsion_nm.RemoveFromGraph();
/* Convert back to model time */
curve_torsion_nm = Operate.Mux(curve_torsion_nm, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_torsion_nm.RemoveFromGraph();
THisHelper.SetCurveLabels(
curve_torsion_nm,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Torsion (Nm)"
);
THisHelper.SetLineStyle([curve_torsion_nm], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`max torsion`, curve_torsion_nm.ymax, `Nm`),
new ReadAssessmentValue(`min torsion`, curve_torsion_nm.ymin, `Nm`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_torsion_nm],
model.id,
occupant.position,
OccupantBodyPart.LUMBAR,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadLumbarTorsion>`);
return null;
}
}
/**
* Reads acetabular force from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {string} [side] "Left" or "Right" side
* @returns {?ReadAssessmentOutput}
*/
ReadAcetabularForce(model, occupant, unit_system, occupant_index, side) {
/* Graph title - also used in curve labels */
let graph_title = `Acetabular Force`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.PELVIS);
let values = [];
switch (occupant.product) {
case OccupantProduct.SID2:
let acetabulum_force = raw_measurements.GetCurve(Measurement.ACETABULUM_FORCE);
if (!acetabulum_force) {
WarningMessage(
`Unable to read acetabulum force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAcetabularForce>`
);
return null;
}
/* Convert time to seconds */
let curve_acetabulum_force_s = Operate.Dix(
acetabulum_force,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_acetabulum_force_s.RemoveFromGraph();
/* Filter with C600 */
let curve_acetabulum_force_c600 = Operate.C600(curve_acetabulum_force_s, 0.00001);
curve_acetabulum_force_c600.RemoveFromGraph();
/* Convert to kN */
let curve_acetabulum_force_kn = Operate.Div(
curve_acetabulum_force_c600,
WorkflowUnits.ForceToKiloNewtonFactor(unit_system)
);
curve_acetabulum_force_kn.RemoveFromGraph();
/* Convert back to model time */
let curve_force = Operate.Mux(
curve_acetabulum_force_kn,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_force.RemoveFromGraph();
THisHelper.SetCurveLabels(
curve_force,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_force], this.GetCurveColourByIndex(occupant_index));
values = [
new ReadAssessmentValue(`max acetabular force`, curve_force.ymax, `kN`),
new ReadAssessmentValue(`min acetabular force`, curve_force.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_force],
model.id,
occupant.position,
OccupantBodyPart.PELVIS,
values
);
case OccupantProduct.THOR:
/** @type {Curve} */
let force_x = null;
/** @type {Curve} */
let force_y = null;
/** @type {Curve} */
let force_z = null;
if (side == "Left") {
Message("AA");
force_x = raw_measurements.GetCurve(Measurement.LEFT_FORCE_X);
force_y = raw_measurements.GetCurve(Measurement.LEFT_FORCE_Y);
force_z = raw_measurements.GetCurve(Measurement.LEFT_FORCE_Z);
Message(force_x.id);
} else if (side == "Right") {
force_x = raw_measurements.GetCurve(Measurement.RIGHT_FORCE_X);
force_y = raw_measurements.GetCurve(Measurement.RIGHT_FORCE_Y);
force_z = raw_measurements.GetCurve(Measurement.RIGHT_FORCE_Z);
} else {
ErrorMessage(`Invalid side '${side}' in <ProtocolAssessment.ReadAcetabularForce>`);
return null;
}
if (!force_x) {
WarningMessage(
`Unable to read ${side} X force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAcetabularForce>`
);
}
if (!force_y) {
WarningMessage(
`Unable to read ${side} Y force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAcetabularForce>`
);
}
if (!force_z) {
WarningMessage(
`Unable to read ${side} Z force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadAcetabularForce>`
);
}
/* Resultant force */
let curve_force_res = Operate.Vec(force_x, force_y, force_z);
curve_force_res.RemoveFromGraph();
/* For left pelvis, when Fx>0, c_com = 1 * c_force_res; when Fx <= 0, c_com = 0 * c_force_res = 0;
* For right pelvis, when Fx<0, c_com = 1 * c_force_res; when Fx >= 0, c_com = 0 * c_force_res = 0; */
let curve_com_res = new Curve(Curve.FirstFreeID());
if (side == "Left") {
for (let i = 1; i <= force_x.npoints; i++) {
let com_x_data = force_x.GetPoint(i);
if (com_x_data[1] > 0) {
curve_com_res.AddPoint(com_x_data[0], curve_force_res.GetPoint(i)[1]);
} else {
curve_com_res.AddPoint(com_x_data[0], 0);
}
}
} else if (side == "Right") {
for (let i = 1; i <= force_x.npoints; i++) {
let com_x_data = force_x.GetPoint(i);
if (com_x_data[1] < 0) {
curve_com_res.AddPoint(com_x_data[0], curve_force_res.GetPoint(i)[1]);
} else {
curve_com_res.AddPoint(com_x_data[0], 0);
}
}
}
curve_com_res.RemoveFromGraph();
/* Convert forces kN */
let curve_com_kn = Operate.Div(curve_com_res, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_com_kn.RemoveFromGraph();
/* Convert time to seconds */
let curve_com_s = Operate.Dix(curve_com_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_com_s.RemoveFromGraph();
/* Filter with C600 */
let curve_com_c600 = Operate.C600(curve_com_s, 0.00001);
curve_com_c600.RemoveFromGraph();
/* Convert back to model time */
let curve_com = Operate.Mux(curve_com_c600, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_com.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_com,
`${occupant} ${side} Acetabulum Compression`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Compression (kN)"
);
THisHelper.SetLineStyle([curve_com], this.GetCurveColourByIndex(occupant_index));
values = [new ReadAssessmentValue(`${side} max compression`, curve_com.ymax, `kN`)];
return new ReadAssessmentOutput(
`${side} ${graph_title}`,
[curve_com],
model.id,
occupant.position,
OccupantBodyPart.PELVIS,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadAcetabularForce>`);
return null;
}
}
/**
* Reads ilium force from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadIliumForce(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Ilium Force`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.PELVIS);
let values = [];
switch (occupant.product) {
case OccupantProduct.SID2:
let iliac_force = raw_measurements.GetCurve(Measurement.ILIAC_FORCE);
if (!iliac_force) {
WarningMessage(
`Unable to read iliac force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadIliumForce>`
);
return null;
}
/* Convert time to seconds */
let curve_iliac_force_s = Operate.Dix(iliac_force, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_iliac_force_s.RemoveFromGraph();
/* Filter with C600 */
let curve_iliac_force_c600 = Operate.C600(curve_iliac_force_s, 0.00001);
curve_iliac_force_c600.RemoveFromGraph();
/* Convert to kN */
let curve_iliac_force_kn = Operate.Div(
curve_iliac_force_c600,
WorkflowUnits.ForceToKiloNewtonFactor(unit_system)
);
curve_iliac_force_kn.RemoveFromGraph();
/* Convert back to model time */
let curve_force = Operate.Mux(curve_iliac_force_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_force.RemoveFromGraph();
THisHelper.SetCurveLabels(
curve_force,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_force], this.GetCurveColourByIndex(occupant_index));
values = [
new ReadAssessmentValue(`max ilium compression`, curve_force.ymax, `kN`),
new ReadAssessmentValue(`min ilium compression`, curve_force.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_force],
model.id,
occupant.position,
OccupantBodyPart.PELVIS,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadIliumForce>`);
return null;
}
}
/**
* Reads pelvis force from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @returns {?ReadAssessmentOutput}
*/
ReadPelvisForce(model, occupant, unit_system, occupant_index) {
/* Graph title - also used in curve labels */
let graph_title = `Pelvis Force`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.PELVIS);
let values = [];
switch (occupant.product) {
case OccupantProduct.WSID:
case OccupantProduct.ES2RE:
let force = raw_measurements.GetCurve(Measurement.PUBIC_SYMPHYSIS_FORCE);
if (!force) {
WarningMessage(
`Unable to read pelvis force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadPelvisForce>`
);
return null;
}
/* Convert time to seconds */
let curve_force_s = Operate.Dix(force, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_force_s.RemoveFromGraph();
/* Filter with C600 */
let curve_force_c600 = Operate.C600(curve_force_s, 0.00001);
curve_force_c600.RemoveFromGraph();
/* Convert to kN */
let curve_force_kn = Operate.Div(curve_force_c600, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_force_kn.RemoveFromGraph();
/* Convert back to model time */
curve_force_kn = Operate.Mux(curve_force_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_force_kn.RemoveFromGraph();
THisHelper.SetCurveLabels(
curve_force_kn,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_force_kn], this.GetCurveColourByIndex(occupant_index));
values = [
new ReadAssessmentValue(`max force`, curve_force_kn.ymax, `kN`),
new ReadAssessmentValue(`min force`, curve_force_kn.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_force_kn],
model.id,
occupant.position,
OccupantBodyPart.PELVIS,
values
);
case OccupantProduct.SID2:
let iliac_force = raw_measurements.GetCurve(Measurement.ILIAC_FORCE);
let acetabulum_force = raw_measurements.GetCurve(Measurement.ACETABULUM_FORCE);
if (!iliac_force) {
WarningMessage(
`Unable to read iliac force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadPelvisForce>`
);
return null;
}
if (!acetabulum_force) {
WarningMessage(
`Unable to read acetabulum force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadPelvisForce>`
);
return null;
}
/* Convert time to seconds */
let curve_iliac_force_s = Operate.Dix(iliac_force, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_acetabulum_force_s = Operate.Dix(
acetabulum_force,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_iliac_force_s.RemoveFromGraph();
curve_acetabulum_force_s.RemoveFromGraph();
/* Filter with C600 */
let curve_iliac_force_c600 = Operate.C600(curve_iliac_force_s, 0.00001);
let curve_acetabulum_force_c600 = Operate.C600(curve_acetabulum_force_s, 0.00001);
curve_iliac_force_c600.RemoveFromGraph();
curve_acetabulum_force_c600.RemoveFromGraph();
/* Convert to kN */
let curve_iliac_force_kn = Operate.Div(
curve_iliac_force_c600,
WorkflowUnits.ForceToKiloNewtonFactor(unit_system)
);
let curve_acetabulum_force_kn = Operate.Div(
curve_acetabulum_force_c600,
WorkflowUnits.ForceToKiloNewtonFactor(unit_system)
);
curve_iliac_force_kn.RemoveFromGraph();
curve_acetabulum_force_kn.RemoveFromGraph();
/* Convert back to model time */
curve_iliac_force_kn = Operate.Mux(
curve_iliac_force_kn,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_acetabulum_force_kn = Operate.Mux(
curve_acetabulum_force_kn,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_iliac_force_kn.RemoveFromGraph();
curve_acetabulum_force_kn.RemoveFromGraph();
/* Add forces together */
let curve_force = Operate.Add(curve_iliac_force_kn, curve_acetabulum_force_kn);
curve_force.RemoveFromGraph();
THisHelper.SetCurveLabels(
curve_force,
`${occupant} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_force], this.GetCurveColourByIndex(occupant_index));
values = [
new ReadAssessmentValue(`max force`, curve_force.ymax, `kN`),
new ReadAssessmentValue(`min force`, curve_force.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_force],
model.id,
occupant.position,
OccupantBodyPart.PELVIS,
values
);
case OccupantProduct.THOR:
/** @type {Curve} */
let curve_left_force = null;
/** @type {Curve} */
let curve_right_force = null;
/* Assess left and right acetabulum */
for (let i = 0; i < 2; i++) {
let force_x = null;
let force_y = null;
let force_z = null;
let side = "";
if (i == 0) {
force_x = raw_measurements.GetCurve(Measurement.LEFT_FORCE_X);
force_y = raw_measurements.GetCurve(Measurement.LEFT_FORCE_Y);
force_z = raw_measurements.GetCurve(Measurement.LEFT_FORCE_Z);
side = "left";
} else if (i == 1) {
force_x = raw_measurements.GetCurve(Measurement.RIGHT_FORCE_X);
force_y = raw_measurements.GetCurve(Measurement.RIGHT_FORCE_Y);
force_z = raw_measurements.GetCurve(Measurement.RIGHT_FORCE_Z);
side = "right";
}
if (!force_x) {
WarningMessage(
`Unable to read acetabulum ${side} X force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadPelvisForce>`
);
return null;
}
if (!force_y) {
WarningMessage(
`Unable to read acetabulum ${side} Y force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadPelvisForce>`
);
return null;
}
if (!force_z) {
WarningMessage(
`Unable to read acetabulum ${side} Z force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadPelvisForce>`
);
return null;
}
/* Vector combine the forces */
let force = Operate.Vec(force_x, force_y, force_z);
/* Convert time to seconds */
let curve_force_s = Operate.Dix(force, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_force_s.RemoveFromGraph();
/* Filter with C600 */
let curve_force_c600 = Operate.C600(curve_force_s, 0.00001);
curve_force_c600.RemoveFromGraph();
/* Convert to kN */
let curve_force_kn = Operate.Div(
curve_force_c600,
WorkflowUnits.ForceToKiloNewtonFactor(unit_system)
);
curve_force_kn.RemoveFromGraph();
/* Convert back to model time */
curve_force_kn = Operate.Mux(curve_force_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_force_kn.RemoveFromGraph();
if (i == 0) {
curve_left_force = curve_force_kn;
THisHelper.SetCurveLabels(
curve_force_kn,
`${occupant} Left Acetabulum Force`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_force_kn], this.GetCurveColourByIndex(occupant_index));
} else if (i == 1) {
curve_right_force = curve_force_kn;
THisHelper.SetCurveLabels(
curve_force_kn,
`${occupant} Right Acetabulum Force`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle(
[curve_force_kn],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
}
}
values = [
new ReadAssessmentValue(`max left force`, curve_left_force.ymax, `kN`),
new ReadAssessmentValue(`min left force`, curve_left_force.ymin, `kN`),
new ReadAssessmentValue(`max right force`, curve_right_force.ymax, `kN`),
new ReadAssessmentValue(`min right force`, curve_right_force.ymin, `kN`)
];
return new ReadAssessmentOutput(
graph_title,
[curve_left_force, curve_right_force],
model.id,
occupant.position,
OccupantBodyPart.PELVIS,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadPelvisForce>`);
return null;
}
}
/**
* Reads femur axial force from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {string} side "Left" or "Right" side
* @returns {?ReadAssessmentOutput}
*/
ReadFemurAxial(model, occupant, unit_system, occupant_index, side) {
/* Graph title - also used in curve labels */
let graph_title = `Femur Axial`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.FEMUR);
switch (occupant.supplier) {
case OccupantSupplier.ATD:
case OccupantSupplier.HUMANETICS:
case OccupantSupplier.LSTC:
/** @type {Curve} */
let axial = null;
if (side == "Left") {
axial = raw_measurements.GetCurve(Measurement.LEFT_AXIAL_FORCE);
} else if (side == "Right") {
axial = raw_measurements.GetCurve(Measurement.RIGHT_AXIAL_FORCE);
} else {
ErrorMessage(`Invalid side '${side}' in <ProtocolAssessment.ReadFemurAxial>`);
return null;
}
if (!axial) {
WarningMessage(
`Unable to read axial force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadFemurAxial>`
);
return null;
}
/* Convert forces kN */
let curve_axial_kn = Operate.Div(axial, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_axial_kn.RemoveFromGraph();
/* Convert time to seconds */
let curve_axial_s = Operate.Dix(curve_axial_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_axial_s.RemoveFromGraph();
/* Filter with C600 */
let curve_axial_c600 = Operate.C600(curve_axial_s, 0.00001);
curve_axial_c600.RemoveFromGraph();
/* Convert back to model time */
let curve_axial = Operate.Mux(curve_axial_c600, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_axial.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_axial,
`${occupant} ${side} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_axial], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`${side} max axial`, curve_axial.ymax, `kN`),
new ReadAssessmentValue(`${side} min axial`, curve_axial.ymin, `kN`)
];
return new ReadAssessmentOutput(
`${side} ${graph_title}`,
[curve_axial],
model.id,
occupant.position,
OccupantBodyPart.FEMUR,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadFemurAxial>`);
return null;
}
}
/**
* Reads femur resultant force from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {string} side "Left" or "Right" side
* @returns {?ReadAssessmentOutput}
*/
ReadFemurForce(model, occupant, unit_system, occupant_index, side) {
/* Graph title - also used in curve labels */
let graph_title = `Femur Force`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.FEMUR);
switch (occupant.supplier) {
case OccupantSupplier.ATD:
case OccupantSupplier.HUMANETICS:
case OccupantSupplier.LSTC:
/** @type {Curve} */
let force_x = null;
/** @type {Curve} */
let force_y = null;
/** @type {Curve} */
let force_z = null;
if (side == "Left") {
force_x = raw_measurements.GetCurve(Measurement.LEFT_FORCE_X);
force_y = raw_measurements.GetCurve(Measurement.LEFT_FORCE_Y);
force_z = raw_measurements.GetCurve(Measurement.LEFT_FORCE_Z);
} else if (side == "Right") {
force_x = raw_measurements.GetCurve(Measurement.RIGHT_FORCE_X);
force_y = raw_measurements.GetCurve(Measurement.RIGHT_FORCE_Y);
force_z = raw_measurements.GetCurve(Measurement.RIGHT_FORCE_Z);
} else {
ErrorMessage(`Invalid side '${side}' in <ProtocolAssessment.ReadFemurForce>`);
return null;
}
if (!force_x) {
WarningMessage(
`Unable to read X force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadFemurForce>`
);
return null;
}
if (!force_y) {
WarningMessage(
`Unable to read Y force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadFemurForce>`
);
return null;
}
if (!force_z) {
WarningMessage(
`Unable to read Z force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadFemurForce>`
);
return null;
}
/* Convert forces kN */
let curve_force_x_kn = Operate.Div(force_x, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
let curve_force_y_kn = Operate.Div(force_y, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
let curve_force_z_kn = Operate.Div(force_z, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_force_x_kn.RemoveFromGraph();
curve_force_y_kn.RemoveFromGraph();
curve_force_z_kn.RemoveFromGraph();
/* Convert time to seconds */
let curve_force_x_s = Operate.Dix(curve_force_x_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_force_y_s = Operate.Dix(curve_force_y_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_force_z_s = Operate.Dix(curve_force_z_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_force_x_s.RemoveFromGraph();
curve_force_y_s.RemoveFromGraph();
curve_force_z_s.RemoveFromGraph();
/* Filter with C600 */
let curve_force_x_c600 = Operate.C600(curve_force_x_s, 0.00001);
let curve_force_y_c600 = Operate.C600(curve_force_y_s, 0.00001);
let curve_force_z_c600 = Operate.C600(curve_force_z_s, 0.00001);
curve_force_x_c600.RemoveFromGraph();
curve_force_y_c600.RemoveFromGraph();
curve_force_z_c600.RemoveFromGraph();
/* Vector combine */
let curve_force = Operate.Vec(curve_force_x_c600, curve_force_y_c600, curve_force_z_c600);
curve_force.RemoveFromGraph();
/* Convert back to model time */
curve_force = Operate.Mux(curve_force, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_force.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_force,
`${occupant} ${side} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_force], this.GetCurveColourByIndex(occupant_index));
let values = [
new ReadAssessmentValue(`${side} max force`, curve_force.ymax, `kN`),
new ReadAssessmentValue(`${side} min force`, curve_force.ymin, `kN`)
];
return new ReadAssessmentOutput(
`${side} ${graph_title}`,
[curve_force],
model.id,
occupant.position,
OccupantBodyPart.FEMUR,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadFemurForce>`);
return null;
}
}
/**
* Reads femur compression exceedence from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {string} side "Left" or "Right" side
* @returns {?ReadAssessmentOutput}
*/
ReadFemurCompressionExceedence(model, occupant, unit_system, occupant_index, side) {
/* Graph title - also used in curve labels */
let graph_title = `Femur Compression Exceedence`;
switch (occupant.supplier) {
case OccupantSupplier.ATD:
case OccupantSupplier.HUMANETICS:
case OccupantSupplier.LSTC:
let axial_output = this.ReadFemurAxial(model, occupant, unit_system, occupant_index, side);
if (!axial_output) return null;
if (axial_output.curves.length == 0) return null;
let curve_axial = axial_output.curves[0];
let curve_compression_exc = Operate.Exc(curve_axial, "negative");
curve_compression_exc.RemoveFromGraph();
let curve_compression_exc_abs = Operate.Abs(curve_compression_exc);
curve_compression_exc_abs.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_compression_exc_abs,
`${occupant} ${side} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_compression_exc_abs], this.GetCurveColourByIndex(occupant_index));
let values = [];
return new ReadAssessmentOutput(
`${side} ${graph_title}`,
[curve_compression_exc_abs],
model.id,
occupant.position,
OccupantBodyPart.FEMUR,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadFemurCompressionExceedence>`);
return null;
}
}
/**
* Reads femur compression vs impulse from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {string} side "Left" or "Right" side
* @returns {?ReadAssessmentOutput}
*/
ReadFemurCompressionVsImpulse(model, occupant, unit_system, occupant_index, side) {
/* Graph title - also used in curve labels */
let graph_title = `Femur Compression vs. Impulse`;
switch (occupant.supplier) {
case OccupantSupplier.ATD:
case OccupantSupplier.HUMANETICS:
case OccupantSupplier.LSTC:
let axial_output = this.ReadFemurAxial(model, occupant, unit_system, occupant_index, side);
if (!axial_output) return null;
if (axial_output.curves.length == 0) return null;
let curve_axial = axial_output.curves[0];
/* To calculate the Knee-Thigh-Hip (KTH) rating we need the peak compressive force
* and the time when the force last equals zero prior to the peak compressive force.
*
* The curve is then integrated from this time up to the time after the peak force when
* the compressive force equals 4.050kN. This is the force impulse.
*/
let peak_compression = -curve_axial.ymin;
let time_of_peak = curve_axial.x_at_ymin;
/* Find the last time when the force equals zero prior to the peak compressive force
* and the last time when the compressive force equals 4050N after the peak force (this new guide line as per the May 2021 test protocol) */
let time_pre_peak_com = 0.0;
let time_post_peak_com = 0.0;
for (let i = 0; i < curve_axial.npoints; i++) {
let p = curve_axial.GetPoint(i);
if (p[0] < time_of_peak) {
if (p[1] >= 0.0) time_pre_peak_com = p[0];
}
if (p[0] > time_of_peak) {
if (p[1] <= -4.05) time_post_peak_com = p[0];
}
}
/* If the peak compressive force is the very last point, then <time_post_peak_com>
* will still be zero, so set it to <time_of_peak> */
if (time_post_peak_com == 0.0) time_post_peak_com = time_of_peak;
/* Clip the curve between these times and then integrate */
let curve_clipped = Operate.Clip(curve_axial, time_pre_peak_com, time_post_peak_com, -1e20, 0.0);
curve_clipped.RemoveFromGraph();
let curve_int = Operate.Int(curve_clipped);
curve_int.RemoveFromGraph();
/* Convert from -ve kNs to +ve Ns */
curve_int = Operate.Mul(curve_int, -1000.0);
curve_int.RemoveFromGraph();
/* Get last value to get impulse */
let p = curve_int.GetPoint(curve_int.npoints);
let impulse = p[1];
/* Curve to show force v impulse point */
let curve_force_v_impulse = new Curve(Curve.FirstFreeID());
curve_force_v_impulse.AddPoint(peak_compression, impulse);
curve_force_v_impulse.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_force_v_impulse,
`${occupant} ${side} ${graph_title}`,
`Force (kN)`,
"Impulse (Ns)"
);
THisHelper.SetLineStyle(
[curve_force_v_impulse],
this.GetCurveColourByIndex(occupant_index),
LineStyle.SOLID,
// @ts-ignore
Symbol.CROSS
);
let values = [
new ReadAssessmentValue(`${side} peak compression`, peak_compression, `kN`),
new ReadAssessmentValue(`${side} impulse`, impulse, `Ns`)
];
return new ReadAssessmentOutput(
`${side} ${graph_title}`,
[curve_force_v_impulse],
model.id,
occupant.position,
OccupantBodyPart.FEMUR,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadFemurCompressionVsImpulse>`);
return null;
}
}
/**
* Reads knee displacement from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {string} side "Left" or "Right" side
* @returns {?ReadAssessmentOutput}
*/
ReadKneeDisplacement(model, occupant, unit_system, occupant_index, side) {
/* Graph title - also used in curve labels */
let graph_title = `Knee Displacement`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.KNEE);
switch (`${occupant.product}${occupant.physiology}`) {
case `${OccupantProduct.HIII}${OccupantPhysiology.M50}`:
case `${OccupantProduct.THOR}${OccupantPhysiology.M50}`:
/** @type {Curve} */
let curve_disp = null;
if (side == "Left") {
curve_disp = raw_measurements.GetCurve(Measurement.LEFT_TRANSLATION);
} else if (side == "Right") {
curve_disp = raw_measurements.GetCurve(Measurement.RIGHT_TRANSLATION);
} else {
ErrorMessage(`Invalid side '${side}' in <ProtocolAssessment.ReadKneeDisplacement>`);
return null;
}
if (!curve_disp) {
WarningMessage(
`Unable to read displacement from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadKneeDisplacement>`
);
return null;
}
/* Convert time to seconds */
let curve_disp_s = Operate.Dix(curve_disp, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_s.RemoveFromGraph();
/* Convert displacement to mm */
let curve_disp_mm = Operate.Div(curve_disp_s, WorkflowUnits.LengthToMillimetresFactor(unit_system));
curve_disp_mm.RemoveFromGraph();
/* Filter with C180 */
let curve_disp_c180 = Operate.C180(curve_disp_mm, 0.00001);
curve_disp_c180.RemoveFromGraph();
/* Convert back to model time */
curve_disp_c180 = Operate.Mux(curve_disp_c180, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_disp_c180.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_disp_c180,
`${occupant} ${side} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Displacment (mm)"
);
THisHelper.SetLineStyle(curve_disp_c180, this.GetCurveColourByIndex(occupant_index));
let values = [new ReadAssessmentValue(`${side} max displacement`, curve_disp_c180.ymax, `mm`)];
return new ReadAssessmentOutput(
`${side} ${graph_title}`,
[curve_disp_c180],
model.id,
occupant.position,
OccupantBodyPart.KNEE,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadKneeDisplacement>`);
return null;
}
}
/**
* Reads tibia compression force from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {string} side "Left" or "Right" side
* @returns {?ReadAssessmentOutput}
*/
ReadTibiaCompression(model, occupant, unit_system, occupant_index, side) {
/* Graph title - also used in curve labels */
let graph_title = `Tibia Compression`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.TIBIA);
switch (`${occupant.product}${occupant.physiology}`) {
case `${OccupantProduct.HIII}${OccupantPhysiology.M50}`:
case `${OccupantProduct.THOR}${OccupantPhysiology.M50}`:
/** @type {Curve} */
let curve_upper_com = null;
/** @type {Curve} */
let curve_lower_com = null;
/* Assess upper and lower tibia */
for (let i = 0; i < 2; i++) {
/** @type {Curve} */
let curve_com = null;
if (side == "Left") {
if (i == 0) {
curve_com = raw_measurements.GetCurve(Measurement.LEFT_UPPER_AXIAL_FORCE);
} else {
curve_com = raw_measurements.GetCurve(Measurement.LEFT_LOWER_AXIAL_FORCE);
}
} else if (side == "Right") {
if (i == 0) {
curve_com = raw_measurements.GetCurve(Measurement.RIGHT_UPPER_AXIAL_FORCE);
} else {
curve_com = raw_measurements.GetCurve(Measurement.RIGHT_LOWER_AXIAL_FORCE);
}
} else {
ErrorMessage(`Invalid side '${side}' in <ProtocolAssessment.ReadTibiaCompression>`);
return null;
}
if (!curve_com) {
WarningMessage(
`Unable to read tibia compression force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadTibiaCompression>`
);
return null;
}
/* Convert time to seconds */
let curve_com_s = Operate.Dix(curve_com, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_com_s.RemoveFromGraph();
/* Filter with C600 */
let curve_com_c600 = Operate.C600(curve_com_s, 0.00001);
curve_com_c600.RemoveFromGraph();
/* Convert force to kN */
let curve_com_kn = Operate.Div(curve_com_c600, WorkflowUnits.ForceToKiloNewtonFactor(unit_system));
curve_com_kn.RemoveFromGraph();
/* Convert back to model time */
curve_com_kn = Operate.Mux(curve_com_kn, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_com_kn.RemoveFromGraph();
/* For THOR, multiply by -1 to make compression +ve */
if (occupant.product == OccupantProduct.THOR) {
curve_com_kn = Operate.Mul(curve_com_kn, -1);
curve_com_kn.RemoveFromGraph();
}
/* Assign to the correct curve */
if (i == 0) {
curve_upper_com = curve_com_kn;
THisHelper.SetCurveLabels(
curve_upper_com,
`${occupant} ${side} Upper ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle([curve_com_kn], this.GetCurveColourByIndex(occupant_index));
} else if (i == 1) {
curve_lower_com = curve_com_kn;
THisHelper.SetCurveLabels(
curve_lower_com,
`${occupant} ${side} Lower ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Force (kN)"
);
THisHelper.SetLineStyle(
[curve_com_kn],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
}
}
let values = [
new ReadAssessmentValue(`${side} upper compression`, curve_upper_com.ymax, `kN`),
new ReadAssessmentValue(`${side} lower compression`, curve_lower_com.ymax, `kN`)
];
return new ReadAssessmentOutput(
`${side} ${graph_title}`,
[curve_upper_com, curve_lower_com],
model.id,
occupant.position,
OccupantBodyPart.TIBIA,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadTibiaCompression>`);
return null;
}
}
/**
* Reads tibia index from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {string} side "Left" or "Right" side
* @returns {?ReadAssessmentOutput}
*/
ReadTibiaIndex(model, occupant, unit_system, occupant_index, side) {
/* Graph title - also used in curve labels */
let graph_title = `Tibia Index`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.TIBIA);
switch (`${occupant.product}${occupant.physiology}`) {
case `${OccupantProduct.HIII}${OccupantPhysiology.M50}`:
case `${OccupantProduct.THOR}${OccupantPhysiology.M50}`:
/* Get compression curves for upper and lower tibia */
let compression_output = this.ReadTibiaCompression(model, occupant, unit_system, occupant_index, side);
if (!compression_output) return null;
if (compression_output.curves.length < 2) return null;
/** @type {Curve} */
let curve_upper_ti = null;
let upper_ti = null;
/** @type {Curve} */
let curve_lower_ti = null;
let lower_ti = null;
/* Get critical loads for Tibia Index calc */
let tibia_index_critical_loads = occupant.GetTibiaIndexCriticalLoads();
/* Assess upper and lower tibia */
for (let i = 0; i < 2; i++) {
/** @type {Curve} */
let curve_axial = compression_output.curves[i];
/** @type {Curve} */
let curve_bending_y = null;
/** @type {Curve} */
let curve_bending_z = null;
if (side == "Left") {
if (i == 0) {
curve_bending_y = raw_measurements.GetCurve(Measurement.LEFT_UPPER_BENDING_X);
curve_bending_z = raw_measurements.GetCurve(Measurement.LEFT_UPPER_BENDING_Y);
} else {
curve_bending_y = raw_measurements.GetCurve(Measurement.LEFT_LOWER_BENDING_X);
curve_bending_z = raw_measurements.GetCurve(Measurement.LEFT_LOWER_BENDING_Y);
}
} else if (side == "Right") {
if (i == 0) {
curve_bending_y = raw_measurements.GetCurve(Measurement.RIGHT_UPPER_BENDING_X);
curve_bending_z = raw_measurements.GetCurve(Measurement.RIGHT_UPPER_BENDING_Y);
} else {
curve_bending_y = raw_measurements.GetCurve(Measurement.RIGHT_LOWER_BENDING_X);
curve_bending_z = raw_measurements.GetCurve(Measurement.RIGHT_LOWER_BENDING_Y);
}
} else {
ErrorMessage(`Invalid side '${side}' in <ProtocolAssessment.ReadTibiaIndex>`);
return null;
}
if (!curve_axial) {
WarningMessage(
`Unable to read axial force from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadTibiaIndex>`
);
return null;
}
if (!curve_bending_y) {
WarningMessage(
`Unable to read y bending moment from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadTibiaIndex>`
);
return null;
}
if (!curve_bending_z) {
WarningMessage(
`Unable to read z bending moment from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadTibiaIndex>`
);
return null;
}
/* Convert time to seconds */
let curve_axial_s = Operate.Dix(curve_axial, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_bending_y_s = Operate.Dix(
curve_bending_y,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
let curve_bending_z_s = Operate.Dix(
curve_bending_z,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_axial_s.RemoveFromGraph();
curve_bending_y_s.RemoveFromGraph();
curve_bending_z_s.RemoveFromGraph();
/* Filter with C600 (axial is already filtered) */
let curve_bending_y_c600 = Operate.C600(curve_bending_y_s, 0.00001);
let curve_bending_z_c600 = Operate.C600(curve_bending_z_s, 0.00001);
curve_bending_y_c600.RemoveFromGraph();
curve_bending_z_c600.RemoveFromGraph();
/* Convert moments to Nm (axial is already in kN) */
let curve_bending_y_nm = Operate.Div(
curve_bending_y_c600,
WorkflowUnits.MomentToNewtonMetreFactor(unit_system)
);
let curve_bending_z_nm = Operate.Div(
curve_bending_z_c600,
WorkflowUnits.MomentToNewtonMetreFactor(unit_system)
);
curve_bending_y_nm.RemoveFromGraph();
curve_bending_z_nm.RemoveFromGraph();
/* Resultant moment */
let curve_bending_resultant = Operate.Vec2d(curve_bending_y_nm, curve_bending_z_nm);
curve_bending_resultant.RemoveFromGraph();
/* Convert back to model time */
let curve_axial_kn = Operate.Mux(curve_axial_s, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_bending_resultant = Operate.Mux(
curve_bending_resultant,
WorkflowUnits.TimeToSecondsFactor(unit_system)
);
curve_axial_kn.RemoveFromGraph();
curve_bending_resultant.RemoveFromGraph();
/* Calculate the Tibia Index = |Mr/Mrc| + |Fz/Fzc| */
let curve_tibia_index = new Curve(Curve.FirstFreeID());
let n = curve_bending_resultant.npoints;
for (let p = 1; p <= n; p++) {
let p_axial = curve_axial_kn.GetPoint(p);
let p_bending = curve_bending_resultant.GetPoint(p);
let time = p_bending[0];
let ti =
Math.abs(p_bending[1] / tibia_index_critical_loads.bending) +
Math.abs(p_axial[1] / tibia_index_critical_loads.compression);
curve_tibia_index.AddPoint(time, ti);
}
curve_tibia_index.RemoveFromGraph();
/* Assign to the correct curve */
if (i == 0) {
curve_upper_ti = curve_tibia_index;
THisHelper.SetCurveLabels(
curve_upper_ti,
`${occupant} ${side} Upper ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Tibia Index"
);
THisHelper.SetLineStyle([curve_tibia_index], this.GetCurveColourByIndex(occupant_index));
/* For some reason curve_upper_ti.ymax is returning 0.0,
* so loop over all the points to get the max */
let max = 0.0;
for (let p = 1; p <= curve_upper_ti.npoints; p++) {
max = Math.max(curve_upper_ti.GetPoint(p)[1], max);
}
upper_ti = max;
} else if (i == 1) {
curve_lower_ti = curve_tibia_index;
THisHelper.SetCurveLabels(
curve_lower_ti,
`${occupant} ${side} Lower ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Tibia Index"
);
THisHelper.SetLineStyle(
[curve_tibia_index],
this.GetCurveColourByIndex(occupant_index),
LineStyle.DASH2
);
/* For some reason curve_lower_ti.ymax is returning 0.0,
* so loop over all the points to get the max */
let max = 0.0;
for (let p = 1; p <= curve_lower_ti.npoints; p++) {
max = Math.max(curve_lower_ti.GetPoint(p)[1], max);
}
lower_ti = max;
}
}
let values = [
new ReadAssessmentValue(`${side} upper tibia index`, upper_ti),
new ReadAssessmentValue(`${side} lower tibia index`, lower_ti)
];
return new ReadAssessmentOutput(
`${side} ${graph_title}`,
[curve_upper_ti, curve_lower_ti],
model.id,
occupant.position,
OccupantBodyPart.TIBIA,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadTibiaIndex>`);
return null;
}
}
/**
* Reads foot acceleration from the given occupant
* processing them according to the procedures set
* out in the occupant manual, e.g. filtering the raw
* measurements
* @param {Model} model Model to read data from
* @param {WorkflowOccupant} occupant WorkflowOccupant to read data from
* @param {number} unit_system Unit system
* @param {number} occupant_index Index of the curve style to use
* @param {string} side "Left" or "Right" side
* @returns {?ReadAssessmentOutput}
*/
ReadFootAcceleration(model, occupant, unit_system, occupant_index, side) {
/* Graph title - also used in curve labels */
let graph_title = `Foot Acceleration`;
let raw_measurements = occupant.ReadRawBodyPartMeasurements(model, OccupantBodyPart.FOOT);
switch (`${occupant.product}${occupant.physiology}`) {
case `${OccupantProduct.HIII}${OccupantPhysiology.M50}`:
/** @type {Curve} */
let curve_x = null;
/** @type {Curve} */
let curve_y = null;
/** @type {Curve} */
let curve_z = null;
if (side == "Left") {
curve_x = raw_measurements.GetCurve(Measurement.LEFT_ACCELERATION_X);
curve_y = raw_measurements.GetCurve(Measurement.LEFT_ACCELERATION_Y);
curve_z = raw_measurements.GetCurve(Measurement.LEFT_ACCELERATION_Z);
} else if (side == "Right") {
curve_x = raw_measurements.GetCurve(Measurement.RIGHT_ACCELERATION_X);
curve_y = raw_measurements.GetCurve(Measurement.RIGHT_ACCELERATION_Y);
curve_z = raw_measurements.GetCurve(Measurement.RIGHT_ACCELERATION_Z);
} else {
ErrorMessage(`Invalid side '${side}' in <ProtocolAssessment.ReadFootAcceleration>`);
return null;
}
if (!curve_x) {
WarningMessage(
`Unable to read x acceleration from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadFootAcceleration>`
);
return null;
}
if (!curve_y) {
WarningMessage(
`Unable to read y acceleration from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadFootAcceleration>`
);
return null;
}
if (!curve_z) {
WarningMessage(
`Unable to read z acceleration from occupant M${model.id} ${occupant} in <ProtocolAssessment.ReadFootAcceleration>`
);
return null;
}
/* Convert acceleration to g */
let curve_x_g = Operate.Div(curve_x, WorkflowUnits.GravityConstant(unit_system));
let curve_y_g = Operate.Div(curve_y, WorkflowUnits.GravityConstant(unit_system));
let curve_z_g = Operate.Div(curve_z, WorkflowUnits.GravityConstant(unit_system));
curve_x_g.RemoveFromGraph();
curve_y_g.RemoveFromGraph();
curve_z_g.RemoveFromGraph();
/* Convert time to seconds */
let curve_x_g_s = Operate.Dix(curve_x_g, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_y_g_s = Operate.Dix(curve_y_g, WorkflowUnits.TimeToSecondsFactor(unit_system));
let curve_z_g_s = Operate.Dix(curve_z_g, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_x_g_s.RemoveFromGraph();
curve_y_g_s.RemoveFromGraph();
curve_z_g_s.RemoveFromGraph();
/* Filter with C1000 */
let curve_x_c1000 = Operate.C1000(curve_x_g_s, 0.00001);
let curve_y_c1000 = Operate.C1000(curve_y_g_s, 0.00001);
let curve_z_c1000 = Operate.C1000(curve_z_g_s, 0.00001);
curve_x_c1000.RemoveFromGraph();
curve_y_c1000.RemoveFromGraph();
curve_z_c1000.RemoveFromGraph();
/* Vector combine */
let curve_vec = Operate.Vec(curve_x_c1000, curve_y_c1000, curve_z_c1000);
curve_vec.RemoveFromGraph();
/* Convert back to model time */
curve_vec = Operate.Mux(curve_vec, WorkflowUnits.TimeToSecondsFactor(unit_system));
curve_vec.RemoveFromGraph();
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_vec,
`${occupant} ${side} ${graph_title}`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Acceleration (g)"
);
THisHelper.SetLineStyle(curve_vec, this.GetCurveColourByIndex(occupant_index));
let values = [new ReadAssessmentValue(`${side} peak acceleration`, curve_vec.ymax, `g`)];
return new ReadAssessmentOutput(
`${side} ${graph_title}`,
[curve_vec],
model.id,
occupant.position,
OccupantBodyPart.FOOT,
values
);
default:
WarningMessage(`${occupant.name} not supported in <ProtocolAssessment.ReadFootAcceleration>`);
return null;
}
}
/**
* Object to return from the structure Read functions
* @typedef ReadStructureOutput
* @property {?Curve[]} curves Output curves
* @property {Object} values Object with values for the output
* @property {string} graph_title Graph title (also used in curve labels)
*/
/**
* Reads vertical intrusion from the given structure
* @param {Model} model Model to read data from
* @param {Structure} structure Structure to read data from
* @param {number} unit_system Unit system
* @param {number} structure_index Index of the curve style to use
* @param {string} [structure_name = null] Alternative structure name to use in graph title - required for assessment types that plot results from multiple structures on one graph, e.g. pedals
* @returns {?ReadStructureOutput}
*/
ReadVerticalIntrusion(model, structure, unit_system, structure_index, structure_name) {
let name = structure_name ? structure_name : structure;
let graph_title = `${name} Vertical Intrusion`;
let raw_measurements = structure.ReadRawStructureMeasurements(model);
let curve_intrusion = raw_measurements.GetCurve(Measurement.VERTICAL_INTRUSION);
if (!curve_intrusion) {
WarningMessage(
`Unable to read deflection for structure M${model.id} ${structure} in <ProtocolAssessment.ReadVerticalIntrusion>`
);
return null;
}
/* Convert to mm */
let curve_intrusion_mm = Operate.Div(curve_intrusion, WorkflowUnits.LengthToMillimetresFactor(unit_system));
curve_intrusion_mm.RemoveFromGraph();
/* Get the last value */
let p = curve_intrusion_mm.GetPoint(curve_intrusion_mm.npoints);
let intrusion = p[1];
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_intrusion_mm,
`${structure} Vertical Intrusion`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Intrusion (mm)"
);
THisHelper.SetLineStyle([curve_intrusion_mm], this.GetCurveColourByIndex(structure_index));
let values = [new ReadAssessmentValue(`vertical intrusion`, intrusion, `mm`)];
return new ReadAssessmentOutput(
graph_title,
[curve_intrusion_mm],
model.id,
`Structure`,
structure.component_type,
values
);
}
/**
* Reads lateral intrusion from the given structure
* @param {Model} model Model to read data from
* @param {Structure} structure Structure to read data from
* @param {number} unit_system Unit system
* @param {number} structure_index Index of the curve style to use
* @param {string} [structure_name = null] Alternative structure name to use in graph title - required for assessment types that plot results from multiple structures on one graph, e.g. pedals
* @returns {?ReadStructureOutput}
*/
ReadLateralIntrusion(model, structure, unit_system, structure_index, structure_name) {
let name = structure_name ? structure_name : structure;
let graph_title = `${name} Lateral Intrusion`;
let raw_measurements = structure.ReadRawStructureMeasurements(model);
let curve_intrusion = raw_measurements.GetCurve(Measurement.LATERAL_INTRUSION);
if (!curve_intrusion) {
WarningMessage(
`Unable to read deflection for structure M${model.id} ${structure} in <ProtocolAssessment.ReadLateralIntrusion>`
);
return null;
}
/* Convert to mm */
let curve_intrusion_mm = Operate.Div(curve_intrusion, WorkflowUnits.LengthToMillimetresFactor(unit_system));
curve_intrusion_mm.RemoveFromGraph();
/* Get the last value */
let p = curve_intrusion_mm.GetPoint(curve_intrusion_mm.npoints);
let intrusion = p[1];
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_intrusion_mm,
`${structure} Lateral Intrusion`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Intrusion (mm)"
);
THisHelper.SetLineStyle([curve_intrusion_mm], this.GetCurveColourByIndex(structure_index));
let values = [new ReadAssessmentValue(`lateral intrusion`, intrusion, `mm`)];
return new ReadAssessmentOutput(
graph_title,
[curve_intrusion_mm],
model.id,
`Structure`,
structure.component_type,
values
);
}
/**
* Reads fore/aft intrusion from the given structure
* @param {Model} model Model to read data from
* @param {Structure} structure Structure to read data from
* @param {number} unit_system Unit system
* @param {number} structure_index Index of the curve style to use
* @param {string} [structure_name = null] Alternative structure name to use in graph title - required for assessment types that plot results from multiple structures on one graph, e.g. pedals
* @returns {?ReadStructureOutput}
*/
ReadForeAftIntrusion(model, structure, unit_system, structure_index, structure_name) {
let name = structure_name ? structure_name : structure;
let graph_title = `${name} Fore/Aft Intrusion`;
let raw_measurements = structure.ReadRawStructureMeasurements(model);
let curve_intrusion = raw_measurements.GetCurve(Measurement.FORE_AFT_INTRUSION);
if (!curve_intrusion) {
WarningMessage(
`Unable to read deflection for structure M${model.id} ${structure} in <ProtocolAssessment.ReadForeAftIntrusion>`
);
return null;
}
/* Fore/aft intrusion = -ve elongation is rearward so multiply by -1.0 first */
curve_intrusion = Operate.Mul(curve_intrusion, -1.0);
curve_intrusion.RemoveFromGraph();
/* Convert to mm */
let curve_intrusion_mm = Operate.Div(curve_intrusion, WorkflowUnits.LengthToMillimetresFactor(unit_system));
curve_intrusion_mm.RemoveFromGraph();
/* Get the last value */
let p = curve_intrusion_mm.GetPoint(curve_intrusion_mm.npoints);
let intrusion = p[1];
/* Set the labels and line style */
THisHelper.SetCurveLabels(
curve_intrusion_mm,
`${structure} Fore/Aft Intrusion`,
`Time (${WorkflowUnits.TimeUnit(unit_system)})`,
"Intrusion (mm)"
);
THisHelper.SetLineStyle([curve_intrusion_mm], this.GetCurveColourByIndex(structure_index));
let values = [new ReadAssessmentValue(`fore-aft intrusion`, intrusion, `mm`)];
return new ReadAssessmentOutput(
graph_title,
[curve_intrusion_mm],
model.id,
`Structure`,
structure.component_type,
values
);
}
/**
* Reads all intrusions (vertical, lateral and fore/aft) from the given structure
* @param {Model} model Model to read data from
* @param {Structure} structure Structure to read data from
* @param {number} unit_system Unit system
* @param {number} structure_index Index of the curve style to use
* @returns {?ReadStructureOutput}
*/
ReadAllIntrusion(model, structure, unit_system, structure_index) {
let graph_title = `${structure} Intrusion`;
let vertical_output = this.ReadVerticalIntrusion(model, structure, unit_system, structure_index);
if (!vertical_output) return null;
if (vertical_output.curves.length == 0) return null;
let curve_vertical = vertical_output.curves[0];
let lateral_output = this.ReadLateralIntrusion(model, structure, unit_system, structure_index);
if (!lateral_output) return null;
if (lateral_output.curves.length == 0) return null;
let curve_lateral = lateral_output.curves[0];
let fore_aft_output = this.ReadForeAftIntrusion(model, structure, unit_system, structure_index);
if (!fore_aft_output) return null;
if (fore_aft_output.curves.length == 0) return null;
let curve_fore_aft = fore_aft_output.curves[0];
/* Set the labels and line style */
THisHelper.SetLineStyle([curve_vertical], this.GetCurveColourByIndex(structure_index));
THisHelper.SetLineStyle([curve_lateral], this.GetCurveColourByIndex(structure_index + 1));
THisHelper.SetLineStyle([curve_fore_aft], this.GetCurveColourByIndex(structure_index + 2));
/* Values */
let values = [];
values = values.concat(vertical_output.values);
values = values.concat(lateral_output.values);
values = values.concat(fore_aft_output.values);
return new ReadAssessmentOutput(
graph_title,
[curve_vertical, curve_lateral, curve_fore_aft],
model.id,
`Structure`,
structure.component_type,
values
);
}
}
/**
* Class to hold data for an occupant its model ID and unit system
* Used as an argument in the ProtocolAssessment.DoOccupantAssessment function
*/
class DoOccupantAssessmentOccupantData {
/**
* @param {WorkflowOccupant} occupant WorkflowOccupant instance
* @param {number} model_id Model ID
* @param {number} unit_system Unit system, e.g. Workflow.UNIT_SYSTEM_U2
* @example
* let od = new DoOccupantAssessmentOccupantData(o, 1, Workflow.UNIT_SYSTEM_U2);
*/
constructor(occupant, model_id, unit_system) {
this.occupant = occupant;
this.model_id = model_id;
this.unit_system = unit_system;
}
/* Instance property getter and setters */
/**
* WorkflowOccupant instance
* @type {WorkflowOccupant} */
get occupant() {
return this._occupant;
}
set occupant(new_occupant) {
if (!(new_occupant instanceof WorkflowOccupant)) {
throw new Error(
`<occupant> is not a WorkflowOccupant instance in <DoOccupantAssessmentOccupantData> constructor`
);
}
this._occupant = new_occupant;
}
/**
* Model ID
* @type {number} */
get model_id() {
return this._model_id;
}
set model_id(new_model_id) {
if (typeof new_model_id != "number") {
throw new Error(`<model_id> is not a number in <DoOccupantAssessmentOccupantData> constructor`);
}
this._model_id = new_model_id;
}
/**
* Unit system, e.g. Workflow.UNIT_SYSTEM_U2
* @type {number} */
get unit_system() {
return this._unit_system;
}
set unit_system(new_unit_system) {
if (typeof new_unit_system != "number") {
throw new Error(`<unit_system> is not a number in <DoOccupantAssessmentOccupantData> constructor`);
}
this._unit_system = new_unit_system;
}
}
/**
* Class to hold data for a structure its model ID and unit system
* Used as an argument in the ProtocolAssessment.DoStructureAssessment function
*/
class DoStructureAssessmentStructureData {
/**
* @param {Structure} structure Structure instance
* @param {number} model_id Model ID
* @param {number} unit_system Unit system, e.g. Workflow.UNIT_SYSTEM_U2
* @example
* let sd = new DoStructureAssessmentStructureData(s, 1, Workflow.UNIT_SYSTEM_U2);
*/
constructor(structure, model_id, unit_system) {
this.structure = structure;
this.model_id = model_id;
this.unit_system = unit_system;
}
/* Instance property getter and setters */
/**
* Structure instance
* @type {Structure} */
get structure() {
return this._structure;
}
set structure(new_structure) {
if (!(new_structure instanceof Structure)) {
throw new Error(
`<structure> is not a Structure instance in <DoStructureAssessmentStructureData> constructor`
);
}
this._structure = new_structure;
}
/**
* Model ID
* @type {number} */
get model_id() {
return this._model_id;
}
set model_id(new_model_id) {
if (typeof new_model_id != "number") {
throw new Error(`<model_id> is not a number in <DoStructureAssessmentStructureData> constructor`);
}
this._model_id = new_model_id;
}
/**
* Unit system, e.g. Workflow.UNIT_SYSTEM_U2
* @type {number} */
get unit_system() {
return this._unit_system;
}
set unit_system(new_unit_system) {
if (typeof new_unit_system != "number") {
throw new Error(`<unit_system> is not a number in <DoStructureAssessmentStructureData> constructor`);
}
this._unit_system = new_unit_system;
}
}
/**
* Class to hold options that can be passed to the
* ProtocolAssessment.DoOccupantAssessment and ProtocolAssessment.DoStructureAssessment functions
*/
class DoAssessmentOptions {
/**
* @param {string} graph_layout Graph layout
* @param {number} first_graph_id First graph ID to use when plotting results
* @param {number} first_page_id First page ID to use when plotting results
* @param {boolean} blank_all Switch whether to blank all existing curves and datums before plotting results
* @param {boolean} remove_existing_graphs Switch whether to remove existing graphs from a page
* @param {string} [output_dir=""] Output directory for "reporter" images
*/
constructor(graph_layout, first_graph_id, first_page_id, blank_all, remove_existing_graphs, output_dir = "") {
this.graph_layout = graph_layout;
this.first_graph_id = first_graph_id;
this.first_page_id = first_page_id;
this.blank_all = blank_all;
this.remove_existing_graphs = remove_existing_graphs;
this.output_dir = output_dir;
}
/* Instance property getter and setters */
/**
* Graph layout
* @type {string} */
get graph_layout() {
return this._graph_layout;
}
set graph_layout(new_graph_layout) {
if (typeof new_graph_layout != "string") {
throw new Error(`<graph_layout> is not a string in <DoAssessmentOptions> constructor`);
}
if (DoAssessmentOptions.GraphLayouts().indexOf(new_graph_layout) == -1) {
throw new Error(`Invalid graph layout: ${new_graph_layout} in <DoAssessmentOptions> constructor`);
}
this._graph_layout = new_graph_layout;
}
/** Switch whether to blank all existing curves and datums before plotting results
* @type {boolean} */
get blank_all() {
return this._blank_all;
}
set blank_all(new_blank_all) {
if (typeof new_blank_all != "boolean") {
throw new Error(`<blank_all> is not a boolean in <DoAssessmentOptions>`);
}
this._blank_all = new_blank_all;
}
/** Switch whether to remove existing graphs from a page
* @type {boolean} */
get remove_existing_graphs() {
return this._remove_existing_graphs;
}
set remove_existing_graphs(new_remove_existing_graphs) {
if (typeof new_remove_existing_graphs != "boolean") {
throw new Error(`<remove_existing_graphs> is not a boolean in <DoAssessmentOptions>`);
}
this._remove_existing_graphs = new_remove_existing_graphs;
}
/** First graph ID to use when plotting results
* @type {number} */
get first_graph_id() {
return this._first_graph_id;
}
set first_graph_id(new_first_graph_id) {
if (typeof new_first_graph_id != "number") {
throw new Error(`<first_graph_id> is not a number in <DoAssessmentOptions> constructor`);
}
this._first_graph_id = new_first_graph_id;
}
/** First page ID to use when plotting results
* @type {number} */
get first_page_id() {
return this._first_page_id;
}
set first_page_id(new_first_page_id) {
if (typeof new_first_page_id != "number") {
throw new Error(`<first_page_id> is not a number in <DoAssessmentOptions> constructor`);
}
this._first_page_id = new_first_page_id;
}
/**
* Output directory
* @type {string} */
get output_dir() {
return this._output_dir;
}
set output_dir(new_output_dir) {
if (typeof new_output_dir != "string") {
throw new Error(`<output_dir> is not a string in <DoAssessmentOptions> constructor`);
}
if (new_output_dir != "" && !File.IsDirectory(new_output_dir)) {
throw new Error(`<output_dir> is not a valid directory in <DoAssessmentOptions> constructor`);
}
this._output_dir = new_output_dir;
}
/** Reporter graph layout option
* @type {string} */
static get GRAPH_LAYOUT_REPORTER() {
return "reporter";
}
/** same page graph layout option
* @type {string} */
static get GRAPH_LAYOUT_SAME_PAGE() {
return "same_page";
}
/** Separate pages graph layout option
* @type {string} */
static get GRAPH_LAYOUT_SEPARATE_PAGES() {
return "separate_pages";
}
/**
* Return a list of valid graph layouts
* @returns {string[]}
*/
static GraphLayouts() {
return [
DoAssessmentOptions.GRAPH_LAYOUT_REPORTER,
DoAssessmentOptions.GRAPH_LAYOUT_SAME_PAGE,
DoAssessmentOptions.GRAPH_LAYOUT_SEPARATE_PAGES
];
}
}
/**
* Class used to return results from the body part Read functions
* @param {string} parameter The name of the parameter being reported
* @param {number} value The numerical value
* @param {string} [units] The units associated with the value (default none - empty string)
* @example
* let values = [
* new ReadAssessmentValue(`max shear`, curve_shear.ymax, `kN`),
* new ReadAssessmentValue(`min shear`, curve_shear.ymin, `kN`)
* ];
*/
class ReadAssessmentValue {
constructor(parameter, value, units = "") {
this.parameter = parameter;
this.value = value;
this.units = units;
}
}
/**
* Class used to return results from the occupant Read functions
* @param {string} graph_title Graph title (also used in curve labels)
* @param {?Curve[]} curves Output curves
* @param {number} model_id Model ID
* @param {string} location Occupant position or `Structure`
* @param {string} component Body part type or structure component type
* @param {ReadAssessmentValue[]} values Array of ReadAssessmentValue objects
* @example
* let output = new ReadAssessmentOutput(graph_title, [curve_shear], model.id, occupant.position, OccupantBodyPart.NECK, values);
*/
class ReadAssessmentOutput {
constructor(graph_title, curves, model_id, location, component, values) {
this.graph_title = graph_title;
this.curves = curves;
this.model_id = model_id;
this.location = location;
this.component = component;
this.values = values;
}
}
/**
* Class used to return results from the ProtocolAssessment.DoOccupantAssessment and
* ProtocolAssessment.DoStructureAssessment functions
*/
class DoAssessmentResults {
constructor() {
this.outputs = [];
this.last_graph_id = 1;
this.last_page_id = 1;
}
/* Instance property getter and setters */
/**
* Output object, containing results data for body parts
* @type {Array} */
get outputs() {
return this._outputs;
}
set outputs(new_outputs) {
if (!Array.isArray(new_outputs)) {
throw new Error(`<outputs> is not an array in <DoAssessmentResults>`);
}
this._outputs = new_outputs;
}
/** Last graph ID. This is the last graph id used when plotting results
* @type {number} */
get last_graph_id() {
return this._last_graph_id;
}
set last_graph_id(new_last_graph_id) {
if (typeof new_last_graph_id != "number") {
throw new Error(`<last_graph_id> is not a number in <DoAssessmentResults>`);
}
this._last_graph_id = new_last_graph_id;
}
/** Last page ID. This is the last page id used when plotting results
* @type {number} */
get last_page_id() {
return this._last_page_id;
}
set last_page_id(new_last_page_id) {
if (typeof new_last_page_id != "number") {
throw new Error(`<last_page_id> is not a number in <DoAssessmentResults>`);
}
this._last_page_id = new_last_page_id;
}
}