pre_entities_of_interest.js

// module: TRUE
// @ts-ignore
import { gui } from "./pre_entities_of_interest_gui.jsi";

let workflow_filename = Workflow.WorkflowDefinitionFilename();
let m; // the model that is selected in the set up function
let entities_of_interest = []; //object for storing data to go into the .JSON
let entityID; // When selecting entities, the ID of the selected parts
let widgetItemNumberForEdit; // When editing an entry, this holds the widget item number that is being edited

/**
 * @typedef {Object} EntityOfInterest
 * @property {string} name
 * @property {string[]} parts
 * @property {String} type
 * @property {String[]} partsOfEntity
 */

/**
 * The data object in the workflow file
 * @typedef {Object} data
 * @property {EntityOfInterest[]} entities_of_interest
 */
/** @type {data} */
let data;

set_up();

/* Show the first window "Window1" */

if (gui) gui.Window1.Show(false);

/**
 * Sets up the window with callback functions from the GUI, selects the model to be used and fills the WidgetItems for the entity type combobox
 */
function set_up() {
    // Selecting the model to use if there are multiple ones open in PRIMER
    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();
    }
    m = gui.model;

    // Reading in any previously saved data
    let numModels = Workflow.NumberOfModels();
    for (let i = 0; i < numModels; i++) {
        let modelID = Workflow.ModelIdFromIndex(i);
        if (modelID != gui.model.number) {
            continue;
        }

        /** @type {data} */
        // @ts-ignore
        data = Workflow.ModelUserDataFromIndex(i);
        try {
            for (let i = 0; i < data.entities_of_interest.length; i++) {
                let entityName = data.entities_of_interest[i].name.toString();
                let entityType = data.entities_of_interest[i].type.toString();
                entityID = data.entities_of_interest[i].parts.toString();

                // Adds the entry to the listbox and sets the hover value to the entities
                let newEntry = new WidgetItem(gui.Window1.lbEntity, entityName + " | " + entityType + " | " + entityID);
                newEntry.hover = entityID;

                // If the entry is a part set then finds the parts in the part set and saves it to partsInEntity
                let partsInEntity = [];
                if (entityType == "Part Set") {
                    partsInEntity = find_parts_in_set();
                }
                // Saves the data to the entities_of_interest array ready to be added to the .JSON file
                entities_of_interest.push({
                    name: entityName,
                    parts: entityID.split(","),
                    type: entityType,
                    partsOfEntity: partsInEntity
                });
            }
        } catch (e) {
            ErrorMessage("Error reading saved data");
        }
    }

    gui.Window1.txtName.onChange = update_buttons;
    gui.Window1.lbEntity.onClick = update_buttons;
    gui.Window1.cbEntity.onChange = clear_IDs;
    gui.Window1.btnSelect.onClick = select_main;
    gui.Window1.btnDeleteSelected.onClick = delete_selected;
    gui.Window1.btnUnblank.onClick = unblank;
    gui.Window1.btnOnly.onClick = only;
    gui.Window1.btnAdd.onClick = add;
    gui.Window1.btnEdit.onClick = edit;
    gui.Window1.btnSaveToFile.onClick = save_to_file1;
    gui.Window1.btnSaveToModel.onClick = save_to_model;
    gui.Window1.helpBtn.onClick = workflow_manual;
    gui.WindowEdit.btnUpdate.onClick = update_edit;
    gui.WindowEdit.btnCancel.onClick = close_edit;
    gui.WindowEdit.btnSelect.onClick = select_edit;

    gui.Window1.cbEntity.RemoveAllWidgetItems();
    new WidgetItem(gui.Window1.cbEntity, "Parts");
    new WidgetItem(gui.Window1.cbEntity, "Part Set");

    gui.Window1.lblLine.Line(Widget.DARKGREY, 0, 50, 100, 50);

    update_buttons();
}

/**
 * Enables and Disables the buttons on the window according to how many Widget Items there are and how many are selected
 */
function update_buttons() {
    if (gui.Window1.txtName.text == "" || entityID == "" || entityID == null) {
        gui.Window1.btnAdd.active = false;
    } else {
        gui.Window1.btnAdd.active = true;
    }

    let widgetItems = gui.Window1.lbEntity.WidgetItems();
    if (widgetItems == null) {
        gui.Window1.btnDeleteSelected.active = false;
        gui.Window1.btnEdit.active = false;
        gui.Window1.btnOnly.active = false;
    } else {
        let widgetItemsSelected = 0;
        for (let i = 0; i < widgetItems.length; i++) {
            if (widgetItems[i].selected) {
                widgetItemsSelected++;
            }
        }
        if (widgetItemsSelected == 0) {
            gui.Window1.btnDeleteSelected.active = false;
            gui.Window1.btnOnly.active = false;
        }
        if (widgetItemsSelected != 0) {
            gui.Window1.btnDeleteSelected.active = true;
            gui.Window1.btnOnly.active = true;
        }
        if (widgetItemsSelected == 1) {
            gui.Window1.btnEdit.active = true;
        } else {
            gui.Window1.btnEdit.active = false;
        }
    }
}

/**
 * When the entity type is changed, the entityID list of selected entity's is cleared to prevent being able to add entities IDs of a non matching entity type.
 */
function clear_IDs() {
    entityID = null;
    update_buttons();
}

/**
 * Sends to the select function with parameter main
 */
function select_main() {
    select("main");
}

/**
 * Sends to the select function with parameter edit
 */
function select_edit() {
    select("edit");
}

/**
 * Select the entities for the entry and populate the textbox
 * @param {string} method
 */
function select(method) {
    let f = AllocateFlag();
    let selection;
    let p;
    if (method == "main") {
        if (gui.Window1.cbEntity.text == "Parts") {
            selection = Part.Select(f, "Pick parts of interest", m, true);
            p = Part.First(m);
        }
        if (gui.Window1.cbEntity.text == "Part Set") {
            // @ts-ignore this is because VS Code is checking it based on the core JavaScript Set class, which doesn't have a Select function or PART constant.
            selection = Set.Select(Set.PART, f, "Pick part sets of interest", m, true);
            // @ts-ignore
            p = Set.First(m, Set.PART);
        }
    }
    if (method == "edit") {
        if (entities_of_interest[widgetItemNumberForEdit].type.toString() == "Parts") {
            selection = Part.Select(f, "Pick parts of interest", m, true);
            p = Part.First(m);
        }
        if (entities_of_interest[widgetItemNumberForEdit].type.toString() == "Part Set") {
            // @ts-ignore this is because VS Code is checking it based on the core JavaScript Set class, which doesn't have a Select function or PART constant.
            selection = Set.Select(Set.PART, f, "Pick part sets of interest", m, true);
            // @ts-ignore
            p = Set.First(m, Set.PART);
        }
    }
    if (selection == null) {
        ReturnFlag(f);
        return;
    }

    let text = "";
    let first = true;

    while (p) {
        if (p.Flagged(f)) {
            if (first) {
                text += p.label;
                first = false;
            } else {
                text += "," + p.label;
            }
        }

        p = p.Next();
    }

    ReturnFlag(f);

    entityID = text;

    if (method == "edit") {
        gui.WindowEdit.txtID.text = entityID;
    }

    update_buttons();
}

/**
 * Unblank all parts
 */
function unblank() {
    m.Unblank();
    View.Ac();
}

/**
 * Only selected parts from the listbox
 */
function only() {
    m.Blank();

    let widgetItems = gui.Window1.lbEntity.WidgetItems();
    for (let i = 0; i < widgetItems.length; i++) {
        if (widgetItems[i].selected) {
            let parts;
            if (entities_of_interest[i].type.toString() == "Parts") {
                parts = entities_of_interest[i].parts;
            }
            if (entities_of_interest[i].type.toString() == "Part Set") {
                parts = entities_of_interest[i].partsOfEntity;
            }
            parts = parts.toString();
            parts = parts.split(",");
            let parts_flag = AllocateFlag();
            for (let x = 0; x < parts.length; x++) {
                let p = Part.GetFromID(m, Number(parts[x]));
                p.SetFlag(parts_flag);
            }
            Part.UnblankFlagged(m, parts_flag);
            ReturnFlag(parts_flag);
        }
    }
    View.Ac();
}

/**
 * Adds an entry to the listbox
 */
function add() {
    // Checks if entry has a name
    let entityName = gui.Window1.txtName.text;
    if (entityName == "") {
        Window.Message("", "You must enter a name for your entity");
        return;
    }
    // Checks if entry has a unique name
    for (let x = 0; x < entities_of_interest.length; x++) {
        let entities_of_interest_name = entities_of_interest[x].name.toString();
        entities_of_interest_name = entities_of_interest_name;
        if (entityName == entities_of_interest_name) {
            Window.Message("", "You can not have entities with the same name");
            return;
        }
    }

    // Checks if the entry has entities selected
    if (entityID == "" || entityID == " " || entityID == null) {
        Window.Message("", "You must select an entity for this entry");
        return;
    }
    let entityType = gui.Window1.cbEntity.text;

    // Adds the entry to the listbox and sets the hover value to the entities
    let newEntry = new WidgetItem(gui.Window1.lbEntity, entityName + " | " + entityType + " | " + entityID);
    newEntry.hover = entityID;

    // If the entry is a part set then finds the parts in the part set and saves it to partsInEntity
    let partsInEntity = [];
    if (entityType == "Part Set") {
        partsInEntity = find_parts_in_set();
    }
    // Saves the data to the entities_of_interest array ready to be added to the .JSON file
    entities_of_interest.push({
        name: entityName,
        parts: entityID.split(","),
        type: entityType,
        partsOfEntity: partsInEntity
    });
}

/**
 * Finds parts in a part set by spooling through its setID
 * @returns {Array} partsInEntity
 */
function find_parts_in_set() {
    let partsInEntity = [];
    let partID;
    let setIDs = entityID.split(",");
    for (let i = 0; i < setIDs.length; i++) {
        let setID = Number(setIDs[i]);
        // @ts-ignore
        let s = Set.GetFromID(m, setID, Set.PART);
        s.StartSpool();
        while ((partID = s.Spool())) {
            partsInEntity.push(partID.toString());
        }
    }
    return partsInEntity;
}

/**
 * Deletes a selcted entry from the listbox
 */
function delete_selected() {
    let widgetItems = gui.Window1.lbEntity.WidgetItems();

    // If there are no entries selected then prints message and returns
    if (widgetItems == null) {
        Window.Message("", "No entries avaliable to delete");
        return;
    }
    let widgetItemsSelected = 0;
    for (let i = 0; i < widgetItems.length; i++) {
        if (widgetItems[i].selected) {
            widgetItemsSelected++;
        }
    }
    if (widgetItemsSelected == 0) {
        Window.Message("", "No entries selected to delete");
    }

    // Loops through WidgetItems
    for (let i = 0; i < widgetItems.length; i++) {
        // If the WidgetItem is selected
        if (widgetItems[i].selected) {
            // Finds the name of the selected WidgetItem
            let widgetItemText = widgetItems[i].text;
            let widgetItemTextArray = widgetItemText.split("|");
            let widgetItemName = widgetItemTextArray[0];
            widgetItemName = widgetItemName.toString();

            // Loops through the entities_of_interest arreay
            for (let x = 0; x < entities_of_interest.length; x++) {
                // If the name of the selected WidgetItem matches the name in the entities_of_interest array
                let entities_of_interest_name = entities_of_interest[x].name.toString();
                entities_of_interest_name = entities_of_interest_name + " ";
                if (widgetItemName == entities_of_interest_name) {
                    // Removes the entry from the entities_of_interest array
                    entities_of_interest.splice(x, 1);
                }
            }
            // Removes the WidgetItem
            gui.Window1.lbEntity.RemoveWidgetItem(widgetItems[i]);
        }
    }
    gui.Window1.Redraw();

    update_buttons();
}

/**
 * Opens the WindowEdit window ready to edit an entry and validates this is possible
 */
function edit() {
    let widgetItems = gui.Window1.lbEntity.WidgetItems();

    // If no entry is selected then returns
    if (widgetItems == null) {
        Window.Message("", "No entrys avaliable to edit");
        return;
    }

    // Checks that their is only 1 WidgetItem selected, if it is then open WindowEdit window, if show message and return
    let widgetsSelected = 0;
    for (let i = 0; i < widgetItems.length; i++) {
        if (widgetItems[i].selected) {
            widgetsSelected++;
        }
    }
    if (widgetsSelected == 1) {
        let widgetItemsEdit = gui.Window1.lbEntity.WidgetItems();
        for (let i = 0; i < widgetItemsEdit.length; i++) {
            if (widgetItemsEdit[i].selected) {
                widgetItemNumberForEdit = i;
                gui.WindowEdit.txtName.text = entities_of_interest[i].name.toString();
                gui.WindowEdit.txtID.text = entities_of_interest[i].parts.toString();
                gui.WindowEdit.Show(false);
            }
        }
    } else if (widgetsSelected == 0) {
        Window.Message("", "You need to select an entry to edit");
        return;
    } else {
        Window.Message("", "You can only edit 1 entry at a time");
        return;
    }
}

/**
 * Edits a selected entry from the listbox if the update button is pressed in the WindowEdit window
 */
function update_edit() {
    let widgetItems = gui.Window1.lbEntity.WidgetItems();

    // Checks entry has a name
    let entityName = gui.WindowEdit.txtName.text;
    if (entityName == "") {
        Window.Message("", "You must enter a name for your entity");
        return;
    }

    // Checks user hasn't given the entry the name of an existing entry
    for (let x = 0; x < entities_of_interest.length; x++) {
        let entities_of_interest_name = entities_of_interest[x].name.toString();
        if (entityName == entities_of_interest_name) {
            if (entityName == entities_of_interest[widgetItemNumberForEdit].name) {
                Message("Keeping the same entity name");
            } else {
                Window.Message("", "You can not have entities with the same name");
                return;
            }
        }
    }

    // Updates the entites_of_interest array with the updated information
    entities_of_interest[widgetItemNumberForEdit].name = gui.WindowEdit.txtName.text;
    let parts = gui.WindowEdit.txtID.text.split(",");
    entities_of_interest[widgetItemNumberForEdit].parts = parts;

    // Updates the WidgetItem hover value with the new entityID
    widgetItems[widgetItemNumberForEdit].hover = parts;

    // If part set is selected then finds the parts belonging to each set to add to the entities_of_interest array
    if (entities_of_interest[widgetItemNumberForEdit].type.toString() == "Part Set") {
        entities_of_interest[widgetItemNumberForEdit].partsOfEntity = find_parts_in_set();
    }

    // Updates the WidgetItem text
    let newString =
        entities_of_interest[widgetItemNumberForEdit].name +
        " | " +
        entities_of_interest[widgetItemNumberForEdit].type +
        " | " +
        entities_of_interest[widgetItemNumberForEdit].parts;
    widgetItems[widgetItemNumberForEdit].text = newString;
    gui.WindowEdit.Hide();
}

/**
 * Closes WindowEdit window
 */
function close_edit() {
    gui.WindowEdit.Hide();
}

/**
 * Saves data to .JSON file
 */
function save_to_file1() {
    /* Get data to write to file */

    var data = { entities_of_interest: [] };

    for (var i = 0; i < entities_of_interest.length; i++) {
        data["entities_of_interest"].push({
            name: entities_of_interest[i].name,
            parts: entities_of_interest[i].parts,
            type: entities_of_interest[i].type,
            partsOfEntity: entities_of_interest[i].partsOfEntity
        });
    }

    /* Ask the user where to write it */
    var filename = Window.GetFile(".json", true);

    if (filename == null) return;

    /* API call to write workflow file */

    Workflow.WriteToFile(data, filename, workflow_filename);

    Message("Written workflow file to " + filename);
}

/**
 * Saves data to the model
 */
function save_to_model() {
    /* Get data to write to file */

    var data = { entities_of_interest: [] };

    for (var i = 0; i < entities_of_interest.length; i++) {
        data["entities_of_interest"].push({
            // do we need the entites_of_interest thing? Intrusion contour plot doesn't have anything named...
            name: entities_of_interest[i].name,
            parts: entities_of_interest[i].parts,
            type: entities_of_interest[i].type,
            partsOfEntity: entities_of_interest[i].partsOfEntity
        });
    }

    let model = Model.Select("Select the model to write to");

    /* API call to write workflow to a model */

    Workflow.WriteToModel(data, model, workflow_filename);

    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.");
}

/**
 * Opens up the clickhelp manual when the help button is pressed
 */
function workflow_manual() {
    OpenManual("primer", "entities-of-interest.html");
}