2. Automate PRIMER Startup and Model Load w

Why a wrapper instead of a direct command line

Oasys PRIMER is a GUI-driven pre-processor. It accepts a model file as a command-line argument, but it does not provide a native "load, do nothing, exit" switch. If you simply call primer.exe model.key from a scheduler or CI job, the process stays alive waiting for a user to close the window, and the job hangs until a timeout kills it. A batch wrapper solves this by launching PRIMER with the model, waiting for the model to finish loading, then issuing a clean shutdown through PRIMER's own scripting interface rather than terminating the process.

The goal of this case is narrow and deliberately so: prove that an automation harness can start PRIMER, load a named model, and terminate with a predictable exit code — with zero mouse clicks. Everything in later cases (free-edge extraction, duplicate detection, report export) depends on this harness being reliable, so it is worth building carefully and verifying explicitly.

Choosing the automation channel

PRIMER exposes two automation surfaces that matter here:

  • Command-line model argument — passing the model path on the process command line. This is the most stable way to get a specific file loaded at startup, and it avoids depending on any "recent files" or preference state.
  • PRIMER's scripting interface — a script file executed inside the running session, which can query model state and request application exit. This is what lets the wrapper close PRIMER deterministically instead of killing it.

The wrapper's job is to sequence these two surfaces and to gate on a real readiness signal, not a fixed sleep. A fixed sleep is the most common cause of flaky harnesses: too short and the script runs before the model is loaded; too long and every job pays the penalty.

Treat "model loaded" as a state you can observe, not a duration you guess. The wrapper should poll a scriptable readiness condition (for example, the model's node count being non-zero, or the expected model title being present) and only then proceed to shutdown.

Wrapper structure

The reference implementation below is a Python wrapper. It is intentionally small: launch, wait for readiness, request exit, verify exit code. It assumes PRIMER is installed and that a script-capable interface is available; the exact executable name and script-invocation mechanism should be confirmed against your installed PRIMER version before first use.

import subprocess, time, sys, pathlib

PRIMER_EXE = r"C:\Program Files\Oasys\PRIMER\primer.exe"   # confirm on your install
MODEL      = pathlib.Path(r"D:\models\frame_baseline.key")
READY_SCRIPT = pathlib.Path(r"D:\automation\primer_ready_and_exit.py")
TIMEOUT_S  = 180
POLL_S     = 2

def run_wrapper():
    if not MODEL.exists():
        print(f"FAIL: model not found: {MODEL}")
        return 2
    if not READY_SCRIPT.exists():
        print(f"FAIL: control script not found: {READY_SCRIPT}")
        return 2

    cmd = [PRIMER_EXE, str(MODEL), "-script", str(READY_SCRIPT)]
    print("LAUNCH:", " ".join(cmd))
    proc = subprocess.Popen(cmd)

    deadline = time.time() + TIMEOUT_S
    while time.time() < deadline:
        rc = proc.poll()
        if rc is not None:
            # PRIMER exited on its own (script requested exit, or crash)
            print(f"PRIMER exited with code {rc}")
            return 0 if rc == 0 else 1
        time.sleep(POLL_S)

    print("FAIL: timeout waiting for PRIMER to exit")
    proc.terminate()
    return 3

if __name__ == "__main__":
    sys.exit(run_wrapper())

Two design points are worth calling out. First, the wrapper does not call proc.kill() on the happy path — it lets the in-session script request a normal application exit, so PRIMER can flush any state it needs to. Second, the timeout is a safety net, not the primary control: if the timeout fires, that is a genuine failure to investigate, not a normal outcome.

What the in-session control script must do

The control script runs inside PRIMER after the model argument has been processed. Its responsibilities are: confirm the model is actually loaded, record a small readiness marker (so the wrapper or a log can prove it), and request exit. A minimal, version-tolerant pattern is:

# primer_ready_and_exit.py  (runs inside PRIMER's scripting context)
# 1. Confirm a model is present and has content.
model = GetCurrentModel()          # confirm exact API name for your version
if model is None:
    LogError("No model loaded at script start")
    RequestExit(1)
else:
    node_count = model.GetNodeCount()
    LogInfo(f"Model loaded: {model.GetTitle()}  nodes={node_count}")
    if node_count == 0:
        LogError("Model loaded but contains zero nodes")
        RequestExit(1)
    else:
        RequestExit(0)

The exact function names (GetCurrentModel, GetNodeCount, RequestExit) are placeholders for the scripting API your PRIMER build exposes; substitute the real identifiers from the installed scripting reference. The structure — check model, check content, log, exit with a code — is what matters and is stable across versions.

Readiness and exit-code contract

The wrapper and the control script together define a small contract. Making it explicit is what turns a one-off script into a harness you can trust in a pipeline.

Exit codeMeaningTypical cause
0Model loaded, non-empty, clean exitNormal success
1PRIMER ran but reported a problemZero-node model, load error, script error
2Wrapper precondition failedModel or control script path missing
3Timeout — PRIMER did not exitScript not invoked, modal dialog blocking exit
A modal dialog (missing license, "file changed on disk", unsaved-changes prompt) will block RequestExit and surface as exit code 3. If you see repeated timeouts, check for dialogs before assuming the script is wrong.

Verifying the harness without manual interaction

Run the wrapper from a plain terminal with no PRIMER window already open. A successful run should show the launch line, a PRIMER window appearing and closing on its own, and a final PRIMER exited with code 0. Confirm the log contains the model title and a non-zero node count — that is your proof the model actually loaded rather than PRIMER starting empty and exiting immediately.

To prove the harness is genuinely driving PRIMER and not just returning success, run two negative checks: point MODEL at a non-existent path (expect exit 2), and point it at a deliberately empty or corrupt model (expect exit 1 with a logged error). If both negatives behave as specified, the positive path is trustworthy.

Wrapper harness: launch → readiness gate → clean exit Python wrapper check MODEL + script exist Popen(primer.exe MODEL -script) poll every 2 s, timeout 180 s map exit code → result launch PRIMER session loads MODEL from argv runs control script GetCurrentModel / node count RequestExit(code) state model loaded nodes > 0 ? yes RequestExit(0) wrapper returns 0 no exit 1 load/script error no exit within 180 s timeout → exit 3 check for modal dialog path missing exit 2 precondition failed Verification checks Positive: log shows model title + non-zero node count, wrapper returns 0. Negative A: bad MODEL path → 2. Negative B: empty/corrupt model → 1. Repeated 3 → inspect for blocking dialog.

Common failure symptoms and what they point to

  • PRIMER window opens but never closes, wrapper hits timeout (3). The control script was not invoked — check the -script argument spelling against your build, or a modal dialog is blocking RequestExit.
  • Wrapper returns 0 but the log has no model title. PRIMER started empty and the script exited before load completed; the readiness check is being skipped or the model argument is being ignored.
  • Exit 1 with "zero nodes". The model path resolved but the file is empty or failed to parse; verify the file opens interactively before blaming the harness.
  • Intermittent exit 3 on a fast machine. Usually a race between the script starting and the model finishing load — move the readiness poll into the script itself rather than relying on the wrapper's fixed poll interval.

Once this wrapper returns 0 on a known-good model and returns 1 and 2 on the two negative checks, the harness is proven. Later cases can then attach real work — free-edge extraction, duplicate detection, report export — to the same launch/readiness/exit skeleton without re-solving the startup problem.