FERS aus JavaScript verwenden

Der FERS-Solver ist als WebAssembly-Build derselben Rust-Rechenmaschine veröffentlicht, die auch die Web-App antreibt. Er läuft im Browser oder in Node, rechnet ohne Server-Roundtrip und benötigt im kostenlosen Tarif keinen API-Schlüssel.

Installation

Aus derselben Rechenmaschine werden zwei Pakete mit identischer Schnittstelle veröffentlicht — nur Installation und Bundler-Einrichtung unterscheiden sich. Nehmen Sie -web für alles, was durch einen Bundler läuft, und das Paket ohne Suffix für Node auf dem Server.

# Browser apps (Vite, Next.js, webpack):
npm install @ferscloud/fers-calculation-web

# Node.js / server-side:
npm install @ferscloud/fers-calculation

Beide Pakete tragen die Version der Rechenmaschine und werden gemeinsam aktualisiert. Binden Sie sie an dieselbe exakte Version statt an einen Caret-Bereich.

Bundler einrichten

@ferscloud/fers-calculation-web ist ein WASM-ES-Modul, das sich über ein Top-Level-await initialisiert. Die meisten Bundler brauchen dafür eine einmalige Konfigurationsänderung; das Node-Paket braucht keine.

Vite

Das Plugin hinzufügen und anschließend ein Build-Target setzen, das Top-Level-await unterstützt.

npm i -D vite-plugin-wasm

Vite-Konfiguration

// vite.config.ts
import wasm from "vite-plugin-wasm";

// The package initialises with a top-level await, so the build target
// has to be one that supports it. esnext needs no second plugin.
export default {
  plugins: [wasm()],
  build: { target: "esnext" },
};

Next.js und webpack

Das Paket transpilieren und asynchrones WebAssembly aktivieren. asyncFunction ist nötig, weil sich das Modul mit einem Top-Level-await initialisiert.

// next.config.js
module.exports = {
  transpilePackages: ["@ferscloud/fers-calculation-web"],
  webpack(config) {
    config.experiments = { ...config.experiments, asyncWebAssembly: true };
    config.output.environment = {
      ...config.output.environment,
      asyncFunction: true,
    };
    return config;
  },
};

Der Solver ist synchron und rechenlastig. Führen Sie ihn bei größeren Modellen in einem Web Worker aus, damit der UI-Thread frei bleibt.

Ein Modell rechnen

Kein init()-Aufruf und kein API-Schlüssel. Der kostenlose Tarif deckt jedes Modell bis 100 Stäbe ab.

import { calculate_from_json } from "@ferscloud/fers-calculation";

// No init() call needed — the nodejs and bundler builds initialise the
// WASM module automatically on import.
const res = JSON.parse(calculate_from_json(JSON.stringify(myModel)));

if (res.ok) {
  const data = res.result; // displacements, member_results, unity_checks, …
} else {
  console.error(res.error.code, res.error.message);
}

Das Ergebnis-Envelope

Jeder Solver-Aufruf liefert ein JSON-Envelope, Sie parsen also einmal und verzweigen über ok — nie müssen Sie prüfen, ob die zurückgegebene Zeichenkette wie ein Fehler aussieht. Der Rückgabewert ist immer gültiges JSON.

// success
{ "ok": true,  "result": { /* the full result document */ } }

// failure (invalid model, over the member limit, malformed JSON, …)
{ "ok": false, "error": { "code": "LimitExceeded",
                          "message": "Number of members (250) exceeds allowed maximum of 100" } }
error.codeBedeutung
InvalidJsonDie Modellzeichenkette war kein gültiges JSON.
LimitExceededMehr Stäbe als der aktive Tarif erlaubt (100 kostenlos, 10,000 Pro).
SolveErrorDas Modell wurde gelesen, ließ sich aber nicht lösen — meist eine singuläre Steifigkeitsmatrix wegen fehlender oder unzureichender Auflager.
InternalPanicEin Fehler in der Rechenmaschine. Bitte melden Sie ihn.
InternalSerializationDie Ergebnisse ließen sich nicht serialisieren. Ebenfalls ein meldenswerter Fehler.

TypeScript-Typen

Das Browser-Paket liefert generierte Modelltypen neben den Funktionssignaturen mit. Sie werden aus dem OpenAPI-Schema der Rechenmaschine erzeugt und folgen damit der veröffentlichten Version, statt von Hand gepflegt zu werden.

import type {
  FERS,
  ResultsBundle,
} from "@ferscloud/fers-calculation-web/fers-models";

// FERS         — the input model
// ResultsBundle — the `result` payload of a successful envelope
// Both are generated from the engine's OpenAPI schema, so they track
// the published version.

FERS im kostenlosen Tarif nennen

Ergebnisse im kostenlosen Tarif tragen ein attribution-Objekt, das im Solver erzeugt wird. Pro-Ergebnisse, die mit gültigem Token gerechnet wurden, tragen es nicht — derselbe Code zeigt also im kostenlosen Tarif einen Hinweis und liefert unter Pro White-Label-Ausgabe, ohne dass ein Schalter zu setzen wäre.

Daneben werden getFersAttribution, fersAttributionText und createFersBadge exportiert. Alle nehmen das Envelope, das geparste Modell oder das Ergebnispaket entgegen und geben bei einem Pro-Ergebnis oder einem Fehler-Envelope nichts zurück.

import { fersAttributionHtml } from "@ferscloud/fers-calculation-web/badge.js";

// Free-tier results carry result.attribution; this renders the credit.
// With a Pro solve token the field is absent and this returns "" — the
// same code white-labels, with no flag to set.
const credit = fersAttributionHtml(res);

Wenn Sie den kostenlosen Tarif in einer Anwendung einsetzen, zeigen Sie den Hinweis bitte an. Es ist das Einzige, worum der kostenlose Tarif bittet. Verfügbar ab Engine 0.2.61.

Pro-Grenzen mit einem Solve-Token

Die Pro-Grenzen werden über ein kurzlebiges signiertes Token freigeschaltet, das der FERS-Cloud-Server ausstellt. Das Token wird innerhalb des WebAssembly-Moduls gegen einen im Binary hinterlegten Ed25519-Public-Key geprüft und ist daher nicht fälschbar.

Ihr Server hält den API-Schlüssel und tauscht ihn gegen ein 30-Minuten-Token; der Browser sieht immer nur das Token. Halten Sie FERS_API_KEY serverseitig — niemals im Browser-Code.

// pages/api/solve-token.ts
const FERS_API_KEY = process.env.FERS_API_KEY!;

let cached: { token: string; expiresAt: number } | null = null;

export default async function handler(req, res) {
  const now = Date.now();

  // Re-use if more than 5 minutes remain
  if (cached && cached.expiresAt - now > 5 * 60 * 1000) {
    return res.json({ token: cached.token });
  }

  const resp = await fetch("https://ferscloud.com/api/solver/token", {
    method: "POST",
    headers: { "X-API-Key": FERS_API_KEY },
  });

  if (!resp.ok) return res.status(502).json({ error: "Token fetch failed" });

  const { token, expiresAt } = await resp.json();
  cached = { token, expiresAt: new Date(expiresAt).getTime() };
  return res.json({ token });
}

Den Solver mit Token aufrufen

Fehlt das Token oder ist es abgelaufen oder ungültig, fällt der Solver auf die kostenlose Stabgrenze zurück — er wirft nie eine Ausnahme. Ein echter Berechnungsfehler kommt weiterhin als { ok: false, error } zurück und nicht als Exception.

import { calculate_from_json_with_token } from "@ferscloud/fers-calculation";

const { token } = await fetch("/api/solve-token").then((r) => r.json());

const res = JSON.parse(
  calculate_from_json_with_token(JSON.stringify(myModel), token),
);
if (!res.ok) throw new Error(`${res.error.code}: ${res.error.message}`);
const data = res.result;
KostenlosPro
Maximale Stabzahl10010,000
Funktioncalculate_from_jsoncalculate_from_json_with_token
Token erforderlichNeinJa
Gültigkeit des Tokens30 Minuten

Biegelinie

Setzen Sie include_member_deflected_shape in den Analyseoptionen des Modells, um je Stab eine unmittelbar zeichenbare, lastexakte Biegelinie zu erhalten — die globale Verformung des Stabes entlang seiner Länge abgetastet — statt die Kurve selbst zu rekonstruieren.

Sie ist standardmäßig aus, damit die Antwort schlank bleibt, und fehlt in member_results, wenn sie nicht angefordert wurde.

const model = {
  /* … model + load cases … */
  analysis: {
    /* … */
    options: {
      /* … */
      include_member_deflected_shape: true,
    },
  },
};

const res = JSON.parse(calculate_from_json(JSON.stringify(model)));
const mr = res.result.results.loadcases["…"].member_results["1"];

// mr.member_displacements: [{ x_frac, displacement: [dx, dy, dz] }, …]
//   x_frac 0–1 along the member; displacement in the global input frame.

Andere Zugänge

Verwandte Seiten

Siehe auch

Häufige Fragen

Brauche ich einen API-Schlüssel, um im Browser zu rechnen?

Nein. calculate_from_json funktioniert ohne Schlüssel und ohne Konto für Modelle bis 100 Stäbe. Ein Schlüssel wird nur benötigt, um das Pro-Solve-Token auszustellen.

Verlässt das Modell den Browser?

Nein. Der Solver ist nach WebAssembly kompiliert und läuft auf dem Client, Modell und Ergebnisse bleiben also auf dem Rechner des Nutzers. An FERS Cloud wird nichts gesendet.

Warum braucht Vite zusätzliche Konfiguration?

Das Browser-Paket ist ein WASM-ES-Modul, das sich mit einem Top-Level-await initialisiert. vite-plugin-wasm übernimmt den WebAssembly-Import, und build.target: "esnext" behält dieses await in der Ausgabe — das setzt Chrome, Edge oder Firefox 89+ bzw. Safari 15+ voraus. webpack braucht asyncWebAssembly und asyncFunction in den Experiments- und Output-Einstellungen. Steht vite-plugin-top-level-await noch aus einer früheren Fassung dieser Seite in Ihrer Konfiguration, können Sie es entfernen: mit aktuellen @swc/core-Versionen schlägt damit vite build fehl.

Sollte ich den Solver in einem Web Worker ausführen?

Ab einer Handvoll Stäbe: ja. Der Solver ist synchron und rechenlastig, ein Aufruf im UI-Thread blockiert das Rendering für die Dauer der Berechnung.

Brauche ich einen init()-Aufruf?

Nein. Sowohl der Node- als auch der Bundler-Build initialisieren das WebAssembly-Modul beim Import automatisch. Ältere Dokumentation zeigte await init(); das ist nicht mehr nötig.

Was passiert, wenn das Solve-Token abgelaufen ist?

Der Solver fällt stillschweigend auf die kostenlosen Grenzen zurück. Er wirft bei einem ungültigen Token nie eine Ausnahme; ein Modell innerhalb der kostenlosen Stabgrenze rechnet weiter, ein größeres liefert LimitExceeded.

Welches Paket nehme ich auf dem Server?

@ferscloud/fers-calculation, gebaut für das Node-Target. Es braucht keine Bundler-Konfiguration und initialisiert sich beim Import selbst.

Sind die Ergebnisse dieselben wie in der Web-App?

Ja — es ist dieselbe Rust-Rechenmaschine, nur für ein anderes Target kompiliert. Die Web-App selbst nutzt genau diesen WebAssembly-Build.