This is a custom StartupTrigger for TransSECS Host projects. It performs two key tasks:
The script includes a Swing-based popup window for entering a PPID and choosing a file.
At startup, a background thread is launched:
Once the host is found:
When the host reports status = 0 (Connected):
If the host never becomes available:
// === 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; } // --- Startup Trigger Main Loop --- var r = new java.lang.Runnable() { //Defines a background thread that will execute the startup logic. run: function() { var host = null; var count = 0; while (host == null) { //Polls the system every 50 ms to see if the TransSecsController (the Host object) has been created. Thread.sleep(50); count++; // count and timeout check. Increments a counter each loop. After 100 iterations (≈ 5 seconds), stops waiting and logs a message that no host was found. host = TransSecsController.getSingleController(); if (host == null && count % 10 == 0) { //Every 10 loops (≈ 0.5 seconds), logs "Still waiting for host." for debugging/monitoring purposes. DataSource.logMessage("Startup Trigger: Still waiting for host."); } if (count >= 100) { DataSource.logMessage("Startup Trigger: Host did not connect after ~5 seconds. Stopping wait."); break; } } if (host != null) { //Once the host is found, it logs success and waits another 1 second to ensure the host is fully initialized. DataSource.logMessage("Startup Trigger: Host model is ready."); Thread.sleep(1000); host.addConnectionStatusListener(new ConnectionStatusListener() { //Subscribes to connection status changes. When the status changes to 0 (connected), it triggers the UI builder (buildUI(host)) in the Swing UI thread. connectionStatusChanged: function(connectionStatus, comment) { if (connectionStatus == 0) { // Connected SwingUtilities.invokeLater(function() { //Ensures that the UI is built safely in the event dispatch thread, preventing concurrency issues. buildUI(host); }); } } }); DataSource.logMessage("Startup Trigger: Connection Status Listener added."); } else { DataSource.logMessage("Startup Trigger: No host available after timeout. UI will not be shown."); } } }; new Thread(r).start(); //Starts the thread to run this logic asynchronously so it does not block other operations.