Dokumentation Referenz
Dateiformate
_Entwurf (Runde 14): Der Text wird noch geschrieben; die Abschnitte können noch wachsen._
Programme
Ein Programm ist eine G-Code-Textdatei im Dialekt von Grbl 1.1, den Twinwrights eigener Kernel spricht. Studio liest jede solche Datei. Die Programme, die das CAM von Studio schreibt, sehen so aus:
(Sensor bracket) (tool T1 flat 6 n3 L20, 6061-T6: S13263, F836, plunge F279, depth of cut 1.20 mm, stepover 2.40 mm) (program zero: on the blank's top; the machine's top first; T1 is the length reference) G21 G90 G17 G94 G54 G53 G0 Z0 T1 M6 G53 G0 Z0 M3 S13263 G4 P3 (up to speed) (face X-2..62 Y-2..42, 0.3 mm deep: 1 layer of 12 passes) ... M5 M9 G53 G0 Z0 M30
| Teil | Was es ist |
|---|---|
| Programmkopf | Kommentare: der Titel des Auftrags; das erste Werkzeug, das Material und die wichtigsten Schnittdaten; wo der Nullpunkt des Programms liegt |
| Modi | G21 G90 G17 G94 G54: Millimeter, absolut, die XY-Ebene, Vorschub pro Minute, Nullpunktverschiebung G54 |
G53 G0 Z… | Eine Fahrt an das obere Ende von Z, in Maschinenkoordinaten: am Anfang, bei jedem Werkzeugwechsel und am Ende. Das ist die Höhe, auf der die Referenzfahrt Z zurücklässt. |
T… M6 | Ein Werkzeugwechsel. Die Steuerung misst das neue Werkzeug am Werkzeuglängensensor. |
M3 S…, G4 P… | Die Spindel ein, dann eine Verweilzeit, während sie auf Drehzahl kommt. Die Pumpe einer wassergekühlten Spindel (M8) kommt zuerst. |
| Formelement-Kommentare | Jedes Formelement beginnt mit einem Kommentar, der sagt, was es bearbeitet |
| Bewegungen | G0, G1 und Kreisbögen G2/G3 mit I und J. Gebohrt wird mit G81 oder mit G83 und Entspanen; die R-Ebene liegt 1 mm über der Oberseite. |
| Ende | M5, M9, die Fahrt nach oben, M30 |
Kommentare stehen in Klammern. Die Sätze haben keine Nummern.
Studio bewahrt jede Version eines Programms im Programmordner (Dokumente/Twinwright Studio/programs) auf, in einem Ordner mit dem Namen des Programms:
| Datei | Was sie enthält |
|---|---|
v0001.nc | Der Text des Programms, wie er gespeichert wurde |
v0001.json | Seine Version, die Zeit (UTC), wer es erstellt hat, warum, die Version, aus der es entstand, sein Hash und seine Sätze |
v0001.ops.json | Was aus jedem Formelement wurde (Bearbeitung, Strategie, Werkzeug, Sätze, Drehzahl, Vorschub): Das Einrichteblatt liest sie |
Aufträge und Aufspannungen
Ein Auftrag ist die Beschreibung eines Teils, aus der das CAM Programme erstellt. Er ist ein JSON-Objekt:
| Schlüssel | Bedeutung |
|---|---|
name | Der Name des Programms, zum Beispiel bracket.nc |
title, description | Texte für den Programmkopf und das Einrichteblatt |
material | Das Material, zum Beispiel 6061-T6; Bezeichnungen aus anderen Normen werden erkannt |
tool | Das Werkzeug in der Spindel, als Spezifikation (flat 6 n3 L20) |
tools | Die Werkzeugtabelle: {"1": "flat 6 n3 L20", "2": "drill 3.4 118 L20"}. Fehlt sie, gilt die der Maschine. |
cutting | Werte, die die gewählten überschreiben: rpm, feed, ap, ae, finish_ap; corner_steps (true oder false: die Innenecken einer Wand zuerst in Stufen schneiden) |
safe_z | Die Höhe, auf der die Fahrten über das Teil verlaufen, in Millimetern über dem Werkstücknullpunkt |
zero | Wo der Werkstücknullpunkt liegt: front-left-top (der Standardwert) oder center-top |
features | Die Formelemente, in der Reihenfolge, in der sie gefräst werden. Siehe Programme erstellen. |
setups | Statt features: ein Objekt für jede Aufspannung |
nest | Statt features: auf einer Platte verschachtelte Teile |
Eine Aufspannung hat eigene features und gibt an, wie das Teil aus der vorhergehenden Aufspannung zu ihr gelangt:
| Schlüssel | Bedeutung |
|---|---|
turn | u: um X gewendet (vorn und hinten tauschen die Plätze); v: um Y (links und rechts) |
fixture | Ab dieser Aufspannung anders gehalten (vise, clamps, tape …) |
zero | Der erneut gefundene Werkstücknullpunkt: front-left-top oder center-top. Fehlt er, gilt nach einem Wenden oder einem neuen Spannmittel front-left-top, sonst der Nullpunkt der vorherigen Aufspannung. |
name, tool, tools, cutting, safe_z | Die der Aufspannung selbst; sie gehen denen des Auftrags vor |
{"name": "bracket.nc", "title": "Sensor bracket", "material": "6061-T6",
"tools": {"1": "flat 6 n3 L20", "2": "drill 3.4 118 n2 L20"},
"features": [
{"op": "face", "x0": -2, "y0": -2, "x1": 62, "y1": 42, "depth": 0.3, "tool": 1},
{"op": "pocket", "shape": "rect", "x0": 6, "y0": 6, "x1": 28, "y1": 34, "r": 3, "depth": 4, "tool": 1},
{"op": "pocket", "shape": "circle", "cx": 44, "cy": 20, "d": "Ø12 H7", "depth": 6, "tool": 1},
{"op": "drill", "points": [[44, 6], [44, 34]], "depth": 8, "tool": 2}]}
Jede Version eines Auftrags wird neben dem Programm aufbewahrt, das sie erzeugt hat, als job-v0001.json: mit ihrer Version, der Zeit (UTC), wer sie erstellt hat (operator oder ai:<client>), einer Notiz, der Programmversion, die sie erzeugt hat, und dem Auftrag selbst. Ein Auftrag in Aufspannungen führt außerdem für jede Aufspannung das Programm, die erzeugte Version und deren Hash auf.
Aufzeichnungen (MCAP)
--mcap FILE zeichnet einen Lauf in eine MCAP-Datei auf (mcap.dev, Formatversion 0): ein Kanal für jede Art von Daten, JSON-Nachrichten, jeder Kanal mit seinem JSON-Schema, nicht in Chunks aufgeteilt und nicht komprimiert, und am Ende eine Zusammenfassung mit den Statistiken und einer CRC. Die Tests von Studio lesen die Dateien wieder ein und prüfen sie gegen die MCAP-Spezifikation; Anwendungen, die MCAP lesen, wie PlotJuggler und Foxglove, sollen sie öffnen können, aber das wurde noch nicht ausprobiert.
--rec-period MSlegt fest, wie oft die abgetasteten Kanäle geschrieben werden (1 ms, wenn nichts angegeben ist).- Eine Aufzeichnung ändert nichts am Lauf: Ein aufgezeichneter Lauf und derselbe Lauf ohne Aufzeichnung sind Bit für Bit gleich.
--play FILE.mcapspielt eine Aufzeichnung in Studio ab, mit einer Zeitleiste: Abspielen, Pausieren, Springen, 0,25- bis 10-fache Geschwindigkeit. Während des Abspielens wird nichts simuliert.
Zeit
Die Log-Zeit jeder Nachricht ist die virtuelle Zeit des Zwillings, in Nanosekunden ab dem Einschalten. Die meisten Kanäle tragen außerdem t, dieselbe Zeit in Sekunden. Ein abgetasteter Kanal schreibt nur dann eine Nachricht, wenn sich etwas darin geändert hat.
Der Dateikopf
Ein Metadatensatz mit dem Namen mc1sim sagt, was der Lauf war:
| Schlüssel | Bedeutung |
|---|---|
machine | Der Name der Maschine |
drive_model | Das Antriebsmodell des Laufs: L0, L1, L2, L2/3 (drei Massen) oder L3 |
f_cpu | Der Takt der Steuerung, Hz |
nvs | Ein Hash der gespeicherten Einstellungen der Steuerung zu Beginn des Laufs, 16 Hexadezimalziffern |
sample_period_ms | Die Abtastperiode |
time | virtual, ns from power-on |
Kanäle
| Kanal | Geschrieben | Feld | Typ | Einheit | Bedeutung |
|---|---|---|---|---|---|
/machine | abgetastet | pos | number[3] | mm | Wo jede Achse steht: die Mechanik, nicht die Vorstellung der Steuerung davon |
mpos | number[3] oder null | mm | Die Maschinenposition der Steuerung (MPos); null, solange die Steuerung aus ist | ||
lost | number[3] | Die Schritte, die jede Achse verloren hat | |||
feed | number | mm/min | Der aktuelle Vorschub der Steuerung | ||
tip | number[3] | mm | Die Werkzeugspitze gegenüber dem Werkstück: bei eingeschaltetem Fehlermodell des Zwillings um dessen Fehler verschoben, bei ausgeschaltetem dasselbe wie pos | ||
/drives | abgetastet, mit einem Antriebsmodell ab L1 | ferr_um | number[3] | µm | Der Schleppfehler jedes Antriebs |
state | string[3] | Der Zustand jedes Antriebs | |||
km1 | boolean | Das Schütz KM1 ist geschlossen | |||
bus48 | boolean | Die 48-V-Versorgung der Antriebe ist eingeschaltet | |||
/spindle | abgetastet, mit einem Spindelantrieb | f_out | number | Hz | Die Ausgangsfrequenz des Antriebs |
rpm | number | min⁻¹ | Die Drehzahl der Spindel | ||
amps | number | A | Der Ausgangsstrom des Antriebs | ||
load_pct | number | % | Die Last, wie der Antrieb sie meldet | ||
torque_nm | number | N·m | Das Drehmoment des Motors | ||
bus_v | number | V | Die Zwischenkreisspannung des Antriebs | ||
cmd_rpm | number | min⁻¹ | Die Drehzahl, die die Steuerung vorgibt | ||
run, pump | boolean | Der Antrieb läuft; die Pumpe ist an | |||
power | string | Die eigene Versorgung des Antriebs: ready, boot oder off | |||
fault | string | Der Fehler des Antriebs, wie ihn dessen Anzeige zeigt | |||
/controller | bei Änderung | state | string | Der Zustand der Steuerung (Off, solange sie aus ist) | |
alarm | integer | Der Alarm, in dem sie sich befindet (0: keiner) | |||
homed | integer | Die referenzierten Achsen, je ein Bit: X 1, Y 2, Z 4 | |||
limits | integer | Die aktiven Endschaltereingänge, je ein Bit | |||
probe, estop, motor_fault, door | boolean | Die Eingänge für Messtaster, Not-Halt, Motorfehler und Tür (die MC-1 hat keine Tür) | |||
on | boolean | Die Steuerung ist eingeschaltet | |||
/power | bei Änderung | mains, v24, mcu, km1, bus48 | boolean | Netz; die 24-V-Versorgung; die laufende Steuerung; KM1; die 48-V-Versorgung | |
vfd_mains, vfd, pump | boolean, string, boolean | Das Netz des Spindelantriebs; sein Zustand (none, ready, boot, off); die Pumpe | |||
/serial/in | sobald es eintrifft | text | string | Die Bytes, die die Steuerung empfangen hat; Steuerzeichen als \u-Escapes | |
/serial/out | sobald es eintrifft | line | string | Jede Zeile, die die Steuerung geschrieben hat | |
/events | sobald sie auftreten | text | string | Jeder Eintrag des Ereignisprotokolls: Marken, die Versorgung, die Antriebe, der Spindelantrieb, Kollisionen, Kabelbrüche | |
/cut | abgetastet, mit einem Rohteil | cutting | boolean | Es wird Material abgetragen; solange nicht, sind die folgenden Werte 0, außer rpm und removed_mm3 | |
mrr_mm3_s | number | mm³/s | Die Materialabtragsrate | ||
ap_mm, ae_mm | number | mm | Die axiale Schnitttiefe und die radiale Schnittbreite | ||
fz_mm | number | mm | Der Vorschub pro Zahn | ||
rpm | number | min⁻¹ | Die Drehzahl der Spindel | ||
fx_n, fy_n, fz_n | number | N | Die Schnittkraft entlang der Maschinenachsen | ||
torque_nm, power_w | number | N·m, W | Das Schnittmoment und die Schnittleistung | ||
kc_j_mm3 | number | J/mm³ | Die spezifische Schnittenergie | ||
removed_mm3 | number | mm³ | Das bisher abgetragene Material | ||
/input | sobald es eintrifft | line | string | Die Eingaben der Sitzung, Zeile für Zeile, wie --record sie schreibt (siehe Sitzungsprotokolle) | |
/shadow | sobald es eintrifft, im Schattenmodus | host_s | number | s | Die Zeit des Computers |
dir | string | t zur realen Maschine, r von ihr | |||
data | string | Die Bytes, jedes als ein Zeichen U+0000–U+00FF, bis zu 1 KB pro Nachricht | |||
/ui/state, /sound | für den Player | Der Zustands-Frame der Ansichten (30 pro Sekunde, solange er sich ändert) und der Frame des Klangs (alle 10 ms virtueller Zeit, wenn er sich ändert). Eigene Kanäle von Studio: Ihre Felder können sich von Version zu Version ändern. |
Pin-Verläufe (VCD)
--vcd FILE schreibt die Pins der Steuerung als VCD-Datei (IEEE 1364), die GTKWave und PulseView lesen: Der Scope mcu enthält die Pins der Steuerung, jede Änderung zu dem Taktschritt, in dem sie geschieht (die Schritt-Pins als Impulse von $0 Mikrosekunden); der Scope machine enthält die Leitungen außerhalb der Steuerung: die Versorgungen, KM1, den Spindelantrieb, die Pumpe, den Not-Halt-Taster, den Werkzeuglängensensor, die fünf Schalter. Die Zeitskala beträgt 1 ns.
Sitzungsprotokolle
--record FILE schreibt die Eingaben einer Sitzung, jede mit ihrer virtuellen Zeit: was die Sender-Programme gesendet haben, die Echtzeitbefehle, die Befehle der Ansichten. --replay FILE führt die Sitzung Bit für Bit noch einmal aus, mit derselben Version von Studio und denselben Maschinendateien. --replay FILE --branch S spielt sie bis S Sekunden ab und läuft von dort live weiter; mit --record hat der Zweig ein eigenes Protokoll, das vom übergeordneten Protokoll ausgeht (# branch <parent> <ticks>). Ein Protokoll ist Text; seine Zeilen sind Studios eigenes Format und können sich von Version zu Version ändern.
Das Weltarchiv
Was der Zwilling zwischen den Sitzungen behält, so wie es eine reale Maschine tut, wenn sie aus- und wieder eingeschaltet wird: wo die Achsen stehen, das Rohteil mit seinen Schnitten, das Werkzeug in der Spindel, der Speicher der Steuerung und die Uhr der Platine sowie die Wärme, die abkühlt, während die Maschine aus ist. Die Steuerung startet kalt: Die Maschine wird erneut referenziert.
Das Fenster bewahrt die Welt jeder Maschine im Zustandsordner auf (unter Windows %LOCALAPPDATA%\Twinwright\Studio): die der ersten Maschine in world, die der anderen in machines\<id>\world. --world DIR verwendet einen anderen Ordner; --world-reset beginnt mit dem Werkszustand. Stapelläufe, Tests, Wiederholungen, abgespielte Aufzeichnungen und die Hintergrundmaschinen der KI behalten keine Welt, es sei denn, --world ist angegeben. Eine Instanz zur Zeit öffnet eine Welt; eine zweite beginnt mit dem Werkszustand und speichert nichts.
| Datei | Was sie enthält |
|---|---|
world.json | Der Stand am Ende der Sitzung (siehe unten), zuerst nach world.json.tmp geschrieben, dann umbenannt |
stock.snap | Die Zellen des Rohteils, geschnitten oder nicht (binär, TWSTOCK1) |
eeprom.bin | Der nichtflüchtige Speicher der Steuerung: ihre Einstellungen, Nullpunktverschiebungen und Werkzeugdaten, bei jeder Änderung sofort durchgeschrieben |
world.json:
| Schlüssel | Bedeutung |
|---|---|
world | Die Version des Formats: 1 |
saved_at, saved_epoch | Wann es gespeichert wurde: UTC und Sekunden seit 1970 |
version | Die Version von Studio, die es gespeichert hat |
pos | Wo die Achsen standen, mm (X, Y, Z) |
tool | Das Werkzeug in der Spindel, als Spezifikation (flat 6 n3 L20) |
tool_table | Die Werkzeugtabelle des Auftrags: {"1": "flat 6 n3 L20", ...} |
tool_auto | Der Zwilling spielt bei einem Werkzeugwechsel den Bediener: Er setzt das Werkzeug aus der Tabelle ein und drückt Zyklusstart |
setup | Die Werkstückspannung und das Rohteil, wie sie „Aufspannung speichern“ im Fenster schreibt (null: keine) |
cells | stock.snap enthält die Zellen des Rohteils |
thermal | Die Wärme: screws (jede Gewindespindel über dem Rahmen), frame (über 20 °C), ambient (der Raum über 20 °C), part_heat und part_offset (das Werkstück über dem Rahmen: was die Schnitte einbringen, und ein vorgegebener Versatz), alle in K; spindle_um, die Längung der Spindel in µm |
rtc | Der Stand der Uhr der Platine, s |
probe_cal | Der Messtaster, wie er zuletzt kalibriert wurde: der Durchmesser der Kugel, ihr wirksamer Radius, das Lobing und seine Phase, der Durchmesser der Ringlehre, die größte Abweichung, wann und von wem (erst nach einer Kalibrierung) |
odometer | travel_mm und reversals jeder Achse, spindle_h (die Stunden der Spindel) und on_h (die Stunden der Anwendung), über die Sitzungen aufsummiert |
Beim nächsten Start stehen die Achsen dort, wo sie standen; dasselbe Rohteil kommt mit seinen Zellen zurück, ebenso das Werkzeug und die Tabelle; die Wärme hat sich so lange abgekühlt, wie die Maschine aus war, jedes Teil mit seiner eigenen Zeitkonstante seinem Ruhewert entgegen; die Uhr der Platine ist weitergelaufen. --world-off S gibt an, wie lange die Maschine aus war (für Tests; sonst die Zeit seit dem Speichern). Beim Start angegebene --pos, --tool, --stock, --setup und --part haben Vorrang vor dem Archiv.
Lizenzdateien
Eine Lizenzdatei (.twlic) ist ein JSON-Objekt in UTF-8, signiert mit Ed25519. Studio prüft sie auf dem Computer, ohne Netzwerk. Wo die Dateien liegen und wie sie verwendet werden: Lizenzen, Arbeitsplätze und Aktivierung.
Dieses Beispiel stammt aus den gemeinsamen Testvektoren. Es ist mit dem Testschlüssel signiert (kid test), den Release-Builds nicht akzeptieren:
{"v": 1, "kid": "test", "id": "TWL-TEST000001", "edition": "pro", "kind": "subscription", "seats": 1,
"holder": "Weber Lab, TU Example", "issued": "2026-11-02T10:00:00Z", "valid_until": "2027-11-02T10:00:00Z",
"renew_after": "2027-10-28T10:00:00Z", "updates_until": null, "fp": ["0123456789abcdef0123456789abcdef"],
"mc1_serial": null,
"sig": "h3FTqZnDYtq8Q7_0ZuELLeDrTfyZnkTPgvDWSywTyl7BCJJxGIyhrBQUjYHJiE1_WOWaFarqbveFiMLrEfUyCA"}
Felder
| Feld | Typ | Bedeutung |
|---|---|---|
v | integer | Die Version des Formats: 1. Studio behandelt eine Datei einer anderen Version als ungültig. |
kid | string | Welcher der Schlüssel von Twinwright die Datei signiert hat |
id | string | Die Nummer der Lizenz: TWL- und 10 Buchstaben und Ziffern. Die Verlängerung sendet sie. |
edition | string | maker oder pro |
kind | string | subscription, perpetual (Kauflizenz) oder owner (MC-1-Besitzer) |
seats | integer | Die Anzahl der Arbeitsplätze |
holder | string | Für wen die Lizenz gilt: eine Person oder eine Organisation. Nie eine E-Mail-Adresse. |
issued | Datum und Uhrzeit | Wann die Datei signiert wurde, UTC, YYYY-MM-DDTHH:MM:SSZ |
valid_until | Datum und Uhrzeit oder null | Abonnement: das Ende des bezahlten Zeitraums. Kauflizenz: das Ende des Erstattungszeitraums in der ersten Datei, null in der dauerhaften. |
renew_after | Datum und Uhrzeit oder null | Ab wann Studio versucht, die Datei zu verlängern |
updates_until | Datum oder null | Kauflizenz, MC-1-Besitzer: Die bis zu diesem Tag veröffentlichten Versionen haben die kostenpflichtigen Funktionen |
fp | Array von Zeichenketten | Die Computer, an die die Lizenz gebunden ist: die Hashes ihrer Fingerabdrücke, bis zu drei je Arbeitsplatz |
mc1_serial | string oder null | MC-1-Besitzer: die Seriennummer der Maschine. Studio prüft die Maschine beim Betrieb nicht. |
sig | string | Die Ed25519-Signatur der anderen Felder, base64url ohne Padding |
Ein Feld, das Studio nicht kennt, ist Teil der signierten Bytes, und Studio ignoriert seine Bedeutung.
Die signierten Bytes
Die Signatur deckt die Datei ohne sig ab, geschrieben in einer kanonischen Form:
- Schlüssel in der Reihenfolge der Unicode-Codepoints;
- kein Leerraum;
- Zeichenketten mit den kürzesten Escape-Folgen von JSON;
- Ganzzahlen ohne Dezimalpunkt; keine anderen Zahlen, und kein Schlüssel doppelt.
Studio schreibt die Datei in dieser Form, bevor es die Signatur prüft. Leerzeichen, Zeilenumbrüche und die Reihenfolge der Schlüssel in der Datei spielen deshalb keine Rolle. Ein geänderter Wert lässt die Prüfung fehlschlagen.
Der Hash des Fingerabdrucks
Der Hash besteht aus den ersten 32 Hexadezimalziffern von SHA-256 über die Bytes twinwright-fp-v1, ein Nullbyte und die ID des Computers:
| System | Die ID des Computers |
|---|---|
| Windows | Der Wert MachineGuid unter HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography |
| Linux | /etc/machine-id |
Ohne beides erzeugt Studio eine zufällige ID und legt sie im Lizenzordner ab (machine.id); sie zu löschen macht den Computer zu einem anderen. Die ID selbst verlässt den Computer nie.
Zum Beispiel ergibt die ID 8b1c3a0e-5f2d-4c7a-9e61-2d0f4b7a1c55 den Hash 0a2d31bb82176516415efa7b3a340562.
Der Anforderungscode
Der Anforderungscode für die Offline-Aktivierung ist das kanonische UTF-8-JSON {"fp": <the hash>, "version": <Studio's version>} in base64url ohne =-Padding. Zum Beispiel ergeben der Hash 0123456789abcdef0123456789abcdef und die Version 1.0.0:
eyJmcCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwidmVyc2lvbiI6IjEuMC4wIn0
TwinwrightStudio.exe --licence-request gibt den Anforderungscode dieses Computers aus.
Testdateien
Eine Testphase ist eine Lizenzdatei wie die anderen, vom Lizenzserver von Twinwright signiert: "kind": "trial", der Hash dieses Computers in fp und valid_until 30 Tage nach Beginn der Testphase. Sie wird nie verlängert. Eine von Hand geänderte Testdatei gilt nicht mehr.
Das Maschinenarchiv
Jeder Schlüssel des Archivs einer Maschine, mit den Werten der MC-1: Schema des Maschinenprofils.