structure.mjs

// module: TRUE

import { THisHelper } from "../post/this/this.mjs";
import { BaseComponent, BaseComponentWidgets, BaseEntity, BaseEntityWidgets } from "./base.mjs";
import { CrashTest } from "./crash_tests.mjs";
import { Measurement, ComponentMeasurementCurves } from "./measurement.mjs";

export { StructureEntity, Structure, StructureEntityWidgets, StructureWidgets };

/** Class representing an entity to extract data from part of a structure<br><br>
 * It extends the BaseEntity class
 * @extends BaseEntity
 * @param {string} entity_type Entity type constant
 * @param {number|string} id Entity id (label or database history name)
 * @param {string} name Entity name (this is used as the label in the GUI)
 * @param {string} tag Entity tag
 * @param {Measurement[]} measurements List of raw measurements that can be read from the entity
 * @example
 * let entity = new StructureEntity(
 *                          StructureEntity.SPRING_TRANSLATIONAL,
 *                          1,
 *                          "(X) Steering column Intrusion Spring",
 *                          StructureEntity.STEERING_COLUMN_FORE_AFT,
 *                          [new Measurement(Measurement.FORE_AFT_DISPLACEMENT, "ET")]);
 */
class StructureEntity extends BaseEntity {
    constructor(entity_type, id, name, tag, measurements) {
        super(entity_type, id, name, tag, measurements);
    }

    /* Class constant getters (read-only) */

    // #region Tags
    /** A_PILLAR tag
     * @type {string} */
    static get A_PILLAR() {
        return "A_PILLAR";
    }

    /** B_PILLAR tag
     * @type {string} */
    static get B_PILLAR() {
        return "B_PILLAR";
    }

    /** ACCELERATOR_PEDAL_FORE_AFT tag
     * @type {string} */
    static get ACCELERATOR_PEDAL_FORE_AFT() {
        return "ACCELERATOR_PEDAL_FORE_AFT";
    }

    /** ACCELERATOR_PEDAL_LATERAL tag
     * @type {string} */
    static get ACCELERATOR_PEDAL_LATERAL() {
        return "ACCELERATOR_PEDAL_LATERAL";
    }

    /** ACCELERATOR_PEDAL_VERTICAL tag
     * @type {string} */
    static get ACCELERATOR_PEDAL_VERTICAL() {
        return "ACCELERATOR_PEDAL_VERTICAL";
    }

    /** BRAKE_PEDAL_FORE_AFT tag
     * @type {string} */
    static get BRAKE_PEDAL_FORE_AFT() {
        return "BRAKE_PEDAL_FORE_AFT";
    }

    /** BRAKE_PEDAL_LATERAL tag
     * @type {string} */
    static get BRAKE_PEDAL_LATERAL() {
        return "BRAKE_PEDAL_LATERAL";
    }

    /** BRAKE_PEDAL_VERTICAL tag
     * @type {string} */
    static get BRAKE_PEDAL_VERTICAL() {
        return "BRAKE_PEDAL_VERTICAL";
    }

    /** CLUTCH_PEDAL_FORE_AFT tag
     * @type {string} */
    static get CLUTCH_PEDAL_FORE_AFT() {
        return "CLUTCH_PEDAL_FORE_AFT";
    }

    /** CLUTCH_PEDAL_LATERAL tag
     * @type {string} */
    static get CLUTCH_PEDAL_LATERAL() {
        return "CLUTCH_PEDAL_LATERAL";
    }

    /** CLUTCH_PEDAL_VERTICAL tag
     * @type {string} */
    static get CLUTCH_PEDAL_VERTICAL() {
        return "CLUTCH_PEDAL_VERTICAL";
    }

    /** PARKING_BRAKE_FORE_AFT tag
     * @type {string} */
    static get PARKING_BRAKE_FORE_AFT() {
        return "PARKING_BRAKE_FORE_AFT";
    }

    /** PARKING_BRAKE_LATERAL tag
     * @type {string} */
    static get PARKING_BRAKE_LATERAL() {
        return "PARKING_BRAKE_LATERAL";
    }

    /** PARKING_BRAKE_VERTICAL tag
     * @type {string} */
    static get PARKING_BRAKE_VERTICAL() {
        return "PARKING_BRAKE_VERTICAL";
    }

    /** DOOR_FORE_AFT tag
     * @type {string} */
    static get DOOR_FORE_AFT() {
        return "DOOR_FORE_AFT";
    }

    /** FOOTREST_FORE_AFT tag
     * @type {string} */
    static get FOOTREST_FORE_AFT() {
        return "FOOTREST_FORE_AFT";
    }

    /** FOOTREST_LATERAL tag
     * @type {string} */
    static get FOOTREST_LATERAL() {
        return "FOOTREST_LATERAL";
    }

    /** FOOTREST_VERTICAL tag
     * @type {string} */
    static get FOOTREST_VERTICAL() {
        return "FOOTREST_VERTICAL";
    }

    /** LEFT_INSTRUMENT_PANEL_FORE_AFT tag
     * @type {string} */
    static get LEFT_INSTRUMENT_PANEL_FORE_AFT() {
        return "LEFT_INSTRUMENT_PANEL_FORE_AFT";
    }

    /** RIGHT_INSTRUMENT_PANEL_FORE_AFT tag
     * @type {string} */
    static get RIGHT_INSTRUMENT_PANEL_FORE_AFT() {
        return "RIGHT_INSTRUMENT_PANEL_FORE_AFT";
    }

    /** LEFT_TOEPAN_FORE_AFT tag
     * @type {string} */
    static get LEFT_TOEPAN_FORE_AFT() {
        return "LEFT_TOEPAN_FORE_AFT";
    }

    /** LEFT_TOEPAN_LATERAL tag
     * @type {string} */
    static get LEFT_TOEPAN_LATERAL() {
        return "LEFT_TOEPAN_LATERAL";
    }

    /** LEFT_TOEPAN_VERTICAL tag
     * @type {string} */
    static get LEFT_TOEPAN_VERTICAL() {
        return "LEFT_TOEPAN_VERTICAL";
    }

    /** CENTRE_TOEPAN_FORE_AFT tag
     * @type {string} */
    static get CENTRE_TOEPAN_FORE_AFT() {
        return "CENTRE_TOEPAN_FORE_AFT";
    }

    /** CENTRE_TOEPAN_LATERAL tag
     * @type {string} */
    static get CENTRE_TOEPAN_LATERAL() {
        return "CENTRE_TOEPAN_LATERAL";
    }

    /** CENTRE_TOEPAN_VERTICAL tag
     * @type {string} */
    static get CENTRE_TOEPAN_VERTICAL() {
        return "CENTRE_TOEPAN_VERTICAL";
    }

    /** RIGHT_TOEPAN_FORE_AFT tag
     * @type {string} */
    static get RIGHT_TOEPAN_FORE_AFT() {
        return "RIGHT_TOEPAN_FORE_AFT";
    }

    /** RIGHT_TOEPAN_LATERAL tag
     * @type {string} */
    static get RIGHT_TOEPAN_LATERAL() {
        return "RIGHT_TOEPAN_LATERAL";
    }

    /** RIGHT_TOEPAN_VERTICAL tag
     * @type {string} */
    static get RIGHT_TOEPAN_VERTICAL() {
        return "RIGHT_TOEPAN_VERTICAL";
    }

    /** STEERING_COLUMN_FORE_AFT tag
     * @type {string} */
    static get STEERING_COLUMN_FORE_AFT() {
        return "STEERING_COLUMN_FORE_AFT";
    }

    /** STEERING_COLUMN_LATERAL tag
     * @type {string} */
    static get STEERING_COLUMN_LATERAL() {
        return "STEERING_COLUMN_LATERAL";
    }

    /** STEERING_COLUMN_VERTICAL tag
     * @type {string} */
    static get STEERING_COLUMN_VERTICAL() {
        return "STEERING_COLUMN_VERTICAL";
    }

    /** LEFT_LOWER_DASH_FORE_AFT tag
     * @type {string} */
    static get LEFT_LOWER_DASH_FORE_AFT() {
        return "LEFT_LOWER_DASH_FORE_AFT";
    }

    /** LEFT_LOWER_DASH_LATERAL tag
     * @type {string} */
    static get LEFT_LOWER_DASH_LATERAL() {
        return "LEFT_LOWER_DASH_LATERAL";
    }

    /** LEFT_LOWER_DASH_VERTICAL tag
     * @type {string} */
    static get LEFT_LOWER_DASH_VERTICAL() {
        return "LEFT_LOWER_DASH_VERTICAL";
    }

    /** CENTRE_LOWER_DASH_FORE_AFT tag
     * @type {string} */
    static get CENTRE_DASH_FORE_AFT() {
        return "CENTRE_DASH_FORE_AFT";
    }

    /** CENTRE_DASH_LATERAL tag
     * @type {string} */
    static get CENTRE_DASH_LATERAL() {
        return "CENTRE_DASH_LATERAL";
    }

    /** CENTRE_DASH_VERTICAL tag
     * @type {string} */
    static get CENTRE_DASH_VERTICAL() {
        return "CENTRE_DASH_VERTICAL";
    }

    /** RIGHT_LOWER_DASH_FORE_AFT tag
     * @type {string} */
    static get RIGHT_LOWER_DASH_FORE_AFT() {
        return "RIGHT_LOWER_DASH_FORE_AFT";
    }

    /** RIGHT_LOWER_DASH_LATERAL tag
     * @type {string} */
    static get RIGHT_LOWER_DASH_LATERAL() {
        return "RIGHT_LOWER_DASH_LATERAL";
    }

    /** RIGHT_LOWER_DASH_VERTICAL tag
     * @type {string} */
    static get RIGHT_LOWER_DASH_VERTICAL() {
        return "RIGHT_LOWER_DASH_VERTICAL";
    }

    /** UPPER_DASH_FORE_AFT tag
     * @type {string} */
    static get UPPER_DASH_FORE_AFT() {
        return "UPPER_DASH_FORE_AFT";
    }

    /** UPPER_DASH_LATERAL tag
     * @type {string} */
    static get UPPER_DASH_LATERAL() {
        return "UPPER_DASH_LATERAL";
    }

    /** UPPER_DASH_VERTICAL tag
     * @type {string} */
    static get UPPER_DASH_VERTICAL() {
        return "UPPER_DASH_VERTICAL";
    }

    /** LOWER_HINGE_1_FORE_AFT tag
     * @type {string} */
    static get LOWER_HINGE_1_FORE_AFT() {
        return "LOWER_HINGE_1_FORE_AFT";
    }

    /** LOWER_HINGE_1_LATERAL tag
     * @type {string} */
    static get LOWER_HINGE_1_LATERAL() {
        return "LOWER_HINGE_1_LATERAL";
    }

    /** LOWER_HINGE_1_VERTICAL tag
     * @type {string} */
    static get LOWER_HINGE_1_VERTICAL() {
        return "LOWER_HINGE_1_VERTICAL";
    }

    /** LOWER_HINGE_2_FORE_AFT tag
     * @type {string} */
    static get LOWER_HINGE_2_FORE_AFT() {
        return "LOWER_HINGE_2_FORE_AFT";
    }

    /** LOWER_HINGE_2_LATERAL tag
     * @type {string} */
    static get LOWER_HINGE_2_LATERAL() {
        return "LOWER_HINGE_2_LATERAL";
    }

    /** LOWER_HINGE_2_VERTICAL tag
     * @type {string} */
    static get LOWER_HINGE_2_VERTICAL() {
        return "LOWER_HINGE_2_VERTICAL";
    }

    /** LOWER_HINGE_3_FORE_AFT tag
     * @type {string} */
    static get LOWER_HINGE_3_FORE_AFT() {
        return "LOWER_HINGE_3_FORE_AFT";
    }

    /** LOWER_HINGE_3_LATERAL tag
     * @type {string} */
    static get LOWER_HINGE_3_LATERAL() {
        return "LOWER_HINGE_3_LATERAL";
    }

    /** LOWER_HINGE_3_VERTICAL tag
     * @type {string} */
    static get LOWER_HINGE_3_VERTICAL() {
        return "LOWER_HINGE_3_VERTICAL";
    }

    /** UPPER_HINGE_1_FORE_AFT tag
     * @type {string} */
    static get UPPER_HINGE_1_FORE_AFT() {
        return "UPPER_HINGE_1_FORE_AFT";
    }

    /** UPPER_HINGE_1_LATERAL tag
     * @type {string} */
    static get UPPER_HINGE_1_LATERAL() {
        return "UPPER_HINGE_1_LATERAL";
    }

    /** UPPER_HINGE_1_VERTICAL tag
     * @type {string} */
    static get UPPER_HINGE_1_VERTICAL() {
        return "UPPER_HINGE_1_VERTICAL";
    }

    /** UPPER_HINGE_2_FORE_AFT tag
     * @type {string} */
    static get UPPER_HINGE_2_FORE_AFT() {
        return "UPPER_HINGE_2_FORE_AFT";
    }

    /** UPPER_HINGE_2_LATERAL tag
     * @type {string} */
    static get UPPER_HINGE_2_LATERAL() {
        return "UPPER_HINGE_2_LATERAL";
    }

    /** UPPER_HINGE_2_VERTICAL tag
     * @type {string} */
    static get UPPER_HINGE_2_VERTICAL() {
        return "UPPER_HINGE_2_VERTICAL";
    }

    /** UPPER_HINGE_3_FORE_AFT tag
     * @type {string} */
    static get UPPER_HINGE_3_FORE_AFT() {
        return "UPPER_HINGE_3_FORE_AFT";
    }

    /** UPPER_HINGE_3_LATERAL tag
     * @type {string} */
    static get UPPER_HINGE_3_LATERAL() {
        return "UPPER_HINGE_3_LATERAL";
    }

    /** UPPER_HINGE_3_VERTICAL tag
     * @type {string} */
    static get UPPER_HINGE_3_VERTICAL() {
        return "UPPER_HINGE_3_VERTICAL";
    }

    /** ROCKER_PANEL_1_LATERAL tag
     * @type {string} */
    static get ROCKER_PANEL_1_LATERAL() {
        return "ROCKER_PANEL_1_LATERAL";
    }

    /** ROCKER_PANEL_2_LATERAL tag
     * @type {string} */
    static get ROCKER_PANEL_2_LATERAL() {
        return "ROCKER_PANEL_2_LATERAL";
    }

    /** ROCKER_PANEL_3_LATERAL tag
     * @type {string} */
    static get ROCKER_PANEL_3_LATERAL() {
        return "ROCKER_PANEL_3_LATERAL";
    }

    /** HEAD_EXCURSION tag
     * @type {string} */
    static get HEAD_EXCURSION() {
        return "HEAD_EXCURSION";
    }

    // #endregion

    /**
     * Returns an array of all the available entity tag strings
     * @returns {string[]}
     * @example
     * let entity_tags = StructureEntity.EntityTags();
     */
    static EntityTags() {
        return [
            StructureEntity.A_PILLAR,
            StructureEntity.B_PILLAR,
            StructureEntity.ACCELERATOR_PEDAL_FORE_AFT,
            StructureEntity.ACCELERATOR_PEDAL_LATERAL,
            StructureEntity.ACCELERATOR_PEDAL_VERTICAL,
            StructureEntity.BRAKE_PEDAL_FORE_AFT,
            StructureEntity.BRAKE_PEDAL_LATERAL,
            StructureEntity.BRAKE_PEDAL_VERTICAL,
            StructureEntity.CLUTCH_PEDAL_FORE_AFT,
            StructureEntity.CLUTCH_PEDAL_LATERAL,
            StructureEntity.CLUTCH_PEDAL_VERTICAL,
            StructureEntity.PARKING_BRAKE_FORE_AFT,
            StructureEntity.PARKING_BRAKE_LATERAL,
            StructureEntity.PARKING_BRAKE_VERTICAL,
            StructureEntity.DOOR_FORE_AFT,
            StructureEntity.FOOTREST_FORE_AFT,
            StructureEntity.FOOTREST_LATERAL,
            StructureEntity.FOOTREST_VERTICAL,
            StructureEntity.LEFT_INSTRUMENT_PANEL_FORE_AFT,
            StructureEntity.RIGHT_INSTRUMENT_PANEL_FORE_AFT,
            StructureEntity.LEFT_TOEPAN_FORE_AFT,
            StructureEntity.LEFT_TOEPAN_LATERAL,
            StructureEntity.LEFT_TOEPAN_VERTICAL,
            StructureEntity.CENTRE_TOEPAN_FORE_AFT,
            StructureEntity.CENTRE_TOEPAN_LATERAL,
            StructureEntity.CENTRE_TOEPAN_VERTICAL,
            StructureEntity.RIGHT_TOEPAN_FORE_AFT,
            StructureEntity.RIGHT_TOEPAN_LATERAL,
            StructureEntity.RIGHT_TOEPAN_VERTICAL,
            StructureEntity.STEERING_COLUMN_FORE_AFT,
            StructureEntity.STEERING_COLUMN_LATERAL,
            StructureEntity.STEERING_COLUMN_VERTICAL,
            StructureEntity.LEFT_LOWER_DASH_FORE_AFT,
            StructureEntity.LEFT_LOWER_DASH_LATERAL,
            StructureEntity.LEFT_LOWER_DASH_VERTICAL,
            StructureEntity.CENTRE_DASH_FORE_AFT,
            StructureEntity.CENTRE_DASH_LATERAL,
            StructureEntity.CENTRE_DASH_VERTICAL,
            StructureEntity.RIGHT_LOWER_DASH_FORE_AFT,
            StructureEntity.RIGHT_LOWER_DASH_LATERAL,
            StructureEntity.RIGHT_LOWER_DASH_VERTICAL,
            StructureEntity.UPPER_DASH_FORE_AFT,
            StructureEntity.UPPER_DASH_LATERAL,
            StructureEntity.UPPER_DASH_VERTICAL,
            StructureEntity.LOWER_HINGE_1_FORE_AFT,
            StructureEntity.LOWER_HINGE_1_LATERAL,
            StructureEntity.LOWER_HINGE_1_VERTICAL,
            StructureEntity.LOWER_HINGE_2_FORE_AFT,
            StructureEntity.LOWER_HINGE_2_LATERAL,
            StructureEntity.LOWER_HINGE_2_VERTICAL,
            StructureEntity.LOWER_HINGE_3_FORE_AFT,
            StructureEntity.LOWER_HINGE_3_LATERAL,
            StructureEntity.LOWER_HINGE_3_VERTICAL,
            StructureEntity.UPPER_HINGE_1_FORE_AFT,
            StructureEntity.UPPER_HINGE_1_LATERAL,
            StructureEntity.UPPER_HINGE_1_VERTICAL,
            StructureEntity.UPPER_HINGE_2_FORE_AFT,
            StructureEntity.UPPER_HINGE_2_LATERAL,
            StructureEntity.UPPER_HINGE_2_VERTICAL,
            StructureEntity.UPPER_HINGE_3_FORE_AFT,
            StructureEntity.UPPER_HINGE_3_LATERAL,
            StructureEntity.UPPER_HINGE_3_VERTICAL,
            StructureEntity.ROCKER_PANEL_1_LATERAL,
            StructureEntity.ROCKER_PANEL_2_LATERAL,
            StructureEntity.ROCKER_PANEL_3_LATERAL,
            StructureEntity.HEAD_EXCURSION
        ];
    }
}

/** Class representing a structural component<br><br>
 * It extends the BaseComponent class
 * @extends BaseComponent
 * @param {string} structure_type Structure type constant
 * @param {StructureEntity[]} entities Array of StructureEntity instances
 * @example
 * let structure = new Structure(Structure.A_PILLAR, [entity1, entity2]);
 */
class Structure extends BaseComponent {
    constructor(structure_type, entities) {
        super(structure_type, entities);
    }

    /* Class constant getters (read-only) */

    // #region Types

    /** A-Pillar structure
     * @type {string} */
    static get A_PILLAR() {
        return "A-Pillar";
    }

    /** B-Pillar structure
     * @type {string} */
    static get B_PILLAR() {
        return "B-Pillar";
    }

    /** Accelerator pedal structure
     * @type {string} */
    static get ACCELERATOR_PEDAL() {
        return "Accelerator Pedal";
    }

    /** Brake pedal structure
     * @type {string} */
    static get BRAKE_PEDAL() {
        return "Brake Pedal";
    }

    /** Clutch pedal structure
     * @type {string} */
    static get CLUTCH_PEDAL() {
        return "Clutch Pedal";
    }

    /** Parking brake structure
     * @type {string} */
    static get PARKING_BRAKE() {
        return "Parking Brake";
    }

    /** Driver left toepan structure
     * @type {string} */
    static get DRIVER_LEFT_TOEPAN() {
        return "Driver Left Toepan";
    }

    /** Driver centre toepan structure
     * @type {string} */
    static get DRIVER_CENTRE_TOEPAN() {
        return "Driver Centre Toepan";
    }

    /** Driver right toepan structure
     * @type {string} */
    static get DRIVER_RIGHT_TOEPAN() {
        return "Driver Right Toepan";
    }

    /** Passenger left toepan structure
     * @type {string} */
    static get PASSENGER_LEFT_TOEPAN() {
        return "Passenger Left Toepan";
    }

    /** Passenger centre toepan structure
     * @type {string} */
    static get PASSENGER_CENTRE_TOEPAN() {
        return "Passenger Centre Toepan";
    }

    /** Passenger right toepan structure
     * @type {string} */
    static get PASSENGER_RIGHT_TOEPAN() {
        return "Passenger Right Toepan";
    }

    /** Door structure
     * @type {string} */
    static get DOOR() {
        return "Door";
    }

    /** Driver footrest structure
     * @type {string} */
    static get DRIVER_FOOTREST() {
        return "Driver Footrest";
    }

    /** Passenger footrest structure
     * @type {string} */
    static get PASSENGER_FOOTREST() {
        return "Passenger Footrest";
    }

    /** Left instrument panel structure
     * @type {string} */
    static get LEFT_INSTRUMENT_PANEL() {
        return "Left Instrument Panel";
    }

    /** Right instrument panel structure
     * @type {string} */
    static get RIGHT_INSTRUMENT_PANEL() {
        return "Right Instrument Panel";
    }

    /** Steering column structure
     * @type {string} */
    static get STEERING_COLUMN() {
        return "Steering Column";
    }

    /** Passenger left lower dash structure
     * @type {string} */
    static get PASSENGER_LEFT_LOWER_DASH() {
        return "Passenger Left Lower Dash";
    }

    /** Passenger right lower dash structure
     * @type {string} */
    static get PASSENGER_RIGHT_LOWER_DASH() {
        return "Passenger Right Lower Dash";
    }

    /** Passenger centre dash structure
     * @type {string} */
    static get PASSENGER_CENTRE_DASH() {
        return "Passenger Centre Dash";
    }

    /** Driver upper dash structure
     * @type {string} */
    static get DRIVER_UPPER_DASH() {
        return "Driver Upper Dash";
    }

    /** Passenger upper dash structure
     * @type {string} */
    static get PASSENGER_UPPER_DASH() {
        return "Passenger Upper Dash";
    }

    /** Driver lower hinge 1 structure
     * @type {string} */
    static get DRIVER_LOWER_HINGE_1() {
        return "Driver Lower Hinge 1";
    }

    /** Driver lower hinge 2 structure
     * @type {string} */
    static get DRIVER_LOWER_HINGE_2() {
        return "Driver Lower Hinge 2";
    }

    /** Driver lower hinge 3 structure
     * @type {string} */
    static get DRIVER_LOWER_HINGE_3() {
        return "Driver Lower Hinge 3";
    }

    /** Passenger lower hinge 1 structure
     * @type {string} */
    static get PASSENGER_LOWER_HINGE_1() {
        return "Passenger Lower Hinge 1";
    }

    /** Passenger lower hinge 2 structure
     * @type {string} */
    static get PASSENGER_LOWER_HINGE_2() {
        return "Passenger Lower Hinge 2";
    }

    /** Passenger lower hinge 3 structure
     * @type {string} */
    static get PASSENGER_LOWER_HINGE_3() {
        return "Passenger Lower Hinge 3";
    }

    /** Driver upper hinge 1 structure
     * @type {string} */
    static get DRIVER_UPPER_HINGE_1() {
        return "Driver Upper Hinge 1";
    }

    /** Driver upper hinge 2 structure
     * @type {string} */
    static get DRIVER_UPPER_HINGE_2() {
        return "Driver Upper Hinge 2";
    }

    /** Driver upper hinge 3 structure
     * @type {string} */
    static get DRIVER_UPPER_HINGE_3() {
        return "Driver Upper Hinge 3";
    }

    /** Passenger upper hinge 1 structure
     * @type {string} */
    static get PASSENGER_UPPER_HINGE_1() {
        return "Passenger Upper Hinge 1";
    }

    /** Passenger upper hinge 2 structure
     * @type {string} */
    static get PASSENGER_UPPER_HINGE_2() {
        return "Passenger Upper Hinge 2";
    }

    /** Passenger upper hinge 3 structure
     * @type {string} */
    static get PASSENGER_UPPER_HINGE_3() {
        return "Passenger Upper Hinge 3";
    }

    /** Driver rocker panel 1 structure
     * @type {string} */
    static get DRIVER_ROCKER_PANEL_1() {
        return "Driver Rocker Panel 1";
    }

    /** Driver Rocker panel 2 structure
     * @type {string} */
    static get DRIVER_ROCKER_PANEL_2() {
        return "Driver Rocker Panel 2";
    }

    /** Driver Rocker panel 3 structure
     * @type {string} */
    static get DRIVER_ROCKER_PANEL_3() {
        return "Driver Rocker Panel 3";
    }

    /** Passenger rocker panel 1 structure
     * @type {string} */
    static get PASSENGER_ROCKER_PANEL_1() {
        return "Passenger Rocker Panel 1";
    }

    /** Passenger Rocker panel 2 structure
     * @type {string} */
    static get PASSENGER_ROCKER_PANEL_2() {
        return "Passenger Rocker Panel 2";
    }

    /** Passenger Rocker panel 3 structure
     * @type {string} */
    static get PASSENGER_ROCKER_PANEL_3() {
        return "Passenger Rocker Panel 3";
    }

    /** Head excursion structure
     * @type {string} */
    static get HEAD_EXCURSION() {
        return "Head Excursion";
    }

    // #endregion

    /**
     * From string representation with hypens and underscores replaced with spaces and first letter of every word capitalized
     * @returns {string}
     * @example
     * let s = Structure.fromString("Passenger-Rocker-Panel-3");
     */
    static fromString(structure_str) {
        let re = RegExp(structure_str.replace(/[-_]/g, "."), "i"); //case insensitive matching

        // loop through all the valid types ans check if the regex for structure_str matches. If it does then return the matching string otherwise return the passed structure
        for (let structure of Structure.Types()) {
            //the strings should be the same lenfth and match
            if (re.test(structure) && structure.length == structure_str.length) return structure;
        }

        WarningMessage(`${structure_str} was not a valid structure type.`);
        return structure_str;

    }

    /**
     * Return an alphabetical array of all the available structure type strings
     * @returns {string[]}
     * @example
     * let structure_types = Structure.Types();
     */
    static Types() {
        return [
            Structure.A_PILLAR,
            Structure.B_PILLAR,
            Structure.ACCELERATOR_PEDAL,
            Structure.BRAKE_PEDAL,
            Structure.CLUTCH_PEDAL,
            Structure.PARKING_BRAKE,
            Structure.DRIVER_LEFT_TOEPAN,
            Structure.DRIVER_CENTRE_TOEPAN,
            Structure.DRIVER_RIGHT_TOEPAN,
            Structure.PASSENGER_LEFT_TOEPAN,
            Structure.PASSENGER_CENTRE_TOEPAN,
            Structure.PASSENGER_RIGHT_TOEPAN,
            Structure.DOOR,
            Structure.DRIVER_FOOTREST,
            Structure.PASSENGER_FOOTREST,
            Structure.LEFT_INSTRUMENT_PANEL,
            Structure.RIGHT_INSTRUMENT_PANEL,
            Structure.STEERING_COLUMN,
            Structure.PASSENGER_LEFT_LOWER_DASH,
            Structure.PASSENGER_RIGHT_LOWER_DASH,
            Structure.PASSENGER_CENTRE_DASH,
            Structure.DRIVER_UPPER_DASH,
            Structure.PASSENGER_UPPER_DASH,
            Structure.DRIVER_LOWER_HINGE_1,
            Structure.DRIVER_LOWER_HINGE_2,
            Structure.DRIVER_LOWER_HINGE_3,
            Structure.PASSENGER_LOWER_HINGE_1,
            Structure.PASSENGER_LOWER_HINGE_2,
            Structure.PASSENGER_LOWER_HINGE_3,
            Structure.DRIVER_UPPER_HINGE_1,
            Structure.DRIVER_UPPER_HINGE_2,
            Structure.DRIVER_UPPER_HINGE_3,
            Structure.PASSENGER_UPPER_HINGE_1,
            Structure.PASSENGER_UPPER_HINGE_2,
            Structure.PASSENGER_UPPER_HINGE_3,
            Structure.DRIVER_ROCKER_PANEL_1,
            Structure.DRIVER_ROCKER_PANEL_2,
            Structure.DRIVER_ROCKER_PANEL_3,
            Structure.PASSENGER_ROCKER_PANEL_1,
            Structure.PASSENGER_ROCKER_PANEL_2,
            Structure.PASSENGER_ROCKER_PANEL_3,
            Structure.HEAD_EXCURSION
        ].sort();
    }

    /**
     * Creates a Structure instance from user data
     * @param {Object} user_data_structure A single structure object from the user data
     * @returns {Structure}
     * @example
     * for (let s of user_data.structures) {
     *     let structure = Structure.CreateFromUserData(s);
     * }
     */
    static CreateFromUserData(user_data_structure) {
        let s = user_data_structure; /* Shorten name for convenience */

        /** @type {StructureEntity[]} */
        let entities = [];

        for (let e of s.entities) {
            let measurements = [];

            for (let m of e.measurements) {
                measurements.push(new Measurement(m.name, m.component));
            }

            entities.push(new StructureEntity(e.entity_type, e.id, e.name, e.tag, measurements));
        }

        let structure = new Structure(s.component_type, entities);

        return structure;
    }

    /**
     * Returns a Structure instance with the required
     * StructureEntities. The entity IDs are set to 0.
     * @param {string} structure_type Structure type constant
     * @returns {Structure}
     * @example
     * let structure = Structure.CreateStructure(Structure.A_PILLAR);
     */
    static CreateStructure(structure_type) {
        /** @type {StructureEntity[]} */
        let entities = [];

        switch (structure_type) {
            case Structure.A_PILLAR:
                entities = Structure.CreateAPillarEntities();
                break;

            case Structure.B_PILLAR:
                entities = Structure.CreateBPillarEntities();
                break;

            case Structure.ACCELERATOR_PEDAL:
                entities = Structure.CreateAcceleratorPedalEntities();
                break;

            case Structure.BRAKE_PEDAL:
                entities = Structure.CreateBrakePedalEntities();
                break;

            case Structure.PARKING_BRAKE:
                entities = Structure.CreateParkingBrakeEntities();
                break;

            case Structure.CLUTCH_PEDAL:
                entities = Structure.CreateClutchPedalEntities();
                break;

            case Structure.DRIVER_LEFT_TOEPAN:
                entities = Structure.CreateToepanEntities("Driver", "Left");
                break;

            case Structure.DRIVER_CENTRE_TOEPAN:
                entities = Structure.CreateToepanEntities("Driver", "Centre");
                break;

            case Structure.DRIVER_RIGHT_TOEPAN:
                entities = Structure.CreateToepanEntities("Driver", "Right");
                break;

            case Structure.PASSENGER_LEFT_TOEPAN:
                entities = Structure.CreateToepanEntities("Passenger", "Left");
                break;

            case Structure.PASSENGER_CENTRE_TOEPAN:
                entities = Structure.CreateToepanEntities("Passenger", "Centre");
                break;

            case Structure.PASSENGER_RIGHT_TOEPAN:
                entities = Structure.CreateToepanEntities("Passenger", "Right");
                break;

            case Structure.DOOR:
                entities = Structure.CreateDoorEntities();
                break;

            case Structure.DRIVER_FOOTREST:
                entities = Structure.CreateFootrestEntities("Driver");
                break;

            case Structure.PASSENGER_FOOTREST:
                entities = Structure.CreateFootrestEntities("Passenger");
                break;

            case Structure.LEFT_INSTRUMENT_PANEL:
                entities = Structure.CreateLeftInstrumentPanelEntities();
                break;

            case Structure.RIGHT_INSTRUMENT_PANEL:
                entities = Structure.CreateRightInstrumentPanelEntities();
                break;

            case Structure.STEERING_COLUMN:
                entities = Structure.CreateSteeringColumnEntities();
                break;

            case Structure.PASSENGER_LEFT_LOWER_DASH:
                entities = Structure.CreateLowerDashEntities("Left");
                break;

            case Structure.PASSENGER_RIGHT_LOWER_DASH:
                entities = Structure.CreateLowerDashEntities("Right");
                break;

            case Structure.PASSENGER_CENTRE_DASH:
                entities = Structure.CreateCentreDashEntities();
                break;

            case Structure.DRIVER_UPPER_DASH:
                entities = Structure.CreateUpperDashEntities("Driver");
                break;

            case Structure.PASSENGER_UPPER_DASH:
                entities = Structure.CreateUpperDashEntities("Passenger");
                break;

            case Structure.DRIVER_LOWER_HINGE_1:
                entities = Structure.CreateHingeEntities("Driver", "Lower", 1);
                break;

            case Structure.DRIVER_LOWER_HINGE_2:
                entities = Structure.CreateHingeEntities("Driver", "Lower", 2);
                break;

            case Structure.DRIVER_LOWER_HINGE_3:
                entities = Structure.CreateHingeEntities("Driver", "Lower", 3);
                break;

            case Structure.PASSENGER_LOWER_HINGE_1:
                entities = Structure.CreateHingeEntities("Passenger", "Lower", 1);
                break;

            case Structure.PASSENGER_LOWER_HINGE_2:
                entities = Structure.CreateHingeEntities("Passenger", "Lower", 2);
                break;

            case Structure.PASSENGER_LOWER_HINGE_3:
                entities = Structure.CreateHingeEntities("Passenger", "Lower", 3);
                break;

            case Structure.DRIVER_UPPER_HINGE_1:
                entities = Structure.CreateHingeEntities("Driver", "Upper", 1);
                break;

            case Structure.DRIVER_UPPER_HINGE_2:
                entities = Structure.CreateHingeEntities("Driver", "Upper", 2);
                break;

            case Structure.DRIVER_UPPER_HINGE_3:
                entities = Structure.CreateHingeEntities("Driver", "Upper", 3);
                break;

            case Structure.PASSENGER_UPPER_HINGE_1:
                entities = Structure.CreateHingeEntities("Passenger", "Upper", 1);
                break;

            case Structure.PASSENGER_UPPER_HINGE_2:
                entities = Structure.CreateHingeEntities("Passenger", "Upper", 2);
                break;

            case Structure.PASSENGER_UPPER_HINGE_3:
                entities = Structure.CreateHingeEntities("Passenger", "Upper", 3);
                break;

            case Structure.DRIVER_ROCKER_PANEL_1:
                entities = Structure.CreateRockerPanelEntities("Driver", 1);
                break;

            case Structure.DRIVER_ROCKER_PANEL_2:
                entities = Structure.CreateRockerPanelEntities("Driver", 2);
                break;

            case Structure.DRIVER_ROCKER_PANEL_3:
                entities = Structure.CreateRockerPanelEntities("Driver", 3);
                break;

            case Structure.PASSENGER_ROCKER_PANEL_1:
                entities = Structure.CreateRockerPanelEntities("Passenger", 1);
                break;

            case Structure.PASSENGER_ROCKER_PANEL_2:
                entities = Structure.CreateRockerPanelEntities("Passenger", 2);
                break;

            case Structure.PASSENGER_ROCKER_PANEL_3:
                entities = Structure.CreateRockerPanelEntities("Passenger", 3);
                break;

            case Structure.HEAD_EXCURSION:
                entities = Structure.CreateHeadExcursionEntities();
                break;

            default:
                ErrorMessage(`Unknown structure type ${structure_type} in Structure.CreateStructure()`);
        }

        return new Structure(structure_type, entities);
    }

    /**
     * Creates and returns StructureEntity instances for the A-Pillar structure
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateAPillarEntities();
     */
    static CreateAPillarEntities() {
        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "A-Pillar Intrusion Spring",
                StructureEntity.A_PILLAR,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the B-Pillar structure<br><br>
     *
     * The B-Pillar assessment is done in D3PLOT by taking a cross-section through the
     * structure. It requires the cut-section method (constant X, Y or Z, or three nodes)
     * the B-Pillar parts, shift deform nodes, the ground Z coordinate, the seat centre
     * Y coordinate and the H-point Z coordinate.<br><br>
     *
     * This is different to the all the other structure assessments that are carried out
     * in T/HIS by extracting data from database history entities.<br><br>
     *
     * As such, this doesn't really fit into the Structure - StructureEntity - Measurement
     * model, so just return an empty array here so that it appears in the list of structures
     * in the GUI.<br><br>
     *
     * The pre and post GUIs have bespoke logic for selecting, writing, reading and
     * processing the B-Pillar data.<br><br>
     *
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateBPillarEntities();
     */
    static CreateBPillarEntities() {
        return [];
    }

    /**
     * Creates and returns StructureEntity instances for the accelerator pedal structure
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateAcceleratorPedalEntities();
     */
    static CreateAcceleratorPedalEntities() {
        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(X) Accelerator Pedal Intrusion Spring",
                StructureEntity.ACCELERATOR_PEDAL_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(Y) Accelerator Pedal Intrusion Spring",
                StructureEntity.ACCELERATOR_PEDAL_LATERAL,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(Z) Accelerator Pedal Intrusion Spring",
                StructureEntity.ACCELERATOR_PEDAL_VERTICAL,
                [new Measurement(Measurement.VERTICAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the brake pedal structure
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateBrakePedalEntities();
     */
    static CreateBrakePedalEntities() {
        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(X) Brake Pedal Intrusion Spring",
                StructureEntity.BRAKE_PEDAL_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(Y) Brake Pedal Intrusion Spring",
                StructureEntity.BRAKE_PEDAL_LATERAL,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(Z) Brake Pedal Intrusion Spring",
                StructureEntity.BRAKE_PEDAL_VERTICAL,
                [new Measurement(Measurement.VERTICAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the parking brake structure
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateParkingBrakeEntities();
     */
    static CreateParkingBrakeEntities() {
        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(X) Parking Brake Intrusion Spring",
                StructureEntity.PARKING_BRAKE_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(Y) Parking Brake Intrusion Spring",
                StructureEntity.PARKING_BRAKE_LATERAL,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(Z) Parking Brake Intrusion Spring",
                StructureEntity.PARKING_BRAKE_VERTICAL,
                [new Measurement(Measurement.VERTICAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the clutch pedal structure
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateClutchPedalEntities();
     */
    static CreateClutchPedalEntities() {
        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(X) Clutch Pedal Intrusion Spring",
                StructureEntity.CLUTCH_PEDAL_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(Y) Clutch Pedal Intrusion Spring",
                StructureEntity.CLUTCH_PEDAL_LATERAL,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(Z) Clutch Pedal Intrusion Spring",
                StructureEntity.CLUTCH_PEDAL_VERTICAL,
                [new Measurement(Measurement.VERTICAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for a toepan structure
     * @param {string} occupant "Driver" or "Passenger"
     * @param {string} location "Left", "Centre" or "Right"
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateToepanEntities("Driver", "Left");
     */
    static CreateToepanEntities(occupant, location) {
        if (occupant != "Driver" && occupant != "Passenger") {
            ErrorMessage(`Invalid occupant ${occupant} passed to Structure.CreateToepanEntities()`);
            return [];
        }

        if (location != "Left" && location != "Centre" && location != "Right") {
            ErrorMessage(`Invalid location ${location} passed to Structure.CreateToepanEntities()`);
            return [];
        }

        /* Entity tags */

        let fore_aft_entity_tag = StructureEntity[`${location.toUpperCase()}_TOEPAN_FORE_AFT`];
        let lateral_entity_tag = StructureEntity[`${location.toUpperCase()}_TOEPAN_LATERAL`];
        let vertical_entity_tag = StructureEntity[`${location.toUpperCase()}_TOEPAN_VERTICAL`];

        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(X) ${occupant} ${location} Toepan Intrusion Spring`,
                fore_aft_entity_tag,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Y) ${occupant} ${location} Toepan Intrusion Spring`,
                lateral_entity_tag,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Z) ${occupant} ${location} Toepan Intrusion Spring`,
                vertical_entity_tag,
                [new Measurement(Measurement.VERTICAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the door structure
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateDoorEntities();
     */
    static CreateDoorEntities() {
        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(X) Door Intrusion Spring",
                StructureEntity.DOOR_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the footrest structure
     * @param {string} occupant "Driver" or "Passenger"
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateFootrestEntities("Driver");
     */
    static CreateFootrestEntities(occupant) {
        if (occupant != "Driver" && occupant != "Passenger") {
            ErrorMessage(`Invalid occupant ${occupant} passed to Structure.CreateFootrestEntities()`);
            return [];
        }

        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(X) ${occupant} Footrest Intrusion Spring`,
                StructureEntity.FOOTREST_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Y) ${occupant} Footrest Intrusion Spring`,
                StructureEntity.FOOTREST_LATERAL,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Z) ${occupant} Footrest Intrusion Spring`,
                StructureEntity.FOOTREST_VERTICAL,
                [new Measurement(Measurement.VERTICAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the left instrument panel structure
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateLeftInstrumentEntities();
     */
    static CreateLeftInstrumentPanelEntities() {
        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(X) Left Instrument Panel Intrusion Spring",
                StructureEntity.LEFT_INSTRUMENT_PANEL_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the right instrument panel structure
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateRightInstrumentEntities();
     */
    static CreateRightInstrumentPanelEntities() {
        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(X) Right Instrument Panel Intrusion Spring",
                StructureEntity.RIGHT_INSTRUMENT_PANEL_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the steering column structure
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateSteeringColumnEntities();
     */
    static CreateSteeringColumnEntities() {
        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(X) Steering Column Intrusion Spring",
                StructureEntity.STEERING_COLUMN_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(Y) Steering Column Intrusion Spring",
                StructureEntity.STEERING_COLUMN_LATERAL,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                "(Z) Steering Column Intrusion Spring",
                StructureEntity.STEERING_COLUMN_VERTICAL,
                [new Measurement(Measurement.VERTICAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the lower dash structure
     * @param {string} side "Left" or "Right" side
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateLowerDashEntities("Left");
     */
    static CreateLowerDashEntities(side) {
        if (side != "Left" && side != "Right") {
            ErrorMessage(`Invalid side ${side} passed to Structure.CreateLowerDashEntities()`);
            return [];
        }

        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(X) Passenger ${side} Lower Dash Intrusion Spring`,
                StructureEntity.LEFT_LOWER_DASH_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Y) Passenger ${side} Lower Dash Intrusion Spring`,
                StructureEntity.LEFT_LOWER_DASH_LATERAL,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Z) Passenger ${side} Lower Dash Intrusion Spring`,
                StructureEntity.LEFT_LOWER_DASH_VERTICAL,
                [new Measurement(Measurement.VERTICAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the centre dash structure
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateCentreDashEntities();
     */
    static CreateCentreDashEntities() {
        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(X) Passenger Centre Dash Intrusion Spring`,
                StructureEntity.CENTRE_DASH_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Y) Passenger Centre Dash Intrusion Spring`,
                StructureEntity.CENTRE_DASH_LATERAL,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Z) Passenger Centre Dash Intrusion Spring`,
                StructureEntity.CENTRE_DASH_VERTICAL,
                [new Measurement(Measurement.VERTICAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the upper dash structure
     * @param {string} occupant "Driver" or "Passenger"
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateUpperDashEntities("Driver");
     */
    static CreateUpperDashEntities(occupant) {
        if (occupant != "Driver" && occupant != "Passenger") {
            ErrorMessage(`Invalid occupant ${occupant} passed to Structure.CreateUpperDashEntities()`);
            return [];
        }

        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(X) ${occupant} Upper Dash Intrusion Spring`,
                StructureEntity.UPPER_DASH_FORE_AFT,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Y) ${occupant} Upper Dash Intrusion Spring`,
                StructureEntity.UPPER_DASH_LATERAL,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Z) ${occupant} Upper Dash Intrusion Spring`,
                StructureEntity.UPPER_DASH_VERTICAL,
                [new Measurement(Measurement.VERTICAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for a hinge structure
     * @param {string} occupant "Driver" or "Passenger"
     * @param {string} position "Lower" or "Upper"
     * @param {number} number Hinge number 1, 2 or 3
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateHingeEntities("Driver", "Lower", 1);
     */
    static CreateHingeEntities(occupant, position, number) {
        if (occupant != "Driver" && occupant != "Passenger") {
            ErrorMessage(`Invalid occupant ${occupant} passed to Structure.CreateHingeEntities()`);
            return [];
        }

        if (position != "Lower" && position != "Upper") {
            ErrorMessage(`Invalid position ${position} passed to Structure.CreateHingeEntities()`);
            return [];
        }

        if (number < 1 || number > 3) {
            ErrorMessage(`Invalid number ${number} passed to Structure.CreateHingeEntities()`);
            return [];
        }

        /* Entity tags */

        let fore_aft_entity_tag = StructureEntity[`${position.toUpperCase()}_HINGE_${number}_FORE_AFT`];
        let lateral_entity_tag = StructureEntity[`${position.toUpperCase()}_HINGE_${number}_LATERAL`];
        let vertical_entity_tag = StructureEntity[`${position.toUpperCase()}_HINGE_${number}_VERTICAL`];

        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(X) ${occupant} ${position} Hinge ${number} Intrusion Spring`,
                fore_aft_entity_tag,
                [new Measurement(Measurement.FORE_AFT_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Y) ${occupant} ${position} Hinge ${number} Intrusion Spring`,
                lateral_entity_tag,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            ),
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Z) ${occupant} ${position} Hinge ${number} Intrusion Spring`,
                vertical_entity_tag,
                [new Measurement(Measurement.VERTICAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for a rocker panel structure
     * @param {string} occupant "Driver" or "Passenger"
     * @param {number} number Rocker panel number 1, 2 or 3
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateRockerPanelEntities("Driver", 1);
     */
    static CreateRockerPanelEntities(occupant, number) {
        if (occupant != "Driver" && occupant != "Passenger") {
            ErrorMessage(`Invalid occupant ${occupant} passed to Structure.CreateRockerPanelEntities()`);
            return [];
        }

        if (number < 1 || number > 3) {
            ErrorMessage(`Invalid number ${number} passed to Structure.CreateRockerPanelEntities()`);
            return [];
        }

        let lateral_entity_tag = StructureEntity[`ROCKER_PANEL_${number}_LATERAL`];

        return [
            new StructureEntity(
                StructureEntity.SPRING_TRANSLATIONAL,
                0,
                `(Y) ${occupant} Rocker Panel ${number} Intrusion Spring`,
                lateral_entity_tag,
                [new Measurement(Measurement.LATERAL_INTRUSION, "ET")]
            )
        ];
    }

    /**
     * Creates and returns StructureEntity instances for the Head Excursion structure
     *
     * The Head Excursion assessment is done in D3PLOT by taking a cross-section through the
     * structure. It requires the cut-section method (constant X, Y or Z, or three nodes)
     * the head parts, barrier parts, shift deform nodes, the seat centre Y coordinate and
     * whether there's a counter measure.<br><br>
     *
     * This is different to the all the other structure assessments that are carried out
     * in T/HIS by extracting data from database history entities.<br><br>
     *
     * As such, this doesn't really fit into the Structure - StructureEntity - Measurement
     * model, so just return an empty array here so that it appears in the list of structures
     * in the GUI.<br><br>
     *
     * The pre and post GUIs have bespoke logic for selecting, writing, reading and
     * processing the Head Excursion data.<br><br>
     * @returns {StructureEntity[]}
     * @example
     * let entities = Structure.CreateBPillarEntities();
     */
    static CreateHeadExcursionEntities() {
        return [];
    }

    /**
     * Reads the raw structure measurements into T/HIS and returns
     * them in a ComponentMeasurementCurves instance
     * @param {Model} model Model to read data from
     * @returns {ComponentMeasurementCurves}
     */
    ReadRawStructureMeasurements(model) {
        let entity_tags = StructureEntity.EntityTags();

        let entities = [];

        for (let tag of entity_tags) {
            entities.push(this.GetEntityByTag(tag));
        }

        let measurement_curves = new ComponentMeasurementCurves();

        for (let entity of entities) {
            if (entity != null) {
                for (let measurement of entity.measurements) {
                    let curve = THisHelper.ReadData(model, entity.entity_type, entity.id, measurement.component, true);

                    if (curve) {
                        measurement_curves.AddCurve(measurement.name, curve);
                    }
                }
            }
        }

        return measurement_curves;
    }

    /**
     * String representation
     * @returns {string}
     * @example
     * let s = structure.toString();
     */
    toString() {
        return `${this.component_type}`;
    }
}

/** Class representing widgets for selecting Entity ids in a Structure
 * It extends the BaseEntityWidgets class
 * @extends BaseEntityWidgets
 * @param {string} entity_type Entity type constant
 * @param {Widget} label The entity label widget
 * @param {Widget} textbox The entity textbox widget
 * @param {string} tag The entity tag
 * @example
 * let entity_widgets = new StructureEntityWidgets(label, textbox, StructureEntity.A_PILLAR);
 */
class StructureEntityWidgets extends BaseEntityWidgets {
    constructor(entity_type, label, textbox, tag) {
        super(entity_type, label, textbox, tag, StructureEntity.EntityTags());
    }
}

/** Class representing the entity widgets for a Structure
 * It extends the BaseComponentWidgets class
 * @param {string} structure_type Structure type, e.g. Structure.A_PILLAR
 * @param {Widget} label The body part label widget
 * @param {StructureEntityWidgets[]} entity_widgets Array of entity widgets
 * @example
 * let structure_widgets = new StructureWidgets(Structure.A_PILLAR, label, entity_widgets);
 */
class StructureWidgets extends BaseComponentWidgets {
    constructor(structure_type, label, entity_widgets) {
        super(label, entity_widgets);

        this.structure_type = structure_type;
    }

    /* Instance property getter and setters */

    /** Structure type
     * @type {string} */
    get structure_type() {
        return this._structure_type;
    }
    set structure_type(new_structure_type) {
        if (typeof new_structure_type != "string") {
            throw new Error("structure_type must be a string");
        }

        if (Structure.Types().indexOf(new_structure_type) == -1) {
            throw new Error(`Invalid structure_type: ${new_structure_type}`);
        }

        this._structure_type = new_structure_type;
    }
}

/* TODO -
 * Update docs describing base classes and the new structure classes - including steps for adding stuff.
 * Might need to update links on home page.
 * Do assessments...
 **/