_Brouillon (itération 14) : le texte est en cours de rédaction ; les sections peuvent encore s’allonger._
Un programme est un fichier texte G-code dans le dialecte de Grbl 1.1, que parle le noyau propre à Twinwright. Studio lit n’importe quel fichier de ce genre. Les programmes que sa FAO écrit ressemblent à ceci :
(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 |
| Élément | Ce que c’est |
|---|---|
| En-tête | Des commentaires : le titre du travail ; le premier outil, la matière et les principales données de coupe ; l’emplacement du zéro du programme |
| Modes | G21 G90 G17 G94 G54 : millimètres, absolu, plan XY, avance par minute, décalage d’origine G54 |
G53 G0 Z… | Un déplacement vers le haut de Z, en coordonnées machine : au début, à chaque changement d’outil et à la fin. Cette hauteur est celle où la prise d’origine laisse Z. |
T… M6 | Un changement d’outil. Le contrôleur mesure le nouvel outil sur le capteur de longueur d’outil. |
M3 S…, G4 P… | La broche en marche, puis une temporisation pendant qu’elle monte en vitesse. La pompe d’une broche refroidie par eau (M8) passe en premier. |
| Commentaires d’entité | Chaque entité commence par un commentaire qui dit ce qu’elle usine |
| Déplacements | G0, G1, et les arcs G2/G3 avec I et J. Le perçage utilise G81, ou G83 avec débourrage, son plan R à 1 mm au-dessus de la face supérieure. |
| Fin | M5, M9, le déplacement vers le haut, M30 |
Les commentaires sont entre parenthèses. Les lignes ne sont pas numérotées.
Studio conserve chaque version d’un programme dans le dossier des programmes (Documents/Twinwright Studio/programs), dans un dossier portant le nom du programme :
| Fichier | Ce qu’il contient |
|---|---|
v0001.nc | Le texte du programme, tel qu’il a été enregistré |
v0001.json | Sa version, l’heure (UTC), qui l’a créé, pourquoi, la version dont il provient, son hachage et ses lignes |
v0001.ops.json | Ce que chaque entité est devenue (opération, stratégie, outil, lignes, vitesse, avance) : une fiche de bridage le lit |
Un travail est la description d’une pièce à partir de laquelle la FAO produit des programmes. C’est un objet JSON :
| Clé | Signification |
|---|---|
name | Le nom du programme, par exemple bracket.nc |
title, description | Les textes de l’en-tête du programme et de la fiche de bridage |
material | La matière, par exemple 6061-T6 ; les désignations d’autres normes sont comprises |
tool | L’outil dans la broche, sous forme de spécification (flat 6 n3 L20) |
tools | La table d’outils : {"1": "flat 6 n3 L20", "2": "drill 3.4 118 L20"}. Si elle est omise : celle de la machine. |
cutting | Des valeurs qui remplacent les valeurs choisies : rpm, feed, ap, ae, finish_ap ; corner_steps (true ou false : les angles intérieurs d’une paroi taillés d’abord par paliers) |
safe_z | La hauteur à laquelle se font les déplacements de transit, en millimètres au-dessus de l’origine pièce |
zero | L’emplacement de l’origine pièce : front-left-top (par défaut) ou center-top |
features | Les entités, dans l’ordre où elles sont usinées. Voir Créer des programmes. |
setups | À la place de features : un objet par phase |
nest | À la place de features : des pièces imbriquées sur une plaque |
Une phase a ses propres features et indique comment la pièce y arrive depuis la phase précédente :
| Clé | Signification |
|---|---|
turn | u : retournée autour de X (l’avant et l’arrière changent de place) ; v : autour de Y (la gauche et la droite) |
fixture | Maintenue autrement à partir de cette phase (vise, clamps, tape …) |
zero | L’origine pièce retrouvée : front-left-top ou center-top. Si elle est omise après un retournement ou un nouveau montage : front-left-top ; sinon, l’origine de la phase précédente. |
name, tool, tools, cutting, safe_z | Ceux propres à la phase, prioritaires sur ceux du travail |
{"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}]} |
Chaque version d’un travail est conservée à côté du programme qu’elle a produit, sous le nom job-v0001.json : sa version, l’heure (UTC), qui l’a créée (operator ou ai:<client>), une note, la version du programme qu’elle a produit et le travail lui-même. Un travail décrit en phases liste aussi, pour chaque phase, son programme, la version produite et son hachage.
--mcap FILE enregistre une exécution dans un fichier MCAP (mcap.dev, format version 0) : un canal pour chaque type de données, des messages JSON, chaque canal avec son schéma JSON, sans découpage en blocs et sans compression, et un résumé à la fin avec les statistiques et un CRC. Les tests de Studio relisent les fichiers selon la spécification MCAP ; les outils qui lisent MCAP, comme PlotJuggler et Foxglove, sont censés les ouvrir, mais cela n’a pas encore été essayé.
--rec-period MS règle la fréquence d’écriture des canaux échantillonnés (1 ms si l’option n’est pas donnée).--play FILE.mcap relit un enregistrement dans Studio, avec une ligne de temps : lecture, pause, positionnement, de 0,25 à 10 fois la vitesse. Rien n’est simulé pendant la relecture.L’horodatage d’enregistrement de chaque message est le temps virtuel du jumeau, en nanosecondes depuis la mise sous tension. La plupart des canaux portent aussi t, le même temps en secondes. Un canal échantillonné n’écrit un message que lorsque quelque chose y a changé.
Un enregistrement de métadonnées nommé mc1sim décrit l’exécution :
| Clé | Signification |
|---|---|
machine | Le nom de la machine |
drive_model | Le modèle de variateur de l’exécution : L0, L1, L2, L2/3 (trois masses) ou L3 |
f_cpu | L’horloge du contrôleur, en Hz |
nvs | Un hachage des réglages mémorisés du contrôleur au début de l’exécution, 16 chiffres hexadécimaux |
sample_period_ms | La période d’échantillonnage |
time | virtual, ns from power-on |
| Canal | Écriture | Champ | Type | Unité | Signification |
|---|---|---|---|---|---|
/machine | échantillonné | pos | nombre[3] | mm | Où se trouve chaque axe : la mécanique, non la position que le contrôleur s’en fait |
mpos | nombre[3] ou null | mm | La position machine du contrôleur (MPos) ; null tant que le contrôleur est éteint | ||
lost | nombre[3] | Les pas perdus par chaque axe | |||
feed | nombre | mm/min | La vitesse d’avance du contrôleur à l’instant présent | ||
tip | nombre[3] | mm | La pointe de l’outil par rapport à la pièce : avec le modèle d’erreurs du jumeau activé, déplacée par ses erreurs ; désactivé, identique à pos | ||
/drives | échantillonné, avec un modèle de variateur à partir de L1 | ferr_um | nombre[3] | µm | L’erreur de poursuite de chaque variateur |
state | chaîne[3] | L’état de chaque variateur | |||
km1 | booléen | Le contacteur KM1 est fermé | |||
bus48 | booléen | L’alimentation 48 V des variateurs est active | |||
/spindle | échantillonné, avec un variateur de broche | f_out | nombre | Hz | La fréquence de sortie du variateur |
rpm | nombre | tr/min | La vitesse de la broche | ||
amps | nombre | A | Le courant de sortie du variateur | ||
load_pct | nombre | % | La charge telle que le variateur la rapporte | ||
torque_nm | nombre | N·m | Le couple du moteur | ||
bus_v | nombre | V | La tension du bus continu du variateur | ||
cmd_rpm | nombre | tr/min | La vitesse que demande le contrôleur | ||
run, pump | booléen | Le variateur en marche ; la pompe en marche | |||
power | chaîne | L’alimentation propre du variateur : ready, boot ou off | |||
fault | chaîne | Le défaut du variateur, tel que son afficheur l’indique | |||
/controller | à chaque changement | state | chaîne | L’état du contrôleur (Off tant qu’il est éteint) | |
alarm | entier | L’alarme dans laquelle il se trouve (0 : aucune) | |||
homed | entier | Les axes dont la prise d’origine est faite, un bit chacun : X 1, Y 2, Z 4 | |||
limits | entier | Les entrées de fin de course qui sont actives, un bit chacune | |||
probe, estop, motor_fault, door | booléen | Les entrées du palpeur, de l’arrêt d’urgence, du défaut moteur et de la porte (la MC-1 n’a pas de porte) | |||
on | booléen | Le contrôleur est sous tension | |||
/power | à chaque changement | mains, v24, mcu, km1, bus48 | booléen | Le secteur ; l’alimentation 24 V ; le contrôleur en marche ; KM1 ; l’alimentation 48 V | |
vfd_mains, vfd, pump | booléen, chaîne, booléen | Le secteur du variateur de broche ; son état (none, ready, boot, off) ; la pompe | |||
/serial/in | à l’arrivée | text | chaîne | Les octets que le contrôleur a reçus ; les caractères de contrôle sous forme d’échappements \u | |
/serial/out | à l’arrivée | line | chaîne | Chaque ligne que le contrôleur a écrite | |
/events | au fil des événements | text | chaîne | Chaque entrée du journal d’événements : repères, alimentation, variateurs, variateur de broche, collisions, fils rompus | |
/cut | échantillonné, avec un brut | cutting | booléen | De la matière est enlevée ; sinon, les valeurs ci-dessous valent 0, sauf rpm et removed_mm3 | |
mrr_mm3_s | nombre | mm³/s | Le débit d’enlèvement de matière | ||
ap_mm, ae_mm | nombre | mm | La profondeur axiale et la largeur radiale de la coupe | ||
fz_mm | nombre | mm | L’avance par dent | ||
rpm | nombre | tr/min | La vitesse de la broche | ||
fx_n, fy_n, fz_n | nombre | N | L’effort de coupe selon les axes de la machine | ||
torque_nm, power_w | nombre | N·m, W | Le couple et la puissance de coupe | ||
kc_j_mm3 | nombre | J/mm³ | L’énergie spécifique de coupe | ||
removed_mm3 | nombre | mm³ | La matière enlevée jusqu’ici | ||
/input | à l’arrivée | line | chaîne | Les entrées de la session, ligne par ligne telles que --record les écrit (voir Journaux de session) | |
/shadow | à l’arrivée, en mode ombre | host_s | nombre | s | Le temps de l’ordinateur |
dir | chaîne | t vers la machine réelle, r depuis elle | |||
data | chaîne | Les octets, chacun sous forme d’un caractère U+0000–U+00FF, jusqu’à 1 Ko par message | |||
/ui/state, /sound | pour le lecteur | La trame d’état des vues (30 par seconde tant qu’elle change) et la trame du son (toutes les 10 ms de temps virtuel quand elle change). Propres à Studio : leurs champs peuvent changer d’une version à l’autre. |
--vcd FILE écrit les pins du contrôleur dans un fichier VCD (IEEE 1364), que lisent GTKWave et PulseView : le scope mcu contient les pins du contrôleur, chaque changement au tick d’horloge où il se produit (les pins de pas sous forme d’impulsions de $0 microsecondes) ; le scope machine contient les lignes extérieures au contrôleur : les alimentations, KM1, le variateur de broche, la pompe, le bouton d’arrêt d’urgence, le capteur de longueur d’outil, les cinq capteurs. L’échelle de temps est de 1 ns.
--record FILE écrit les entrées d’une session, chacune avec son temps virtuel : ce qu’ont envoyé les logiciels d’envoi, les commandes temps réel, les commandes des vues. --replay FILE rejoue la session, bit à bit, avec la même version de Studio et les mêmes fichiers machine. --replay FILE --branch S la rejoue jusqu’à S secondes, puis continue en direct à partir de là ; avec --record, la branche a un journal à elle, qui part de celui de son parent (# branch <parent> <ticks>). Un journal est un texte ; ses lignes sont propres à Studio et peuvent changer d’une version à l’autre.
Ce que le jumeau conserve entre deux sessions, comme le fait une machine réelle quand on l’éteint puis la rallume : la position des axes, le brut avec ses usinages, l’outil dans la broche, la mémoire du contrôleur et l’horloge de la carte, et la chaleur, qui retombe pendant que la machine est éteinte. Le contrôleur démarre à froid : la prise d’origine de la machine est à refaire.
La fenêtre conserve le monde de chaque machine dans le dossier d’état (sous Windows %LOCALAPPDATA%\Twinwright\Studio) : celui de la première machine dans world, celui des autres dans machines\<id>\world. --world DIR utilise un autre dossier ; --world-reset repart de l’état d’usine. Les exécutions par lots, les tests, les rejeux, les enregistrements relus et les machines d’arrière-plan de l’IA ne conservent aucun monde si --world n’est pas donné. Un seul processus à la fois ouvre un monde ; un deuxième repart de l’état d’usine et n’enregistre rien.
| Fichier | Ce qu’il contient |
|---|---|
world.json | La fin de la session (ci-dessous), écrite d’abord dans world.json.tmp, puis renommée |
stock.snap | Les cellules du brut, usinées ou non (binaire, TWSTOCK1) |
eeprom.bin | La mémoire conservée du contrôleur : ses réglages, ses décalages et ses données d’outils, écrits au fil de leurs changements |
world.json :
| Clé | Signification |
|---|---|
world | La version du format : 1 |
saved_at, saved_epoch | Quand il a été enregistré : en UTC, et en secondes depuis 1970 |
version | La version de Studio qui l’a enregistré |
pos | Où se trouvaient les axes, en mm (X, Y, Z) |
tool | L’outil dans la broche, sous forme de spécification (flat 6 n3 L20) |
tool_table | La table d’outils du travail : {"1": "flat 6 n3 L20", ...} |
tool_auto | Le jumeau joue l’opérateur lors d’un changement d’outil : il met en place l’outil de la table et appuie sur départ cycle |
setup | Le bridage et le brut, tels que les écrit le bouton Enregistrer le bridage de la fenêtre (null : aucun) |
cells | stock.snap contient les cellules du brut |
thermal | La chaleur : screws (chaque vis au-dessus du bâti), frame (le bâti au-dessus de 20 °C), ambient (la salle au-dessus de 20 °C), part_heat et part_offset (la pièce au-dessus du bâti : ce que les coupes y apportent, et un décalage donné), tous en K ; spindle_um, la croissance de la broche en µm |
rtc | La lecture de l’horloge de la carte, en s |
probe_cal | Le palpeur tel qu’il a été étalonné en dernier : le diamètre de la bille, son rayon effectif, les lobes triples et leur phase, le diamètre de la bague étalon, l’écart maximal, quand et par qui (seulement une fois qu’il a été étalonné) |
odometer | travel_mm et reversals de chaque axe, spindle_h (les heures de la broche) et on_h (les heures du logiciel), cumulés sur les sessions |
Au démarrage suivant, les axes sont là où ils se trouvaient ; le même brut revient avec ses cellules, ainsi que l’outil et la table ; la chaleur a diminué pendant le temps où la machine était éteinte, chaque élément vers son repos selon sa propre constante de temps ; l’horloge de la carte a continué de tourner. --world-off S indique combien de temps la machine est restée éteinte (pour les tests ; sinon, le temps écoulé depuis l’enregistrement). --pos, --tool, --stock, --setup et --part donnés au démarrage l’emportent sur l’archive.
Un fichier de licence (.twlic) est un objet JSON en UTF-8, signé avec Ed25519. Studio le vérifie sur l’ordinateur, sans le réseau. Où se trouvent les fichiers et comment ils sont utilisés : Licences, postes et activation.
Cet exemple provient des vecteurs de test partagés. Il est signé avec la clé de test (kid test), que les versions publiées n’acceptent pas :
{"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"} |
| Champ | Type | Signification |
|---|---|---|
v | entier | La version du format : 1. Studio considère comme non valide un fichier d’une autre version. |
kid | chaîne | Laquelle des clés de Twinwright a signé le fichier |
id | chaîne | Le numéro de la licence : TWL- suivi de 10 lettres et chiffres. Le renouvellement l’envoie. |
edition | chaîne | maker ou pro |
kind | chaîne | subscription, perpetual (achat définitif) ou owner (propriétaire d’une MC-1) |
seats | entier | Le nombre de postes |
holder | chaîne | Le titulaire de la licence : une personne ou une organisation. Jamais une adresse e-mail. |
issued | date et heure | Quand le fichier a été signé, en UTC, YYYY-MM-DDTHH:MM:SSZ |
valid_until | date et heure, ou null | Abonnement : la fin de la période payée. Achat définitif : la fin de la période de remboursement dans le premier fichier, null dans le fichier permanent. |
renew_after | date et heure, ou null | À partir de quand Studio essaie de renouveler le fichier |
updates_until | date, ou null | Achat définitif, propriétaire d’une MC-1 : les versions publiées jusqu’à ce jour ont les fonctions payantes |
fp | tableau de chaînes | Les ordinateurs auxquels la licence est liée : les hachages de leurs empreintes, jusqu’à trois par poste |
mc1_serial | chaîne, ou null | Propriétaire d’une MC-1 : le numéro de série de la machine. Studio ne vérifie pas la machine lorsqu’il s’exécute. |
sig | chaîne | La signature Ed25519 des autres champs, en base64url sans remplissage |
Un champ que Studio ne connaît pas fait partie des octets signés, et Studio ignore ce qu’il signifie.
La signature couvre le fichier sans sig, écrit sous une forme canonique :
Studio écrit le fichier sous cette forme avant de vérifier la signature. Les espaces, les sauts de ligne et l’ordre des clés dans le fichier n’ont donc pas d’importance. Une valeur modifiée fait échouer la vérification.
Le hachage est formé des 32 premiers chiffres hexadécimaux du SHA-256 calculé sur les octets twinwright-fp-v1, un octet nul, et l’identifiant de l’ordinateur :
| Système | Identifiant de l’ordinateur |
|---|---|
| Windows | La valeur MachineGuid sous HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography |
| Linux | /etc/machine-id |
À défaut de l’un comme de l’autre, Studio crée un identifiant aléatoire et le conserve dans le dossier des licences (machine.id) ; le supprimer fait de l’ordinateur un autre ordinateur. L’identifiant lui-même ne quitte jamais l’ordinateur.
Par exemple, l’identifiant 8b1c3a0e-5f2d-4c7a-9e61-2d0f4b7a1c55 donne le hachage 0a2d31bb82176516415efa7b3a340562.
Le code de demande pour l’activation hors ligne est le JSON canonique en UTF-8 {"fp": <the hash>, "version": <Studio's version>}, en base64url sans remplissage =. Par exemple, le hachage 0123456789abcdef0123456789abcdef et la version 1.0.0 donnent :
eyJmcCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwidmVyc2lvbiI6IjEuMC4wIn0 |
TwinwrightStudio.exe --licence-request affiche le code de demande de cet ordinateur.
Un essai est un fichier de licence comme les autres, signé par le serveur de licences de Twinwright : "kind": "trial", le hachage de cet ordinateur dans fp, et valid_until 30 jours après le début de l’essai. Il n’est jamais renouvelé. Un fichier d’essai modifié à la main n’est plus valable.
Chaque clé de l’archive d’une machine, avec les valeurs de la MC-1 : Schéma du profil de machine.