Experte

Scripting API

Runtime-Scripting für Tabellen, Scenegraph-Motion, Roboter und UI-Overlays

Übersicht

confBuild-Skripte laufen direkt im aktiven Editor. Sie können Tabellendaten lesen und schreiben, THREE.js-Objekte direkt manipulieren, wiederverwendbare Szenenanimationen anlegen, URDF-Roboter steuern und eigene Oberflächen über dem Editor einblenden.

confBuild Scripting Übersicht

Tabellenlogik

Verwenden Sie ActiveWorkbook, um Parameter zu lesen, Formeln zu schreiben, über Tabellen zu iterieren und gezielte Modell-Rebuilds auszulösen.

Direkte Scenegraph-Steuerung

Mit API.getSceneObject(), API.updateSceneObject() und API.renderScene() ändern Sie die Szene live, ohne das Spreadsheet-Modell neu zu berechnen.

Animationen und Motion

Erstellen Sie benannte Animationen, scrubben Sie deren Fortschritt oder treiben Sie Maschinen und URDF-Roboter direkt aus JavaScript an.

Eigene UI und Debugging

Mounten Sie HTML-, jQuery-UI- oder React-Panels im Editor und lesen Sie Logs sowie Laufzeitfehler direkt aus der Script-API aus.

Schnellstart

Die Runtime ist um drei zentrale Einstiegspunkte organisiert.

Namespace Wofür er verwendet wird
ActiveWorkbook Tabellen lesen, schreiben, Bereiche abfragen und Hilfsfunktionen für Sheets
API UI-Updates, Scenegraph-Helfer, Animationen, Roboter, Script-UI und Debugging
scene Szenenbezogene Helfer wie das Erstellen und Aktualisieren von Connectors

Refresh-Regeln

  • Rufen Sie await API.updateUI() auf, nachdem Sie Tabellenzellen mit ActiveWorkbook geändert haben.
  • Rufen Sie API.renderScene() auf, wenn Sie nur den Live-Scenegraph angepasst haben und keinen Spreadsheet-Rebuild ausloesen.
  • Zelladressen sind 0-basiert: { sheet: 0, row: 0, col: 0 } ist die erste Zelle.
  • Verwenden Sie für Strings am besten doppelte Anführungszeichen.

Minimales Beispiel

async function projectLoaded(project) {
  const width = await ActiveWorkbook.getCellSerialized({ sheet: 0, row: 0, col: 1 });
  console.log("Projekt geladen:", project?.name, "aktuelle Breite:", width);

  await ActiveWorkbook.setCellContents({ sheet: 0, row: 4, col: 1 }, "Bereit");
  await API.updateUI();
}

Spreadsheet-Operationen

Verwenden Sie ActiveWorkbook für jede zellbasierte Projektlogik.

Spreadsheet Scripting Beispiel
Funktion Beschreibung
ActiveWorkbook.getCellSerialized(address) Aktuellen serialisierten Zellwert lesen
ActiveWorkbook.getCellFormula(address) Zellenformel lesen
ActiveWorkbook.setCellContents(address, value) Wert oder Formel in eine Zelle schreiben
ActiveWorkbook.getRangeValues(range) Einen Zellbereich auslesen
ActiveWorkbook.getRangeFormulas(range) Formeln eines Bereichs auslesen
ActiveWorkbook.getSheetDimensions(sheetId) Zeilen- und Spaltenanzahl eines Sheets zurückgeben
ActiveWorkbook.simpleCellAddressFromString("B5", 0) Eine Tabellenadresse in ein 0-basiertes Adressobjekt umwandeln
ActiveWorkbook.simpleCellAddressToString(address, 0) Ein Adressobjekt wieder in Tabellen-Notation umwandeln
API.getCell("B2") / API.setCell("B2", value) Eine A1-Zelle über einen script-freundlichen Wrapper lesen oder schreiben
API.getParam(name) / API.setParam(name, value) Eine benannte Parameterzeile lesen oder schreiben, wenn das Sheet Parameterlabels nutzt
API.batchWorkbookUpdates(callback) Mehrere Workbook-Schreibvorgänge sammeln und das Modell am Ende nur einmal aktualisieren

Beispiel: Lesen, verarbeiten, schreiben

async function processRows() {
  const dims = await ActiveWorkbook.getSheetDimensions(0);

  for (let row = 1; row < dims.rows; row++) {
    const input = await ActiveWorkbook.getCellSerialized({ sheet: 0, row, col: 0 });
    if (!input) {
      continue;
    }

    const result = Number(input) * 2;
    await ActiveWorkbook.setCellContents({ sheet: 0, row, col: 1 }, result);
  }

  await API.updateUI();
}

Actions und Cleanup

Skripte können Befehle direkt im Editor sichtbar machen und langlebige Ressourcen registrieren, die beim nächsten Script-Reload automatisch aufgeraeumt werden.

Funktion Beschreibung
API.registerActions(actions) Script-Befehle als Buttons im Script-Debug-Panel anzeigen
API.disposeOnReload(disposer, label) Eigene Cleanup-Logik für den nächsten Script-Reload registrieren
API.setInterval() / API.setTimeout() Timer erzeugen, die beim Script-Reload automatisch gelöscht werden
API.on(target, eventName, handler) Einen Event-Listener hinzufügen, der beim Script-Reload automatisch entfernt wird

Beispiel: Befehle registrieren

async function ONLOADED() {
  API.registerActions([
    { id: "openDoor", label: "Tür öffnen", run: openDoor },
    { id: "closeDoor", label: "Tür schließen", run: closeDoor }
  ]);
}

async function openDoor() {
  await API.setAnimationValue("door", 1);
}

async function closeDoor() {
  await API.setAnimationValue("door", 0);
}

Live-Scenegraph-Helfer

Verwenden Sie die Runtime-API, wenn Sie sofortige Transform-, Sichtbarkeits- oder Metadaten-Änderungen benoetigen, ohne Geometrie aus dem Spreadsheet neu zu erzeugen.

Funktion Beschreibung
API.getSelectedSceneObject() Das aktuell selektierte THREE.js-Objekt zurückgeben
API.getSceneObject(target) Ein Objekt über outputid, Szenenpfad, Namen oder Objektreferenz aufloesen
API.getSceneObjectState(target) Einen einfachen Snapshot von Position, Rotation, Scale, Sichtbarkeit und Parent-Info lesen
API.updateSceneObject(target, updates) Absolute oder relative Position, Rotation, Scale, Sichtbarkeit, Name oder userData ändern
API.renderScene(force?) Den Viewport nach direkten Scenegraph-Änderungen neu rendern

Unterstützte Felder für updateSceneObject()

Sie können position, translate, rotation, rotate, scale, scaleBy, visible, name, userData, angleUnit und render übergeben.

Beispiel: Selektiertes Teil verschieben

async function nudgeSelection() {
  const selected = API.getSelectedSceneObject();
  const target =
    selected?.userData?.outputid ||
    selected?.userData?.path ||
    selected?.name;

  if (!target) {
    console.warn("Kein stabiler Szenen-Target gefunden");
    return;
  }

  await API.updateSceneObject(target, {
    translate: { x: 25, z: 10 },
    rotate: { z: 15 },
    angleUnit: "degrees",
    userData: { lastAction: "nudgeSelection" }
  });

  API.renderScene();
}

Szenenanimationen

Szenenanimationen laufen direkt auf dem Live-THREE.js-Scenegraph. Sie eignen sich für Maschinenbewegungen, Öffnen-Schließen-Verhalten, Verfahrwege und wiederkehrende visuelle Zustände.

Funktion Beschreibung
API.animate(target, config) Eine Animation direkt aus Target und Konfiguration erzeugen
API.setAnimation(id, config) Eine benannte Animation erstellen oder ersetzen
API.playAnimation(id?, restart?) Eine Animation oder alle Animationen abspielen
API.pauseAnimation(id?) Eine Animation oder alle Animationen pausieren
API.stopAnimation(id?, restore?) Die Bewegung stoppen und optional die urspruengliche Transform-Basis wiederherstellen
API.setAnimationProgress(id, progress) Den Fortschritt zwischen 0 und 1 setzen
API.setAnimationValue(id, value) Direkt zu einem konkreten Wert zwischen from und to springen
API.getAnimation(id) Status, Richtung, Dauer und Fortschritt einer Animation lesen
API.listAnimations(id?) Alle Animationen oder eine passende Gruppe auflisten

Referenz für die Animations-Konfiguration

Verwenden Sie property als "position", "rotation" oder "scale". Die Bewegungsachse definieren Sie mit axis. Dazu kommen from, to, durationMs, optional delayMs, der Loop-Modus "once", "repeat" oder "pingpong" und Easing wie "linear", "easeOutQuad" oder "easeInOutCubic".

Tipp: Verwenden Sie einen stabilen Target-Identifier. Bevorzugen Sie outputid, dann Szenenpfad, dann Objektnamen.

Beispiel: Hub eines Pneumatikzylinders

async function ONLOADED() {
  const selected = API.getSelectedSceneObject();
  const target =
    selected?.userData?.outputid ||
    selected?.userData?.path ||
    selected?.name;

  if (!target) {
    console.warn("Kein Target für die Zylinder-Animation");
    return;
  }

  await API.setAnimation("pistonRod", {
    target,
    property: "position",
    axis: [0, 0, 1],
    from: 0,
    to: 120,
    durationMs: 900,
    loop: "pingpong",
    easing: "easeInOutCubic",
    autoplay: false,
    restoreOnStop: true
  });

  await API.setAnimationProgress("pistonRod", 0);
}

async function playCylinder() {
  await API.playAnimation("pistonRod", true);
}

async function setCylinderStroke(strokeMm) {
  await API.setAnimationValue("pistonRod", strokeMm);
}

URDF-Roboter

Die Scripting-API kann URDF-Roboter laden, deren Joints auslesen, sie über Jointwerte bewegen, Inverse Kinematik berechnen und Toolpaths direkt aus Projektskripten abfahren.

Funktion Beschreibung
API.addRobot(urdfUrl, name, options) Einen URDF-Roboter in die aktuelle Szene laden
API.listRobots() Manager-geladene und Sheet-erzeugte Roboter als Snapshots zurückgeben
API.getRobot(target) Roboterstatus über ID oder Namen lesen
API.setActiveRobot(target) Einen Roboter als aktiven Roboter für Folgeaktionen markieren
API.getRobotJoints(target) Joints mit Limits, Achse und aktuellem Wert auflisten
API.setRobotJointValue(target, jointName, value, angleUnit) Einen Joint sofort setzen
API.setRobotJointValues(target, values, angleUnit) Mehrere Joints gleichzeitig setzen
API.animateRobotJointValues(target, values, durationMs, angleUnit, steps) Joints über die Zeit weich animieren
API.updateRobot(target, updates) Basisposition, Rotation, Scale oder Sichtbarkeit des Roboters ändern
API.getRobotEndEffectorPosition(target) Die aktuelle Endeffektor-Position lesen
API.solveRobotIK(target, position) Inverse Kinematik für eine Ziel-TCP-Position loesen
API.followRobotPath(target, path) Ein Array von TCP-Wegpunkten mit optionaler Zeit pro Punkt abfahren
API.homeRobot(target) Die Robot-Joints in die Home-Pose zurücksetzen
API.stopRobotMotion() Den aktuellen Roboter-Motion-Run stoppen
API.removeRobot(target) Einen einzelnen Roboter aus der Szene entfernen
API.clearRobots() Alle verwalteten Roboter entfernen

Toolhead- und TCP-Positionierung

API.solveRobotIK() loest aktuell nur auf die Endeffektor-Position, nicht auf ein vollständiges Orientierungsziel. Wenn die Werkzeugausrichtung wichtig ist, kombinieren Sie TCP-Zielpunkte mit expliziten Wrist-Joint-Werten oder einer anschließenden Joint-Korrektur.

Für Prozessbewegungen sollten Sie immer approach, work und retract definieren, statt direkt auf den Arbeitspunkt zu springen.

Beispiel: Einen TCP-Pfad abfahren

async function runRobotPath() {
  const robots = API.listRobots();
  const robot = robots[0];

  if (!robot) {
    console.warn("Kein URDF-Roboter im Projekt geladen");
    return;
  }

  const joints = API.getRobotJoints(robot.id);
  console.log("Verfügbare Joints:", joints.map((joint) => joint.name));

  if (joints.length > 0) {
    await API.animateRobotJointValues(robot.id, {
      [joints[0].name]: 20
    }, 500, "degrees", 24);
  }

  const tcpApproach = { x: 650, y: 0, z: 980, time: 300 };
  const tcpWork = { x: 650, y: 0, z: 900, time: 400 };
  const tcpRetract = { x: 650, y: 0, z: 1020, time: 300 };

  await API.followRobotPath(robot.id, [tcpApproach, tcpWork, tcpRetract]);

  const ee = API.getRobotEndEffectorPosition(robot.id);
  console.log("Finale TCP-Position:", ee);
}

Eigene Script-UI

Skripte können leichte Interface-Layer direkt über dem Editor einblenden. Das eignet sich für Status-Panels, eigene Tools, Dialoge und Debug-Helfer.

Funktion Beschreibung
API.requireLibrary(name) Unterstützte Runtime-Bibliotheken wie "jquery", "jquery-ui", "react" oder "babel" laden
API.loadExternalScript(url, globalName?) Ein externes Script laden und optional auf einen globalen Export warten
API.loadExternalStyle(url, id?) Ein externes Stylesheet einbinden
API.createScriptMountPoint(mountId, options) Ein absolutes oder fix positioniertes Mount-Element erzeugen
API.removeScriptMountPoint(mountId) Einen bestimmten Script-UI-Mount entfernen
API.clearScriptUi() Alle vom aktuellen Skript erzeugten Mount-Points entfernen
API.createReactRoot(mountId, options) Nach automatischem Laden von React einen React-Root erzeugen
API.renderReactComponent(mountId, elementFactory, options) Ein React-Element oder eine Component-Factory in ein Mount rendern
API.renderReactComponentFromSource(mountId, source, options, componentName) React-Quellcode mit Babel kompilieren und rendern
API.openJQueryDialog(mountId, html, dialogOptions, mountOptions) Einen jQuery-UI-Dialog aus Script-Code erzeugen

Beispiel: jQuery-UI-Statusdialog öffnen

async function showScriptStatus() {
  await API.openJQueryDialog(
    "script-status",
    "<p>Robot setup complete.</p><p>Ready for path execution.</p>",
    { title: "Script Status", width: 360 },
    { target: "body", top: 24, right: 24 }
  );
}

Beispiel: Kleines React-Panel rendern

async function showReactPanel() {
  await API.renderReactComponent(
    "react-status",
    (React) => React.createElement(
      "div",
      {
        style: {
          padding: "14px",
          borderRadius: "12px",
          background: "#111827",
          color: "#ffffff",
          boxShadow: "0 10px 30px rgba(0,0,0,0.25)"
        }
      },
      "Scene animation bereit"
    ),
    { target: "body", right: 24, bottom: 24, width: 260 }
  );
}

Events und Lifecycle-Hooks

Skripte können auf Editorzustand, Auswahlwechsel und direkte Objektinteraktionen reagieren.

Projekt- und Auswahl-Events

Event Beschreibung
projectLoaded(project) Wird aufgerufen, wenn ein Projekt fertig geladen ist
projectActivated(project) Wird aufgerufen, wenn ein Projekt aktiv wird
ONLOADED() Wird aufgerufen, wenn DOM und Script-Umgebung bereit sind
projectSelected(project) Wird aufgerufen, wenn ein Projekt oder Subprojekt selektiert wird
projectDeselected(project) Wird aufgerufen, wenn ein Projekt oder Subprojekt deselektiert wird
sheetSelected(sheet) Wird aufgerufen, wenn ein Sheet selektiert wird
sheetDeselected(sheet) Wird aufgerufen, wenn ein Sheet deselektiert wird
connectorSelected(connectorData) Wird aufgerufen, wenn ein Connector in der Szene selektiert wird

Maus-Events auf 3D-Objekten

Event Beschreibung
ONENTER(object) Hover-Enter-Helfer, der in vielen Projektskripten genutzt wird
ONLEAVE(object) Hover-Leave-Helfer, der in vielen Projektskripten genutzt wird
MouseClick(object) Wird beim Klick auf ein Objekt aufgerufen
MouseEnter(object) Wird beim Eintritt des Cursors in ein Objekt aufgerufen
MouseLeave(object) Wird beim Verlassen des Objekts durch den Cursor aufgerufen
MouseMove(object) Wird während der Bewegung über einem Objekt aufgerufen
MouseDown(object) Wird beim Druecken der Maustaste auf einem Objekt aufgerufen
MouseUp(object) Wird beim Loslassen der Maustaste auf einem Objekt aufgerufen

Beispiel: Hervorheben bei Hover

const originalColors = new Map();

function MouseEnter(object) {
  if (!originalColors.has(object.uuid) && object.material?.color) {
    originalColors.set(object.uuid, object.material.color.getHex());
  }

  object.material?.color?.set?.("#ff6600");
  object.material?.emissive?.set?.("#221100");
  API.renderScene();
}

function MouseLeave(object) {
  const originalColor = originalColors.get(object.uuid);
  if (originalColor && object.material?.color) {
    object.material.color.setHex(originalColor);
  }

  object.material?.emissive?.set?.("#000000");
  API.renderScene();
}

Debugging

Die Runtime führt eine Script-spezifische Debug-Historie und speichert den letzten Laufzeitfehler, damit Sie Fehler nicht im Blindflug suchen müssen.

Funktion Beschreibung
console.log(), console.warn(), console.error() Beschriftete Meldungen in die Script-Konsole schreiben
API.getScriptDebugHistory() Aktuelle strukturierte Debug-Eintraege zurückgeben
API.getLastScriptError() Den letzten Laufzeitfehler mit Position und Codeausschnitt zurückgeben
API.clearScriptDebugHistory() Debug-Historie und Error-State zurücksetzen

Beispiel: Den letzten Fehler prüfen

function inspectScriptState() {
  console.log("Debug history:", API.getScriptDebugHistory());
  console.log("Last script error:", API.getLastScriptError());
}

Best Practices

Diese Muster helfen dabei, Skripte vorhersehbar, schnell und gut debugbar zu halten.

  • Verwenden Sie ActiveWorkbook nur für Daten- und Geometrieaenderungen, die wirklich einen Modell-Rebuild benoetigen.
  • Nutzen Sie API.batchWorkbookUpdates(), wenn ein Skript mehrere Zellen auf einmal aendert.
  • Nutzen Sie API.registerActions() für Befehle, die Anwender aus dem Script-Debug-Panel starten sollen.
  • Nutzen Sie API.on(), API.setInterval() oder API.disposeOnReload() für Listener und Timer, damit Reloads sie sauber entfernen.
  • Nutzen Sie API.updateSceneObject() und API.setAnimation() für fluessige Runtime-Motion, statt pro Frame Tabellenzellen zu schreiben.
  • Bevorzugen Sie stabile Szenen-Targets: zuerst outputid, dann Szenenpfad, dann Objektname.
  • Ermitteln Sie echte Roboternamen und Joint-Namen mit API.listRobots() und API.getRobotJoints(), bevor Sie Roboterbefehle ausgeben.
  • Planen Sie für Roboter-Prozessbewegungen immer Toolhead-Punkte als approach, work und retract.
  • Lassen Sie in fruehen Script-Versionen bewusst console.log()-Ausgaben stehen. Das spart später viel Debug-Zeit.
  • Wenn Sie eine screenshot-basierte Einführung in den Editor wollen, lesen Sie das Scripts Editor Workflow-Tutorial.

Mit Scripting starten

Setzen Sie Ihr Wissen in die Praxis um