// module: TRUE
/* Classes to be used as base classes for child classes to extend */
import { Measurement } from "./measurement.mjs";
export { BaseOccupant, BaseEntity, BaseComponent, BaseEntityWidgets, BaseComponentWidgets };
/**
*
*/
class BaseOccupant {
constructor(position) {
this.position = position;
}
/**
* Occupant position constant
* @type {string} */
get position() {
return this._position;
}
set position(new_position) {
if (BaseOccupant.Positions().indexOf(new_position) == -1) {
throw new Error(`Invalid position: ${new_position}`);
}
this._position = new_position;
}
// #region Class constant getters (read-only)
// #region position getters
/** Driver
* @type {string} */
static get DRIVER() {
return "Driver";
}
/** Front passenger
* @type {string} */
static get FRONT_PASSENGER() {
return "Front passenger";
}
/** Rear driver side
* @type {string} */
static get REAR_DRIVER_SIDE() {
return "Rear driver side";
}
/** Rear passenger side
* @type {string} */
static get REAR_PASSENGER_SIDE() {
return "Rear passenger side";
}
/** Middle passenger
* @type {string} */
static get REAR_MIDDLE() {
return "Rear middle passenger";
}
// #region side getters
/** Left side
* @type {string} */
static get LEFT() {
return "left";
}
/** Middle side
* @type {string} */
static get MIDDLE() {
return "middle";
}
/** Right side
* @type {string} */
static get RIGHT() {
return "right";
}
/** Front
* @type {string} */
static get FRONT() {
return "front";
}
/** Rear
* @type {string} */
static get REAR() {
return "rear";
}
/**
* Get the row of an occupant based on position
* @returns {string}
*/
GetRow() {
switch (this.position) {
case BaseOccupant.DRIVER:
case BaseOccupant.FRONT_PASSENGER:
return BaseOccupant.FRONT;
default:
return BaseOccupant.REAR;
}
}
/**
* Return an array of all the available position strings
* @returns {string[]}
* @example
* let positions = WorkflowOccupant.Positions();
*/
static Positions() {
return [
BaseOccupant.DRIVER,
BaseOccupant.FRONT_PASSENGER,
BaseOccupant.REAR_DRIVER_SIDE,
BaseOccupant.REAR_MIDDLE,
BaseOccupant.REAR_PASSENGER_SIDE
];
}
}
/** Class to represent an entity for extracting data<br><br>
* This is intended to be a base class for other classes to extend, so shouldn't
* be used directly.<br><br>
* To extend it the static EntityTags() method needs to be implemented in the child class
* as well as the entity tag constants.
*/
class BaseEntity {
/**
* @param {string} entity_type Entity type constant
* @param {string|number} id Entity id (label or database history name)
* @param {string} name Entity name
* @param {string} tag Entity tag
* @param {Measurement[]} measurements List of raw measurements that can be read from the entity
* @param {string} [history_title=""] The entity database history title
* @param {string} [iso=""] The entity iso name
*/
constructor(entity_type, id, name, tag, measurements, history_title = "", iso = "") {
this.entity_type = entity_type;
this.id = id;
this.name = name;
this.tag = tag;
this.measurements = measurements;
this.history_title = history_title;
this.iso = iso;
}
/* Instance property getter and setters */
/** Entity type constant
* @type {string} */
get entity_type() {
return this._entity_type;
}
set entity_type(new_entity_type) {
if (BaseEntity.EntityTypes().indexOf(new_entity_type) == -1) {
throw new Error(`Invalid entity_type: ${new_entity_type}`);
}
this._entity_type = new_entity_type;
}
/** Entity id (label or database history name)
* @type {string|number} */
get id() {
return this._id;
}
set id(new_id) {
if (typeof new_id != "number" && typeof new_id != "string") {
throw new Error(`id must be a number or string: ${new_id}`);
}
this._id = new_id;
}
/** Entity name
* @type {string} */
get name() {
return this._name;
}
set name(new_name) {
if (typeof new_name != "string") {
throw new Error(`name must be a string: ${new_name}`);
}
this._name = new_name;
}
/** Entity tag
* @type {string} */
get tag() {
return this._tag;
}
set tag(new_tag) {
/* Need to call the child class static EntityTags() method.
* I don't know if this is the right way to do it, but it
* seems to work although VSCode highlights it as an error,
* hence the @ts-ignore comment.
*/
// @ts-ignore
if (this.constructor.EntityTags().indexOf(new_tag) == -1) {
throw new Error(`Invalid tag: ${new_tag}`);
}
this._tag = new_tag;
}
/**
* Measurements
* @type {Measurement[]} */
get measurements() {
return this._measurements;
}
set measurements(new_measurements) {
if (!(new_measurements instanceof Array)) {
throw new Error("measurements must be an array");
}
for (let new_entity of new_measurements) {
if (!(new_entity instanceof Measurement)) {
throw new Error("measurements must be an array of Measurement instances");
}
}
this._measurements = new_measurements;
}
/** The history_title text for the label
* @type {string} */
get history_title() {
return this._history_title;
}
set history_title(new_history_title) {
if (typeof new_history_title != "string") {
throw new Error(`Invalid history_title: ${new_history_title}`);
}
//try and add a history title if it can be found
if (this.history_title == "") new_history_title = BaseEntity.GetHistoryTitleForId(this.entity_type, this.id);
this._history_title = new_history_title;
}
/** The iso text for the label
* @type {string} */
get iso() {
return this._iso;
}
set iso(new_iso) {
if (typeof new_iso != "string") {
throw new Error(`Invalid iso: ${new_iso}`);
}
this._iso = new_iso;
}
/* Class constant getters (read-only) */
// #region Types
/**
* Node entity type
* @type {string} */
static get NODE() {
return "node";
}
/**
* Beam entity type
* @type {string} */
static get BEAM_BASIC() {
return "beam basic";
}
/**
* X-Section entity type
* @type {string} */
static get XSECTION() {
return "section";
}
/**
* Translational spring entity type
* @type {string} */
static get SPRING_TRANSLATIONAL() {
return "spring tr";
}
/**
* Rotational spring entity type
* @type {string} */
static get SPRING_ROTATIONAL() {
return "spring rot";
}
/**
* Joint entity type
* @type {string} */
static get JOINT() {
return "joint tr";
}
/**
* Part entity type
* @type {string} */
static get PART() {
return "part";
}
// #endregion
/* Class methods */
/**
* this function returns the history title of the entity from the current model (gui.model) if the title exists
* @param {string} entity_type
* @param {string|number} id integer id of entity
* @returns {string} if blank then it means no history title set or id was not int
*/
static GetHistoryTitleForId(entity_type, id) {
let id_number = parseInt(id.toString());
Window.Warning(
"GetHistoryTitleForId hardwired model",
"Function GetHistoryTitleForId is still using hardwired Model.First."
);
if (!isNaN(id_number)) {
let history = History.First(Model.First());
let xsec = CrossSection.First(Model.First());
/* 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.id == id_number) return history.heading;
history = history.Next();
}
break;
case BaseEntity.BEAM_BASIC:
while (history) {
if (history.type == History.BEAM && history.id == id_number) return history.heading;
history = history.Next();
}
break;
case BaseEntity.SPRING_TRANSLATIONAL:
case BaseEntity.SPRING_ROTATIONAL:
while (history) {
if (history.type == History.DISCRETE && history.id == id_number) return history.heading;
history = history.Next();
}
break;
case BaseEntity.XSECTION:
while (xsec) {
if (xsec.label == id_number) return history.heading;
xsec = xsec.Next();
}
break;
}
}
return ""; //return blank string
}
/**
* Returns an array of all the available entity tag strings
* This needs to be overridden by child classes
* @returns {string[]}
*/
static EntityTags() {
throw new Error("BaseEntity.EntityTags() method must be overridden by child class");
}
/**
* Return an array of all the available entity type strings
* @returns {string[]}
* @example
* let entity_types = BaseEntity.EntityTypes();
*/
static EntityTypes() {
return [
BaseEntity.NODE,
BaseEntity.BEAM_BASIC,
BaseEntity.XSECTION,
BaseEntity.JOINT,
BaseEntity.SPRING_TRANSLATIONAL,
BaseEntity.SPRING_ROTATIONAL,
BaseEntity.PART
];
}
/**
* Interactively pick an entity by type
* @param {string} entity_type Entity type constant
* @param {Model} model Restrict picking to this model
* @returns {string|number}
* @example
* let entity_id = BaseEntity.Pick(BaseEntity.NODE);
*/
static Pick(entity_type, model) {
let picked_object = null;
if (entity_type == BaseEntity.NODE) {
picked_object = Node.Pick("Pick a node", model);
} else if (entity_type == BaseEntity.BEAM_BASIC) {
picked_object = Beam.Pick("Pick a beam", model);
} else if (entity_type == BaseEntity.XSECTION) {
picked_object = CrossSection.Pick("Pick a database cross section", model);
} else if (entity_type == BaseEntity.JOINT) {
picked_object = Joint.Pick("Pick a joint", model);
} else if (entity_type == BaseEntity.SPRING_TRANSLATIONAL) {
picked_object = Discrete.Pick("Pick a translational spring", model);
} else if (entity_type == BaseEntity.SPRING_ROTATIONAL) {
picked_object = Discrete.Pick("Pick a rotational spring", model);
} else if (entity_type == BaseEntity.PART) {
picked_object = Part.Pick("Pick a part", model);
}
if (picked_object == null) return null;
return picked_object.label;
}
/**
* Interactively select an entity by type
* @param {string} entity_type Entity type constant
* @param {Model} model Restrict selection to this model
* @param {boolean} [allow_multiple=false] Allow multiple selection
* @returns {?number|number[]}
* @example
* let entity_id = BaseEntity.Select(BaseEntity.NODE);
*/
static Select(entity_type, model, allow_multiple = false) {
let flag = AllocateFlag();
let selected = null;
if (entity_type == BaseEntity.NODE) {
selected = Node.Select(flag, "Select a node", model);
} else if (entity_type == BaseEntity.BEAM_BASIC) {
selected = Beam.Select(flag, "Select a beam", model);
} else if (entity_type == BaseEntity.XSECTION) {
selected = CrossSection.Select(flag, "Select a database cross section", model);
} else if (entity_type == BaseEntity.JOINT) {
selected = Joint.Select(flag, "Select a joint", model);
} else if (entity_type == BaseEntity.SPRING_TRANSLATIONAL) {
selected = Discrete.Select(flag, "Select a translational spring", model);
} else if (entity_type == BaseEntity.SPRING_ROTATIONAL) {
selected = Discrete.Select(flag, "Select a rotational spring", model);
} else if (entity_type == BaseEntity.PART) {
selected = Part.Select(flag, "Select a part", model);
}
if (selected == null) {
ReturnFlag(flag);
return null;
}
if (!allow_multiple && selected > 1) {
Window.Warning("Multiple entities selected", "Please select only one entity");
ReturnFlag(flag);
return null;
}
let selected_objects = [];
if (entity_type == BaseEntity.NODE) {
selected_objects = Node.GetFlagged(model, flag);
} else if (entity_type == BaseEntity.BEAM_BASIC) {
selected_objects = Beam.GetFlagged(model, flag);
} else if (entity_type == BaseEntity.XSECTION) {
selected_objects = CrossSection.GetFlagged(model, flag);
} else if (entity_type == BaseEntity.JOINT) {
selected_objects = Joint.GetFlagged(model, flag);
} else if (entity_type == BaseEntity.SPRING_TRANSLATIONAL) {
selected_objects = Discrete.GetFlagged(model, flag);
} else if (entity_type == BaseEntity.SPRING_ROTATIONAL) {
selected_objects = Discrete.GetFlagged(model, flag);
} else if (entity_type == BaseEntity.PART) {
selected_objects = Part.GetFlagged(model, flag);
}
ReturnFlag(flag);
/* Return a scalar value if only one item can be selected,
* otherwise return an array of labels for the selected items. */
if (!allow_multiple) {
return selected_objects[0].label;
} else {
let ids = [];
for (let object of selected_objects) {
ids.push(object.label);
}
return ids;
}
}
/* Instance methods */
/**
* JSON representation
* @returns {object}
* @example
* let json = entity.toJSON();
*/
toJSON() {
return {
entity_type: this.entity_type,
id: this.id,
name: this.name,
tag: this.tag,
measurements: this.measurements
};
}
}
/** Class to represent a component that data should be extracted from<br><br>
* This is intended to be a base class for other classes to extend, so shouldn't
* be used directly.<br><br>
* To extend it the static Types() method needs to be implemented in the child class
* as well as the type constants.
*/
class BaseComponent {
/**
* @param {string} component_type Component type constant
* @param {BaseEntity[]} entities Array of BaseEntity instances
*/
constructor(component_type, entities) {
this.component_type = component_type;
this.entities = entities;
}
/**
* Component constant
* @type {string} */
get component_type() {
return this._component_type;
}
set component_type(new_component_type) {
/* Need to call the child class static Types() method.
* I don't know if this is the right way to do it, but it
* seems to work although VSCode highlights it as an error,
* hence the @ts-ignore comment.
*/
// @ts-ignore
if (this.constructor.Types().indexOf(new_component_type) == -1) {
throw new Error(`Invalid component_type: ${new_component_type}`);
}
this._component_type = new_component_type;
}
/**
* Array of BaseEntity instances
* @type {BaseEntity[]} */
get entities() {
return this._entities;
}
set entities(new_entities) {
if (!(new_entities instanceof Array)) {
throw new Error("entities must be an array");
}
for (let new_entity of new_entities) {
if (!(new_entity instanceof BaseEntity)) {
throw new Error("entities must be an array of OccupantEntity instances");
}
}
this._entities = new_entities;
}
/**
* Returns an array of all the available component type strings
* This needs to be overridden by child classes
* @returns {string[]}
*/
static Types() {
throw new Error("BaseComponent.Types() method must be overridden by child class");
}
/**
* JSON representation
* @returns {object}
* @example
* let json = component.toJSON();
*/
toJSON() {
return {
component_type: this.component_type,
entities: this.entities
};
}
/**
* Get an BaseEntity by tag
* @param {string} tag Entity tag
* @returns {?BaseEntity}
* @example
* let entity = component.GetEntityByTag(OccupantEntity.HEAD_NODE);
*/
GetEntityByTag(tag) {
for (let entity of this.entities) {
if (entity.tag == tag) return entity;
}
return null;
}
}
/** Class to represent a label and textbox widget for selecting Entity ids<br><br>
* This is intended to be a base class for other classes to extend, so shouldn't
* be used directly.<br><br>
*/
class BaseEntityWidgets {
/**
* Create a new 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
* @param {string[]} valid_tags Valid entity tags
* @param {string} [hover=""] The entity label hover text
* @example
* let entity_widgets = new BaseEntityWidgets(label, textbox, "MY_TAG", ["MY_TAG", "MY_OTHER_TAG"], hover_text);
*/
constructor(entity_type, label, textbox, tag, valid_tags, hover = "") {
/* Needs to be defined first as it's used to check the tag property is valid */
this.valid_tags = valid_tags;
this.entity_type = entity_type;
this.label = label;
this.textbox = textbox;
this.tag = tag;
this.hover = hover;
}
/* Instance property getter and setters */
/** Entity type constant
* @type {string} */
get entity_type() {
return this._entity_type;
}
set entity_type(new_entity_type) {
if (BaseEntity.EntityTypes().indexOf(new_entity_type) == -1) {
throw new Error(`Invalid entity_type: ${new_entity_type}`);
}
this._entity_type = new_entity_type;
}
/** The entity label widget
* @type {Widget} */
get label() {
return this._label;
}
set label(new_label) {
if (!(new_label instanceof Widget)) {
throw new Error("label must be a Widget");
}
this._label = new_label;
}
/** The entity textbox widget
* @type {Widget} */
get textbox() {
return this._textbox;
}
set textbox(new_textbox) {
if (!(new_textbox instanceof Widget)) {
throw new Error("textbox must be a Widget");
}
this._textbox = new_textbox;
}
/** The entity tag
* @type {string} */
get tag() {
return this._tag;
}
set tag(new_tag) {
if (this.valid_tags.indexOf(new_tag) == -1) {
throw new Error(`Invalid tag: ${new_tag}`);
}
this._tag = new_tag;
}
/** The hover text for the label
* @type {string} */
get hover() {
return this.label.hover;
}
set hover(new_hover) {
if (typeof new_hover != "string") {
throw new Error(`Invalid hover_text: ${new_hover}`);
}
this.label.hover = new_hover;
}
/** The valid tags
* @type {string[]} */
get valid_tags() {
return this._valid_tags;
}
set valid_tags(new_valid_tags) {
if (!(new_valid_tags instanceof Array)) {
throw new Error("valid_tags must be an array");
}
this._valid_tags = new_valid_tags;
}
/* Instance methods */
/**
* Hide the label and textbox widgets
* @example
* entity_widgets.Hide();
*/
Hide() {
this.label.Hide();
this.textbox.Hide();
}
/**
* Show the label and textbox widgets
* @example
* entity_widgets.Show();
*/
Show() {
this.label.Show();
this.textbox.Show();
}
}
/** Class to represent all the label and textbox widgets for selecting Entity ids for a component<br><br>
* This is intended to be a base class for other classes to extend, so shouldn't
* be used directly.<br><br>
*/
class BaseComponentWidgets {
/**
* Create a new BaseComponentWidgets
* @param {Widget} label The component label widget
* @param {BaseEntityWidgets[]} entity_widgets Array of entity widgets
* @example
* let component_widgets = new BaseComponentWidgets(label, entity_widgets);
*/
constructor(label, entity_widgets) {
this.label = label;
this.entity_widgets = entity_widgets;
}
/* Instance property getter and setters */
/** The component label widget
* @type {Widget} */
get label() {
return this._label;
}
set label(new_label) {
if (!(new_label instanceof Widget)) {
throw new Error("label must be a Widget");
}
this._label = new_label;
}
/** Array of entity widgets
* @type {BaseEntityWidgets[]} */
get entity_widgets() {
return this._entity_widgets;
}
set entity_widgets(new_entity_widgets) {
if (!(new_entity_widgets instanceof Array)) {
throw new Error("entity_widgets must be an Array");
}
for (let new_entity_widget of new_entity_widgets) {
if (!(new_entity_widget instanceof BaseEntityWidgets)) {
throw new Error("entities must be an array of BaseEntityWidgets instances");
}
}
this._entity_widgets = new_entity_widgets;
}
/* Instance methods */
/**
* Hide all the entity widgets for this component
* @example
* component_widgets.Hide();
*/
Hide() {
this.label.Hide();
for (let entity_widgets of this.entity_widgets) {
entity_widgets.Hide();
}
}
/**
* Show all the entity widgets for this component
* @example
* component_widgets.Show();
*/
Show() {
this.label.Show();
for (let entity_widgets of this.entity_widgets) {
entity_widgets.Show();
}
}
}