User Tools

Site Tools


transsecs_host:startup_trigger_host_recipesend_ui

This is an old revision of the document!


Startup Trigger (Host + RecipeSend UI)

What is this script?

This is a custom StartupTrigger for TransSECS Host projects. It performs two key tasks:

  • Waits for the TransSECS Host controller to become available.
  • Sets up a Connection Status Listener that, once communications are established, prompts the user to choose a PPID + file and sends it to the tool using S7F3 (RecipeSend).

The script includes a Swing-based popup window for PPID and file selection.

Key Features

  • Swing UI: Lets the user enter a PPID and select a file to send as a recipe.
  • Background host discovery: Uses a separate thread to wait until the host is ready.
    1. In deployment: host is usually ready immediately.
    2. In SIM mode: the thread waits until the user toggles SIM → LIVE.
  • Safe listener setup: Adds a short delay before registering the listener, preventing race conditions in deployment.
  • Null-safe message bean lookup: Ensures `RecipeSend` bean is available before sending.
  • Robust error handling: Uses `RuntimeException` instead of Nashorn `Error` to avoid Java `ClassCastException`.

How It Works

At startup: a background thread begins waiting for TransSecsController.getSingleController() to return non-null.

If in SIM mode: the loop continues until the user toggles to LIVE.

If in deployment: host is found almost immediately.

After discovery: waits 1 second for project initialization, then registers the Connection Status Listener.

On connection: when the host reports connection status 0 (operational), the script:

Pops up the Swing UI.

Collects PPID and file bytes.

Sends an S7F3 RecipeSend message with the user’s inputs.

Benefits

Works seamlessly in both SIM and deployment modes.

No CPU churn (500 ms polling).

No premature null errors on deployment.

User-friendly: clear logs, safe cancel behavior, and popup UI for recipe send.

Full Code

// === Imports ===
var TransSecsController      = Java.type("com.ergotech.transsecs.secs.TransSecsController");
var SecsFormat10             = Java.type("com.ergotech.secs.SecsFormat10");
var ConnectionStatusListener = Java.type("com.ergotech.transsecs.secs.host.ConnectionStatusListener"); 
var DataSource               = Java.type("com.ergotech.vib.servers.DataSource");
 
var UIManager          = Java.type("javax.swing.UIManager");
var SwingUtilities     = Java.type("javax.swing.SwingUtilities");
var JFrame             = Java.type("javax.swing.JFrame");
var WindowConstants    = Java.type("javax.swing.WindowConstants");
var WindowAdapter      = Java.type("java.awt.event.WindowAdapter");
var GridBagLayout      = Java.type("java.awt.GridBagLayout");
var GridBagConstraints = Java.type("java.awt.GridBagConstraints");
var Insets             = Java.type("java.awt.Insets");
var FlowLayout         = Java.type("java.awt.FlowLayout");
var JButton            = Java.type("javax.swing.JButton");
var JLabel             = Java.type("javax.swing.JLabel");
var JTextField         = Java.type("javax.swing.JTextField");
var JOptionPane        = Java.type("javax.swing.JOptionPane");
var JFileChooser       = Java.type("javax.swing.JFileChooser");
 
var Files             = Java.type("java.nio.file.Files");
var CompletableFuture = Java.type("java.util.concurrent.CompletableFuture");
var HashMap           = Java.type("java.util.HashMap");
var Thread            = Java.type("java.lang.Thread");
var RuntimeException  = Java.type("java.lang.RuntimeException");
 
// We'll store our singleton UI+state under this key.
var KEY = "TransSECS.ScriptParamWindow";
 
// --- Singleton state accessor ---
function getState() {
    var state = UIManager.get(KEY);
    if (state != null) return state;
 
    state = new HashMap(); // { frame, ppidField, fileField, fileBytes, pending }
 
    var frame = new JFrame("TransSECS – PPID + File");
    frame.setLayout(new GridBagLayout());
    frame.setSize(560, 180);
    frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
 
    var ppidField = new JTextField();
    var fileField = new JTextField();
    fileField.setEditable(false);
 
    var chooseBtn = new JButton("Choose File…");
    var okBtn     = new JButton("OK");
    var cancelBtn = new JButton("Cancel");
 
    var c = new GridBagConstraints();
    c.insets = new Insets(6,6,6,6);
    c.fill   = GridBagConstraints.HORIZONTAL;
 
    c.weightx = 0; c.gridx = 0; c.gridy = 0; frame.add(new JLabel("PPID:"), c);
    c.weightx = 1; c.gridx = 1; c.gridy = 0; frame.add(ppidField, c);
 
    c.weightx = 0; c.gridx = 0; c.gridy = 1; frame.add(new JLabel("File:"), c);
    c.weightx = 1; c.gridx = 1; c.gridy = 1; frame.add(fileField, c);
    c.weightx = 0; c.gridx = 2; c.gridy = 1; frame.add(chooseBtn, c);
 
    var JPanel = Java.type("javax.swing.JPanel");
    var buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    buttonPanel.add(cancelBtn); buttonPanel.add(okBtn);
    c.gridwidth = 3; c.gridx = 0; c.gridy = 2; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.EAST;
    frame.add(buttonPanel, c);
 
    // --- Handlers ---
    chooseBtn.addActionListener(function(e) {
        var fc = new JFileChooser();
        if (fc.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) {
            try {
                var bytes = Files.readAllBytes(fc.getSelectedFile().toPath());
                state.put("fileBytes", bytes);
                fileField.setText(fc.getSelectedFile().getAbsolutePath());
            } catch (ex) {
                JOptionPane.showMessageDialog(frame, "Failed to read file: " + ex.getMessage(),
                                              "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
    });
 
    okBtn.addActionListener(function(e) {
        var pending = state.get("pending");
        if (pending != null && !pending.isDone()) {
            var ppid = String(ppidField.getText() || "");
            var body = state.get("fileBytes");
            if (ppid.isEmpty()) {
                JOptionPane.showMessageDialog(frame, "Please enter a PPID.", "Missing PPID",
                                              JOptionPane.WARNING_MESSAGE);
                return;
            }
            if (body == null || body.length == 0) {
                JOptionPane.showMessageDialog(frame, "Please choose a file.", "Missing File",
                                              JOptionPane.WARNING_MESSAGE);
                return;
            }
            var selection = new HashMap();
            selection.put("ppid", ppid);
            selection.put("body", body);
            pending.complete(selection);
        }
    });
 
    function hideAndCancel(reason) {
        try { frame.setVisible(false); } catch (ignore) {}
        var pending = state.get("pending");
        if (pending != null && !pending.isDone()) {
            pending.completeExceptionally(new RuntimeException(reason || "Cancelled"));
        }
    }
 
    cancelBtn.addActionListener(function(e) { hideAndCancel("Cancelled by user"); });
    frame.addWindowListener(new WindowAdapter() {
        windowClosing: function(evt) { hideAndCancel("Window closed"); }
    });
 
    frame.setLocationByPlatform(true);
 
    state.put("frame", frame);
    state.put("ppidField", ppidField);
    state.put("fileField", fileField);
    UIManager.put(KEY, state);
    return state;
}
 
// Show non-modal popup and return a CompletableFuture that completes on OK/Cancel
function requestParams() {
    var state  = getState();
    var frame  = state.get("frame");
    var future = new CompletableFuture();
    state.put("pending", future);
 
    SwingUtilities.invokeLater(function () {
        if (!frame.isVisible()) frame.setVisible(true);
        frame.toFront();
        frame.requestFocus();
    });
 
    return future;
}
 
// --- Background host discovery ---
var r = new java.lang.Runnable() {
    run: function() {
        var host = null;
        var count = 0;
 
        // Wait until host is ready (deployment: quick; SIM: wait for toggle)
        while (host == null) {
            Thread.sleep(500);
            count++;
            host = TransSecsController.getSingleController();
 
            if (host == null && count % 10 == 0) {
                DataSource.logMessage("Startup Trigger: Still waiting for host (toggle SIM→LIVE if needed)");
            }
        }
 
        DataSource.logMessage("Startup Trigger: Host model " + host + " is ready after " + count + " checks.");
 
        // Delay: give project time to finish initialization
        Thread.sleep(1000);
 
        // Connection listener
        host.addConnectionStatusListener(new ConnectionStatusListener() {
            connectionStatusChanged: function(connectionStatus, comment) {
                DataSource.logMessage("ConnectionStatusListener: " + comment + " (" + connectionStatus + ")");
                if (connectionStatus == 0) {
                    try {
                        var h   = TransSecsController.getSingleController();
                        var msg = h.getMessageBean("RecipeSend"); // or "S7F5"
 
                        if (msg == null) {
                            DataSource.logMessage("ConnectionStatusListener: RecipeSend message bean not ready yet, skipping.");
                            return;
                        }
 
                        var selection = requestParams().get();
                        msg.setPPID(selection.get("ppid"));
                        msg.setPPBodyFormat(new SecsFormat10(selection.get("body")));
                        msg.sendMessage();
 
                    } catch (e) {
                        print("Error in S7F3 handler script:\n" + e);
                    }
                }
            }
        });
 
        DataSource.logMessage("Startup Trigger: Connection Status Listener added.");
    }
};
 
// Start in a background thread (avoids blocking project load)
new Thread(r).start();
transsecs_host/startup_trigger_host_recipesend_ui.1755622628.txt.gz · Last modified: by amyw

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki