// module: TRUE
// @ts-ignore
import { gui } from "./pre_automotive_assessment_gui.jsi";
import { BaseEntity, BaseEntityWidgets } from "../modules/shared/base.mjs";
import {
BodyPartWidgets,
Occupant,
OccupantEntity,
OccupantEntityWidgets,
OccupantWidgets
} from "../modules/shared/occupant.mjs";
import { JSPath } from "../modules/shared/path.mjs";
import { Structure, StructureEntityWidgets, StructureWidgets } from "../modules/shared/structure.mjs";
import { WorkflowUnitsCombobox } from "../../modules/units.mjs";
import { OccupantVersion } from "../modules/shared/occupant_version.mjs";
import { Protocol, Protocols } from "../modules/shared/protocols.mjs";
import { ProtocolVehicle, VehicleOccupant } from "../modules/shared/vehicle.mjs";
import { WorkflowOccupant } from "../modules/shared/workflow_occupant.mjs";
export {
get_user_data,
gui,
add_new_occupant,
filter_version_drop_down,
update_occupant_names_combobox,
set_selected_widget_item,
GetProtocolVehicle
};
// var OccupantVersion = OccupantVersion.GetInstance();
/* Workflow definition filename is passed as an argument to this script from the Workflow menu.
* It's required when writing the user data to a file or model. */
let workflow_definition_filename = Workflow.WorkflowDefinitionFilename();
/* Models can contain many DATABASE HISTORY cards, so increase the max number of widgets */
Options.max_widgets = 10000;
/* New UI */
Window.Theme(Window.THEME_CURRENT);
/* Set the workflows directory */
JSPath.SetWorkflowsDirectory(JSPath.PRE);
/* Initialise the Protocols class */
Protocols.Initialise();
/* Setup the GUI and show the main window */
if (gui) {
try {
/* Ask the user which model to use */
gui.model = Model.Select("Select the model to use");
if (gui.model == null) {
Window.Information("", "You need to select a model before you can use this workflow");
Exit();
}
/* Set up the gui */
setup_gui();
/* Update main window */
update_main_window();
/* Show the main window */
gui.wdw_pre_automotive.Show(false);
} catch (e) {
ErrorMessage(`Something went wrong: ${e}.\n${e.stack}`);
}
}
/**
* this is used to set up the initial display of the vehicle occupants selection area adding onClicks
* and hiding initially disabled buttons and moving 'Add' buttons to the correct location
*/
function setup_vehicle_occupants() {
/*add the vehicle*/
//TODO update image based on THEME (dark or not)
//default set the vehicle hand drive to left when opened in PRIMER (or use user data to set it if present)
gui.drive_side = VehicleOccupant.LHD;
gui.wdw_pre_automotive.radio_hand_drive.ItemAt(0).selected = true; // = VehicleOccupant.LHD;
/*assign onClick callbacks for all buttons and add front_rear adn side properties*/
for (let front_rear_side of [
[WorkflowOccupant.FRONT, WorkflowOccupant.LEFT],
[WorkflowOccupant.FRONT, WorkflowOccupant.RIGHT],
[WorkflowOccupant.REAR, WorkflowOccupant.LEFT],
[WorkflowOccupant.REAR, WorkflowOccupant.RIGHT],
[WorkflowOccupant.REAR, WorkflowOccupant.MIDDLE]
]) {
/*store front_rear and side value in seperate temporary variables*/
let front_rear = front_rear_side[0];
let side = front_rear_side[1];
/*get hold hod add, edit and delete buttons for the seat position*/
let add_btn_widget = gui.wdw_pre_automotive[`btn_${front_rear}_${side}_add`];
let edit_btn_widget = gui.wdw_pre_automotive[`btn_${front_rear}_${side}_edit`];
let delete_btn_widget = gui.wdw_pre_automotive[`btn_${front_rear}_${side}_delete`];
add_btn_widget.onClick = add_edit_occupant;
edit_btn_widget.onClick = add_edit_occupant;
delete_btn_widget.onClick = delete_occupant;
/*add front_rear and side properties and onClick to the buttons and hide them to start with*/
for (let button of [add_btn_widget, edit_btn_widget, delete_btn_widget]) {
button.front_rear = front_rear;
button.side = side;
button.Hide();
}
/*Move occupant 'Add' button up to make it lie on top of edit and delete buttons and Show it so it is initially visible
note they are origionally 6 units below to make it easier to work with them in the gui builder */
add_btn_widget.top = edit_btn_widget.top;
add_btn_widget.bottom = edit_btn_widget.bottom;
add_btn_widget.Show();
/* update occupant button text*/
let expected_occupant_btn_widget = gui.wdw_pre_automotive[`lbl_${front_rear}_${side}_expected_occupant`];
let selected_occupant_btn_widget = gui.wdw_pre_automotive[`lbl_${front_rear}_${side}_selected_occupant`];
expected_occupant_btn_widget.text = "not required";
selected_occupant_btn_widget.text = "<empty>";
selected_occupant_btn_widget.category = Widget.NO_CATEGORY;
selected_occupant_btn_widget.foreground = Widget.BLACK;
selected_occupant_btn_widget.background = Widget.COLOUR_NEUTRAL;
}
}
/**
* set the gui.drive_side and the radio button to the specified hand drive (if valid)
* defaults to LHD if not valid and prints a warning message
* @param {string} drive_side
*/
function set_vehicle_drive_side(drive_side) {
if ((drive_side = VehicleOccupant.GetValidHandDrive(drive_side))) {
gui.drive_side = drive_side;
for (let radio_wi of gui.wdw_pre_automotive.radio_hand_drive.WidgetItems()) {
if (radio_wi.text == drive_side) {
radio_wi.selected = true;
break;
}
}
}
}
/**
* update the vehicle occupant gui based on the passed vehicle and hand drive
* @param {ProtocolVehicle} vehicle
* @param {string} drive_side
*/
function update_vehicle_occupants(vehicle, drive_side) {
set_vehicle_drive_side(drive_side);
if (!vehicle) {
WarningMessage("Vehicle is null");
return;
}
/*
do it like this as drive_side could be invalid and
set_vehicle_drive_side also sets gui.drive_side to be valid
i.e. defaults to VehicleOccupant.LHD*/
drive_side = gui.drive_side;
for (let vehicle_occupant of vehicle.Occupants()) {
/*store front_rear and side value in seperate temporary variables*/
let front_rear = vehicle_occupant.front_rear;
let side = vehicle_occupant.GetSide(drive_side);
/* get widgets associated with occupant */
let add_btn_widget = gui.wdw_pre_automotive[`btn_${front_rear}_${side}_add`];
let edit_btn_widget = gui.wdw_pre_automotive[`btn_${front_rear}_${side}_edit`];
let delete_btn_widget = gui.wdw_pre_automotive[`btn_${front_rear}_${side}_delete`];
let selected_occupant_btn_widget = gui.wdw_pre_automotive[`lbl_${front_rear}_${side}_selected_occupant`];
let expected_occupant_btn_widget = gui.wdw_pre_automotive[`lbl_${front_rear}_${side}_expected_occupant`];
/* get add button and make it generic colour (or perhaps disabled) */
add_btn_widget.category = Widget.CATEGORY_GENERIC;
//reset selected_occupant_btn_widget
selected_occupant_btn_widget.text = "<empty>";
selected_occupant_btn_widget.category = Widget.NO_CATEGORY;
selected_occupant_btn_widget.foreground = Widget.BLACK;
selected_occupant_btn_widget.background = Widget.COLOUR_NEUTRAL;
/* update expected occupant button text*/
let expected_occupant_text = "not required";
//clear vehicle_occupant property
// @ts-ignore
expected_occupant_btn_widget.vehicle_occupant = null;
if (!vehicle_occupant.Empty()) {
expected_occupant_text = `${vehicle_occupant.product}-${vehicle_occupant.physiology}`;
// Ignore squiggles below because Widget objects can have user defined properties added
// @ts-ignore
expected_occupant_btn_widget.vehicle_occupant = vehicle_occupant;
//set the colour of text to latent (meaning the user could/should defined it by pressing Add button)
selected_occupant_btn_widget.foreground = Widget.COLOUR_LATENT;
/* set add button category to the Widget.CATEGORY_APPLY because we want to encourage the user to press it*/
add_btn_widget.category = Widget.CATEGORY_APPLY;
}
/* set the expected occupant text */
expected_occupant_btn_widget.text = expected_occupant_text;
let wf_occupant = null;
if ((wf_occupant = get_seat_occupant(side, front_rear))) {
let temp_occupant = OccupantVersion.GetFromName(wf_occupant.name);
selected_occupant_btn_widget.text = `${temp_occupant.product}-${temp_occupant.physiology}`;
if (expected_occupant_text == "not required") {
/**
* colour it neutral if the expected_occupant_text is "not required" as we do not care if an occupant is defined*/
selected_occupant_btn_widget.category = Widget.NO_CATEGORY;
// selected_occupant_btn_widget.foreground = Widget.BLACK;
// selected_occupant_btn_widget.background = Widget.COLOUR_NEUTRAL;
} else if (selected_occupant_btn_widget.text == expected_occupant_text) {
/**
* colour it green if the texts matche as this means that the occupant defined for this seat
* is of the same product and physiology as required by the regulation*/
selected_occupant_btn_widget.category = Widget.CATEGORY_SAFE_ACTION;
} else {
/**
* colour it red if the text does not match as this means that the occupant defined for this seat
* is of a different product and physiology as required by the regulation*/
selected_occupant_btn_widget.category = Widget.CATEGORY_WARNING_ACTION;
}
add_btn_widget.Hide();
edit_btn_widget.Show();
delete_btn_widget.Show();
} else {
add_btn_widget.active = expected_occupant_text != "not required";
add_btn_widget.Show();
edit_btn_widget.Hide();
delete_btn_widget.Hide();
}
// Message(`wf_occupant = ${wf_occupant} ${front_rear} ${side}`);
}
/* change activity of delete button */
gui.wdw_pre_automotive.btn_delete_all_occupants.active = gui.occupants.length != 0;
/*redraw the window so that changes appear immediately*/
gui.wdw_pre_automotive.Redraw();
}
/**
* returns the workflow occupant for that seat or null if not found
* @param {string} side
* @param {string} front_rear
* @returns {?WorkflowOccupant}
*/
function get_seat_occupant(side, front_rear) {
for (let wf_occupant of gui.occupants) {
// Message(`${wf_occupant} ${front_rear} ${side} len gui.occupants = ${gui.occupants.length}`);
if (
//wf_occupant.position == vehicle_occupant.position &&
wf_occupant.side == side &&
wf_occupant.front_rear == front_rear
) {
return wf_occupant;
}
}
return null;
}
/**
* update the filters on the occupant window to only allow valid occupants
*/
function set_up_occupant_window(side, front_rear) {
let wdw = gui.wdw_occupant;
// if gui.current_occupant is not null then we are in edit mode
gui.current_occupant = get_seat_occupant(side, front_rear);
if (gui.current_occupant) {
Message(`Edit occupant ${front_rear} ${side}`);
edit_occupant();
return;
}
/*add an occupant, but help the user out by pre-filtering dropdowns
/* get expected occupant button for current seat */
let expected_occupant_btn_widget = gui.wdw_pre_automotive[`lbl_${front_rear}_${side}_expected_occupant`];
//alternatively could get this from protocol, but do it this way so that REPORTER works but passing a SpecifiedVehicle class json
let vehicle_occupant = expected_occupant_btn_widget.vehicle_occupant;
if (vehicle_occupant && !vehicle_occupant.Empty()) {
wdw.cbx_occupant_name.active = true;
wdw.cbx_occupant_supplier.active = true;
wdw.cbx_occupant_product.active = true;
wdw.cbx_occupant_physiology.active = true;
set_selected_widget_item(wdw.cbx_occupant_supplier, "all");
set_selected_widget_item(wdw.cbx_occupant_product, vehicle_occupant.product);
set_selected_widget_item(wdw.cbx_occupant_physiology, vehicle_occupant.physiology);
set_selected_widget_item(wdw.cbx_occupant_position, vehicle_occupant.position);
set_selected_widget_item(wdw.cbx_occupant_side, side);
set_selected_widget_item(wdw.cbx_occupant_front_rear, vehicle_occupant.front_rear);
//once all the widget items have been set call update filters to update the drop-down
//list of occupant names
let occupant_names = filter_version_drop_down();
update_occupant_names_combobox(occupant_names);
Message(`Add occupant ${front_rear} ${side}`);
add_new_occupant();
}
}
/**
* Sets the GUI up with things not set in the GUI Builder,
* e.g. combobox items are added dynamically here using the list
* of possible values from the WorkflowOccupant class.
*/
function setup_gui() {
/* Initialise GUI global variables */
/* Entity ID offset */
gui.offset = 0;
/* Array to store the WorkflowOccupant instances */
gui.occupants = [];
/* Currently selected occupant */
gui.current_occupant = null;
/* Array to store the Structure instances */
gui.structures = [];
/* Currently selected structure */
gui.current_structure = null;
/* Currently selected entity */
gui.current_entity_tag = "";
gui.current_entity_type = "";
/* B-Pillar entity values */
initialise_b_pillar_entities();
/* Assign callbacks */
/* Regulation and crash test on change callbacks */
gui.wdw_pre_automotive.cbx_protocol_test.onChange = crash_test_changed;
gui.wdw_pre_automotive.cbx_protocol_regulation.onChange = regulation_changed;
gui.wdw_pre_automotive.cbx_protocol_version.onChange = version_changed;
/* Set up initial state of occupants region and add occupant button callbacks */
setup_vehicle_occupants();
/* Occupant callbacks */
gui.wdw_pre_automotive.radio_hand_drive.onClick = update_vehicle_hand_drive;
gui.wdw_pre_automotive.btn_flip_occupants.onClick = flip_occupants;
gui.wdw_pre_automotive.btn_delete_all_occupants.onClick = delete_all_occupants;
gui.wdw_occupant.btn_use_id_num.onClick = update_and_toggle_ids_between_num_and_dbhistitle;
gui.wdw_occupant.btn_use_dbhistitle.onClick = update_and_toggle_ids_between_num_and_dbhistitle;
gui.wdw_occupant.txt_entity_offset.onChange = offset_changed;
gui.wdw_occupant.btn_update_occupant.onClick = update_current_occupant_and_close;
gui.wdw_occupant.btn_cancel_occupant.onClick = close_occupant_window;
gui.wdw_occupant.cbx_occupant_name.onChange = update_occupant_window;
gui.wdw_occupant.cbx_occupant_supplier.onChange = update_filters;
gui.wdw_occupant.cbx_occupant_product.onChange = update_filters;
gui.wdw_occupant.cbx_occupant_physiology.onChange = update_filters;
gui.wdw_occupant.cbx_occupant_supplier.onClick = disable_invalid_filter_options;
gui.wdw_occupant.cbx_occupant_product.onClick = disable_invalid_filter_options;
gui.wdw_occupant.cbx_occupant_physiology.onClick = disable_invalid_filter_options;
gui.wdw_occupant.btn_save_occupant.onClick = save_occupant_to_file;
gui.wdw_occupant.btn_save_occupant.Hide(); /* This is for internal use only, so hide from users */
/* Structure callbacks */
gui.wdw_pre_automotive.btn_add_structure.onClick = add_new_structure;
gui.wdw_pre_automotive.btn_add_default_structures.onClick = add_default_structures;
gui.wdw_pre_automotive.lbx_structures.onChange = update_structure_edit_and_delete_buttons;
gui.wdw_pre_automotive.lbx_structures.onClick = update_structure_edit_and_delete_buttons;
gui.wdw_pre_automotive.btn_edit_structure.onClick = edit_structure;
gui.wdw_pre_automotive.btn_delete_structure.onClick = delete_selected_structure;
gui.wdw_structure.btn_update_structure.onClick = update_current_structure_and_close;
gui.wdw_structure.btn_cancel_structure.onClick = close_structure_window;
gui.wdw_structure.cbx_structure.onChange = update_structure_window;
/* Picking and selecting callbacks */
gui.popup_select_entities.btn_pick_entity.onClick = pick_entity;
gui.popup_select_entities.btn_select_entity.onClick = select_entity;
/* Save data callbacks */
gui.wdw_pre_automotive.btn_save_to_file.onClick = save_to_file;
gui.wdw_pre_automotive.btn_save_to_model.onClick = save_to_model;
/* Add widget items to the units combobox */
gui.wdw_pre_automotive.cbx_unit_system = new WorkflowUnitsCombobox(gui.wdw_pre_automotive.cbx_unit_system);
/* Get list of possible options for test combobox and add widget items */
let tests = Protocols.GetAllCrashTests();
for (let test of tests) {
new WidgetItem(gui.wdw_pre_automotive.cbx_protocol_test, test);
}
/* Add line to label to separate save buttons from selection widgets */
gui.wdw_pre_automotive.lbl_line.Line(Widget.DARKGREY, 0, 50, 100, 50);
/* Create the widgets in the structures window */
create_structures_widgets();
/* Create the widgets in the occupant window */
create_occupant_widgets();
/* Add buttons for selecting database history nodes/beams/discrete and cross sections */
add_buttons_to_select_entities_popup();
/* Get any data that already exists for the model */
let success = read_model_data();
if (!success) {
/* Set up regulations combo box with regulations (widget items) supported by currently selected crash test */
update_regulations();
update_versions();
/* Update Occupants gui */
UpdateVehicleGUI();
}
}
/**
* update the vehicle based on the currently selected version
* @returns {?ProtocolVehicle}
*/
function GetProtocolVehicle() {
let regulation = get_selected_regulation();
let crash_test = get_selected_crash_test_protocol();
let version = gui.wdw_pre_automotive.cbx_protocol_version.text;
let vehicle = Protocols.GetProtocolVehicle(regulation, crash_test, version);
if (vehicle) {
return vehicle;
} else {
ErrorMessage(`Could not find protocol vehicle for ${regulation} ${crash_test} ${version}`);
}
return null;
}
/**
* Creates the widgets in the structures window
*/
function create_structures_widgets() {
/* Get list of possible options for comboxes in the structures window and add widget items */
let structure_types = Structure.Types();
for (let i = 0; i < structure_types.length; i++)
new WidgetItem(gui.wdw_structure.cbx_structure, structure_types[i]);
/* Create widgets for selecting entity IDs */
gui.structure_widgets = [];
let max_bottom = 1;
for (let structure_type of structure_types) {
let top = gui.wdw_structure.cbx_structure.bottom + 1;
let bottom = top + 6;
let structure = Structure.CreateStructure(structure_type);
/* The B-Pillar is a non-standard structure, that doesn't have any StructureEntity
* instances defined for it (see CreateBPillarEntities() for an explanation).
*
* Bespoke logic is required here to create the required widgets.
*
* For other strutures create the widgets from the StructureEntity instances.
*/
if (structure.component_type == Structure.B_PILLAR) {
bottom = create_b_pillar_widgets(gui.wdw_structure, top, bottom);
} else {
/* No entities for this structure */
if (structure.entities.length == 0) continue;
/* Structure header */
let structure_label = new Widget(
gui.wdw_structure,
Widget.LABEL,
1,
147,
top,
bottom,
structure.component_type.toUpperCase()
);
structure_label.category = Widget.CATEGORY_TITLE;
top = bottom + 1;
bottom = top + 6;
/* Entity widgets */
/** @type {StructureEntityWidgets[]} */
let entity_widgets = [];
for (let structure_entity of structure.entities) {
let label = new Widget(gui.wdw_structure, Widget.LABEL, 1, 82, top, bottom, structure_entity.name);
label.justify = Widget.LEFT;
let textbox = new Widget(
gui.wdw_structure,
Widget.TEXTBOX,
83,
147,
top,
bottom,
structure_entity.id.toString()
);
textbox.popupWindow = gui.popup_select_entities;
textbox.popupDirection = Widget.RIGHT;
textbox.onPopup = structure_entity_on_popup;
textbox.onChange = update_structure_window;
/* Tag and entity type used in popup from textbox for selecting the entity */
// @ts-ignore
textbox.entity_tag = structure_entity.tag;
// @ts-ignore
textbox.entity_type = structure_entity.entity_type;
top = bottom + 1;
bottom = top + 6;
entity_widgets.push(
new StructureEntityWidgets(structure_entity.entity_type, label, textbox, structure_entity.tag)
);
}
let structure_widgets = new StructureWidgets(structure.component_type, structure_label, entity_widgets);
gui.structure_widgets.push(structure_widgets);
}
max_bottom = Math.max(max_bottom, bottom);
}
/* Create a label widget at the bottom of the window so it maps to the correct size to fit all the entity widgets on */
gui.wdw_structure.lbl_occupant_bottom = new Widget(
gui.wdw_structure,
Widget.LABEL,
1,
147,
max_bottom,
max_bottom + 1,
""
);
}
/**
* Creates the B-Pillar widgets, returning the bottom position of the last widget created
* @param {Window} window Window to create widgets in
* @param {number} start_top Starting top position of widgets
* @param {number} start_bottom Starting bottom position of widgets
* @returns {number}
*/
function create_b_pillar_widgets(window, start_top, start_bottom) {
let top = start_top;
let bottom = start_bottom;
/* All the widgets are added to an object on the gui object */
gui.b_pillar_widgets = {};
gui.b_pillar_widgets.lbl_structure = new Widget(
window,
Widget.LABEL,
1,
147,
top,
bottom,
Structure.B_PILLAR.toUpperCase()
);
gui.b_pillar_widgets.lbl_structure.category = Widget.CATEGORY_TITLE;
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_cut_section = new Widget(
window,
Widget.LABEL,
1,
82,
top,
bottom,
"Cut Section Definition Method"
);
gui.b_pillar_widgets.lbl_cut_section.justify = Widget.LEFT;
gui.b_pillar_widgets.cbx_cut_section = new Widget(window, Widget.COMBOBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.cbx_cut_section.onChange = b_pillar_callback;
new WidgetItem(gui.b_pillar_widgets.cbx_cut_section, "Constant X");
new WidgetItem(gui.b_pillar_widgets.cbx_cut_section, "Constant Y");
new WidgetItem(gui.b_pillar_widgets.cbx_cut_section, "Constant Z");
new WidgetItem(gui.b_pillar_widgets.cbx_cut_section, "Three Nodes");
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_cut_section_node_1 = new Widget(
window,
Widget.LABEL,
1,
82,
top,
bottom,
"Cut Section Node 1"
);
gui.b_pillar_widgets.lbl_cut_section_node_1.justify = Widget.LEFT;
gui.b_pillar_widgets.txt_cut_section_node_1 = new Widget(window, Widget.TEXTBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.txt_cut_section_node_1.onChange = b_pillar_callback;
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_cut_section_node_2 = new Widget(
window,
Widget.LABEL,
1,
82,
top,
bottom,
"Cut Section Node 2"
);
gui.b_pillar_widgets.lbl_cut_section_node_2.justify = Widget.LEFT;
gui.b_pillar_widgets.txt_cut_section_node_2 = new Widget(window, Widget.TEXTBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.txt_cut_section_node_2.onChange = b_pillar_callback;
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_cut_section_node_3 = new Widget(
window,
Widget.LABEL,
1,
82,
top,
bottom,
"Cut Section Node 3"
);
gui.b_pillar_widgets.lbl_cut_section_node_3.justify = Widget.LEFT;
gui.b_pillar_widgets.txt_cut_section_node_3 = new Widget(window, Widget.TEXTBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.txt_cut_section_node_3.onChange = b_pillar_callback;
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_pre_crash_parts = new Widget(window, Widget.LABEL, 1, 82, top, bottom, "Pre-Crash Parts");
gui.b_pillar_widgets.lbl_pre_crash_parts.justify = Widget.LEFT;
gui.b_pillar_widgets.txt_pre_crash_parts = new Widget(window, Widget.TEXTBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.txt_pre_crash_parts.onChange = b_pillar_callback;
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_post_crash_parts = new Widget(
window,
Widget.LABEL,
1,
82,
top,
bottom,
"Post-Crash Parts"
);
gui.b_pillar_widgets.lbl_post_crash_parts.justify = Widget.LEFT;
gui.b_pillar_widgets.txt_post_crash_parts = new Widget(window, Widget.TEXTBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.txt_post_crash_parts.onChange = b_pillar_callback;
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_shift_deform_node_1 = new Widget(window, Widget.LABEL, 1, 82, top, bottom, "Shift Node 1");
gui.b_pillar_widgets.lbl_shift_deform_node_1.justify = Widget.LEFT;
gui.b_pillar_widgets.txt_shift_deform_node_1 = new Widget(window, Widget.TEXTBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.txt_shift_deform_node_1.onChange = b_pillar_callback;
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_shift_deform_node_2 = new Widget(window, Widget.LABEL, 1, 82, top, bottom, "Shift Node 2");
gui.b_pillar_widgets.lbl_shift_deform_node_2.justify = Widget.LEFT;
gui.b_pillar_widgets.txt_shift_deform_node_2 = new Widget(window, Widget.TEXTBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.txt_shift_deform_node_2.onChange = b_pillar_callback;
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_shift_deform_node_3 = new Widget(window, Widget.LABEL, 1, 82, top, bottom, "Shift Node 3");
gui.b_pillar_widgets.lbl_shift_deform_node_3.justify = Widget.LEFT;
gui.b_pillar_widgets.txt_shift_deform_node_3 = new Widget(window, Widget.TEXTBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.txt_shift_deform_node_3.onChange = b_pillar_callback;
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_ground_z = new Widget(window, Widget.LABEL, 1, 82, top, bottom, "Ground Z");
gui.b_pillar_widgets.lbl_ground_z.justify = Widget.LEFT;
gui.b_pillar_widgets.txt_ground_z = new Widget(window, Widget.TEXTBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.txt_ground_z.onChange = b_pillar_callback;
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_seat_centre_y = new Widget(window, Widget.LABEL, 1, 82, top, bottom, "Seat Centre Y");
gui.b_pillar_widgets.lbl_seat_centre_y.justify = Widget.LEFT;
gui.b_pillar_widgets.txt_seat_centre_y = new Widget(window, Widget.TEXTBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.txt_seat_centre_y.onChange = b_pillar_callback;
top = bottom + 1;
bottom = top + 6;
gui.b_pillar_widgets.lbl_h_point_z = new Widget(window, Widget.LABEL, 1, 82, top, bottom, "H-Point Z");
gui.b_pillar_widgets.lbl_h_point_z.justify = Widget.LEFT;
gui.b_pillar_widgets.txt_h_point_z = new Widget(window, Widget.TEXTBOX, 83, 147, top, bottom);
gui.b_pillar_widgets.txt_h_point_z.onChange = b_pillar_callback;
/* Update the values in the widgets */
update_b_pillar_widget_values();
/* Return the bottom coordinate of the last widget created */
return bottom;
}
/**
* Sets the initial values for the B-Pillar entities
*/
function initialise_b_pillar_entities() {
gui.b_pillar_cut_section_method = "Constant X";
gui.b_pillar_cut_section_node_1 = 0;
gui.b_pillar_cut_section_node_2 = 0;
gui.b_pillar_cut_section_node_3 = 0;
gui.b_pillar_pre_crash_parts = [];
gui.b_pillar_post_crash_parts = [];
gui.b_pillar_shift_deform_node_1 = 0;
gui.b_pillar_shift_deform_node_2 = 0;
gui.b_pillar_shift_deform_node_3 = 0;
gui.b_pillar_ground_z = 0;
gui.b_pillar_seat_centre_y = 0;
gui.b_pillar_h_point_z = 0;
}
/**
* Callback function for B-Pillar widgets
*/
function b_pillar_callback() {
switch (this) {
case gui.b_pillar_widgets.cbx_cut_section:
gui.b_pillar_cut_section_method = this.text;
break;
case gui.b_pillar_widgets.txt_cut_section_node_1:
case gui.b_pillar_widgets.txt_cut_section_node_2:
case gui.b_pillar_widgets.txt_cut_section_node_3:
case gui.b_pillar_widgets.txt_shift_deform_node_1:
case gui.b_pillar_widgets.txt_shift_deform_node_2:
case gui.b_pillar_widgets.txt_shift_deform_node_3:
let new_int = parseInt(this.text);
if (isNaN(new_int)) {
WarningMessage("Invalid value, must be an integer");
} else if (new_int < 0) {
WarningMessage("Invalid value, must be greater than or equal to 0");
} else {
if (this == gui.b_pillar_widgets.txt_cut_section_node_1) {
gui.b_pillar_cut_section_node_1 = new_int;
} else if (this == gui.b_pillar_widgets.txt_cut_section_node_2) {
gui.b_pillar_cut_section_node_2 = new_int;
} else if (this == gui.b_pillar_widgets.txt_cut_section_node_3) {
gui.b_pillar_cut_section_node_3 = new_int;
} else if (this == gui.b_pillar_widgets.txt_shift_deform_node_1) {
gui.b_pillar_shift_deform_node_1 = new_int;
} else if (this == gui.b_pillar_widgets.txt_shift_deform_node_2) {
gui.b_pillar_shift_deform_node_2 = new_int;
} else if (this == gui.b_pillar_widgets.txt_shift_deform_node_3) {
gui.b_pillar_shift_deform_node_3 = new_int;
}
}
break;
case gui.b_pillar_widgets.txt_pre_crash_parts:
case gui.b_pillar_widgets.txt_post_crash_parts:
let valid = true;
let parts = this.text.trim().split(/\s+/);
let new_parts = [];
for (let p of parts) {
let part = parseInt(p);
if (isNaN(part)) {
WarningMessage("Invalid value, parts must be a space-separated list of integers");
valid = false;
break;
} else if (part < 0) {
WarningMessage("Invalid value, parts must be greater than or equal to 0");
valid = false;
break;
}
new_parts.push(part);
}
if (!valid) break;
if (this == gui.b_pillar_widgets.txt_pre_crash_parts) {
gui.b_pillar_pre_crash_parts = new_parts;
} else if (this == gui.b_pillar_widgets.txt_post_crash_parts) {
gui.b_pillar_post_crash_parts = new_parts;
}
break;
case gui.b_pillar_widgets.txt_ground_z:
case gui.b_pillar_widgets.txt_seat_centre_y:
case gui.b_pillar_widgets.txt_h_point_z:
let new_float = parseFloat(this.text);
if (isNaN(new_float)) {
WarningMessage("Invalid value, must be a number");
} else {
if (this == gui.b_pillar_widgets.txt_ground_z) {
gui.b_pillar_ground_z = new_float;
} else if (this == gui.b_pillar_widgets.txt_seat_centre_y) {
gui.b_pillar_seat_centre_y = new_float;
} else if (this == gui.b_pillar_widgets.txt_h_point_z) {
gui.b_pillar_h_point_z = new_float;
}
}
break;
default:
ErrorMessage("Unknown widget in b_pillar_callback()");
}
update_b_pillar_widget_values();
}
/**
* Updates the values in the widgets
*/
function update_b_pillar_widget_values() {
for (let wi of gui.b_pillar_widgets.cbx_cut_section.WidgetItems()) {
if (gui.b_pillar_cut_section_method == wi.text) {
wi.selected = true;
}
}
if (gui.b_pillar_cut_section_method == "Three Nodes") {
gui.b_pillar_widgets.txt_cut_section_node_2.active = true;
gui.b_pillar_widgets.txt_cut_section_node_3.active = true;
} else {
gui.b_pillar_widgets.txt_cut_section_node_2.active = false;
gui.b_pillar_widgets.txt_cut_section_node_3.active = false;
}
gui.b_pillar_widgets.txt_cut_section_node_1.text = gui.b_pillar_cut_section_node_1;
gui.b_pillar_widgets.txt_cut_section_node_2.text = gui.b_pillar_cut_section_node_2;
gui.b_pillar_widgets.txt_cut_section_node_3.text = gui.b_pillar_cut_section_node_3;
gui.b_pillar_widgets.txt_pre_crash_parts.text = gui.b_pillar_pre_crash_parts.join(" ");
gui.b_pillar_widgets.txt_post_crash_parts.text = gui.b_pillar_post_crash_parts.join(" ");
gui.b_pillar_widgets.txt_shift_deform_node_1.text = gui.b_pillar_shift_deform_node_1;
gui.b_pillar_widgets.txt_shift_deform_node_2.text = gui.b_pillar_shift_deform_node_2;
gui.b_pillar_widgets.txt_shift_deform_node_3.text = gui.b_pillar_shift_deform_node_3;
gui.b_pillar_widgets.txt_ground_z.text = gui.b_pillar_ground_z;
gui.b_pillar_widgets.txt_seat_centre_y.text = gui.b_pillar_seat_centre_y;
gui.b_pillar_widgets.txt_h_point_z.text = gui.b_pillar_h_point_z;
}
/**
* Creates the widgets in the occupant window
*/
function create_occupant_widgets() {
/* Get list of possible options for comboxes in the occupant window and add widget items */
let names = WorkflowOccupant.Versions();
// prepend "all" to the array of each filter so that it is the default
let ALL = ["all"];
let suppliers = ALL.concat(WorkflowOccupant.Suppliers());
let products = ALL.concat(WorkflowOccupant.Products());
let physiologies = ALL.concat(WorkflowOccupant.Physiologies());
let positions = WorkflowOccupant.Positions();
let sides = WorkflowOccupant.Sides();
let front_rear = WorkflowOccupant.FrontRear();
update_occupant_names_combobox(names);
for (let i = 0; i < suppliers.length; i++) new WidgetItem(gui.wdw_occupant.cbx_occupant_supplier, suppliers[i]);
for (let i = 0; i < products.length; i++) new WidgetItem(gui.wdw_occupant.cbx_occupant_product, products[i]);
for (let i = 0; i < physiologies.length; i++)
new WidgetItem(gui.wdw_occupant.cbx_occupant_physiology, physiologies[i]);
for (let i = 0; i < positions.length; i++) new WidgetItem(gui.wdw_occupant.cbx_occupant_position, positions[i]);
for (let i = 0; i < sides.length; i++) new WidgetItem(gui.wdw_occupant.cbx_occupant_side, sides[i]);
for (let i = 0; i < front_rear.length; i++) new WidgetItem(gui.wdw_occupant.cbx_occupant_front_rear, front_rear[i]);
/* Create widgets for selecting entity IDs */
gui.occupant_widgets = [];
let max_bottom = 1;
for (let name of names) {
let top = gui.wdw_occupant.btn_use_id_num.bottom + 1;
let bottom = top + 6;
let occupant = WorkflowOccupant.CreateWorkflowOccupantFromOccupant(
name,
WorkflowOccupant.DRIVER,
WorkflowOccupant.LEFT,
WorkflowOccupant.FRONT
);
/** @type {BodyPartWidgets[]} */
let body_part_widgets = [];
for (let occupant_body_part of occupant.body_parts) {
/* No entities for this body part */
if (occupant_body_part.entities.length == 0) continue;
/* Body part header */
let body_part_label = new Widget(
gui.wdw_occupant,
Widget.LABEL,
1,
147,
top,
bottom,
occupant_body_part.component_type.toUpperCase()
);
body_part_label.category = Widget.CATEGORY_TITLE;
top = bottom + 1;
bottom = top + 6;
/* Entity widgets */
/** @type {OccupantEntityWidgets[]} */
let entity_widgets = [];
for (let occupant_entity of occupant_body_part.entities) {
let label = new Widget(gui.wdw_occupant, Widget.LABEL, 1, 82, top, bottom, occupant_entity.name);
label.justify = Widget.LEFT;
let textbox = new Widget(
gui.wdw_occupant,
Widget.TEXTBOX,
83,
147,
top,
bottom,
occupant_entity.id.toString()
);
textbox.popupWindow = gui.popup_select_entities;
textbox.popupDirection = Widget.RIGHT;
textbox.onPopup = occupant_entity_on_popup;
textbox.onChange = update_occupant_window;
/* Tag and entity type used in popup from textbox for selecting the entity */
// @ts-ignore
textbox.entity_tag = occupant_entity.tag;
// @ts-ignore
textbox.entity_type = occupant_entity.entity_type;
top = bottom + 1;
bottom = top + 6;
entity_widgets.push(
new OccupantEntityWidgets(occupant_entity.entity_type, label, textbox, occupant_entity.tag)
);
}
body_part_widgets.push(new BodyPartWidgets(body_part_label, entity_widgets));
}
let occupant_widgets = new OccupantWidgets(name, body_part_widgets);
gui.occupant_widgets.push(occupant_widgets);
max_bottom = Math.max(max_bottom, bottom);
}
/* Create a label widget at the bottom of the window so it maps to the correct size to fit all the entity widgets on */
gui.wdw_occupant.lbl_occupant_bottom = new Widget(
gui.wdw_occupant,
Widget.LABEL,
1,
147,
max_bottom,
max_bottom + 1,
""
);
}
/**
* Reads user data from the selected model and stores it on the gui object
* and populates the widgets with the data
@returns {boolean} successfully read data or not
*/
function read_model_data() {
let success = false;
/* Number of models loaded in PRIMER that have data for this workflow */
let num_models = Workflow.NumberOfModels();
if (num_models == 0) return false;
/* If the model selected by the user has data read it in */
for (let i = 0; i < num_models; i++) {
let model_id = Workflow.ModelIdFromIndex(i);
if (model_id != gui.model.number) continue;
/* Get the user data */
/** @type {UserData} */
// @ts-ignore
let user_data = Workflow.ModelUserDataFromIndex(i);
/* Put in try-catch in case any of the saved data is invalid */
try {
/*Set the drive side from user data */
set_vehicle_drive_side(user_data.drive_side);
/* Get the crash test type */
success = set_selected_widget_item(gui.wdw_pre_automotive.cbx_protocol_test, user_data.crash_test);
if (!success) return false;
/* update the regulation combobox so that the selection can be set (otherwise it may not exist in the list)*/
update_regulations();
/* Get the regulations */
success = set_selected_widget_item(
gui.wdw_pre_automotive.cbx_protocol_regulation,
user_data.regulations[0]
);
if (!success) return false;
/* update the version comboboxe so that the selection can be set (otherwise it may not exist in the list)*/
update_versions();
/* Set the version */
success = set_selected_widget_item(gui.wdw_pre_automotive.cbx_protocol_version, user_data.version);
if (!success) return false;
/* Get the occupants */
let occupants = user_data.occupants;
if (occupants) {
for (let occupant of occupants) {
let o = WorkflowOccupant.CreateWorkflowOccupantFromOccupant(
occupant.name,
occupant.position,
occupant.side,
occupant.front_rear
);
gui.occupants.push(o);
/* Set the entity ids */
for (let body_part of occupant.body_parts) {
for (let entity of body_part.entities) {
let oe = o.GetEntityByTag(entity.tag);
if (oe && entity.id) {
oe.id = entity.id;
}
}
}
}
}
/* Get the structures */
let structures = user_data.structures;
if (structures) {
for (let structure of structures) {
let s = Structure.CreateStructure(structure.component_type);
gui.structures.push(s);
/* Set the entity ids */
for (let entity of structure.entities) {
let se = s.GetEntityByTag(entity.tag);
if (se && entity.id) {
se.id = entity.id;
}
}
}
}
/* Get the B-Pillar data */
/** @type {BPillarStructure} */
let b_pillar = user_data.b_pillar;
if (b_pillar) {
gui.b_pillar_cut_section_method = b_pillar.cut_section_method;
gui.b_pillar_cut_section_node_1 = b_pillar.cut_section_nodes[0];
gui.b_pillar_cut_section_node_2 = b_pillar.cut_section_nodes[1];
gui.b_pillar_cut_section_node_3 = b_pillar.cut_section_nodes[2];
gui.b_pillar_pre_crash_parts = b_pillar.pre_crash_parts;
gui.b_pillar_post_crash_parts = b_pillar.post_crash_parts;
gui.b_pillar_shift_deform_node_1 = b_pillar.shift_deform_nodes[0];
gui.b_pillar_shift_deform_node_2 = b_pillar.shift_deform_nodes[1];
gui.b_pillar_shift_deform_node_3 = b_pillar.shift_deform_nodes[2];
gui.b_pillar_ground_z = b_pillar.ground_z;
gui.b_pillar_seat_centre_y = b_pillar.seat_centre_y;
gui.b_pillar_h_point_z = b_pillar.h_point_z;
}
/* Get the model unit system and set the combobox selected item */
let unit_system = Workflow.ModelUnitSystemFromIndex(i);
gui.wdw_pre_automotive.cbx_unit_system.SetSelectedUnitSystem(unit_system);
} catch (e) {
ErrorMessage(`Error reading saved data: ${e}`);
return false;
}
/* No need to check any other models */
return true;
}
}
/**
* get the currently selected crash test protocol
* @returns {string}
*/
function get_selected_crash_test_protocol() {
return gui.wdw_pre_automotive.cbx_protocol_test.text;
}
/**
* get the currently selected regulation
* @returns {string}
*/
function get_selected_regulation() {
return gui.wdw_pre_automotive.cbx_protocol_regulation.text;
}
/**
* get the currently selected version
* @returns {string}
*/
function get_selected_version() {
return gui.wdw_pre_automotive.cbx_protocol_version.text;
}
/**
* Get the user data object
* @returns {?UserData} User data object
*/
function get_user_data() {
let regulation = get_selected_regulation();
/* Calculate the rib irtracc lengths */
for (let occupant of gui.occupants) {
/* Chest */
occupant.upper_rib_irtracc_length = calculate_irtracc_length(
occupant,
OccupantEntity.CHEST_UPPER_RIB_SPRING_TRANS
);
occupant.mid_rib_irtracc_length = calculate_irtracc_length(
occupant,
OccupantEntity.CHEST_MIDDLE_RIB_SPRING_TRANS
);
occupant.bottom_rib_irtracc_length = calculate_irtracc_length(
occupant,
OccupantEntity.CHEST_BOTTOM_RIB_SPRING_TRANS
);
/* Abdomen */
occupant.upper_abdomen_irtracc_length = calculate_irtracc_length(
occupant,
OccupantEntity.ABDOMEN_UPPER_SPRING_TRANS
);
occupant.bottom_abdomen_irtracc_length = calculate_irtracc_length(
occupant,
OccupantEntity.ABDOMEN_LOWER_SPRING_TRANS
);
}
/** @type {BPillarStructure} B-Pillar structure */
let b_pillar = {
cut_section_method: gui.b_pillar_cut_section_method,
cut_section_nodes: [
gui.b_pillar_cut_section_node_1,
gui.b_pillar_cut_section_node_2,
gui.b_pillar_cut_section_node_3
],
pre_crash_parts: gui.b_pillar_pre_crash_parts,
post_crash_parts: gui.b_pillar_post_crash_parts,
shift_deform_nodes: [
gui.b_pillar_shift_deform_node_1,
gui.b_pillar_shift_deform_node_2,
gui.b_pillar_shift_deform_node_3
],
ground_z: gui.b_pillar_ground_z,
seat_centre_y: gui.b_pillar_seat_centre_y,
h_point_z: gui.b_pillar_h_point_z
};
return {
regulations: [regulation],
crash_test: get_selected_crash_test_protocol(),
version: get_selected_version(),
drive_side: gui.drive_side,
occupants: gui.occupants,
structures: gui.structures,
b_pillar: b_pillar
};
}
/**
* Calculate the irtracc length for a given occupant and rib spring
* If it doesn't exist in the occupant, returns 0.0
* @param {WorkflowOccupant} occupant Occupant
* @param {string} entity_tag Rib spring tag, e.g. OccupantEntity.CHEST_UPPER_RIB_SPRING_TRANS
* @returns {number}
* @example
* let length = calculate_irtracc_length(occupant, OccupantEntity.CHEST_UPPER_RIB_SPRING_TRANS);
*/
function calculate_irtracc_length(occupant, entity_tag) {
let irtracc_trans = occupant.GetEntityByTag(entity_tag);
/* Enitity not defined/not in occupant, so just set length to zero */
if (!irtracc_trans) return 0.0;
/* If the ID is a database history string, convert it to a number
* so it can be used in the GetFromID function */
let id = -1;
if (typeof irtracc_trans.id == "string") {
let history = History.First(gui.model, History.DISCRETE);
while (history) {
if (history.heading == irtracc_trans.id) {
id = history.id;
break;
}
history = history.Next();
}
} else {
id = irtracc_trans.id;
}
if (id == -1) {
ErrorMessage(`Could not find numerical ID for ${irtracc_trans.name}. Setting IR-TRACC length to 0.0.`);
return 0.0;
}
/* Calculate length */
let el = Discrete.GetFromID(gui.model, id);
/* If it's not in the model, return 0.0 */
if (!el) {
return 0.0;
}
let n1 = Node.GetFromID(gui.model, el.n1);
let n2 = Node.GetFromID(gui.model, el.n2);
let x1 = n1.x;
let x2 = n2.x;
let y1 = n1.y;
let y2 = n2.y;
let z1 = n1.z;
let z2 = n2.z;
let dx = x2 - x1;
let dy = y2 - y1;
let dz = z2 - z1;
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
/**
* Get the extra data object
* @returns {WriteToFileArgument_extra} extra data object
*/
function get_extra_data() {
return {
model_unit_system: gui.wdw_pre_automotive.cbx_unit_system.GetSelectedUnitSystem()
};
}
/**
* Add buttons to the 'popup_select_entities' popup for selecting database
* history nodes/beams/discretes and database cross sections directly, rather
* than having to pick/select them in the graphics window
*/
function add_buttons_to_select_entities_popup() {
/* Create buttons for each node/beam/discrete database history item in the
* model, storing them on the gui object so they can be (un)mapped as
* appropriate depending on the entity type that the popup is being mapped for. */
gui.database_buttons = [];
let dy = 6;
let y1 = gui.popup_select_entities.lbl_database_history.bottom;
let y2 = y1 + dy;
let x1 = 1;
let x2 = 81;
let history = History.First(gui.model);
while (history) {
switch (history.type) {
case History.NODE:
case History.BEAM:
case History.DISCRETE:
let history_label = "";
if (history.heading == "") {
history_label = history.id.toString();
} else {
history_label = history.heading;
}
let button = new Widget(gui.popup_select_entities, Widget.BUTTON, x1, x2, y1, y2, history_label);
button.justify = Widget.LEFT;
button.onClick = select_database_item;
/* Store the entity type and label on the widget object to make it easy
* to (un)map the correct buttons depending on what entity type is being selected
* and when processing the button click */
if (history.type == History.NODE) {
// @ts-ignore
button.entity_type = BaseEntity.NODE;
} else if (history.type == History.BEAM) {
// @ts-ignore
button.entity_type = BaseEntity.BEAM_BASIC;
} else if (history.type == History.DISCRETE) {
// @ts-ignore
button.entity_type = BaseEntity.SPRING_TRANSLATIONAL;
}
// @ts-ignore
button.entity_label = history_label;
/* Store the button widget so we can loop over them in the update function */
gui.database_buttons.push(button);
break;
}
history = history.Next();
}
/* Database cross sections */
let xsec = CrossSection.First(gui.model);
while (xsec) {
let xsec_label = "";
if (xsec.heading == "") {
xsec_label = xsec.label.toString();
} else {
xsec_label = xsec.heading;
}
let button = new Widget(gui.popup_select_entities, Widget.BUTTON, x1, x2, y1, y2, xsec_label);
button.justify = Widget.LEFT;
button.onClick = select_database_item;
/* Store the history type and label on the widget object to make it easy
* to (un)map the correct buttons depending on what entity type is being selected
* and when processing the button click */
// @ts-ignore
button.entity_type = BaseEntity.XSECTION;
// @ts-ignore
button.entity_label = xsec_label;
/* Store the button widget so we can loop over them in the update function */
gui.database_buttons.push(button);
xsec = xsec.Next();
}
/* Sort into alphabetical then numerical order */
gui.database_buttons.sort(function (a, b) {
// Sort array of buttons into alphabetical then numerical order
var ai = parseInt(a.text);
var bi = parseInt(b.text);
/* Both text - case insensitive */
if (isNaN(ai) && isNaN(bi)) {
if (a.text.toLowerCase() > b.text.toLowerCase()) {
return 1;
} else if (a.text.toLowerCase() < b.text.toLowerCase()) {
return -1;
} else {
return 0;
}
/* Both numbers */
} else if (!isNaN(ai) && !isNaN(bi)) {
return ai - bi;
/* One text, one number -> text should come first */
} else {
if (isNaN(ai)) {
return -1;
} else {
return 1;
}
}
});
}
/* Callback functions */
/**
* Open the occupant window to add a new occupant
*/
function add_new_occupant() {
gui.current_occupant = null;
update_occupant_window();
update_entity_ids();
gui.wdw_occupant.Show(false);
}
/**
* Open the occupant window to edit the current occupant
*/
function edit_occupant() {
if (!gui.current_occupant) return;
initialise_occupant_window();
gui.wdw_occupant.Show(false);
}
/**
* update the ids in the gui to use the numbers
*/
function update_entity_ids() {
let use_db_history = false;
if (!gui.wdw_occupant.btn_use_dbhistitle.active) use_db_history = true;
let occupant_name = gui.wdw_occupant.cbx_occupant_name.selectedItem.text;
let occupant_widgets = get_occupant_widgets(occupant_name);
let occupant = OccupantVersion.GetFromName(occupant_name);
//var workflow_occupant = gui.current_occupant;
/* Find the the entity widgets with the same tag and set the text to the ID + the offset */
for (let body_part_widgets of occupant_widgets.body_part_widgets) {
for (let entity_widgets of body_part_widgets.entity_widgets) {
let entity = occupant.GetEntityByTag(entity_widgets.tag);
if (use_db_history) {
if (entity.history_title != "") {
entity_widgets.textbox.text = entity.history_title;
} else if (entity.id) {
entity_widgets.textbox.text = entity.id + gui.offset;
}
} else {
if (entity.id) {
entity_widgets.textbox.text = entity.id + gui.offset;
}
}
}
}
// update_occupant_window();
}
/**
* callback to trigger changes crash test changes
*/
function crash_test_changed() {
update_regulations();
update_versions();
UpdateVehicleGUI();
}
/**
* callback to trigger changes when regulation changes
*/
function regulation_changed() {
update_versions();
UpdateVehicleGUI();
}
/**
* callback to trigger changes when version changes
*/
function version_changed() {
UpdateVehicleGUI();
}
/**
* update regulation combobox based on current crash test selection - try to keep regulation the same if it exists in new list
*/
function update_regulations() {
Message(`update_regulations`);
let crash_test = get_selected_crash_test_protocol();
let current_regulation = get_selected_regulation();
let protocols = Protocols.GetOnly("ALL", crash_test, "ALL");
let cbx = gui.wdw_pre_automotive.cbx_protocol_regulation;
/* clears all versions in the combobox and adds the versions for the new regulation */
cbx.RemoveAllWidgetItems();
let regulations = [];
for (let protocol of protocols) {
if (regulations.indexOf(protocol.regulation) == -1) regulations.push(protocol.regulation);
}
/* sort regulation strings alphabetically and select the same one as is currently selected if possible */
for (let regulation of regulations.sort()) {
let wi = new WidgetItem(cbx, regulation.toString());
if (regulation == current_regulation) wi.selected = true;
}
}
/**
* update version combobox based on current crash test selection - try to keep version the same if it exists in new list
*/
function update_versions() {
Message(`update_versions`);
let crash_test = get_selected_crash_test_protocol();
let regulation = get_selected_regulation();
let current_version = get_selected_version();
let cbx = gui.wdw_pre_automotive.cbx_protocol_version;
/* clears all versions in the combobox and adds the versions for the new regulation */
cbx.RemoveAllWidgetItems();
let versions = Protocol.Versions(regulation, crash_test);
for (let version of versions) {
let wi = new WidgetItem(cbx, version);
if (version == current_version) wi.selected = true;
}
}
/**
* callback to toggle between using id numbers and database history titles (if they exisit in JSON or can be extracted from model using IDs)
*/
function update_and_toggle_ids_between_num_and_dbhistitle() {
//make sure to toggle buttons before updating so that the correct state is used
gui.wdw_occupant.btn_use_dbhistitle.active = !gui.wdw_occupant.btn_use_dbhistitle.active;
gui.wdw_occupant.btn_use_id_num.active = !gui.wdw_occupant.btn_use_id_num.active;
update_entity_ids();
}
/**
* callback to toggle between using id numbers and database history titles (if they exisit in JSON or can be extracted from model using IDs)
*/
function update_vehicle_hand_drive() {
//make sure to toggle buttons before updating so that the correct state is used
Message(`Hand drive = ${this.text}`);
gui.drive_side = this.text;
UpdateVehicleGUI();
}
/**
* function to update vehicle gui when regulation/crash_test/version change
*/
function UpdateVehicleGUI() {
Message("Updating vehicle gui");
update_vehicle_occupants(GetProtocolVehicle(), gui.drive_side);
// gui.vehicle.Update(GetProtocolVehicle());
}
/**
* When the offset is changed in the textbox, check it's an integer
* If it's not, then reset it to the original value
*/
function offset_changed() {
//if empty string then set offset to 0
if (gui.wdw_occupant.txt_entity_offset.text == "") gui.offset = 0;
let offset = parseInt(gui.wdw_occupant.txt_entity_offset.text);
if (!isNaN(offset)) {
gui.offset = offset;
}
gui.wdw_occupant.txt_entity_offset.text = gui.offset;
update_entity_ids();
}
/**
* Open the structure window to add a new structure
*/
function add_new_structure() {
gui.current_structure = null;
update_structure_window();
gui.wdw_structure.Show(false);
}
/**
* Adds the structures required for the selected crash test and regulation(s)
*/
function add_default_structures() {
/* Structures required for this crash test/regulation combination */
let structures = Protocol.StructureTypes(get_selected_regulation(), get_selected_crash_test_protocol());
/* Add any structures if they're not already present in the gui.structures array */
for (let structure of structures) {
/* Ignore if the structure is already present */
let present = false;
for (let present_structure of gui.structures) {
if (present_structure.component_type == structure) {
present = true;
}
}
if (present) continue;
/* Add it in */
gui.structures.push(Structure.CreateStructure(structure));
}
Message("Required structures added for the selected crash test and regulation");
update_main_window();
}
/**
* Open the structure window to edit the current structure
*/
function edit_structure() {
if (!gui.current_structure) return;
initialise_structure_window();
gui.wdw_structure.Show(false);
}
/**
* Saves the selected data to a workflow JSON file
*/
function save_occupant_to_file() {
/* Get data to write to file */
let occupant = GetOccupantFromGUI();
if (occupant) {
update_occupant_entity_ids_from_gui(occupant);
// if (occupant.version) var current_version = occupant.version;
let match = occupant.name.match(/(.* v)(.*)/i);
if (match != null) {
var root_name = match[1]; //first capture group
var current_version = match[2]; //first capture group
} else {
Message(`failed to extract version from ${occupant.name}`);
return;
}
let version = Window.GetString(
"Occupant Version",
`Input the occupant version for:\n${occupant.name}`,
current_version
);
if (!version) return;
if (Unix()) {
var occupants_directory = `${GetInstallDirectory()}/workflows/scripts/automotive_assessments/occupants`;
var sub_folders = ["", `/${occupant.supplier}`, `/${occupant.product}`, `/${occupant.physiology}`];
var slash = "/";
} else {
var occupants_directory = `${GetInstallDirectory()}\\workflows\\scripts\\automotive_assessments\\occupants`;
var sub_folders = ["", `\\${occupant.supplier}`, `\\${occupant.product}`, `\\${occupant.physiology}`];
var slash = "\\";
}
//check each subfolder exists and make it if it does not.
for (let sub_folder of sub_folders) {
occupants_directory += sub_folder;
if (!File.Exists(occupants_directory)) {
//make it if it doesn't exist
if (File.Mkdir(occupants_directory)) {
Message(`Successfully made ${occupants_directory} directory.`);
} else {
Message(`Failed to make ${occupants_directory} directory.`);
return;
}
}
}
occupants_directory += slash;
let output_filename = `${occupants_directory}${root_name}${version}.json`;
if (!output_filename) return;
if (File.Exists(output_filename)) {
var answer = Window.Warning(
"Warning",
`${output_filename} already exists. Are you sure you want to overwrite it?`,
Window.YES | Window.CANCEL
);
if (answer == Window.CANCEL) return;
Message(`Overwritting ${output_filename} with new data`);
}
//construct an Occupant so that we can call toJSON to write it out in a consistent format
let occupant_json = new Occupant(
occupant.supplier,
occupant.product,
occupant.physiology,
version,
occupant.body_parts,
occupant.GetChestRotationFactors()
); //make a copy of occupant
/* Write occupant json file */
let f = new File(output_filename, File.WRITE);
let json = occupant_json.toJSON();
for (let p of json.body_parts) {
for (let e of p.entities) {
//try and add a history title if it can be found and is not blank
if (!e.history_title) {
let history_title = BaseEntity.GetHistoryTitleForId(e.entity_type, e.id);
if (history_title != "") {
e.history_title = history_title;
}
}
}
}
f.Write(JSON.stringify(json, null, 4)); //4 spaces
f.Close();
Message("Written occupant to " + output_filename);
}
}
/**
* Saves the selected data to a workflow JSON file
*/
function save_to_file() {
/* Get data to write to file */
let user_data = get_user_data();
//do not save the workflow if some required user data is missing
if (!user_data) return;
let extra = get_extra_data();
/* Ask the user where to write it */
var output_filename = Window.GetFile(".json", true);
if (output_filename == null) return;
/* API call to write workflow file */
Workflow.WriteToFile(user_data, output_filename, workflow_definition_filename, extra);
Message("Written workflow file to " + output_filename);
}
/**
* Saves the selected data to the keyword file
*/
function save_to_model() {
/* Get data to write to file */
let user_data = get_user_data();
//do not save the workflow if some required user data is missing
if (!user_data) return;
let extra = get_extra_data();
/* Ask the user which model to write it to */
var model = Model.Select("Select the model to write to");
if (model == null) return;
/* API call to write workflow to a model */
Workflow.WriteToModel(user_data, model, workflow_definition_filename, extra);
Message(
"Workflow data added to post *END data. You need to write out the model from the main Model->Write menu to save the additions."
);
}
/**
* onclick callback for add and edit buttons
*/
function add_edit_occupant() {
set_up_occupant_window(this.side, this.front_rear);
}
/**
* Delete the associated occupant
*/
function delete_occupant() {
for (let i = 0; i < gui.occupants.length; i++) {
if (gui.occupants[i].side == this.side && gui.occupants[i].front_rear == this.front_rear) {
gui.occupants.splice(i, 1);
gui.current_occupant = null;
Message(`Deleted ${this.front_rear} ${this.side} occupant`);
break;
}
}
update_main_window();
}
/**
* Delete all the occupants
*/
function delete_all_occupants() {
gui.occupants = [];
gui.current_occupant = null;
Message(`Deleted all occupants`);
update_main_window();
}
/**
* Flip all the occupant seats right/left all the occupants and correctly set driver
*/
function flip_occupants() {
for (let occupant of gui.occupants) {
Message(`Flipping ${occupant.name}`);
if (occupant.side == WorkflowOccupant.LEFT) occupant.side = WorkflowOccupant.RIGHT;
else if (occupant.side == WorkflowOccupant.RIGHT) occupant.side = WorkflowOccupant.LEFT;
if (occupant.front_rear == WorkflowOccupant.FRONT) {
if (occupant.position == WorkflowOccupant.DRIVER) occupant.position = WorkflowOccupant.PASSENGER;
else occupant.position = WorkflowOccupant.DRIVER;
}
}
Message(`swapped occupant seats (right/left)`);
update_main_window();
}
/**
* Delete the selected structures
*/
function delete_selected_structure() {
for (let widget_item of gui.wdw_pre_automotive.lbx_structures.WidgetItems()) {
if (widget_item.selected) {
for (let i = 0; i < gui.structures.length; i++) {
if (gui.structures[i] == widget_item.structure) {
gui.structures.splice(i, 1);
gui.current_structure = null;
break;
}
}
}
}
update_main_window();
}
/**
* Close the occupant window
*/
function close_occupant_window() {
gui.wdw_occupant.Hide();
}
/**
* Update the current occupant with the data selected by the user
* and then close the occupant window
*/
function update_current_occupant_and_close() {
let updated = update_current_occupant();
if (updated) {
gui.wdw_occupant.Hide();
update_main_window();
}
}
/**
* Update the current occupant with the data selected by the user
* Returns true if the occupant was updated, false otherwise
* @return {boolean}
*/
function update_current_occupant() {
let wdw = gui.wdw_occupant;
/* A null current occupant means a new one is being added.
* Create a new occupant with the selected values. */
if (gui.current_occupant == null) {
/* If an occupant in this position/side/front_rear already exists ask the user if
* they want to overwrite it and if they say yes set the current occupant to it.
*
* If it doesn't already exist create a new one and add it to the gui.occupants list */
/** @type {?WorkflowOccupant} */
let existing_occupant = null;
for (let occupant of gui.occupants) {
if (
occupant.position == wdw.cbx_occupant_position.selectedItem.text &&
occupant.side == wdw.cbx_occupant_side.selectedItem.text &&
occupant.front_rear == wdw.cbx_occupant_front_rear.selectedItem.text
) {
existing_occupant = occupant;
break;
}
}
if (existing_occupant) {
let answer = Window.Message(
"Occupant already defined",
`'${existing_occupant.toString()}' occupant already exists. Update with new values?`,
Window.YES | Window.NO
);
if (answer == Window.NO) {
return false;
} else {
gui.current_occupant = existing_occupant;
}
} else {
gui.current_occupant = GetOccupantFromGUI();
gui.occupants.push(gui.current_occupant);
}
} else {
/* Update the current occupant with the selected values */
gui.current_occupant.SetOccupantFields = wdw.cbx_occupant_name.selectedItem.text;
gui.current_occupant.position = wdw.cbx_occupant_position.selectedItem.text;
gui.current_occupant.side = wdw.cbx_occupant_side.selectedItem.text;
gui.current_occupant.front_rear = wdw.cbx_occupant_front_rear.selectedItem.text;
}
/* Set the entity IDs */
update_occupant_entity_ids_from_gui(gui.current_occupant);
return true;
}
/**
* update the ids for the occupant
* @param {WorkflowOccupant|Occupant} occupant
*/
function update_occupant_entity_ids_from_gui(occupant) {
for (let body_part of occupant.body_parts) {
for (let entity of body_part.entities) {
let id = get_occupant_entity_id_from_widget_by_tag(occupant.name, entity.tag);
entity.id = id;
}
}
}
/**
* get occupant from the current gui inputs
* @returns {WorkflowOccupant}
*/
function GetOccupantFromGUI() {
let wdw = gui.wdw_occupant;
return WorkflowOccupant.CreateWorkflowOccupantFromOccupant(
wdw.cbx_occupant_name.selectedItem.text,
wdw.cbx_occupant_position.selectedItem.text,
wdw.cbx_occupant_side.selectedItem.text,
wdw.cbx_occupant_front_rear.selectedItem.text
);
}
/**
* Close the structure window
*/
function close_structure_window() {
gui.wdw_structure.Hide();
}
/**
* Update the current structure with the data selected by the user
* and then close the structure window
*/
function update_current_structure_and_close() {
let updated = update_current_structure();
if (updated) {
gui.wdw_structure.Hide();
update_main_window();
}
}
/**
* Update the current structure with the data selected by the user
* Returns true if the structure was updated, false otherwise
* @return {boolean}
*/
function update_current_structure() {
let wdw = gui.wdw_structure;
/* A null current structure means a new one is being added.
* Create a new structure with the selected values. */
if (gui.current_structure == null) {
/* If this structure type already exists ask the user if they want to overwrite it
* and if they say yes set the current structure to it.
*
* If it doesn't already exist create a new one and add it to the gui.structures list
*/
/** @type {?Structure} */
let existing_structure = null;
for (let structure of gui.structures) {
if (structure.component_type == wdw.cbx_structure.selectedItem.text) {
existing_structure = structure;
break;
}
}
if (existing_structure) {
let answer = Window.Message(
"Structure already defined",
`'${existing_structure.component_type}' structure already exists. Update with new values?`,
Window.YES | Window.NO
);
if (answer == Window.NO) {
return false;
} else {
gui.current_structure = existing_structure;
}
} else {
gui.current_structure = Structure.CreateStructure(wdw.cbx_structure.selectedItem.text);
gui.structures.push(gui.current_structure);
}
} else {
/* Update the current structure with the selected values */
gui.current_structure.component_type = wdw.cbx_structure.selectedItem.text;
}
/* Set the entity IDs */
for (let entity of gui.current_structure.entities) {
let id = get_structure_entity_id_from_widget_by_tag(gui.current_structure.component_type, entity.tag);
entity.id = id;
}
return true;
}
/**
* Set the currently selected occupant when the occupant widget item is clicked
*/
function occupant_on_click() {
if (this.selected) {
gui.current_occupant = this.occupant;
} else {
gui.current_occupant = null;
}
}
/**
* Set the currently selected structure when the structure widget item is clicked
*/
function structure_on_click() {
if (this.selected) {
gui.current_structure = this.structure;
} else {
gui.current_structure = null;
}
update_structure_edit_and_delete_buttons();
}
function occupant_entity_on_popup() {
/* Store what the pick is for so it can process it correctly */
gui.current_entity_popup = "occupant";
/* Current entity tag and type */
gui.current_entity_tag = this.entity_tag;
gui.current_entity_type = this.entity_type;
entity_on_popup(this.entity_type);
}
function structure_entity_on_popup() {
/* Store what the pick is for so it can process it correctly */
gui.current_entity_popup = "structure";
/* Current entity tag and type */
gui.current_entity_tag = this.entity_tag;
gui.current_entity_type = this.entity_type;
entity_on_popup(this.entity_type);
}
/**
* Set the currently selected entity when the entity popup is mapped from the textbox
* and map the appropriate database history items for the entity type
* @param {string} entity_type The entity type, e.g. BaseEntity.NODE
*/
function entity_on_popup(entity_type) {
/* Change the text on the label to say what the type is */
if (entity_type == BaseEntity.NODE) {
gui.popup_select_entities.lbl_database_history.text = "DATABASE HISTORY NODE";
} else if (entity_type == BaseEntity.BEAM_BASIC) {
gui.popup_select_entities.lbl_database_history.text = "DATABASE HISTORY BEAM";
} else if (entity_type == BaseEntity.SPRING_ROTATIONAL || entity_type == BaseEntity.SPRING_TRANSLATIONAL) {
gui.popup_select_entities.lbl_database_history.text = "DATABASE HISTORY DISCRETE";
} else if (entity_type == BaseEntity.XSECTION) {
gui.popup_select_entities.lbl_database_history.text = "DATABASE XSECTION";
}
/* Get location to start mapping the database history buttons */
let dy = 6;
let y1 = gui.popup_select_entities.lbl_database_history.bottom;
let y2 = y1 + dy;
/* (Un)map and position the appropriate database history buttons for this entity type */
for (let button of gui.database_buttons) {
if (
entity_type == button.entity_type ||
(entity_type == BaseEntity.SPRING_ROTATIONAL && button.entity_type == BaseEntity.SPRING_TRANSLATIONAL)
) {
button.top = y1;
button.bottom = y2;
button.Show();
y1 = y2;
y2 += dy;
} else {
button.Hide();
}
}
}
/**
* Pick an entity
*/
function pick_entity() {
let id = BaseEntity.Pick(gui.current_entity_type, gui.model);
if (id == null) return;
set_widget_entity_id_by_tag(gui.current_entity_popup, id);
}
/**
* Select an entity
*/
function select_entity() {
let id = BaseEntity.Select(gui.current_entity_type, gui.model);
if (id == null) return;
set_widget_entity_id_by_tag(gui.current_entity_popup, id);
}
/**
* Select a database item
*/
function select_database_item() {
let id = this.entity_label;
set_widget_entity_id_by_tag(gui.current_entity_popup, id);
}
/**
* Set the id on the structure or occupant entity widget by entity tag
* @param {string} type Either "occupant" or "structure"
* @param {number} id Enitity id
*/
function set_widget_entity_id_by_tag(type, id) {
if (type != "occupant" && type != "structure") {
ErrorMessage(`Invalid type '${type}' in set_widget_entity_id_by_tag`);
return;
}
/* Call the appropriate function to set the id on the entity widget */
if (type == "occupant") {
set_occupant_widget_entity_id_by_tag(
gui.wdw_occupant.cbx_occupant_name.selectedItem.text,
gui.current_entity_tag,
id
);
} else if (type == "structure") {
set_structure_widget_entity_id_by_tag(
gui.wdw_structure.cbx_structure.selectedItem.text,
gui.current_entity_tag,
id
);
}
}
/**
* Update the main window
*/
function update_main_window() {
let wdw = gui.wdw_pre_automotive;
/* Recreate widget items in the structures listbox for each currently defined structure
* Store the structure instance on the widget items to use when the edit/delete
* buttons are clicked. */
wdw.lbx_structures.RemoveAllWidgetItems();
for (let structure of gui.structures) {
let wi = new WidgetItem(wdw.lbx_structures, structure.toString());
/* Store the structure on the widget item (to use when the edit or delete button are pressed) */
// @ts-ignore
wi.structure = structure;
wi.onClick = structure_on_click; /* Updates the current occupant when clicked */
/* Select the widget item if it's the current structure */
if (gui.current_structure && wi.text == gui.current_structure.toString()) {
wi.selected = true;
}
/* Colour the widget item red if any of the entity IDs are invalid for this structure */
/* This doesn't work very well especially if you try it in different themes. Commented
* out for now
if (are_structure_entity_ids_valid(occupant)) {
wi.background = Widget.WHITE;
wi.foreground = Widget.BLACK;
} else {
wi.background = Widget.DARKRED;
wi.foreground = Widget.WHITE;
}
*/
}
/* (De)activate the edit and delete buttons depending on whether there is a current structure */
update_structure_edit_and_delete_buttons();
/* If the number of widget items in a list box has decreased you can end up
* with a grey row, so redraw the window */
/* update vehicle gui */
UpdateVehicleGUI();
wdw.Redraw();
}
/**
* Make the structure edit and delete buttons active or inactive
* depending on whether an structure is selected.
*/
function update_structure_edit_and_delete_buttons() {
let wdw = gui.wdw_pre_automotive;
if (gui.current_structure) {
wdw.btn_edit_structure.active = true;
wdw.btn_delete_structure.active = true;
} else {
wdw.btn_edit_structure.active = false;
wdw.btn_delete_structure.active = false;
}
}
/**
* Initialise the occupant window with the data from the current occupant
*/
function initialise_occupant_window() {
let wdw = gui.wdw_occupant;
if (gui.current_occupant) {
/**we need to rebuild the name combo-box as it may only contain a filtered subset of name
* from previous selections so first empty the combobox then add just the current name.
* it doesn't matter that other name widget items are missing as the combo-box will be inactive.
*/
wdw.cbx_occupant_name.RemoveAllWidgetItems();
new WidgetItem(gui.wdw_occupant.cbx_occupant_name, gui.current_occupant.name);
set_selected_widget_item(wdw.cbx_occupant_name, gui.current_occupant.name);
set_selected_widget_item(wdw.cbx_occupant_position, gui.current_occupant.position);
set_selected_widget_item(wdw.cbx_occupant_side, gui.current_occupant.side);
set_selected_widget_item(wdw.cbx_occupant_front_rear, gui.current_occupant.front_rear);
/* Set the text on the entity id widgets */
let occupant_widgets = get_occupant_widgets(gui.wdw_occupant.cbx_occupant_name.selectedItem.text);
for (let body_part_widgets of occupant_widgets.body_part_widgets) {
for (let entity_widgets of body_part_widgets.entity_widgets) {
let entity = gui.current_occupant.GetEntityByTag(entity_widgets.tag);
if (entity) entity_widgets.textbox.text = entity.id;
}
}
}
update_occupant_window();
}
/**
* filter the name drop-down based on the selected supplier, model and physiology filters.
* All versions will be returned if none are selected already. i.e. their value is 'ALL'
* @returns {string[]} occupant name names
*/
function filter_version_drop_down() {
let wdw = gui.wdw_occupant;
let selected_supplier = wdw.cbx_occupant_supplier.text;
let selected_product = wdw.cbx_occupant_product.text;
let selected_physiology = wdw.cbx_occupant_physiology.text;
Message(`Selected supplier: ${selected_supplier}`);
let versions = OccupantVersion.GetOnly(selected_supplier, selected_product, selected_physiology);
if (versions.length == 0) {
let unsupported_str = "";
if (selected_supplier.toLowerCase() != "all") unsupported_str += selected_supplier + " ";
if (selected_product.toLowerCase() != "all") unsupported_str += selected_product + " ";
if (selected_physiology.toLowerCase() != "all") unsupported_str += selected_physiology + " ";
Window.Warning("Unsupported Occupant", `No ${unsupported_str} occupants supported so removing all filters.`);
//set filters to 'ALL'
wdw.cbx_occupant_supplier.ItemAt(0).selected = true;
wdw.cbx_occupant_product.ItemAt(0).selected = true;
wdw.cbx_occupant_physiology.ItemAt(0).selected = true;
//return all supported versions
return WorkflowOccupant.Versions();
}
return versions;
}
/**
* on change callback for supplier, model and physiology callbacks
* to filter the name drop-down to make it easier to pick a name from a long list
*/
function update_filters() {
/*update occupant_names based on current selection*/
let occupant_names = filter_version_drop_down();
if (occupant_names.length == 0) {
Message('No currently supported occupants match the selected filter. Filter defaulting to "all"');
set_selected_widget_item(this, "all");
occupant_names = filter_version_drop_down();
}
update_occupant_names_combobox(occupant_names);
update_occupant_window();
}
/**
* occupant_names is an array of strings of occupant names
*
* @param {string[]} occupant_names
*/
function update_occupant_names_combobox(occupant_names) {
gui.wdw_occupant.cbx_occupant_name.RemoveAllWidgetItems();
for (let i = 0; i < occupant_names.length; i++)
new WidgetItem(gui.wdw_occupant.cbx_occupant_name, occupant_names[i]);
}
/**
* on click callback for filters to grey out invalid options
*/
function disable_invalid_filter_options() {
//deactivate invalid filters (i.e those that will return no versions)
let wdw = gui.wdw_occupant;
let selected_supplier = wdw.cbx_occupant_supplier.text;
let selected_product = wdw.cbx_occupant_product.text;
let selected_physiology = wdw.cbx_occupant_physiology.text;
let filter;
/** the current drop down should not affect the filtered versions so assume it is 'all'*/
if (this == wdw.cbx_occupant_supplier) {
selected_supplier = "all";
filter = "suppliers";
} else if (this == wdw.cbx_occupant_product) {
selected_product = "all";
filter = "products";
} else if (this == wdw.cbx_occupant_physiology) {
selected_physiology = "all";
filter = "physiologies";
}
let versions = OccupantVersion.GetOnly(selected_supplier, selected_product, selected_physiology);
let filters = OccupantVersion.GetValidFilters(versions);
let filter_options = filters[filter];
for (let wi of this.WidgetItems()) {
if (wi.selected) continue;
//if not selected assume it is unselectable unless it is an option in filter_options
wi.selectable = false;
for (let option of filter_options) {
if (wi.text == option) {
wi.selectable = true;
break;
}
}
}
}
/**
* Update the occupant window
*/
function update_occupant_window() {
let wdw = gui.wdw_occupant;
Message(`update_occupant_window`);
/* get selected name name */
let name = wdw.cbx_occupant_name.selectedItem.text;
/* Populate the combobox widgets with data from the currently selected occupant */
if (gui.current_occupant) {
wdw.btn_update_occupant.text = "Update";
/* Grey out name combobox - can't change the occupant name once it's been set
* as the gui.current_occupant instance may not have the same body parts
* as the one it's being changed to, so would require deleting the current occupant
* and then recreating another one, which is a bit more complicated to manage. */
wdw.cbx_occupant_name.active = false;
Message(`Update! ${gui.current_occupant.supplier}`);
// update the values of the name meta data fields then make them inactive
let occupant = OccupantVersion.GetFromName(name);
set_selected_widget_item(wdw.cbx_occupant_supplier, occupant.supplier);
set_selected_widget_item(wdw.cbx_occupant_product, occupant.product);
set_selected_widget_item(wdw.cbx_occupant_physiology, occupant.physiology);
wdw.cbx_occupant_supplier.active = false;
wdw.cbx_occupant_product.active = false;
wdw.cbx_occupant_physiology.active = false;
} else {
wdw.btn_update_occupant.text = "Add";
wdw.cbx_occupant_name.active = true;
wdw.cbx_occupant_supplier.active = true;
wdw.cbx_occupant_product.active = true;
wdw.cbx_occupant_physiology.active = true;
}
/* Reposition and show the relevant entity widgets */
let top = wdw.btn_use_id_num.bottom + 1;
let bottom = top + 6;
for (let occupant_widgets of gui.occupant_widgets) {
if (occupant_widgets.name == wdw.cbx_occupant_name.selectedItem.text) {
occupant_widgets.Show();
for (let body_part_widgets of occupant_widgets.body_part_widgets) {
body_part_widgets.label.top = top;
body_part_widgets.label.bottom = bottom;
top = bottom + 1;
bottom = top + 6;
for (let entity_widgets of body_part_widgets.entity_widgets) {
entity_widgets.label.top = top;
entity_widgets.label.bottom = bottom;
entity_widgets.textbox.top = top;
entity_widgets.textbox.bottom = bottom;
/* Colour the textbox red if the entity ID is invalid */
if (is_entity_widget_id_valid(entity_widgets)) {
entity_widgets.textbox.category = Widget.CATEGORY_TEXT_BOX;
} else {
entity_widgets.textbox.category = Widget.CATEGORY_WARNING_ACTION;
}
top = bottom + 1;
bottom = top + 6;
}
}
} else {
/* Not relevant to this occupant name, so hide the widgets */
occupant_widgets.Hide();
}
}
wdw.Redraw();
}
/**
* Initialise the structure window with the data from the current structure
*/
function initialise_structure_window() {
let wdw = gui.wdw_structure;
if (gui.current_structure) {
set_selected_widget_item(wdw.cbx_structure, gui.current_structure.component_type);
if (gui.current_structure.component_type == Structure.B_PILLAR) {
update_b_pillar_widget_values();
} else {
/* Set the text on the entity id widgets */
let structure_widgets = get_structure_widgets(gui.wdw_structure.cbx_structure.selectedItem.text);
if (structure_widgets) {
for (let entity_widgets of structure_widgets.entity_widgets) {
let entity = gui.current_structure.GetEntityByTag(entity_widgets.tag);
if (entity) entity_widgets.textbox.text = entity.id;
}
}
}
}
update_structure_window();
}
/**
* Update the structure window
*/
function update_structure_window() {
let wdw = gui.wdw_structure;
if (gui.current_structure) {
wdw.btn_update_structure.text = "Update";
/* Grey out the structure type combobox - can't change the structure type once it's been set
* as the gui.current_structure instance won't have the same entities as the one it's
* being changed to, so would require deleting the current structure and then recreating
* another one, which is a bit more complicated to manage. */
wdw.cbx_structure.active = false;
} else {
wdw.btn_update_structure.text = "Add";
wdw.cbx_structure.active = true;
}
/* Show the relevant entity widgets */
for (let structure_widgets of gui.structure_widgets) {
if (structure_widgets.structure_type == wdw.cbx_structure.selectedItem.text) {
structure_widgets.Show();
for (let entity_widgets of structure_widgets.entity_widgets) {
/* Colour the textbox red if the entity ID is invalid */
if (is_entity_widget_id_valid(entity_widgets)) {
entity_widgets.textbox.category = Widget.CATEGORY_TEXT_BOX;
} else {
entity_widgets.textbox.category = Widget.CATEGORY_WARNING_ACTION;
}
}
} else {
/* Not relevant to this structure type, so hide the widgets */
structure_widgets.Hide();
}
}
/* B-Pillar widgets */
for (let w in gui.b_pillar_widgets) {
if (wdw.cbx_structure.selectedItem.text == Structure.B_PILLAR) {
gui.b_pillar_widgets[w].Show();
} else {
gui.b_pillar_widgets[w].Hide();
}
}
wdw.Redraw();
}
/**
* Returns whether the value in the textbox is a valid entity ID,
* i.e. does it exists in any of the models currently loaded
* @param {BaseEntityWidgets} entity_widgets
* @returns {boolean}
*/
function is_entity_widget_id_valid(entity_widgets) {
return is_entity_id_valid(entity_widgets.textbox.text, entity_widgets.entity_type);
}
/**
* Checks if an entity ID is valid (exists in the model)
* @param {string} entity_id Entity ID to check
* @param {string} entity_type Entity type
* @returns {boolean}
*/
function is_entity_id_valid(entity_id, entity_type) {
let id_number = parseInt(entity_id);
if (isNaN(id_number)) {
let history = History.First(gui.model);
let xsec = CrossSection.First(gui.model);
/* It's text, so check the Database history items or Databse cross sections */
switch (entity_type) {
case BaseEntity.NODE:
while (history) {
if (history.type == History.NODE && history.heading == entity_id) return true;
history = history.Next();
}
break;
case BaseEntity.BEAM_BASIC:
while (history) {
if (history.type == History.BEAM && history.heading == entity_id) return true;
history = history.Next();
}
break;
case BaseEntity.SPRING_TRANSLATIONAL:
case BaseEntity.SPRING_ROTATIONAL:
while (history) {
if (history.type == History.DISCRETE && history.heading == entity_id) return true;
history = history.Next();
}
break;
case BaseEntity.XSECTION:
while (xsec) {
if (xsec.heading == entity_id) return true;
xsec = xsec.Next();
}
break;
}
} else {
/* It's a number, so check if the item exists */
switch (entity_type) {
case BaseEntity.NODE:
if (Node.GetFromID(gui.model, id_number)) return true;
break;
case BaseEntity.BEAM_BASIC:
if (Beam.GetFromID(gui.model, id_number)) return true;
break;
case BaseEntity.SPRING_TRANSLATIONAL:
case BaseEntity.SPRING_ROTATIONAL:
if (Discrete.GetFromID(gui.model, id_number)) return true;
break;
case BaseEntity.XSECTION:
if (CrossSection.GetFromID(gui.model, id_number)) return true;
break;
case BaseEntity.JOINT:
if (Joint.GetFromID(gui.model, id_number)) return true;
break;
}
}
/* Get here and it's not been found */
return false;
}
/**
* Checks if all the entity IDs in the occupant are valid
* @param {WorkflowOccupant} occupant WorkflowOccupant instance to check
* @returns {boolean}
*/
function are_occupant_entity_ids_valid(occupant) {
for (let body_part of occupant.body_parts) {
for (let entity of body_part.entities) {
if (!is_entity_id_valid(entity.id.toString(), entity.entity_type)) return false;
}
}
/* Get here and all the entity IDs are valid */
return true;
}
/**
* Checks if all the entity IDs in the structure are valid
* @param {Structure} structure Structure instance to check
* @returns {boolean}
*/
function are_structure_entity_ids_valid(structure) {
for (let entity of structure.entities) {
if (!is_entity_id_valid(entity.id.toString(), entity.entity_type)) return false;
}
/* Get here and all the entity IDs are valid */
return true;
}
/**
* Returns the entity widgets for an occupant name
* @param {string} name Occupant name
* @returns {OccupantWidgets}
*/
function get_occupant_widgets(name) {
for (let occupant_widgets of gui.occupant_widgets) {
if (occupant_widgets.name == name) {
return occupant_widgets;
}
}
return null;
}
/**
* Returns the id on the entity widget by occupant name and entity tag
* @param {string} name Occupant name
* @param {string} tag Entity tag
* @returns {string|number}
*/
function get_occupant_entity_id_from_widget_by_tag(name, tag) {
let occupant_widgets = get_occupant_widgets(name);
if (!occupant_widgets) return "";
for (let body_part_widgets of occupant_widgets.body_part_widgets) {
for (let entity_widgets of body_part_widgets.entity_widgets) {
if (entity_widgets.tag == tag) {
// @ts-ignore
if (!isNaN(entity_widgets.textbox.text)) {
return parseInt(entity_widgets.textbox.text);
} else {
return entity_widgets.textbox.text;
}
}
}
}
return "";
}
/**
* Set the id on the occupant entity widget by entity tag
* @param {string} name Occupant name
* @param {string} tag Entity tag
* @param {number|string} id Entity id
*/
function set_occupant_widget_entity_id_by_tag(name, tag, id) {
let occupant_widgets = get_occupant_widgets(name);
if (!occupant_widgets) return;
for (let body_part_widgets of occupant_widgets.body_part_widgets) {
for (let entity_widgets of body_part_widgets.entity_widgets) {
if (entity_widgets.tag == tag) {
entity_widgets.textbox.text = id.toString();
}
}
}
update_occupant_window();
}
/**
* Returns the entity widgets for an structure name
* @param {string} structure_type Structure type
* @returns {StructureWidgets}
*/
function get_structure_widgets(structure_type) {
for (let structure_widgets of gui.structure_widgets) {
if (structure_widgets.structure_type == structure_type) {
return structure_widgets;
}
}
return null;
}
/**
* Returns the id on the entity widget by structure type and entity tag
* @param {string} structure_type Structure type
* @param {string} tag Entity tag
* @returns {string|number}
*/
function get_structure_entity_id_from_widget_by_tag(structure_type, tag) {
let structure_widgets = get_structure_widgets(structure_type);
if (!structure_widgets) return "";
for (let entity_widgets of structure_widgets.entity_widgets) {
if (entity_widgets.tag == tag) {
// @ts-ignore
if (!isNaN(entity_widgets.textbox.text)) {
return parseInt(entity_widgets.textbox.text);
} else {
return entity_widgets.textbox.text;
}
}
}
return "";
}
/**
* Set the id on the structure entity widget by entity tag
* @param {string} structure_type Structure type
* @param {string} tag Entity tag
* @param {number|string} id Entity id
*/
function set_structure_widget_entity_id_by_tag(structure_type, tag, id) {
let structure_widgets = get_structure_widgets(structure_type);
if (!structure_widgets) return;
for (let entity_widgets of structure_widgets.entity_widgets) {
if (entity_widgets.tag == tag) {
entity_widgets.textbox.text = id.toString();
}
}
update_structure_window();
}
/**
* Set the combobox/listbox selected item from a value
* @param {Widget} cbx Combobox
* @param {string} value Value of the selected item
* @returns {boolean} true if the widget was successfully set
*/
function set_selected_widget_item(cbx, value) {
cbx.selectedItem = null;
for (let widget_item of cbx.WidgetItems()) {
Message(`widget_item.text ${widget_item.text}, value ${value}`);
if (widget_item.text == value) {
cbx.selectedItem = widget_item;
return true;
}
}
if (cbx.selectedItem == null) {
WarningMessage(`Could not select ${value} as it did not match one of the combobox widget items`);
return false;
}
}
/**
* Set the listbox selected items from a list of values
* @param {Widget} lbx listbox
* @param {string[]} values Values of the selected items
*/
function set_selected_widget_items(lbx, values) {
lbx.selectedItem = null;
for (let widget_item of lbx.WidgetItems()) {
for (let value of values) {
if (widget_item.text == value) {
widget_item.selected = true;
lbx.selectedItem = widget_item;
}
}
}
}
function SetUpGUIFromUserData(user_data, unit_system) {
//first set the regulation(s)
/* Get the crash test type */
set_selected_widget_item(gui.wdw_pre_automotive.cbx_protocol_test, user_data.crash_test);
/* Get the regulations */
set_selected_widget_items(gui.wdw_pre_automotive.lbx_protocol_regulations, user_data.regulations);
/* Get the occupants */
let occupants = user_data.occupants;
if (occupants) {
for (let occupant of occupants) {
let o = WorkflowOccupant.CreateWorkflowOccupantFromOccupant(
occupant.name,
occupant.position,
occupant.side,
occupant.front_rear
);
gui.occupants.push(o);
/* Set the entity ids */
for (let body_part of occupant.body_parts) {
for (let entity of body_part.entities) {
let oe = o.GetEntityByTag(entity.tag);
if (oe && entity.id) {
oe.id = entity.id;
}
}
}
}
}
/* Get the structures */
let structures = user_data.structures;
if (structures) {
for (let structure of structures) {
let s = Structure.CreateStructure(structure.component_type);
gui.structures.push(s);
/* Set the entity ids */
for (let entity of structure.entities) {
let se = s.GetEntityByTag(entity.tag);
if (se && entity.id) {
se.id = entity.id;
}
}
}
}
/* Get the model unit system and set the combobox selected item */
//let unit_system = Workflow.ModelUnitSystemFromIndex(i);
gui.wdw_pre_automotive.cbx_unit_system.SetSelectedUnitSystem(unit_system);
}
/**
* Object to store data for B-Pillar
* @typedef {Object} BPillarStructure
* @property {string} cut_section_method Cut section method
* @property {number[]} cut_section_nodes Cut section nodes
* @property {number[]} pre_crash_parts Pre-crash parts
* @property {number[]} post_crash_parts Post-crash parts
* @property {number[]} shift_deform_nodes Shift deform nodes
* @property {number} ground_z Ground Z coordinate
* @property {number} seat_centre_y Seat centre Y coordinate
* @property {number} h_point_z H-Point Z coordinate
*/
/**
* The user data object in the workflow file
* @typedef {Object} UserData
* @property {string} crash_test Crash test
* @property {string[]} regulations Regulations
* @property {string} version Version
* @property {string} drive_side either 'LHD' or 'RHD' vehicle
* @property {WorkflowOccupant[]} occupants Array of WorkflowOccupants
* @property {Structure[]} structures Array of Structures
* @property {BPillarStructure} b_pillar B-Pillar structure
*/