SOFTrobots OS · App-API
Nur über sein signiertes Manifest: capabilities = … nennt die
Rechte, der Kern prüft jeden Aufruf dagegen. Zeilen wie connects =, listens =, opens =,
realtime = grenzen weiter ein. Was die Person im Datei-Dialog wählt, wird eine Freigabe für genau diese Datei –
„Wählen ist Erlauben“.
Über Profil-Dateien im Programmordner (uses-ui,
uses-heap, uses-image, uses-tls …). Gebaut wird mit dem SDK (srapp), die
Laufzeit heißt SOFTrobotsKit. Ohne uses-heap gilt das Echtzeit-Profil ohne Heap.
Je Thema: wofür, welches Recht, dann die Aufrufe mit Signatur, einer Zeile
Erklärung und dem Systemaufruf dahinter (Modul.aufruf, Kennung, Budget). neu = seit heute
(Datei-Dialog, Dateimenü, Fortschritt, dock = no). Unter wichtigen Aufrufen steht ein
zugeklapptes Beispiel, meist aus echten Programmen (apps/…), sonst als „Muster“ markiert; je Thema
folgen längere Beispiele.
Das ≤ 2 ms-Feld ist das Zeitbudget
aus shapes/*.shape: die längste Laufzeit, die der Kern für diesen Shape-Aufruf zusagt (gemessen wird dagegen
bei jedem Aufruf). Reine Bibliotheksaufrufe ohne Systemaufruf haben kein Budget.
19 Themen · 507 Einträge, davon 422 Swift-Aufrufe/Typen · 138 Shape-Aufrufe in 24 Modulen · 242 Beispiele
Rechte & Manifest
Ein Programm bekommt Rechte nur über sein Manifest (Datei manifest im Programmordner, signiert beim Bauen).
Die Zeile capabilities = … nennt die Rechte; der Kern prüft jeden Aufruf dagegen. Ein unbekanntes Recht macht das
Manifest ungültig – ein Programm bekommt nie ein Recht, das der Kern nicht versteht. Ein Aufruf ohne Rechtevermerk in dieser
Übersicht ist jedem Programm offen.
Dazu kommen die Profil-Dateien uses-… im Programmordner: Sie entscheiden, welche Bibliotheken mitgebaut werden
(Heap, Oberfläche, Bilder, TLS …). Ohne uses-heap ist ein Programm im Echtzeit-Profil (-no-allocations).
Manifest-Zeilen
| Eintrag | Bedeutung | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Identität | ||
name = hello | Programmname (Pflicht). | |
id = de.softrobots.hello | Identität als umgekehrte Domain (Pflicht seit 08.11.); Datenordner ist /data/<id>. | |
version = 1.0.3 | Version (Pflicht); ersetzt wird ein Programm nur durch eine höhere. | |
api = 1 | App-API-Version, für die gebaut wurde; ein System, das weniger bietet, startet es nicht. | |
| Rechte & Dokumente | ||
capabilities = system.log, ui.surface | Die Rechte des Programms (Tabelle unten). | |
opens = txt, md | Dokumenttypen, die das Programm öffnet (bis 16); Voraussetzung für Doppelklick in Files und File.openDocument. | |
| Start & Anzeige | ||
dock = no neu | Nie im Dock zeigen, auch nicht während es läuft (der Datei-Dialog chooser). | |
start = demand | update | Nicht beim Booten starten, nur auf Anfrage (bzw. beim ersten Start einer neuen Systemkopie). | |
helper = video | Dient nur einem anderen Programm; wird in Listen nicht angeboten. | |
title / category / icon | Anzeigename, Gruppe (system, productivity, media, robot, tools) und SVG-Symbol 24 × 24. | |
| Speicher | ||
memory = 512 | Heap-Grenze in MiB (16 … 2048, Standard 256); mit der Zeile auch eine Garantie. | |
stack = 4096 | Stapelgröße in KiB (64 … 8192). | |
| Echtzeit & Daten | ||
realtime = 1000 200 stop | Echtzeit-Vertrag: Periode und Budget in µs, Verhalten bei Fristverletzung (log, stop, restart, stop-actuators). | |
record = samples 64 5 3600 [flight] | Eine Messreihe ohne Systemaufrufe (Recorder); flight = Flugschreiber. | |
publish = detections | Eine Veröffentlichung, die andere ohne Systemaufrufe lesen (Erkennungs- oder Arm-Ring). | |
subscribe = video.detections | Bis zu zwei Veröffentlichungen anderer Programme, beim Start nur lesbar eingeblendet. | |
| Netz | ||
connects = api.anthropic.com | Die einzigen Hosts, die das Programm erreichen darf. | |
listens = 443, 80 | TCP-Ports zum Lauschen (mit network.incoming, bis 8). | |
Rechte (capabilities)
| Eintrag | Bedeutung | Shape-Aufruf · Zeitbudget |
|---|---|---|
| System | ||
system.log | Zeilen ins Systemprotokoll schreiben. | ≤ 5 µssystem.log0x0002_0003 |
system.log.read | Das Systemprotokoll lesen (Konsole, Activity). | ≤ 20 µssystem.readLog0x0002_0005 |
system.status | Systemzustand lesen: Belegung, Prozessoren, Verbrauch aller Programme. | ≤ 100 µsprograms.usage0x0006_000E |
system.time | Die Systemuhr stellen (Zeitdienst nach NTS). | ≤ 5 µssystem.adjustClock0x0002_000A |
system.power | Ausschalten und Neustarten. | ohne Budgetsystem.power0x0002_0004 |
system.protection | Geräteschutz: Passwort, Entsperren, Bildschirmsperre, MIC-Anzeige (nur Display-Dienst). | ≤ 5 µsprotection.status0x0007_0001 |
system.update | Signierte Systemupdates in die ruhende Systemkopie installieren. | ≤ 60 supdate.install0x0008_0002 |
system.privacy | Geheimnisse wie API-Schlüssel für Programme setzen (Settings). | ≤ 5 msprograms.setSecret0x0006_0004 |
| Bildschirm & Eingabe | ||
ui.surface | Fenster zeigen (bis zu drei). | ≤ 5 msdisplay.createSurface0x0011_0004 |
display.own | Den Bildschirm besitzen (nur der Display-Dienst). | ≤ 5 msdisplay.open0x0011_0001 |
input.provide / input.receive | Eingabe-Ereignisse liefern (Treiber) bzw. empfangen (Display-Dienst). | ≤ 5 µsdisplay.submitInput0x0011_0006 |
| Dateien & Datenbank | ||
files.own | Dateien im eigenen Ordner /data/<id>; sonst existiert nichts. | ≤ 2 msfiles.open0x0003_0001 |
files.system | Das ganze DATA-Volume (/apps nur lesend), Snapshots. | ≤ 50 µsfiles.snapshot0x0003_000C |
files.mac | Zusätzlich den Mac-Ordner mac/… (nur SOFTrobotsVM). | |
files.dialog neu | Den Öffnen-/Sichern-Dialog des Systems zeigen und die Wahl melden (nur Programm chooser). | ≤ 5 msdialog.answer0x0013_0004 |
database.edit | Jede Datenbank auf DATA schreiben, nicht nur die eigenen (Programm Database). | ≤ 1 msdb.write0x0004_0002 |
| Programme & Kanäle | ||
apps.launch | Programme starten, stoppen, auflisten, Dokumente übergeben, Abstürze lesen. | ≤ 50 msprograms.launch0x0006_0007 |
ipc | Kanäle zwischen Programmen öffnen. | ≤ 1 mschannels.open0x000A_0001 |
develop.build | Programme aus Skripten mit dem Schlüssel dieses Roboters bauen (Develop). | ≤ 3 sprograms.build0x0006_0010 |
| Netz | ||
network.outgoing | Ausgehende TCP-Verbindungen und UDP; mit connects = eingegrenzt. | ≤ 5 snet.connect0x0005_0004 |
network.incoming | Verbindungen auf den Ports der listens =-Zeile annehmen (Programm Server). | ≤ 50 µsnet.listen0x0005_000C |
| Ton & Mikrofon | ||
sound.play | Ton abspielen: ein Ring, den der Mischer des Kerns spielt. | ≤ 1 mssound.open0x0030_0006 |
sound.volume | Die Gesamtlautstärke setzen. | ≤ 50 µssound.setVolume0x0030_0009 |
device.microphone | Mikrofon hören; die rote MIC-Anzeige nennt jedes Programm, das hört. | ≤ 1 msmicrophone.open0x0031_0002 |
microphone.provide | Ein Mikrofon anbieten (Test-Mikrofon). | ≤ 1 msmicrophone.provide0x0031_0006 |
| Kamera | ||
device.camera | Kameras auflisten und ihre Bildringe lesend einblenden. | ≤ 1 mscamera.ring0x0020_0006 |
camera.provide | Eine Kamera anbieten und Bilder hineinschieben (Test-Kamera). | ≤ 1 mscamera.provide0x0020_0007 |
| Roboter & Treiber | ||
safety.stop | Den Not-Halt auslösen und lösen. | ≤ 20 µssafety.stop0x0100_0002 |
safety.actuator | Aktoren treiben: bei Not-Halt sofort geweckt, muss sie sicher machen (Aktor-Dienst). | |
device.usb / device.can | Treiber: Register eines USB- bzw. CAN-FD-Controllers einblenden, DMA-Speicher. | ≤ 1 msdevices.open0x0009_0001 |
Profile (Dateien uses-…)
| Eintrag | Bedeutung | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Profile (Dateien uses-…) | ||
(keine Datei) | System, Files, Channel, Net, Apps, Settings, TextLine, WallClock, FileType, Maths – Echtzeit-tauglich, ohne Heap. | |
uses-heap | Arrays, Strings, Klassen; dazu die Ton-Bibliothek (WAV, FLAC, Pegel, Stimmerkennung). | |
uses-ui | Surface, Canvas, Views, Keyboard, InputField, UITheme, Chart, ScrollBar. | |
uses-image | Picture, PNG, JPEG, GIF, WebP, BMP, TIFF, PDF schreiben/lesen, Matroska (braucht uses-heap). | |
uses-archive | ZIP lesen/schreiben mit WinZip AES-256, Deflate (braucht uses-heap). | |
uses-tls | TLS 1.3 (SecureConnection), DNS über HTTPS, Krypto, Vertrauensanker. | |
uses-ai | Claude (Messages API) – braucht uses-tls und uses-heap. | |
uses-video / uses-opus | VP9-Decoder / Opus-Decoder (braucht uses-heap und uses-image). | |
uses-svg / uses-web / uses-sql | SVG-Renderer; Browser-Code; SQL-Übersetzer für Datenbanken (nur lesend). | |
uses-vision / uses-infer | AprilTag, Farbflecken, Bewegung / int8-Inferenz (Detektoren). | |
uses-qr / uses-httpserver | QR-Code-Encoder / HTTP/1.1-Server-Bausteine. | |
uses-reflex / uses-reflexrt | Reflex-Compiler und -Maschine (Develop) / Echtzeit-Laufzeit. | |
Beispiel: Manifest eines Programms mit Fenster, eigenen Dateien und Datei-Dialog (apps/reader, apps/dialogcheck)
# Reader: shows PDF documents.
name = reader
id = de.softrobots.reader
title = Reader
category = productivity
icon = icon.svg
capabilities = system.log, ui.surface
kind = system
opens = pdf
start = demand
memory = 768
# Programmordner daneben: uses-heap, uses-image, uses-svg, uses-uiBeispiel: Der Datei-Dialog selbst: nie im Dock (apps/chooser/manifest)
name = chooser
id = de.softrobots.chooser
title = File Dialog
category = system
capabilities = system.log, ui.surface, files.system, files.dialog
kind = system
start = demand
helper = display
# Never in the dock, not even while a dialog is open (2026-11-10).
dock = noFenster & Oberfläche
Ein Fenster ist eine Surface: Pixelspeicher und ein Nachrichtenring, die der Kern ins Programm und in den
Display-Dienst einblendet. Das System zeichnet Rahmen, Titel und Menüleiste; das Programm zeichnet seinen Inhalt mit
Canvas oder beschreibt ihn mit Views (VStack, Text, Button …), Farben kommen aus
UITheme. Die Ereignisschleife: nextEvent() bis nil, zeichnen, present, wait.
Jedes switch über SurfaceEvent braucht default: (neue Fälle kommen dazu).
SurfaceFenster des Programms
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Fenster anlegen | ||
static create(timeout: UInt64 = 10_000_000_000) -> Surface? | Fordert ein Fenster an (bis zu drei pro Programm), wartet notfalls auf den Display-Dienst. | ≤ 5 msdisplay.createSurface0x0011_0004 |
Beispielaus apps/text (gekürzt) | ||
requestSize(width: Int, height: Int) | Gewünschte Inhaltsgröße (der Nutzer kann sie ändern). | |
Beispielaus apps/sound (gekürzt) | ||
setTitle(_ text: TextLine) | Name über dem Fenster. | |
Beispielaus apps/web (gekürzt) | ||
destroy() | Schließt eines von mehreren Fenstern (bei nur einem: einfach beenden). | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
| Ereignisse holen | ||
mutating nextEvent() -> SurfaceEvent? | Nächste Nachricht des Dienstes, nil wenn keine wartet; .configure passt canvas an. | |
Beispielaus sdk-apps/greeter (gekürzt) | ||
wait(timeout: UInt64) | Schläft, bis der Dienst etwas schickt oder timeout ns vergehen. | wait (Systemaufruf 3) |
Beispielaus apps/reader (gekürzt) | ||
| Zeichnen & zeigen | ||
private(set) var canvas: Canvas | Die Zeichenfläche, die der Dienst gegeben hat (leer bis zum ersten configure). | |
beginDrawing() | Vor einem Bild aufrufen: der Dienst kopiert das Fenster erst nach present (kein Reißen). | |
Beispielaus apps/reader (gekürzt) | ||
present(_ rect: Rect) -> Bool | Zeigt das geänderte Rechteck; false wenn der Ring voll blieb (später erneut zeigen). | |
Beispielaus sdk-apps/greeter (gekürzt) | ||
presentChanges() | Zeigt alle seit beginDrawing geänderten Kacheln (DamageTracker). | |
| Zeiger, Vordergrund, Ziehen | ||
mutating setPointer(_ shape: PointerShape) | Zeigerform über dem Inhalt (.hand, .text, .arrow, .resize…). | |
Beispielaus sdk-apps/greeter (gekürzt) | ||
raise() | Holt das Fenster nach vorn (z. B. nach einem übergebenen Dokument). | |
Beispielaus apps/database (gekürzt) | ||
carry() | Lässt den Nutzer ein Dokument ziehen; Antwort kommt als .dropped. | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
| Interna | ||
let handle: UInt64 | Nummer des Fensters (für Systemaufruf surfaceNotify). | |
static let frameCounter / frameShown | Byte-Offsets im Ringblatt: Bildzähler (ungerade = wird gezeichnet) und zuletzt gezeigtes Bild. | |
SurfaceEventWas der Dienst dem Programm meldet
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Fenster | ||
case configure(width: Int, height: Int) | Größe geändert / Fenster bereit: neu zeichnen. | |
case focus(Bool) | Fenster bekommt/verliert den Fokus. | |
case close | Nutzer hat das Fenster geschlossen (⌘W): Programm beenden. | |
| Eingabe | ||
case key(code: UInt8, down: Bool) | Taste (HID-Code); durch Keyboard.handle schicken. | |
Beispielaus apps/reader (gekürzt) | ||
case pointer(x: Int, y: Int, buttons: UInt32) | Zeiger bewegt / Taste; Bit 1 (Wert 2) = Primärtaste gedrückt. | |
case scroll(x: Int, y: Int, tenths: Int) | Mausrad in Zehntel-Rasten, positiv vom Nutzer weg. | |
Beispielaus apps/reader (gekürzt) | ||
| Menü & Ziehen | ||
case menu(id: Int) | Ein Menüpunkt (Menüleiste, Kontextmenü oder Kürzel) wurde gewählt. | |
case dropped(program: InlineArray<32, UInt8>, length: Int, x: Int, y: Int) | Getragenes Dokument wurde über einem Programm losgelassen. | |
CanvasZeichnen in Pixeln (logische Punkte × scale)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Flächen | ||
fill(_ rect: Rect, _ color: RGB) | Rechteck füllen. | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
gradient(_ rect: Rect, from top: RGB, to bottom: RGB) | Senkrechter Verlauf. | |
blend(_ rect: Rect, _ color: RGB, alpha: UInt32) | Halbtransparent überlagern. | |
Beispielaus apps/develop (gekürzt) | ||
smoothRect(_ rect: Rect, radius: Int, top: RGB, bottom: RGB) | Abgerundetes Rechteck mit Verlauf, kantengeglättet. | |
| Text | ||
text(_ line: TextLine, x: Int, y: Int, color: RGB, scale requested: Int = 1) -> Int | Text in der Bitmap-Schrift; gibt die Breite zurück. | |
text(_ text: TextLine, x: Int, baseline: Int, face: FontFace, color: RGB) -> Int | Text in einer Systemschrift (.body, .heading …) auf eine Grundlinie. | |
Beispielaus apps/sound (gekürzt) | ||
static width(_ text: TextLine, face: FontFace) -> Int | Breite eines Texts in einer Schrift. | |
Beispielaus apps/sound (gekürzt) | ||
static textWidth(_ line: TextLine, scale: Int = 1) -> Int | Breite in der Bitmap-Schrift. | |
| Pixel & Farben | ||
pixel(_ x: Int, _ y: Int) -> UInt32 / setPixel(_:_:_:) | Einzelnen Punkt lesen/setzen (im Bildschirmformat). | |
encode(_ color: RGB) -> UInt32 / decode(_ value: UInt32) -> RGB | Farbe ↔ Pixelwert. | |
copy(from base: UInt64, stride sourceStride: Int, rect: Rect, to x: Int, _ y: Int, limit: Rect) | Pixel aus einem anderen Speicher kopieren (Bilder, Video). | |
| Teilflächen | ||
region(_ rect: Rect) -> Canvas | Teilfläche als eigene Canvas (Ursprung verschoben, beschnitten). | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
clip(_ rect: Rect) -> Rect | Rechteck auf die Fläche beschneiden. | |
scaled(_ scale: Int) -> Canvas | Dieselbe Fläche mit anderem Maßstab. | |
| Views zeichnen | ||
render<V: View>(_ view: V, in rect: Rect, _ ui: inout UI, background: RGB = UITheme.background) | Views messen und zeichnen; Buttons werden in ui eingetragen. | |
Beispielaus sdk-apps/greeter (gekürzt) | ||
| Maße | ||
let width, height, scale, stride, pixelWidth, pixelHeight, base | Maße (logisch und physisch) und Speicheradresse. | |
ViewsInhalt beschreiben statt zeichnen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Anordnen | ||
VStack(spacing: Int = 8, @ViewBuilder _ content: () -> Content) | Untereinander anordnen. | |
Beispielaus sdk-apps/greeter (gekürzt) | ||
HStack(spacing: Int = 8, @ViewBuilder _ content: () -> Content) | Nebeneinander anordnen. | |
Padded(_ amount: Int, @ViewBuilder _ content: () -> Content) | Innenabstand um einen Stapel. | |
Spacer() / Divider() | Dehnbarer Leerraum / Trennlinie. | |
| Inhalte | ||
Text(_ text: StaticString | TextLine, face: FontFace = .body, color: RGB = UITheme.text) | Ein Text in Systemschrift. | |
Button(_ title: StaticString | TextLine, id: Int, prominent: Bool = true, tint: RGB = UITheme.accent) | Knopf; seine id liefert ui.button(at:). | |
ProgressBar (value, maximum, color) | Fortschrittsbalken im Inhalt (für Aufgaben im Titel: Fortschritt). | |
| Eigene Views | ||
protocol View: measure(_:) / draw(in:on:_:) / flexible / compressible | Eigene Views: messen und zeichnen. | |
UIKnöpfe, Fokus, Tastatur-Navigation
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Knöpfe | ||
init() | Leerer Zustand (global halten). | |
button(at x: Int, _ y: Int) -> Int | id des Knopfs unter dem Zeiger (0: keiner). | |
Beispielaus sdk-apps/greeter (gekürzt) | ||
var pressed / var focused | Gedrückter bzw. fokussierter Knopf. | |
mutating button(_ canvas: Canvas, _ rect: Rect, id: Int, _ title: TextLine, kind: ButtonKind = .secondary, size: ButtonSize = .regular, symbol: ButtonSymbol? = nil, enabled: Bool = true, background: RGB = UITheme.background) | Einen Knopf direkt zeichnen und eintragen (ohne Views). | |
Beispielaus apps/settings (gekürzt) | ||
mutating beginFrame() / addButton(_ id: Int, _ rect: Rect) | Neues Bild beginnen / eigenen Bereich als Knopf eintragen. | |
| Tastatur-Navigation | ||
mutating key(_ press: Keyboard.Result, shift: Bool) -> Int | Tab bewegt den Fokus, Enter/Leertaste drückt; gibt die id des gedrückten Knopfs. | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
UITheme · ButtonLook · FileLookDie Farben und der Knopf-Look des Systems
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Farben | ||
static var background, ground, raised, line, text, muted, faint, accent, accentPressed, onAccent, good, warning, danger, selection, selectionText, chart | Systemfarben (dunkel oder hell, je nach Einstellung des Besitzers). | |
static var codeKeyword, codeValue, codeComment, codeShape | Farben für Quelltext (Develop). | |
static func use(light: Bool) / static var isLight: Bool | Helle oder dunkle Palette wählen / abfragen. | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
static let dark / static let light (UIDesign) | Die beiden Paletten als UIDesign-Werte. | |
| Knopf-Look | ||
ButtonLook.draw(_ canvas: Canvas, _ rect: Rect, _ kind: ButtonKind, _ size: ButtonSize, title: TextLine? = nil, symbol: ButtonSymbol? = nil, state: ButtonState = ButtonState(), background: RGB = UITheme.background) | Knopf im Systemlook zeichnen (Varianten nach ButtonKind, Größen nach ButtonSize). | |
ButtonLook.width(_ title: TextLine, _ size: ButtonSize) -> Int | Breite eines Knopfs. | |
ButtonLook.drawSymbol / roundedFill / colours | Symbol, gerundete Fläche, Farben eines Zustands. | |
| Dateifarben | ||
FileLook.colour(_ name: Span<UInt8>) -> RGB | Farbe, mit der Files einen Dateityp zeigt. | |
KeyboardTastencodes zu Zeichen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Tasten auswerten | ||
init() | Zustand der Umschalttasten. | |
mutating handle(code: UInt8, down: Bool) -> Result | Wertet ein .key-Ereignis aus: .character, .text, .special(…) oder .none. | |
Beispielaus apps/reader (gekürzt) | ||
static utf8(_ code: UInt32, into out: inout InlineArray<4, UInt8>) -> Int | Ein Zeichen als UTF-8. | |
| Umschalttasten | ||
private(set) var shift, control, option, command, altGr | Gedrückte Umschalttasten. | |
InputFieldEin Eingabefeld mit Auswahl, Undo und Zwischenablage
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Anlegen | ||
init(maximumLength: Int = 512, secret: Bool = false) | Feld anlegen; secret zeigt Punkte (Passwörter). | |
Beispielaus apps/reader (gekürzt) | ||
| Eingabe weitergeben | ||
mutating key(_ press: Keyboard.Result, _ keyboard: Keyboard) -> Change | Taste, solange das Feld den Fokus hat; Ergebnis .none/.moved/.edited/.submit/.cancel. | |
Beispielaus apps/reader (gekürzt) | ||
mutating command(_ id: Int) -> Change | Befehl des Bearbeiten-Menüs (EditMenu-ids) ausführen. | |
Beispielaus apps/reader (gekürzt) | ||
mutating pointer(_ x: Int, _ y: Int, down: Bool, pressed: Bool) -> Bool | Zeiger-Ereignis (Klick, Ziehen); true wenn das Feld es nahm. | |
Beispielaus apps/database (gekürzt) | ||
| Zeichnen | ||
mutating draw(_ canvas: Canvas, _ rect: Rect, focused: Bool) | Feld zeichnen: Rahmen, Text, Auswahl, Cursor. | |
Beispielaus apps/reader (gekürzt) | ||
contains(_ x: Int, _ y: Int) -> Bool | Liegt der Zeiger über dem Feld (für den Text-Zeiger)? | |
| Inhalt setzen & lesen | ||
mutating set(_ text: Span<UInt8> | TextLine) / clear() | Text ersetzen / leeren. | |
mutating select(from start: Int, to end: Int) | Cursor und Auswahl setzen. | |
withBytes<R>(_ body: (Span<UInt8>) -> R) -> R / var line: TextLine | Den Inhalt lesen (Bytes oder erste 160 Bytes als Zeile). | |
Beispielaus apps/reader (gekürzt) | ||
| Eigenschaften | ||
var placeholder, bordered, secret, face, textColour, cursorColour, maximumLength | Aussehen und Verhalten. | |
var isEmpty, hasSelection / private(set) var cursor, anchor, count, frame | Zustand des Felds. | |
Rect · Size · RGBGrundtypen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Grundtypen | ||
Rect(x: Int, y: Int, width: Int, height: Int) / right / bottom | Ein Rechteck in Fensterpunkten. | |
Size(width: Int, height: Int) | Eine Größe (Views messen damit). | |
RGB(_ red: UInt8, _ green: UInt8, _ blue: UInt8) / mixed(with other: RGB, _ amount: UInt32) -> RGB | Eine Farbe; Mischung zweier Farben. | |
Canvas.physicalPixel(_:_:) / setPhysicalPixel(_:_:_:) / shifts | Pixel im physischen Raster (ohne scale) und Farbformat des Bildschirms. | |
ScrollBar · ListBar · Chart · DamageTrackerBausteine
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Rollbalken | ||
ScrollBar.draw(_ canvas: Canvas, _ rect: Rect, total: Int, visible: Int, offset: Int, active: Bool = false) | Rollbalken im Systemlook (Variante B). | |
Beispielaus apps/files (gekürzt) | ||
ScrollBar.needed / thumb / part(at:…) / offset(after:…) / offset(dragTo:…) | Braucht es einen Balken, wo liegt der Griff, Klick und Ziehen auswerten. | |
ListBar.place(_ area: Rect, total: Int, visible: Int) -> Int / pointer(…) / draw(_:offset:) | Rollbalken für Listen mit eigenem Zustand. | |
| Diagramme | ||
Chart.frame(_ canvas: Canvas, _ rect: Rect, low: Double, high: Double, lines: Int, labelWidth: Int, …, label: (Double) -> TextLine) -> Plot | Diagrammrahmen mit Gitter und Beschriftung; liefert die Zeichenfläche. | |
Beispielaus apps/activity (gekürzt) | ||
Chart.series(_ canvas: Canvas, _ plot: Plot, count: Int, slots: Int, colour: RGB, area: Bool, value: (Int) -> Double) | Eine Messreihe als Linie oder Fläche. | |
Beispielaus apps/activity (gekürzt) | ||
Chart.cursor / endPoint / dot / niceTop / roundedPanel | Cursor, Endpunkt mit Wert, Punkt, runde Obergrenze, Panel. | |
Plot(area: Rect, low: Double, high: Double): x(of:count:slots:) / y(_:) / index(at:count:slots:) | Umrechnung Wert ↔ Pixel. | |
| Geänderte Kacheln | ||
DamageTracker.changes(_ canvas: Canvas, handle: UInt64, _ changed: (Rect) -> Void) | Findet geänderte Kacheln (für presentChanges). | |
ScreenNur der Display-Dienst (display.own)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Bildschirm | ||
static open() -> (canvas: Canvas, needsFlush: Bool)? | Übernimmt den Bildschirm. | ≤ 5 msdisplay.open0x0011_0001 |
Beispielaus apps/display (gekürzt) | ||
static flush(_ rect: Rect) | Zeigt ein geändertes Rechteck (virtio-gpu). | ≤ 200 µsdisplay.flush0x0011_0002 |
static saveScreenshot() -> Bool | Speichert den Bildschirm als PNG in /data/screenshots. | ≤ 50 msdisplay.screenshot0x0011_0003 |
| Fenster & Eingabe | ||
static surface(_ index: Int) -> SurfaceInfo | Fenster Nummer index aus Sicht des Dienstes. | ≤ 20 µsdisplay.surface0x0011_0005 |
static input(into events: inout InlineArray<96, UInt64>) -> Int | Eingabe-Ereignisse abholen. | inputReceive (Systemaufruf 12) |
static wait(timeout: UInt64) / notify(_ index: Int) | Auf Programme warten / ein Fenster wecken. | wait / surfaceNotify |
Beispiel: Ein Fenster mit Menü, Knopf und Ereignisschleife (sdk-apps/greeter)
nonisolated(unsafe) var ui = UI()
nonisolated(unsafe) var clicks = 0
enum Buttons { static let count = 1 }
@c(app_main)
func appMain() {
guard var surface = Surface.create() else { System.log("greeter: no window"); return }
surface.requestSize(width: 360, height: 220)
surface.addMenu(0, "Counter")
surface.addItem(0, "Count", id: Buttons.count, key: 0x6E) // Cmd+N
surface.publishMenus()
var dirty = true
while true {
while let event = surface.nextEvent() {
switch event {
case .configure: dirty = true
case .close: System.exit(0)
case .menu(let id): if id == Buttons.count { clicks += 1; dirty = true }
case .pointer(let x, let y, let buttons):
let under = ui.button(at: x, y)
surface.setPointer(under != 0 ? .hand : .arrow)
if buttons & 2 != 0 {
if ui.pressed == 0 { ui.pressed = under; dirty = true }
} else if ui.pressed != 0 {
if under == ui.pressed && under == Buttons.count { clicks += 1 }
ui.pressed = 0
dirty = true
}
default: break
}
}
if dirty && surface.canvas.width > 0 {
draw(surface.canvas)
surface.present(Rect(x: 0, y: 0, width: surface.canvas.width, height: surface.canvas.height))
dirty = false
}
surface.wait(timeout: 500_000_000)
}
}Beispiel: Inhalt mit Views beschreiben (sdk-apps/greeter)
private func draw(_ canvas: Canvas) {
var text = TextLine("Clicked ")
text.append(decimal: UInt64(clicks))
text.append(clicks == 1 ? " time" : " times")
let view = Padded(20) {
VStack(spacing: 14) {
Text("greeter", face: .heading)
Text(text, face: .body, color: UITheme.muted)
Button("Count", id: Buttons.count)
Spacer()
}
}
canvas.render(view, in: Rect(x: 0, y: 0, width: canvas.width, height: canvas.height), &ui)
}Beispiel: Tastatur und Suchfeld (apps/reader)
nonisolated(unsafe) var keyboard = Keyboard()
nonisolated(unsafe) var searchInput = InputField(maximumLength: 120)
// beim Start
searchInput.placeholder = TextLine("Search")
// bei case .key(let code, let down):
let press = keyboard.handle(code: code, down: down)
if searchFocused {
switch searchInput.key(press, keyboard) {
case .edited: search(); return true
case .submit: return step(keyboard.shift ? -1 : 1, canvas)
case .cancel: searchFocused = false; return true
case .moved: return true
case .none: break
}
}
// beim Zeichnen
searchInput.draw(canvas, field, focused: searchFocused)Beispiel: Nur geänderte Teile zeigen (apps/reader)
if dirty && surface.canvas.width > 0 {
surface.beginDrawing() // der Dienst kopiert jetzt nicht mitten ins Bild
draw(surface.canvas)
surface.presentChanges() // nur die geänderten Kacheln
dirty = false
}
surface.wait(timeout: 500_000_000)Fortschritt
Neu (10.11.). Ein Programm sagt, was es tut und wie weit es ist („wie viel von wie viel“); das System zeichnet es
überall gleich: eine Kapsel im Fenstertitel mit Balken, Prozent und Restzeit, ein dünner Balken auf der Dock-Kachel; ein
Klick zeigt Details und – wenn erlaubt – Abbrechen. Buddy meldet, wenn eine lange Aufgabe fertig ist, während das Fenster
nicht vorn ist. Höchstens 4 Aufgaben pro Fenster; Meldungen gehen höchstens 10-mal pro Sekunde raus. Kein eigenes Recht
nötig (das Fenster genügt, ui.surface).
Surface (Fortschritt)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Aufgabe starten | ||
progress(_ title: TextLine, total: UInt64, unit: ProgressUnit = .none, cancellable: Bool = false) -> ProgressTask? neu | Startet eine Aufgabe im Titel und im Dock; total 0 = unbekannt (laufender Streifen). Nil bei schon 4 Aufgaben. | surfaceNotify (Systemaufruf 11) |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
| Stand melden | ||
progress(_ task: ProgressTask, done: UInt64) neu | Wie weit die Aufgabe ist (gedrosselt auf 10/s). | |
Beispielaus apps/web (gekürzt) | ||
| Beenden | ||
progress(_ task: ProgressTask, finished end: ProgressEnd, done: UInt64? = nil) neu | Aufgabe beendet; bleibt kurz grün (oder rot) im Titel. | |
Beispielaus apps/web (gekürzt) | ||
| Abbrechen erkennen | ||
isCancelled(_ task: ProgressTask) -> Bool neu | Hat die Person Abbrechen gedrückt? Jederzeit fragen, auch mitten in einer Schleife. | |
Beispielaus apps/web (gekürzt) | ||
static let cancelRequests neu | Byte-Offset der Abbruch-Bits im Ringblatt (ein Bit pro Aufgabe). | |
Typen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Typen | ||
enum ProgressUnit: UInt8 { none, bytes, items } neu | Was done und total zählen. | |
enum ProgressEnd: UInt8 { done, failed, cancelled } neu | Wie eine Aufgabe endete. | |
struct ProgressTask { let id: Int } neu | Eine laufende Aufgabe des Fensters. | |
SurfaceMessage.progress neu | Nachrichtenart 12 (Programm → Dienst). | |
Beispiel: Download mit Fortschritt und Abbrechen (apps/web, Fetch.swift, gekürzt)
// Lange Antworten zeigen ihren Fortschritt (das System zeichnet ihn).
if task == nil {
task = surface?.progress(TextLine("Download Big_Buck_Bunny.webm"),
total: UInt64(response.expectedLength ?? 0), unit: .bytes, cancellable: true)
}
if let running = task {
surface?.progress(running, done: UInt64(response.body.count))
if surface?.isCancelled(running) == true {
cancelled = true
break
}
}
// am Ende (defer):
if let running = task {
surface?.progress(running, finished: cancelled ? .cancelled : result.problem == nil ? .done : .failed)
task = nil
}Dateien & Einstellungen
Mit files.own beginnt jeder Pfad im eigenen Ordner /data/<id> – sonst existiert nichts.
files.system sieht das ganze Volume (/apps nur lesend, system/…, in der VM mac/…);
files.mac ergänzt den eigenen Ordner um den Mac-Ordner. Pfade mit „/“, „.“ und „..“ werden abgelehnt.
Änderungen gelten, wenn die Datei geschlossen wird; pro Systemaufruf höchstens 16 KiB. Ein Programm mit
opens = … bekommt sein übergebenes Dokument mit File.openDocument ohne Datei-Recht, eine im Dialog
gewählte Datei mit File.open(grant, mode:).
Settings sind kleine, private Einstellungen pro Programm (key = value, höchstens 4 KiB), vom System in
DATA gehalten – kein Datei-Recht nötig.
Hinweis: File, Files und FileError liegen in userland/shapes/ und stehen
(noch) nicht in app-api-1.txt – tools/api/symbols.py liest diesen Ordner nicht. Sie gehören trotzdem zur
App-API (README, Abschnitt Files).
FileEine geöffnete Datei
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Öffnen | ||
static open(_ path: Span<UInt8> | StaticString, mode: Mode) -> Result<File, FileError> | Öffnet eine Datei; Modus .read, .replace (anlegen/leeren), .append, .update (lesen und schreiben an jeder Stelle). | ≤ 2 msfiles.open0x0003_0001 |
Beispielaus sdk-apps/notes (gekürzt) | ||
static openDocument(mode: Mode) -> Result<File, FileError> | Öffnet das von Files übergebene Dokument (siehe Apps.document). | ≤ 2 msfiles.open0x0003_0001 |
Beispielaus apps/reader (gekürzt) | ||
static open(_ grant: FileGrant, mode: Mode) -> Result<File, FileError> neu | Öffnet eine im Datei-Dialog gewählte Datei. | ≤ 2 msfiles.open0x0003_0001 |
| Lesen | ||
read(into buffer: inout MutableSpan<UInt8>) -> Result<Int, FileError> | Liest die nächsten Bytes (höchstens 16 KiB); 0 am Ende. | ≤ 2 msfiles.read0x0003_0002 |
Beispielaus sdk-apps/notes (gekürzt) | ||
read(into buffer: inout MutableSpan<UInt8>, at offset: UInt64) -> Result<Int, FileError> | Liest ab einer beliebigen Stelle. | ≤ 2 msfiles.read0x0003_0002 |
Beispielaus apps/sound (gekürzt) | ||
| Schreiben | ||
write(_ data: Span<UInt8>) -> FileError? | Hängt alle Bytes an (in Stücken zu 16 KiB). | ≤ 2 msfiles.write0x0003_0003 |
Beispielaus sdk-apps/notes (gekürzt) | ||
write(_ data: Span<UInt8>, at offset: UInt64) -> FileError? | Schreibt an eine Stelle (höchstens bis zur Größe: keine Löcher). | ≤ 2 msfiles.write0x0003_0003 |
Beispielaus apps/sound (gekürzt) | ||
| Schließen | ||
close() -> FileError? | Schließt und übernimmt die Änderungen. | ≤ 5 msfiles.close0x0003_0004 |
Beispielaus sdk-apps/about (gekürzt) | ||
| Konstanten | ||
static let chunk = 16384 / let handle: UInt64 | Bytes pro Systemaufruf / Nummer der Datei. | |
FilesOrdner, Infos und Änderungen über Pfade
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Ordner | ||
static entry(in path: Span<UInt8>, at index: Int, snapshot: UInt64? = nil) -> Result<Entry?, FileError> | Der index-te Eintrag eines Ordners (optional wie in einem Snapshot); nil nach dem letzten. Index 0 liest neu. | ≤ 2 msfiles.list0x0003_0005 |
Beispielaus apps/testcamera (gekürzt) | ||
static makeDirectory(_ path: Span<UInt8>) -> FileError? | Ordner anlegen (leerer Pfad: der eigene Ordner). | ≤ 2 msfiles.makeFolder0x0003_0007 |
Beispielaus apps/sound (gekürzt) | ||
struct Entry { isDirectory, size, modified, name, nameLength } | Ein Ordnereintrag. | |
| Info | ||
static info(_ path: Span<UInt8> | StaticString, snapshot: UInt64? = nil) -> Result<(isDirectory: Bool, size: UInt64, modified: UInt64), FileError> | Ordner?, Größe (Ordner: Gesamtgröße), letzte Änderung. | ≤ 200 µsfiles.info0x0003_0006 |
Beispielaus apps/shapecheck (gekürzt) | ||
static volume(system: Bool = false) -> InlineArray<5, UInt64>? | DATA oder Systemvolume: Blockgröße, Blöcke, belegt, Snapshots, Generation. | ≤ 50 µsfiles.volume0x0003_000B |
Beispielaus apps/activity (gekürzt) | ||
| Ändern | ||
static remove(_ path: Span<UInt8> | StaticString) -> FileError? | Datei oder leeren Ordner löschen. | ≤ 2 msfiles.remove0x0003_0008 |
Beispielaus apps/shapecheck (gekürzt) | ||
static rename(_ path: Span<UInt8>, to newPath: Span<UInt8>, replacing: Bool) -> FileError? | Verschieben/umbenennen; mit replacing atomar über eine vorhandene Datei. | ≤ 2 msfiles.rename0x0003_0009 |
Beispielaus apps/sharecheck (gekürzt) | ||
static replace(_ path: Span<UInt8>, with data: Span<UInt8>) -> FileError? | Ganzen Inhalt atomar ersetzen (erst .new, dann umbenennen). | ≤ 2 msfiles.open + files.rename0x0003_0001 |
Beispielaus apps/settings (gekürzt) | ||
| Zeigen | ||
static reveal(_ path: Span<UInt8>) -> FileError? | Zeigt eine Datei des eigenen Ordners in Files (Web-Downloads; braucht apps.launch). | ohne Budgetfiles.reveal0x0003_000A |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
Files (Snapshots, files.system)Der Zeitschieber
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Schnappschüsse | ||
static snapshot(after previous: UInt64) -> Snapshot? | Der Snapshot nach previous (0: der älteste). | ≤ 50 µsfiles.snapshot0x0003_000C |
Beispielaus apps/files (gekürzt) | ||
static takeSnapshot() -> Result<UInt64, FileError> | Jetzt einen Snapshot nehmen (bleibt bis gelöscht). | ≤ 50 msfiles.takeSnapshot0x0003_000D |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
static deleteSnapshot(_ id: UInt64) -> FileError? | Snapshot löschen und nur von ihm belegten Platz freigeben. | ≤ 50 msfiles.deleteSnapshot0x0003_000E |
Beispielaus apps/slidercheck (gekürzt) | ||
| Wiederherstellen | ||
static restore(_ path: Span<UInt8>, snapshot: UInt64) -> Result<(name: InlineArray<80, UInt8>, length: Int), FileError> | Datei/Ordner aus einem Snapshot zurückkopieren; nichts wird überschrieben ("name (HH.MM).ext"). | ≤ 2 sfiles.restore0x0003_000F |
Beispielaus apps/files (gekürzt) | ||
FileError
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| FileError | ||
refused, badArgument, notFound, exists, notADirectory, notEmpty, noSpace, ioError, isADirectory, tooManyOpen, inUse, locked, wrongContent | Warum eine Dateioperation scheiterte; locked = DATA noch versiegelt, wrongContent = Inhalt passt nicht zur Endung. | |
var text: StaticString / static from(_ result: UInt64) -> FileError? | Kurzer englischer Satz für Meldungen / Fehler aus einem Rückgabewert. | |
FileTypeDokumenttypen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Dokumenttypen | ||
static of(_ name: Span<UInt8>) -> FileType | Typ nach der Endung. | |
var label: StaticString / isText / isDatabase | Anzeigename und Art. | |
accepts(_ prefix: Span<UInt8>, complete: Bool) -> Bool | Passt der Inhaltsanfang zum Typ? | |
static extensionRange / validUTF8 / namesSVGElement / equal / maxExtension | Hilfen zu Endungen und Inhalt. | |
SettingsEigene Einstellungen (ohne Datei-Recht)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Laden | ||
static load() -> Settings? | Die zuletzt gesicherten Einstellungen (beim ersten Mal leer); nil solange DATA gesperrt ist und für Echtzeit-Programme. | ≤ 200 µsprograms.settings0x0006_0002 |
Beispielaus apps/apicheck (gekürzt) | ||
| Lesen | ||
value(_ key: StaticString) -> TextLine? | Wert eines Schlüssels. | |
Beispielaus apps/apicheck (gekürzt) | ||
number(_ key: StaticString) -> Int? | Wert als ganze Zahl. | |
Beispielaus apps/apicheck (gekürzt) | ||
| Setzen | ||
mutating set(_ key: StaticString, _ value: TextLine) -> Bool | Setzt einen Text; false wenn 4 KiB überschritten würden. | |
Beispielaus apps/apicheck (gekürzt) | ||
mutating set(_ key: StaticString, number: Int) -> Bool | Setzt eine Zahl. | |
Beispielaus apps/apicheck (gekürzt) | ||
| Sichern | ||
save() -> Bool | Schreibt die Einstellungen. | ≤ 2 msprograms.saveSettings0x0006_0003 |
Beispielaus apps/apicheck (gekürzt) | ||
Beispiel: Eigene Datei lesen und schreiben (sdk-apps/notes)
// manifest: capabilities = system.log, ui.surface, files.own (+ uses-heap)
guard case .success(let file) = File.open("notes.txt", mode: .read) else { return }
var bytes: [UInt8] = []
var chunk = InlineArray<1024, UInt8>(repeating: 0)
while true {
var span = chunk.mutableSpan
guard case .success(let count) = file.read(into: &span), count > 0 else { break }
for index in 0..<count { bytes.append(chunk[index]) }
}
file.close()
guard case .success(let out) = File.open("notes.txt", mode: .replace) else { return }
bytes.withUnsafeBufferPointer { buffer in _ = out.write(Span(_unsafeElements: buffer)) }
out.close()Beispiel: Einen Ordner auflisten (apps/testcamera)
var index = 0
while case .success(let entry?) = folder.withUnsafeBufferPointer({ Files.entry(in: Span(_unsafeElements: $0), at: index) }) {
// entry.name[0..<entry.nameLength], entry.isDirectory, entry.size, entry.modified
index += 1
}Beispiel: Das übergebene Dokument öffnen (apps/reader)
// manifest: opens = pdf – kein files-Recht nötig
guard let document = Apps.document(), document.generation != generation else { return false }
generation = document.generation
switch File.openDocument(mode: .read) {
case .failure(let error): message = TextLine(error.text)
case .success(let file): // ... lesen, file.close()
}Beispiel: Einstellungen merken (docs/api/README.md)
if var settings = Settings.load() {
let starts = (settings.number("starts") ?? 0) + 1
settings.set("starts", number: starts)
settings.set("greeting", TextLine("hello robot"))
_ = settings.save()
}Datenbank (SOFTrobotsDB)
Tabellen mit typisierten Feldern, gehalten und geprüft vom Kern (Modul db). Ein Programm beschreibt seine
Tabellen als gespeicherte Shapes in data.shape; shapec --database erzeugt daraus Swift-Code
(openDatabase, je Tabelle ein Typ mit insert, update, delete, set…,
row und den Feldern). Regeln wie unique, Bereiche und Verweise prüft der Kern bei jedem Schreiben;
ein Schreibsatz gilt ganz oder gar nicht. Kein Heap: Schreibsätze, Abfragen und Seiten halten ihre Bytes inline (als
globale Variablen anlegen). Die eigenen Datenbanken liegen im eigenen Ordner (files.own); fremde schreiben
nur mit database.edit.
Wie Dateien liegt die Datenbank-Bibliothek in userland/shapes/ und steht nicht in
app-api-1.txt.
Database
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Öffnen & schließen | ||
openDatabase(_ path: StaticString) -> Result<Database, DatabaseError> | Vom Generator erzeugt: öffnet/legt an und gleicht die Tabellen ans Schema an (Felder hinzu, umbenannt, weg – ohne Zeilen umzuschreiben). | ≤ 2 msdb.open0x0004_0001 |
Beispielaus apps/dbcheck (gekürzt) | ||
static open(_ path: StaticString, schema: Span<UInt8>) -> Result<Database, DatabaseError> | Dasselbe mit einem kompilierten Schema. | ≤ 2 msdb.open0x0004_0001 |
static open(_ path: Span<UInt8>) -> Result<Database, DatabaseError> | Öffnet, wie sie ist (Werkzeuge wie Konsole und Database). | ≤ 2 msdb.open0x0004_0001 |
Beispielaus apps/database (gekürzt) | ||
close() | Schließen. | ≤ 50 µsdb.close0x0004_0005 |
Beispielaus apps/dbcheck (gekürzt) | ||
| Schreiben | ||
write<let capacity: Int>(_ set: inout WriteSet<capacity>, soon: Bool = false) -> Result<Int, DatabaseError> | Wendet einen Schreibsatz an (alles oder nichts); gibt die Zahl eingefügter Zeilen. soon: kehrt sofort zurück, auf der Platte binnen 10 ms. | ≤ 1 msdb.write0x0004_0002 |
Beispielaus apps/dbcheck (gekürzt) | ||
| Abfragen | ||
next(_ query: inout Query<q>, into page: inout QueryPage<output, cursor>) -> Bool | Führt eine Abfrage aus bzw. holt die nächste Seite; false wenn fertig oder Fehler (page.error). | ≤ 5 msdb.query0x0004_0003 |
Beispielaus apps/dbcheck (gekürzt) | ||
next(query: Span<UInt8>, aggregate: Bool, grouped: Bool, into page: …) -> Bool | Dasselbe für eine Abfrage aus SQL.translate (uses-sql). | ≤ 5 msdb.query0x0004_0003 |
firstValue<R>(query: Span<UInt8>, _ body: (Span<UInt8>?) -> R) -> R | Erster Wert einer übersetzten Abfrage. | ≤ 5 msdb.query0x0004_0003 |
| Beschreibung | ||
describe(into buffer: inout MutableSpan<UInt8>) -> Result<Int, DatabaseError> | Tabellen, Felder und Indizes. | ≤ 1 msdb.describe0x0004_0004 |
WriteSet<capacity>Ein Schreibsatz
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Satz anlegen | ||
init() / clear() / var isEmpty | Leerer Satz. | |
| Operationen | ||
insert(_ table: StaticString, id: UInt64 = 0) / update(_:id:) / delete(_:id:) | Operation beginnen (der erzeugte Code ruft das für dich). | |
set(_ field: Int, bytes: | bool: | int:size: | unsigned:size: | float: | double: | text:) / setNull(_:) | Feldwerte der aktuellen Operation. | |
| Ergebnis | ||
internal(set) var insertedIDs / insertedCount | ids der eingefügten Zeilen. | |
Query<capacity>Eine Abfrage
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Tabelle | ||
init(_ table: StaticString) | Abfrage auf einer Tabelle. | |
Beispielaus apps/dbcheck (gekürzt) | ||
| Filtern | ||
where(_ condition: Condition) / whereAny(_:_:_:_:) | Bedingung (UND) / bis zu vier ODER-Bedingungen. | |
Beispielaus apps/dbcheck (gekürzt) | ||
| Sortieren & begrenzen | ||
sorted(by field: Int, descending: Bool = false) / sorted(by:via:descending:) | Sortieren (auch über einen Verweis, Join). | |
Beispielaus apps/dbcheck (gekürzt) | ||
limit(_ rows: Int) / select(_ field: Int) | Höchstens n Zeilen / nur bestimmte Felder. | |
Beispielaus apps/dbcheck (gekürzt) | ||
| Aggregate | ||
count(groupedBy:) / sum / minimum / maximum / average (_ field: Int, groupedBy: Int? = nil) | Aggregate, optional gruppiert. | |
Beispielaus apps/dbcheck (gekürzt) | ||
Felder & Bedingungen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Felder & Bedingungen | ||
NumberField: == != < <= > >= Int64, isNull, isNotNull, via(_ ref:) | Bedingungen auf Zahlenfeldern; via = Feld der verwiesenen Tabelle (Join, höchstens zwei Verweise). | |
Beispielaus apps/dbcheck (gekürzt) | ||
FloatField: == != < <= > >= Double | Bedingungen auf Gleitkommafeldern. | |
TextField: == StaticString, equals, startsWith, contains, before, after | Bedingungen auf Textfeldern. | |
QueryPage<output, cursor>Eine Ergebnisseite
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Seite | ||
init() / reset() | Seite anlegen / für eine neue Abfrage leeren. | |
rowCount, error, plan, index | Zeilen, Fehler, gewählter Plan (Index, Bereich …). | |
| Werte lesen | ||
id(_ row: Int) / number(_:_:signed:) / float / double / text / withBytes / isNull / value | Werte einer Zeile lesen. | |
aggregate(_ row: Int) -> (rows: UInt64, value: Int64?) / groupNumber / withGroupBytes | Ergebnis eines Aggregats bzw. einer Gruppe. | |
Beispielaus apps/dbcheck (gekürzt) | ||
Series & Fehler
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Messreihen | ||
Series.info(_ path: Span<UInt8>) -> Result<Info, FileError> | Was eine Messreihe hält: Satzgröße, Sätze, erste/letzte Zeit. | ≤ 5 msdb.readSeries0x0004_0006 |
Series.read(_ path: Span<UInt8>, from: UInt64, to: UInt64, …) | Sätze eines Zeitbereichs (Flugschreiber, Recorder). | ≤ 5 msdb.readSeries0x0004_0006 |
| Fehler | ||
DatabaseError: .system(FileError), .rule(DatabaseRule, operation:, field:) | Systemfehler oder verletzte Regel (duplicate, outOfRange, badReference …). | |
Beispielaus apps/dbcheck (gekürzt) | ||
Beispiel: Tabellen beschreiben (apps/dbcheck/data.shape)
enum JointStatus { ok @0, warning @1, fault @2 }
shape Joint stored as joints {
name: Text(32) unique
angle: Int32 in -180000 ... 180000
current: Float32?
status: JointStatus = .ok
index status, angle
index angle
}
shape Move stored as moves {
joint: Joint
at: Time
target: Int32
}Beispiel: Schreiben, Regeln, Abfrage über Index, Gruppen (apps/dbcheck)
nonisolated(unsafe) var set = WriteSet<65536>()
nonisolated(unsafe) var query = Query<512>(Joints.table)
nonisolated(unsafe) var page = QueryPage<16384, 1024>()
guard case .success(let robot) = openDatabase("robot.db") else { return fail("cannot open robot.db") }
Joints.insert(&set, name: name.span.extracting(0..<length), angle: 45_000)
Joints.setStatus(&set, .warning)
guard case .success(1) = robot.write(&set) else { return fail("insert failed") }
// Regeln prüft der Kern:
guard case .failure(.rule(.duplicate, operation: 0, field: 1)) = robot.write(&set) else { ... }
// Die zehn größten Winkel über 90°, über den Index rückwärts
query = Query(Joints.table)
query.where(Joints.angle > 90_000)
query.sorted(by: Joints.angle.index, descending: true)
query.limit(10)
page.reset()
while robot.next(&query, into: &page) || page.rowCount > 0 {
for row in 0..<page.rowCount { let joint = Joints.row(&page, row); _ = joint.angle }
if page.rowCount == 0 { break }
}
// Join: Bewegungen, deren Gelenk den Status warning hat
query = Query(Moves.table)
query.where(Joints.status.via(Moves.joint) == 1)
query.count()Netzwerk & TLS
Ausgehend mit network.outgoing: TCP (IPv4/IPv6) und UDP zu genau einem Server. Eine Zeile
connects = host, host begrenzt die erreichbaren Hosts; Namen löst dann nur der Kern auf. Eingehend mit
network.incoming und listens = 443, 80 (nur das Programm Server; nie während eines Not-Halts) – es gibt
keinen öffentlichen Webserver, das Tor liegt im Heimnetz plus WireGuard.
TLS 1.3 kommt mit uses-tls als Bibliothek (SecureConnection: Zertifikatskette gegen die
Vertrauensanker des Systems, DNS über HTTPS, bis zu 4 Verbindungen). Claude mit uses-ai; der API-Schlüssel
ist das Geheimnis des Programms (Settings › Privatsphäre).
SecureConnection und Claude sind Bibliotheken der Profile uses-tls/uses-ai und nicht in
app-api-1.txt erfasst.
NetConnectionEine TCP-Verbindung
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Namen auflösen | ||
static resolve(_ name: StaticString | Span<UInt8>) -> UInt32? | IPv4-Adresse eines Hostnamens (DNS des Netzes). | ≤ 6 snet.resolve0x0005_0003 |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
static resolveIPv6(_ name: Span<UInt8>) -> InlineArray<16, UInt8>? | IPv6-Adresse (AAAA). | ≤ 6 snet.resolve0x0005_0003 |
| Verbinden | ||
init?(address: UInt32, port: UInt16) | Verbindung öffnen (Handshake läuft im Hintergrund). | ≤ 5 snet.connect0x0005_0004 |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
init?(ipv6 address: InlineArray<16, UInt8>, port: UInt16) | Über IPv6. | ≤ 5 snet.connect0x0005_0004 |
| Senden & empfangen | ||
send(_ data: Span<UInt8>) -> Bool | Alle Bytes senden (wartet bei vollem Puffer). | ≤ 1 snet.send0x0005_0005 |
receive(into buffer: inout InlineArray<1024, UInt8>, timeout: UInt64 = 1_000_000_000) -> Int? | Bis 1 KiB empfangen; 0 = noch nichts, nil = Gegenseite hat geschlossen. | ≤ 10 snet.receive0x0005_0006 |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
| Schließen | ||
close() | Senderichtung schließen; das System gibt die Verbindung frei, wenn TCP fertig ist. | ≤ 50 µsnet.close0x0005_0007 |
DatagramSocketUDP zu einem Server
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Öffnen | ||
init?(address: UInt32, port: UInt16) | Verbundener UDP-Socket: Datagramme von anderswo kommen nie an. | ≤ 5 snet.openDatagram0x0005_0008 |
| Senden & empfangen | ||
send(_ data: Span<UInt8>) -> Bool | Ein Datagramm (höchstens 1472 Bytes). | ≤ 1 snet.sendDatagram0x0005_0009 |
receive(into buffer: inout InlineArray<512, UInt8>, timeout: UInt64) -> Int? | Ein Datagramm abwarten; 0 wenn keines kam. | ≤ 10 snet.receiveDatagram0x0005_000A |
| Schließen | ||
close() | Schließen. | ≤ 50 µsnet.closeDatagram0x0005_000B |
NetListener · NetPeerEingehend (network.incoming)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Lauschen | ||
init?(port: UInt16, backlog: UInt8 = 8) | Lauschen; nil wenn der Port nicht im Manifest steht, belegt ist oder Not-Halt gilt. | ≤ 50 µsnet.listen0x0005_000C |
Beispielaus apps/listencheck (gekürzt) | ||
close() | Nicht mehr lauschen; angenommene Verbindungen bleiben. | ≤ 50 µsnet.closeListener0x0005_000F |
| Annehmen | ||
accept(timeout: UInt64 = 1_000_000_000) -> (connection: NetConnection, peer: NetPeer)? | Nächste Verbindung annehmen. | ≤ 10 snet.accept0x0005_000D |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
NetPeer: ipv4, ipv6, overIPv6, port | Wer sich verbunden hat. | |
| Zustand | ||
info() -> ListenerInfo? | Angenommen, abgelehnt, halb offen, wartend. | ≤ 20 µsnet.listenerInfo0x0005_000E |
SecureConnection (uses-tls)TLS 1.3
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Verbinden | ||
static open(_ host: StaticString, port: UInt16 = 443, alpn: StaticString = "", timeout: UInt64 = 10_000_000_000) -> SecureConnection? | Auflösen (DNS über HTTPS), verbinden, Handshake mit Zertifikatsprüfung. | ≤ 5 snet.connect0x0005_0004 |
Beispielaus apps/https (gekürzt) | ||
static open(host: Span<UInt8>, port: UInt16 = 443, timeout: UInt64 = 10_000_000_000) -> SecureConnection? | Für einen erst zur Laufzeit bekannten Host (Browser). | ≤ 5 snet.connect0x0005_0004 |
static open(address: UInt32, serverName host: StaticString, port: UInt16 = 443, alpn: StaticString = "", timeout: UInt64 = 10_000_000_000, dns: Bool = false) -> SecureConnection? | Zu einer bekannten Adresse. | ≤ 5 snet.connect0x0005_0004 |
| Senden & empfangen | ||
send(_ data: Span<UInt8>) -> Bool | Verschlüsseln und senden. | ≤ 1 snet.send0x0005_0005 |
receive(into out: inout InlineArray<1024, UInt8>, timeout: UInt64 = 5_000_000_000) -> Int? | Entschlüsselte Bytes; 0 = noch nichts, nil = geschlossen. | ≤ 10 snet.receive0x0005_0006 |
Beispielaus apps/https (gekürzt) | ||
| Schließen | ||
close() | close_notify senden und schließen. | ≤ 50 µsnet.close0x0005_0007 |
| Diagnose | ||
static lastError / handshakeTime / handshakes / lastSlot | Warum open scheiterte, Dauer des letzten Handshakes. | |
Claude (uses-ai)Eine Frage an die Messages API
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Eine Frage an die Messages API | ||
Claude.ask(_ question: String, jpeg: [UInt8]? = nil, model: String = defaultModel, maxTokens: Int = 700) -> Answer | Fragt (optional mit Bild) und wartet auf die ganze Antwort (höchstens eine Minute). Kamerabilder nur, wenn der Besitzer es erlaubt hat. | ≤ 5 snet.connect …0x0005_0004 |
Beispielaus apps/ask (gekürzt) | ||
Claude.Answer: text, problem, milliseconds, incomplete | Antworttext oder Grund, Dauer. | |
Beispiel: HTTPS-Abruf mit TLS 1.3 (apps/https)
// manifest: capabilities = system.log, network.outgoing (+ uses-tls)
guard let secure = SecureConnection.open("example.com") else {
var line = TextLine("https: ")
line.append(SecureConnection.lastError)
System.log(line)
return
}
let request: StaticString = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"
let sent = request.withUTF8Buffer { bytes in secure.send(Span(_unsafeElements: bytes)) }
guard sent else { System.log("https: sending failed"); return }
var buffer = InlineArray<1024, UInt8>(repeating: 0)
while let count = secure.receive(into: &buffer) {
if count == 0 { break }
// ... buffer[0..<count]
}
secure.close()Beispiel: Klartext-TCP (Muster nach userland/Net.swift)
guard let address = NetConnection.resolve("example.com"),
let connection = NetConnection(address: address, port: 80) else { return }
let request: StaticString = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"
_ = request.withUTF8Buffer { connection.send(Span(_unsafeElements: $0)) }
var buffer = InlineArray<1024, UInt8>(repeating: 0)
while let count = connection.receive(into: &buffer, timeout: 2_000_000_000), count > 0 {
// ...
}
connection.close()Beispiel: Claude fragen (apps/ask)
// manifest: capabilities = …, network.outgoing, device.camera
// connects = api.anthropic.com (+ uses-ai, uses-tls, uses-heap)
let result = Claude.ask(String(decoding: question, as: UTF8.self), jpeg: jpeg)
if let problem = result.problem {
// problem: warum es keine Antwort gibt (kein Schlüssel, kein Netz …)
} else {
answer = Array(result.text.utf8)
}Ton & Mikrofon
Abspielen (sound.play): jedes Programm bekommt einen Ring, den der Mischer des Kerns spielt (48 kHz,
Stereo, Int16). Hören (device.microphone): der Kern schreibt, was ein Mikrofon hört, in einen Ring pro
Mikrofon (48 kHz, 16 Bit, mono/stereo, etwa 1,4 s), lesend in jedes hörende Programm eingeblendet; der Pegel jeder
10-ms-Periode steht daneben. Das Mikrofon läuft nur, solange jemand hört; die rote MIC-Anzeige nennt jedes Programm, und der
Besitzer kann alle stoppen. Die Werkzeuge (Pegel, Auto-Pegel, Rauschtor, Stimmerkennung, Schnitt) kommen mit
uses-heap.
SoundOutputAbspielen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Öffnen | ||
static open() -> SoundOutput? | Der Ring des Programms (bei jedem Aufruf derselbe); nil ohne sound.play oder Tongerät. | ≤ 1 mssound.open0x0030_0006 |
Beispielaus apps/sound (gekürzt) | ||
| Abspielen | ||
write(_ frames: UnsafePointer<Int16>, count: Int) -> Int | Schreibt bis zu count Frames (links, rechts); gibt zurück, wie viele passten. | |
Beispielaus apps/sound (gekürzt) | ||
var room / queued / withDevice: Int | Platz jetzt / geschrieben, nicht gespielt / beim Gerät, noch nicht gehört. | |
Beispielaus apps/sound (gekürzt) | ||
flush() | Verwirft das Wartende (Stopp). | |
Beispielaus apps/sound (gekürzt) | ||
| Lautstärke | ||
var volume: Int | Eigene Lautstärke 0 … 100 (über der Gesamtlautstärke). | |
static var status: (volume: Int, device: Bool, playing: Bool) | Gesamtlautstärke, Gerät vorhanden, spielt gerade. | ≤ 5 µssound.status0x0030_0008 |
Beispielaus apps/settings (gekürzt) | ||
static setMasterVolume(_ volume: Int) -> Bool | Gesamtlautstärke setzen (sound.volume). | ≤ 50 µssound.setVolume0x0030_0009 |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
| Schließen | ||
close() | Ring wird nicht mehr gemischt. | ≤ 5 µssound.close0x0030_0007 |
| Konstanten | ||
static let rate = 48_000 / let capacity: Int | Abtastrate / Größe des Rings. | |
Microphone · MicrophoneRing · MicrophoneInfoHören
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Mikrofon wählen & öffnen | ||
Microphone.info(at index: Int) -> MicrophoneInfo? | Das index-te Mikrofon (name, rate, channels, listening, id). | ≤ 20 µsmicrophone.list0x0031_0001 |
Beispielaus apps/sound (gekürzt) | ||
Microphone.open(_ id: Int? = nil) -> MicrophoneRing? | Hören beginnen (nil: das erste Mikrofon); startet das Mikrofon. | ≤ 1 msmicrophone.open0x0031_0002 |
Beispielaus apps/sound (gekürzt) | ||
Microphone.close(_ id: Int? = nil) -> Bool | Aufhören; das Mikrofon stoppt, wenn niemand mehr hört. | ≤ 1 msmicrophone.close0x0031_0003 |
Beispielaus apps/sound (gekürzt) | ||
| Proben lesen | ||
ring.read(into: UnsafeMutablePointer<Int16>, frames: Int) -> (frames: Int, lost: Int) | Wartende Frames kopieren; wer mehr als den Ring zurück ist, springt zur neuesten Sekunde (lost). | |
Beispielaus apps/sound (gekürzt) | ||
ring.skipToNow() | Wartendes überspringen. | |
| Zustand & Pegel | ||
ring.state: State (running, idle, stoppedByOwner, failed) | Was das Mikrofon tut – z. B. vom Besitzer gestoppt. | |
Beispielaus apps/sound (gekürzt) | ||
ring.peakDecibels / rmsDecibels / periods / available / rate / channels / ringFrames | Pegel der letzten 10 ms (dBFS), Zähler, Wartendes. | |
| Hörer (MIC-Anzeige) | ||
Microphone.listeners(into names: inout InlineArray<512, UInt8>) -> (length: Int, count: Int) | Wer gerade hört (MIC-Anzeige; system.protection). | ≤ 20 µsmicrophone.listeners0x0031_0004 |
Microphone.stopAll() -> Bool | Alle Hörer stoppen (Stopp auf der MIC-Karte; system.protection). | ≤ 1 msmicrophone.stopAll0x0031_0005 |
Beispielaus apps/display (gekürzt) | ||
| Mikrofon anbieten | ||
Microphone.provide(_ name: StaticString, channels: Int = 1) -> Int? | Ein Mikrofon anbieten (microphone.provide). | ≤ 1 msmicrophone.provide0x0031_0006 |
Beispielaus apps/testmicrophone (gekürzt) | ||
Microphone.push(_ id: Int, samples: Int) -> Bool / pushBuffer / pushRoom | Proben aus pushBuffer einspeisen (höchstens 80 ms Stereo). | ≤ 200 µsmicrophone.push0x0031_0007 |
Werkzeuge (uses-heap)Pegel, Dynamik, Stimme, Schnitt
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Pegel | ||
LevelMeter.measure(_ samples: Span<Int16>) -> SoundLevel | RMS und Spitze in dBFS. | |
Beispielaus apps/sound (gekürzt) | ||
LevelMeter.periods(_ samples: Span<Int16>, channels: Int = 1) -> [SoundLevel] | Pegel je 10-ms-Periode. | |
LevelMeter.decibels / amplitude / linear / floor / fullScale | Umrechnungen dB ↔ Amplitude. | |
| Dynamik | ||
AutoLevel(target: Double = -12, minimumGain: -12, maximumGain: 24, silence: -55, ceiling: -1, channels: 1, gain: 0) | Automatische Verstärkung mit Begrenzer; process(&samples). | |
Beispielaus apps/sound (gekürzt) | ||
NoiseGate(threshold: Double = -50, holdMilliseconds: 200, fadeMilliseconds: 5, channels: 1) | Rauschtor; process(&samples), isOpen. | |
Beispielaus apps/sound (gekürzt) | ||
| Stimme | ||
VoiceDetector(margin: 10, minimumLevel: -65, crossings: 0.004...0.25, hangoverMilliseconds: 300, channels: 1, noiseFloor: nil) | Spricht jemand? detect(period) -> Bool, level, noiseFloor. | |
Beispielaus apps/sound (gekürzt) | ||
VoiceDetector.voiceParts(_ samples: [Int16], channels: Int = 1, gapMilliseconds: Int = 300, shortestMilliseconds: Int = 200) -> [Range<Int>] | Sprechstellen einer Aufnahme. | |
Beispielaus apps/sound (gekürzt) | ||
| Aufnahme & Schnitt | ||
PreRecordBuffer(seconds: Int = 5, channels: Int = 1): append / drain / clear / frames | Die letzten Sekunden vor dem Aufnahmestart. | |
Beispielaus apps/sound (gekürzt) | ||
SoundEdit.trim / trimSilenceEnds / splitAtPauses / normalize / voiceBounds / overview | Schneiden, Stille kürzen, an Pausen teilen, normalisieren, Wellenbild. | |
Beispielaus apps/sound (gekürzt) | ||
OverviewBuilder(framesPerColumn:channels:reserveSeconds:): append / overview(columns:) | Wellenbild laufend aufbauen (WaveColumn min/max). | |
| Format | ||
SoundFormat.rate / periodFrames / frames(milliseconds:) | 48 kHz, 480 Frames pro 10 ms. | |
Beispiel: Mikrofon öffnen und lesen (apps/sound, Capture.swift)
// manifest: capabilities = …, sound.play, device.microphone (+ uses-heap)
guard let info = Microphone.info(at: 0) else {
problem = TextLine("There is no microphone.")
return true
}
guard let opened = Microphone.open(info.id) else { return true }
ring = opened
ring?.skipToNow()
autoLevel = AutoLevel(channels: channels, gain: Double(choices.gain))
gate = NoiseGate(channels: channels)
voice = VoiceDetector(channels: channels)
// in der Schleife:
if ring!.state == .stoppedByOwner { problem = TextLine("You stopped the microphone (MIC in the menu bar).") }
while true {
let got = raw.withUnsafeMutableBufferPointer { ring!.read(into: $0.baseAddress!, frames: 4800) }
guard got.frames > 0 else { break }
// ... in 10-ms-Perioden: autoLevel.process(&span), gate.process(&span),
// LevelMeter.measure(span), voice.detect(span)
}
// beim Ende:
Microphone.close()Beispiel: Abspielen über den Mischer (apps/sound, Player.swift, gekürzt)
if output == nil { output = SoundOutput.open() }
guard let output else { return }
while output.room >= 480 && position < total {
let count = min(output.room, 4800, total - position)
// chunk mit count Stereo-Frames füllen (links, rechts)
_ = chunk.withUnsafeBufferPointer { output.write($0.baseAddress!, count: count) }
position += count
}
// Stopp: output.flush()Kamera & Erkennung
Bilder kommen aus den Ringen des Kerns: mit device.camera wird der Bildring einer Kamera lesend ins
Programm eingeblendet (neuestes Bild ohne Systemaufruf); Anbieter (camera.provide: Test-Kamera, USB-Treiber)
schieben JPEG-Bilder hinein. Die Mac-Kamera nimmt nur auf, solange ein Programm den Ring offen hat.
Erkennung läuft im Programm Video (lokales Modell, keine Netzverbindung). Es veröffentlicht seine Treffer im Ring
video.detections (publish = detections); Roboterprogramme abonnieren ihn mit
subscribe = video.detections und lesen ohne Systemaufruf und ohne Warten – auch in einer Echtzeit-Schleife.
Zu alte Bilder werden verworfen („stale pictures must not steer a robot“).
Camera · CameraInfo · CameraRing
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Kamera wählen & öffnen | ||
Camera.info(at index: Int) -> CameraInfo? | Die index-te Kamera (name, width, height, frames, running, id). | ≤ 20 µscamera.list0x0020_0001 |
Beispielaus apps/video (gekürzt) | ||
Camera.open(_ id: Int) -> CameraRing? | Bildring einblenden; startet die Aufnahme. | ≤ 1 mscamera.ring0x0020_0006 |
Beispielaus apps/server (gekürzt) | ||
Camera.close(_ id: Int) -> Bool | Ring ausblenden; die Mac-Kamera stoppt, wenn ihn niemand mehr hat. | ≤ 1 mscamera.close0x0020_0009 |
Beispielaus apps/video (gekürzt) | ||
| Bilder lesen | ||
ring.newest(after: UInt32, into buffer: UnsafeMutableRawBufferPointer) -> (frame: UInt32, count: Int, time: UInt64)? | Kopiert das neueste Bild (JPEG), wenn es neuer als after ist: Nummer, Größe, Aufnahmezeit. | |
Beispielaus apps/video (gekürzt) | ||
ring.latest / width / height / running | Neuestes vollständiges Bild, Maße, läuft die Kamera? | |
| Kamera anbieten | ||
Camera.provide(_ name: StaticString, width: Int, height: Int) -> Int? | Anbieter einer Kamera werden (camera.provide). | ≤ 1 mscamera.provide0x0020_0007 |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
Camera.push(_ id: Int, count: Int) -> Bool / frameBuffer / frameRoom | Das JPEG in frameBuffer (höchstens 448 KiB) als nächstes Bild einspeisen. | ≤ 500 µscamera.push0x0020_0008 |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
PublicationsVeröffentlichen und abonnieren
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Veröffentlichen | ||
static detections() -> DetectionWriter<SharedRing>? | Der eigene Erkennungsring zum Schreiben; nil ohne publish =. | |
Beispielaus apps/video (gekürzt) | ||
| Abonnieren | ||
static subscription(_ index: Int = 0) -> DetectionReader<SharedRing>? | Das index-te Abo (0 oder 1, Reihenfolge im Manifest). | |
Beispielaus apps/rtvision (gekürzt) | ||
| Rohe Ringe | ||
static ownRing() -> SharedRing? / subscriptionRing(_ index: Int) -> SharedRing? | Dieselben Ringe als rohe Wörter (für andere Formate, z. B. den Arm-Ring). | |
DetectionReaderAbonnent
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Lesen | ||
newest(after: UInt32 = 0, now: UInt64, maximumAge: UInt64) -> DetectionBatch? | Neuester Stapel, außer er ist älter als maximumAge ns. | |
Beispielaus apps/rtvision (gekürzt) | ||
| Klassen & Modell | ||
classIndex(of name: StaticString) -> Int? / classByte(_:_:) / classCount | Klasse über den Namen finden, Namen lesen. | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
latest / modelGeneration / modelName | Neueste Stapelnummer; Generation steigt bei Modellwechsel (Klassen neu suchen). | |
DetectionWriterHerausgeber (Video)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Klassen & Modell | ||
setClassName(_ index: Int, length: Int, _ byteAt: (Int) -> UInt8) / classesDone(count: Int) | Klassennamen einmal schreiben. | |
Beispielaus apps/video (gekürzt) | ||
setModel(length: Int, _ byteAt: (Int) -> UInt8) | Name des aktiven Modells (Generation steigt). | |
| Schreiben | ||
mutating publish(_ batch: DetectionBatch) -> UInt32 | Stapel als nächsten schreiben; seine Nummer. | |
Beispielaus apps/video (gekürzt) | ||
var readers: Int | Wie viele Programme gerade lesen. | |
DetectionBatch · Sighting
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Stapel | ||
DetectionBatch: number, frame, captured, done, width, height, count, objects (bis 32) | Was ein Bild zeigte. | |
best(of classIndex: Int) -> Sighting? | Bester Treffer einer Klasse. | |
| Objekt | ||
Sighting(classIndex: Int, score: Double, x0: Double, y0: Double, x1: Double, y1: Double) | Ein Objekt: Klasse, Sicherheit, Kasten als Bruchteile des Bildes. | |
Beispielaus apps/video (gekürzt) | ||
Sighting.centreX / centreY | Mitte des Kastens. | |
| Ring-Format | ||
DetectionRing.format(_:) / magic / version / Offsets | Aufbau des Rings (eine 16-KiB-Seite). | |
Shape-Modul vision / cameraFür Reflex und den Kern
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Für Reflex und den Kern | ||
vision.detections | Neueste Erkennungen (Ring von Video). | ≤ 10 µsvision.detections0x0021_0001 |
vision.mask / vision.motion / vision.markers | Farbmaske mit Flecken, Bewegung zwischen zwei Bildern, AprilTag-Marker. | ≤ 2 msvision.mask0x0021_0002 |
camera.frame / camera.toWorld / camera.fromWorld | Neuestes Bild; Bild- ↔ Weltkoordinaten (Kalibrierung nötig). | ≤ 2 µscamera.toWorld0x0020_0003 |
Beispiel: Neuestes Kamerabild holen und dekodieren (apps/video, Pipeline.swift)
// manifest: capabilities = …, device.camera publish = detections
var list: [CameraInfo] = []
var index = 0
while let info = Camera.info(at: index) { list.append(info); index += 1 }
ring = Camera.open(list[0].id)
func takeFrame() -> Bool {
guard let ring else { return false }
let taken = jpeg.withUnsafeMutableBytes { ring.newest(after: frame.number, into: $0) }
guard let taken else { return false }
guard case .success(let picture) = Picture.decode(Array(jpeg[0..<taken.count])) else { return false }
// ... erkennen, zeichnen
return true
}Beispiel: Erkennungen veröffentlichen (apps/video)
nonisolated(unsafe) var publisher = Publications.detections()
var batch = DetectionBatch()
batch.frame = shown.number
batch.captured = shown.captured
batch.done = done
batch.width = width; batch.height = height
batch.objects[batch.count] = Sighting(classIndex: 3, score: 0.82, x0: 0.1, y0: 0.2, x1: 0.4, y1: 0.6)
batch.count += 1
publisher!.publish(batch)Beispiel: Erkennungen in einer 1-kHz-Schleife lesen (apps/rtvision)
// manifest: realtime = 1000 200 log
// subscribe = video.detections
guard let ring = Publications.subscription(0) else {
System.log("rtvision: no subscription (subscribe line missing)")
return
}
var last: UInt32 = 0
while true {
let now = System.time
if let batch = ring.newest(after: last, now: now, maximumAge: 5_000_000_000) {
last = batch.number
// batch.objects[0..<batch.count], ring.classByte(classIndex, at) für den Namen
}
System.waitForNextPeriod()
}Bilder, PDF & QR
Mit uses-image (braucht uses-heap): Bilder dekodieren und kodieren, PDF schreiben und lesen. Ein
Picture hält RGBA-Pixel. PDF lesen ist ungeprüfte Eingabe – die Programme Reader und Image lesen deshalb nur das
eine Dokument, das ihnen übergeben wurde. QR-Codes mit uses-qr (ohne Heap).
Picture
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Bild | ||
Picture(width: Int = 0, height: Int = 0, pixels: [UInt8] = [], scale: Int = 1) | Ein Bild aus RGBA-Pixeln. | |
Beispielaus apps/apicheck (gekürzt) | ||
static let maxFile / maxPixels | Grenzen gegen zu große Eingaben. | |
PictureError.text | Kurzer Satz für Meldungen. | |
| Dekodieren | ||
static decode(_ file: [UInt8]) -> Result<Picture, PictureError> | PNG, JPEG, GIF, WebP, BMP oder TIFF nach Signatur. | |
Beispielaus apps/video (gekürzt) | ||
Formate
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| PNG & JPEG | ||
PNG.encode(_ picture: Picture) -> [UInt8]? / decode / hasSignature / size(_:) | PNG verlustfrei (RGB wenn deckend, sonst RGBA); Größe ohne Dekodieren. | |
Beispielaus apps/apicheck (gekürzt) | ||
JPEG.encode(_ picture: Picture, quality: Int = 85) -> [UInt8]? / decode / hasSignature | JPEG (Baseline, 4:2:0, Qualität 1 … 100). | |
Beispielaus apps/apicheck (gekürzt) | ||
| GIF & WebP | ||
GIF.decode / hasSignature / maxFrames | GIF, erstes Bild. | |
GIFAnimation.parse(_ file: [UInt8]) -> Result<GIFAnimation, PictureError> | Animiertes GIF: advance(), show(_:), delay(_:), frameCount, pixels. | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
WebP.decode / hasSignature | WebP (verlustbehaftet und verlustfrei). | |
WebPAnimation.parse(_ file: [UInt8]) -> Result<WebPAnimation, PictureError> | Animiertes WebP, wie GIFAnimation. | |
| BMP & TIFF | ||
BMP.encode / decode / hasSignature, TIFF.encode / decode / hasSignature | BMP und TIFF lesen und schreiben. | |
| Entpacken & Senken | ||
Inflater.zlib(_:limit:) / zlib(_:size:) / gzip(_:limit:) | zlib/gzip entpacken (mit Größengrenze). | |
PNGSink / ByteCollector (ByteSink) | Ziele für Bytes beim Kodieren. | |
PDFDocumentPDF schreiben
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Dokument | ||
PDFDocument(width: Double = PDFDocument.a4.width, height: Double = PDFDocument.a4.height) | Neues Dokument (Maße in Punkt). | |
Beispielaus apps/image (gekürzt) | ||
newPage() / data() -> [UInt8] | Neue Seite / das fertige PDF. | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
data(userPassword: [UInt8], ownerPassword: [UInt8], random: [UInt8]) -> [UInt8] | Verschlüsseltes PDF. | |
| Inhalt setzen | ||
text(_ text: String, x: Double, y: Double, size: Double = 11, font: Font = .regular, colour: (UInt8, UInt8, UInt8) = (0, 0, 0)) | Text setzen. | |
Beispielaus apps/image (gekürzt) | ||
picture(_ picture: Picture, x:y:width:height:, jpeg: [UInt8]? = nil) -> Bool | Bild einsetzen (optional das JPEG direkt). | |
line(x0:y0:x1:y1:width:colour:) / rectangle(x:y:width:height:fill:) | Linie, gefülltes Rechteck. | |
| Maße | ||
static width(_ text: String, size: Double, font: Font = .regular) -> Double / a4 | Textbreite, A4-Maße. | |
PDFReaderPDF lesen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Öffnen | ||
init(_ bytes: [UInt8], password: [UInt8] = []) throws(PDFReadError) | Öffnet ein PDF (auch verschlüsselt). | |
Beispielaus apps/reader (gekürzt) | ||
| Seiten & Text | ||
pageCount / page(_ index: Int) -> PDFPage? | Seiten (width, height, rotate, content, resources). | |
Beispielaus apps/files (gekürzt) | ||
text(page index: Int) -> [PDFTextRun] | Textstücke einer Seite mit Lage und Größe. | |
| Navigation | ||
outline() / links(page:) | Inhaltsverzeichnis, Links. | |
Beispielaus apps/reader (gekürzt) | ||
| Rohdaten & Rechte | ||
object / resolve / streamBytes / decode / decodeBytes / trailer | Rohe Objekte für eigene Auswertung (PDFObject, PDFDictionary). | |
isEncrypted / openedAsOwner / permissions / textString(_:) / maximumFile / maximumStream | Verschlüsselung, Rechte, Grenzen. | |
QRCode (uses-qr)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Kodieren | ||
static encode(_ bytes: Span<UInt8>, correction: QRCorrection = .medium, mask: Int? = nil) -> QRCode? | QR-Code im Byte-Modus (Versionen 1 … 10, Stufen L/M/Q/H). | |
Beispielaus apps/server (gekürzt) | ||
static capacity(version: Int, correction: QRCorrection) -> Int | Wie viele Bytes passen. | |
| Lesen | ||
isDark(x: Int, y: Int) -> Bool / size / version / correction / mask | Module lesen. | |
Beispiel: Fenster als PNG oder JPEG sichern (sdk-apps/about)
guard let bytes = jpeg ? JPEG.encode(picture, quality: 90) : PNG.encode(picture),
case .success(let file) = jpeg ? File.open("window.jpg", mode: .replace) : File.open("window.png", mode: .replace) else {
saved = TextLine("Could not save the picture")
return
}
bytes.withUnsafeBufferPointer { buffer in _ = file.write(Span(_unsafeElements: buffer)) }
_ = file.close()Beispiel: Ein Bild als PDF ausgeben (apps/image)
var pdf = PDFDocument()
let margin = 40.0
pdf.text(name.isEmpty ? "Picture" : name, x: margin, y: margin + 14, size: 16, font: .bold)
pdf.text(String(shown.width) + " x " + String(shown.height) + " pixels · SOFTrobots OS Image",
x: margin, y: margin + 32, size: 10, colour: (110, 110, 110))
_ = pdf.picture(shown, x: margin, y: margin + 50, width: 400, height: 300)
let bytes = pdf.data()Beispiel: PDF öffnen und Seiten lesen (apps/reader)
do {
var opened = try PDFReader(fileBytes, password: password)
for index in 0..<opened.pageCount {
if let page = opened.page(index) {
let turned = page.rotate == 90 || page.rotate == 270
pageSizes.append(turned ? (page.height, page.width) : (page.width, page.height))
}
}
let runs = opened.text(page: 0) // [PDFTextRun]: text, x, y, size, width
} catch {
// falsches Passwort, beschädigt …
}Beispiel: QR-Code für eine Einladung (apps/server)
invitationQR = QRCode.encode(bytes.span.extracting(0..<text.count), correction: .medium)
guard let code = invitationQR else { return }
let modules = code.size + 8
for y in 0..<code.size { for x in 0..<code.size where code.isDark(x: x, y: y) { /* Modul füllen */ } }Video- & Tonformate
Container und Codecs für Aufnahme und Wiedergabe: Matroska/WebM (uses-image), VP9
(uses-video), Opus (uses-opus), WAV und FLAC (mit uses-heap).
Video nimmt MKV mit MJPEG, Ton und Erkennungen auf und spielt WebM (VP9 + Opus).
MatroskaWriterMKV schreiben (MJPEG, Ton, Erkennungen)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Anlegen | ||
init(width: Int, height: Int, detections: Bool = false, soundRate: Int = 0, soundChannels: Int = 1) | Neuer Schreiber. | |
mutating header(app: StaticString = "") -> [UInt8] | Kopf der Datei. | |
| Spuren schreiben | ||
mutating frame(_ jpeg: [UInt8], milliseconds: Int) -> [UInt8] | Ein Bild. | |
mutating sound(_ samples: [UInt8], milliseconds: Int) -> [UInt8] | Ein Stück Ton. | |
mutating detections(_ text: [UInt8], milliseconds: Int) -> [UInt8] | Erkennungen als Untertitelspur. | |
| Abschließen | ||
mutating finish() -> [UInt8] / frames / lastTime | Abschluss (Index). | |
MatroskaIndex · MatroskaTrack · MatroskaFrameMKV/WebM lesen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Lesen & springen | ||
MatroskaIndex(size: Int, read: @escaping (Int, Int) -> [UInt8]) throws(MatroskaError) | Liest die Struktur über eine Lesefunktion (ohne alles zu laden). | |
Beispielaus apps/video (gekürzt) | ||
tracks / frames / duration / docType / seek(_ milliseconds: Int, track: Int) -> Int | Spuren, Rahmen (track, time, offset, length, key), Springen. | |
| Spuren | ||
MatroskaTrack.isCodec(_ name: StaticString) -> Bool / codec / codecPrivate / sampleRate / width | Codec einer Spur ("V_VP9", "A_OPUS"). | |
Beispielaus apps/video (gekürzt) | ||
MatroskaID.ebml / cluster / tracks / cues … | Element-IDs. | |
VP9Decoder · OpusDecoder
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Video (VP9) | ||
VP9Decoder() / mutating decode(_ packet: [UInt8]) throws(VP9Error) -> VP9Picture? | Ein VP9-Paket dekodieren. | |
Beispielaus apps/video (gekürzt) | ||
VP9Picture.rgba() -> [UInt8] / y, u, v, yStride, uvStride, width, height | Bild als RGBA oder YUV-Ebenen. | |
| Ton (Opus) | ||
OpusDecoder(head: [UInt8]) / mutating decode(_ packet: [UInt8]) throws(OpusError) -> [Float] / reset() | Opus-Pakete zu Float-Proben; channels, preSkip, gain. | |
WAV · FLAC
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| WAV | ||
WAV(_ file: Span<UInt8>) | Liest einen WAV-Kopf: rate, channels, bits, float, frames, dataStart, milliseconds. | |
Beispielaus apps/sound (gekürzt) | ||
WAV.render(_ file: Span<UInt8>, from first: Int, count: Int, into out: UnsafeMutablePointer<Int16>) -> Int / sample(_:_:_:) | Frames als Int16 (Stereo 48 kHz für SoundOutput). | |
| FLAC schreiben | ||
FLACEncoder(rate: Int, channels: Int, bits: Int, blockSize: Int = 4096, maxLPCOrder: Int = 12) | FLAC-Kodierer. | |
Beispielaus apps/sound (gekürzt) | ||
FLACEncoder.header() / encode(_ samples: Span<Int16> | Span<Int32>) -> [UInt8] / finish() | Kopf, Blöcke, Abschluss (mit endgültigem Kopf). | |
| FLAC lesen | ||
FLACDecoder(_ data: [UInt8]) / init(length:read:) throws(FLACError) | FLAC lesen (ganz oder über eine Lesefunktion). | |
Beispielaus apps/miccheck (gekürzt) | ||
FLACDecoder.nextBlock() throws(FLACError) -> [Int32] / seek(toSample:) / info: FLACInfo | Nächster Block, Springen, Stream-Infos (rate, channels, bits, totalSamples, md5). | |
Beispiel: Aufnahme als MKV beginnen und Bilder anhängen (apps/video, Recording.swift)
guard case .success(let file) = File.open(path, mode: .replace) else { return false }
var writer = MatroskaWriter(width: picture.width * picture.scale, height: picture.height * picture.scale, detections: true,
soundRate: recordSoundFormat.rate, soundChannels: recordSoundFormat.channels)
let header = writer.header()
_ = header.withUnsafeBufferPointer { file.write(Span(_unsafeElements: $0)) }
// je Kamerabild:
let milliseconds = Int((System.time &- recordStarted) / 1_000_000)
let piece = writer.frame(Array(jpeg[0..<jpegCount]), milliseconds: milliseconds)
_ = piece.withUnsafeBufferPointer { file.write(Span(_unsafeElements: $0)) }Beispiel: FLAC-Datei schreiben (apps/sound, SoundFile.swift)
var encoder = FLACEncoder(rate: rate, channels: channels, bits: 16)
pending = encoder.header()
// je 10-ms-Periode:
pending += encoder.encode(samples) // Span<Int16>
// am Ende:
let (tail, finalHeader) = encoder.finish()Archive (ZIP)
Mit uses-archive (braucht uses-heap): ZIP lesen und schreiben; ein Passwort bedeutet immer WinZip
AES-256. Dazu rohes DEFLATE und CRC32.
ZipArchive · ZipEntryLesen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Inhalt lesen | ||
ZipArchive.entries<S: ByteSource>(_ source: inout S) -> Result<[ZipEntry], ZipError> | Inhaltsverzeichnis lesen. | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
ZipEntry: name, size, compressedSize, crc, isDirectory, isEncrypted, isLink, method, aes… | Ein Eintrag. | |
| Entpacken | ||
ZipArchive.extract(_ entries: [ZipEntry], from source: S, to destination: inout D, password: [UInt8]?) -> Result<ZipSummary, ZipError> | Einträge entpacken (ZipSummary: files, folders, bytes, skipped). | |
ZipArchive.safePath(_ entry: ZipEntry) -> [UInt8]? | Sicherer Pfad (kein „..“, kein absoluter Pfad). | |
ZipWriterSchreiben
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Schreiben | ||
beginFile(_ file: Span<UInt8>, time: ZipTime, password: [UInt8]?, salt: Span<UInt8>, _ sink: inout Sink) -> Result<Void, ZipError> | Datei beginnen (mit Passwort: AES-256). | |
write(_ data: Span<UInt8>, _ sink: inout Sink) / endFile(_:) / addFolder(_:time:_:) / finish(_:) | Daten, Dateiende, Ordner, Abschluss. | |
| Zeit | ||
ZipTime(unixSeconds: Int64, offsetSeconds: Int64) | Zeitstempel. | |
Deflate & Prüfsummen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Deflate & Prüfsummen | ||
Deflater: compress(_:_:) / finish(_:) / reset() | Rohes DEFLATE packen. | |
StreamInflater: next(_ source: inout S) -> Result<Range<Int>?, InflateError> / reset() / total | Rohes DEFLATE stückweise entpacken. | |
CRC32: add(_ bytes: Span<UInt8>) / checksum / reset() | CRC-32. | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
Roboter & Sicherheit
Bewegungen sind Effekte: Programme bewegen nie selbst Motoren, sondern bitten den Aktor-Dienst
(actuators, Recht safety.actuator) über den Kanal actuator (Recht ipc). Der Dienst
prüft jede Bewegung gegen die Sicherheitsgrenzen (begrenzt oder lehnt ab) und spricht über CAN mit den Motoren. Der
Not-Halt hält der Kern selbst: jedes Programm darf ihn lesen, auslösen und lösen nur mit safety.stop.
Programme sehen eine Generation, die sich mit jedem Halt und jeder Freigabe ändert (ungerade = gehalten).
Für Arme gibt es Modelle (SR-6, SO-101, UR5e, UR3e, FR3, xArm6, myCobot 280) mit Vorwärts- und Rückwärtskinematik, eine
simulierte Greiferkamera und simulierte Motoren; der Aktor-Dienst veröffentlicht den Arm-Zustand (publish = arm).
System (Not-Halt)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Zustand lesen | ||
System.emergencyStopped: Bool | Gilt der Not-Halt gerade? | ≤ 2 µssafety.state0x0100_0001 |
Beispielaus apps/arm (gekürzt) | ||
System.safetyGeneration: UInt64 | Ändert sich mit jedem Halt und jeder Freigabe; ungerade = gehalten. | ≤ 2 µssafety.state0x0100_0001 |
Beispielaus apps/actuators (gekürzt) | ||
System.safetyState: SafetyState | generation, stopped, sinceStop. | ≤ 2 µssafety.state0x0100_0001 |
System.microsecondsSinceStop: UInt64 | µs seit dem letzten Halt. | ≤ 2 µssafety.state0x0100_0001 |
| Auslösen & lösen | ||
System.emergencyStop(reason: UInt64) -> Bool | Alle Aktoren sofort anhalten (safety.stop). | ≤ 20 µssafety.stop0x0100_0002 |
Beispielaus apps/server (gekürzt) | ||
System.releaseEmergencyStop() -> Bool | Halt lösen; Aktoren warten auf den nächsten Befehl ihrer Programme. | ≤ 20 µssafety.release0x0100_0003 |
Beispielaus apps/arm (gekürzt) | ||
ArmModelEin Arm: Gelenke, Reichweite, Werkzeug
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Arm wählen | ||
static let sr6 / static model(_ index: Int) -> ArmModel | Der simulierte Tischarm / Modell nach Index. | |
static let ids: InlineArray<7, StaticString> | "sr6", "so101", "ur5e", "ur3e", "fr3", "xarm6", "mycobot280". | |
static chosen<let n: Int>(_ id: InlineArray<n, UInt8>, count: Int) -> ArmModel / index(of:count:) | Arm nach den Bytes von arm = (Settings › Robot); SR-6 wenn leer. | |
Beispielaus apps/server (gekürzt) | ||
| Kinematik | ||
forward(_ q: ArmAngles) -> ArmPose | Gelenkwinkel → Punkte des Arms und Werkzeugpose. | ≤ 2 µsarm.forward0x0102_0001 |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
inverse(x: Double, y: Double, z: Double, pitch: Double, near: ArmAngles) -> ArmAngles? | Werkzeugspitze nach (x, y, z) mit Neigung, am nächsten zu near; nil außer Reichweite. | ≤ 20 µsarm.inverse0x0102_0002 |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
| Gelenke & Daten | ||
joint(_ index: Int) -> ArmJoint / count / joints | Gelenke (name, low, high, fastest, strongest, rest). | ≤ 10 µsarm.joints0x0102_0003 |
name, maker, id, reach, payload, tool, restAngles, closedForm, urdf … | Beschreibung des Arms. | |
ArmAngles · ArmPoint · ArmPose · ArmCamera
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| ArmAngles · ArmPoint · ArmPose · ArmCamera | ||
ArmAngles() / subscript(_ index: Int) -> Double | Gelenkwinkel in Radiant (bis 7). | |
Beispielaus apps/server (gekürzt) | ||
ArmPoint(_ x: Double = 0, _ y: Double = 0, _ z: Double = 0) | Punkt in Welt-mm (x vor, y links, z hoch). | |
ArmPose: points, pointCount, tip, approach, pitch | Wo der Arm ist. | |
ArmCamera(_ model: ArmModel, _ q: ArmAngles): toWorld(x:y:depth:) / fromWorld(_:) | Greiferkamera 640 × 480: Pixel ↔ Weltpunkt. | ≤ 2 µscamera.toWorld0x0020_0003 |
SimulatedMotorsDie simulierten Motoren (Aktor-Dienst)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Anlegen | ||
SimulatedMotors(_ model: ArmModel = .sr6) | Arm in Ruhestellung. | |
Beispielaus apps/actuators (gekürzt) | ||
| Steuern | ||
set(joint: Int, target value: Int32, speed limit: Int32) | Sollwert in 0,01° und Geschwindigkeit. | |
Beispielaus apps/actuators (gekürzt) | ||
step(milliseconds: Int) / emergencyStop(_ on: Bool) | Bewegung simulieren / sofort bremsen. | |
| Zustand | ||
moving / position / velocity / target / speed / stopped | Zustand. | |
ArmRing · ArmStateArm-Zustand ohne Systemaufruf lesen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Lesen | ||
ArmRing.read<Ring: SharedWords>(_ ring: Ring) -> ArmState? | Neuester vollständiger Zustand (Abo subscribe = actuators.arm). | |
Beispielaus apps/server (gekürzt) | ||
ArmState: positions, moving, stopped, chosen, id, updates | Gelenkstellungen und Flags. | |
| Schreiben (Dienst) | ||
ArmRing.write(_:_:) | Schreibt der Aktor-Dienst. | |
CANFrameCAN- und CAN-FD-Rahmen
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Rahmen bauen | ||
CANFrame(identifier: UInt32, fd: Bool = true) | Rahmen bis 64 Datenbytes. | |
Beispielaus apps/actuators (gekürzt) | ||
int32(at offset: Int) -> Int32 / setInt32(_:at:) | Werte im Rahmen (little-endian). | |
| Senden & kodieren | ||
encode(into:) -> Int / static decode(_:count:) -> CANFrame? | Als Kanalnachricht. | |
channel.send(_ frame: CANFrame) -> Bool | Rahmen über einen Kanal senden. | |
Beispielaus apps/actuators (gekürzt) | ||
Shape-Module robot / arm / safetyFür Reflex und den Kern
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Bewegen | ||
robot.move | Bewegung starten (Effekt; der Aktor-Dienst führt sie aus). | ≤ 20 µsrobot.move0x0101_0001 |
robot.state | Winkel, Geschwindigkeit, Strom, Temperatur eines Gelenks. | ≤ 5 µsrobot.state0x0101_0002 |
arm.torque | Drehmoment für Echtzeitregelung (nur Echtzeit-Handler). | ≤ 1 µsarm.torque0x0102_0004 |
| Not-Halt | ||
safety.state / stop / release | Not-Halt lesen, auslösen, lösen. | ≤ 20 µssafety.stop0x0100_0002 |
Beispiel: Gelenk 1 über den Aktor-Dienst bewegen, Not-Halt (apps/arm)
// manifest: capabilities = system.log, ipc, ui.surface, safety.stop
guard var actuator = Channel("actuator") else { System.log("arm: no actuator channel"); return }
private func move(_ to: Int32, _ actuator: inout Channel) {
var bytes = InlineArray<9, UInt8>(repeating: 0)
bytes[0] = 1 // 1 = move
let values: InlineArray<2, Int32> = [to, 3_000] // Ziel und Geschwindigkeit in 0,01°
for index in 0..<2 {
let bits = UInt32(bitPattern: values[index])
for byte in 0..<4 { bytes[1 + 4 * index + byte] = UInt8(truncatingIfNeeded: bits >> UInt32(8 * byte)) }
}
_ = actuator.send(bytes.span)
actuator.notify()
}
// Antworten: 2 = Bewegung angenommen/begrenzt/abgelehnt, 3 = Status eines Gelenks
while let count = actuator.receive(into: &message), count >= 13 { /* ... */ }
// Not-Halt-Knopf:
System.emergencyStop(reason: 1)
// Freigeben:
System.releaseEmergencyStop()Beispiel: Not-Halt im Aktor-Dienst beobachten (apps/actuators)
var safety = System.safetyGeneration
if safety & 1 == 1 { motors.emergencyStop(true) }
while true {
let now = System.safetyGeneration
if now != safety { motors.emergencyStop(now & 1 == 1); safety = now }
// ...
}Beispiel: Arm-Zustand lesen und Kinematik (apps/server, Live.swift)
// manifest: subscribe = actuators.arm
guard let ring = Publications.subscriptionRing(0), let state = ArmRing.read(ring) else { return }
let model = ArmModel.chosen(state.id, count: state.idLength)
var q = ArmAngles()
for joint in 0..<model.count {
q[joint] = Double(state.positions[joint]) / 100 * Double.pi / 180 // 0,01° → Radiant
}
let pose = model.forward(q) // pose.tip, pose.points
if let reach = model.inverse(x: 300, y: 0, z: 120, pitch: -Maths.pi / 2, near: q) { _ = reach }Echtzeit
Ein Echtzeit-Programm schreibt realtime = <Periode µs> <Budget µs> [log|stop|restart|stop-actuators] ins
Manifest. Der Kern lässt es nur zu, wenn das Budget auf dem Echtzeit-Kern Platz hat, und startet jede Periode pünktlich.
Echtzeit-Programme haben keinen Heap, keine Dateien und keine Settings (keine uses-heap-Datei); sie rufen
System.waitForNextPeriod() einmal pro Periode. Daten tauschen sie ohne Systemaufruf: Abos
(subscribe =), Arm-Ring, Kanäle mit receivePolling und Messreihen (Recorder,
record = …).
System (Echtzeit)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Periode | ||
System.waitForNextPeriod() | Die Arbeit dieser Periode ist getan; kehrt zum Beginn der nächsten zurück. | periodWait (Systemaufruf 4) |
Beispielaus apps/control (gekürzt) | ||
System.time: UInt64 | Nanosekunden seit dem Start. | ≤ 1 µssystem.now0x0002_0002 |
| Statistik | ||
System.realtimeStats(restartWorst: Bool = false) -> RealtimeStats? | Perioden, verpasste Fristen, schlechteste und mittlere Startlatenz (Sekunden), Periode; nil für normale Programme. | ≤ 2 µssystem.realtime0x0002_000D |
Beispielaus apps/control (gekürzt) | ||
RecorderMessreihen ohne Systemaufruf
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Aufzeichnen | ||
static attach() -> Recorder? | Die Aufnahme des Programms; nil ohne record =-Zeile. | |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
push(time: UInt64, _ data: Span<UInt8>) -> Bool | Einen Satz anhängen (Zeit + bis recordSize − 8 Bytes); false wenn der Ring voll war (gezählt). | |
Beispielaus apps/rtrecord (gekürzt) | ||
| Zustand | ||
dropped / recordSize / flight | Verworfene Sätze, Satzgröße, Flugschreiber (überschreibt das Älteste, gesichert nur bei Not-Halt oder Absturz). | |
Beispiel: 1-kHz-Regelschleife mit Echtzeit-Vertrag (apps/control)
// manifest: realtime = 1000 100 (Periode 1000 µs, Budget 100 µs; keine uses-heap-Datei)
@c(app_main)
func appMain() {
var position: Int64 = 0, target: Int64 = 10_000, integral: Int64 = 0, previousError: Int64 = 0
var nextReport = System.time + 5_000_000_000
while true {
let error = target - position
integral += error
let derivative = error - previousError
previousError = error
position += (error * 8 + integral / 64 + derivative * 2) / 256
if System.time >= nextReport, let stats = System.realtimeStats(restartWorst: true) {
nextReport += 5_000_000_000
var line = TextLine("control: ")
line.append(decimal: stats.missed)
line.append(" missed, worst ")
line.append(decimal: UInt64(stats.worstLatency * 1e9))
line.append(" ns")
System.log(line)
}
System.waitForNextPeriod()
}
}Beispiel: Messwerte aufzeichnen
// manifest: record = samples 64 5 3600 (Name, Satzgröße, Sekunden im Ring, Sekunden auf Platte)
guard let recorder = Recorder.attach() else { return }
var record = InlineArray<56, UInt8>(repeating: 0)
// ... record füllen
_ = recorder.push(time: System.time, record.span)Programme & System
Information über das System, Zeit, Protokoll und Zufall stehen jedem Programm offen (das Protokoll braucht
system.log). Andere Programme starten, stoppen und auflisten braucht apps.launch; was jedes Programm
verbraucht, system.status. Updates, Strom und Geräteschutz sind Rechte der Systemprogramme.
System (allgemein)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Information | ||
System.info() -> SystemStatus? | apiVersion, version, deviceName, uptime, wallClock, cpus, realtimeCPU, memoryTotal/Free, screenWidth/Height, emergencyStopped, dataAvailable, dataProtected, memoryLow, deviceKey, arm, address … | ≤ 5 µssystem.info0x0002_0001 |
Beispielaus apps/actuators (gekürzt) | ||
System.processor(_ index: Int) -> ProcessorInfo? | Kern, Takt, Temperatur, Name (system.status). | ≤ 5 µssystem.processor0x0002_000B |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
System.cpuStatus() -> (cpus: Int, now: UInt64, idle: InlineArray<8, UInt64>)? | Leerlaufzeit je CPU (system.status). | ≤ 5 µssystem.processorTimes0x0002_0006 |
Beispielaus apps/activity (gekürzt) | ||
System.bootMeasurements() -> InlineArray<20, UInt64?>? | Messungen des Kerns von diesem Start (Benchmark). | ≤ 5 µssystem.bootTimes0x0002_000C |
| Protokoll | ||
System.log(_ text: StaticString) -> Bool / log(_ line: TextLine) -> Bool | Eine Zeile ins Systemprotokoll (höchstens 160 Bytes; system.log). | ≤ 5 µssystem.log0x0002_0003 |
Beispielaus apps/reader (gekürzt) | ||
System.log(_ text: UnsafeRawBufferPointer, level: LogLevel) -> Bool | Mit Stufe .debug/.info/.warning/.error (Activity › Log). | ≤ 5 µssystem.log0x0002_0003 |
| Zeit | ||
System.time: UInt64 / System.now() -> Now | ns seit Start / Wanduhr (ns seit 1970), seit Start, Zeitzone. | ≤ 1 µssystem.now0x0002_0002 |
Beispielaus apps/develop (gekürzt) | ||
System.wallClock: UInt64 | Millisekunden seit 1970 (0 solange unbekannt). | ≤ 1 µssystem.now0x0002_0002 |
System.adjustClock(byMilliseconds offset: Int64) -> UInt64? | Uhr verstellen (system.time). | ≤ 5 µssystem.adjustClock0x0002_000A |
| Ablauf | ||
System.sleep(nanoseconds: UInt64) | Schlafen. | sleep (Systemaufruf 2) |
Beispielaus apps/dialogcheck (gekürzt) | ||
System.yield() | Rechenzeit abgeben. | yield (Systemaufruf 1) |
System.exit(_ code: UInt64) -> Never | Programm beenden. | exit (Systemaufruf 0) |
Beispielaus sdk-apps/greeter (gekürzt) | ||
| Zufall | ||
System.random<let n: Int>(_ buffer: inout InlineArray<n, UInt8>) -> Bool | Zufallsbytes aus dem Generator des Kerns (Schlüssel, Nonces). | ≤ 5 µssystem.random0x0002_0009 |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
| Strom | ||
System.power(restart: Bool) | Aus oder Neustart (system.power); kehrt nur bei Ablehnung zurück. | ohne Budgetsystem.power0x0002_0004 |
AppsAndere Programme
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Starten & stoppen | ||
Apps.launch(_ name: Span<UInt8>) -> FileError? | Systemprogramm oder App starten (apps.launch). | ≤ 50 msprograms.launch0x0006_0007 |
Beispielaus apps/apicheck (gekürzt) | ||
Apps.stop(_ name: Span<UInt8>) -> FileError? | Laufendes Programm stoppen (Dienste nicht). | ≤ 50 µsprograms.stop0x0006_0008 |
Beispielaus apps/activity (gekürzt) | ||
Apps.open(_ program: Span<UInt8>, document path: Span<UInt8>) -> FileError? | Programm mit einem Dokument starten oder es ihm übergeben. | ≤ 50 msprograms.open0x0006_0009 |
Beispielaus apps/files (gekürzt) | ||
| Dokument | ||
Apps.document() -> (name: InlineArray<80, UInt8>, length: Int, generation: UInt64)? | Das diesem Programm übergebene Dokument; generation ändert sich mit jedem neuen. | ≤ 5 µsprograms.document0x0006_0001 |
Beispielaus apps/reader (gekürzt) | ||
| Listen | ||
Apps.running(at index: Int) -> Running? | Das index-te laufende Programm (Index 0 fragt neu). | ≤ 50 µsprograms.running0x0006_000A |
Beispielaus apps/console (gekürzt) | ||
Apps.installed(at index: Int) -> Running? | Das index-te installierte Programm. | ≤ 20 msprograms.installed0x0006_000B |
Beispielaus apps/display (gekürzt) | ||
Running: name, id, version, kind, realtime, period, missed, worstLatency, onDemand, helper, seesCamera, hearsMicrophone, sends | Was ein Eintrag zeigt. | |
Running.hiddenFromDock neu | Installiert mit dock = no. | |
Apps.usage(at index: Int) -> Usage? | Verbrauch: CPU-Zeit, Speicher, Heap, Fenster, Datei- und Netzverkehr … (system.status). | ≤ 100 µsprograms.usage0x0006_000E |
Beispielaus apps/activity (gekürzt) | ||
| Abstürze & Signatur | ||
Apps.crash(at index: Int) -> Crash? | Die letzten 8 Abstürze, neueste zuerst: name, reason, sequence, wallClock, pc, memoryLimit, reportPath. | ≤ 20 µsprograms.crashes0x0006_000D |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
Apps.signature(of name: Span<UInt8>) -> SignatureState? | Signatur prüfen wie beim Start. | ≤ 50 msprograms.signature0x0006_000F |
Apps.presentation(of name: Span<UInt8>, into buffer: UnsafeMutableRawPointer) -> Presentation? | Titel, Gruppe und Symbol eines Programms (Dock). | ≤ 5 msprograms.presentation0x0006_000C |
| Löschen | ||
Apps.uninstall(_ name: Span<UInt8>, deleteData: Bool, password: Span<UInt8>) -> TextLine? | Programm aus /apps löschen (Passwort des Besitzers; files.system + apps.launch). | ≤ 3 sprograms.uninstall0x0006_0013 |
Apps.leftover(at:) / deleteLeftover(_:password:) | Datenordner gelöschter Programme. | ≤ 50 msprograms.leftovers0x0006_0014 |
| Protokoll lesen | ||
Apps.logEnd / logLine(from:into:) | Das Systemprotokoll mitlesen (system.log.read). | ≤ 20 µssystem.readLog0x0002_0005 |
BeispielMuster (kurz, nicht aus einem Programm kopiert) | ||
Updates (system.update)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Updates (system.update) | ||
System.updateStatus() -> UpdateStatus? | Welche Systemkopie (A/B) läuft, Probelauf, Rückfall, Versionen. | ≤ 5 µsupdate.status0x0008_0001 |
System.installUpdate(_ path: Span<UInt8>) -> UpdateResult | Signiertes Paket in die ruhende Kopie installieren; startet nach dem Neustart. | ≤ 60 supdate.install0x0008_0002 |
System.setTextStart(_ text: Bool) -> Bool / setStartSound(_ on: Bool) -> Bool | Textstart statt Animation / Startton. | ≤ 50 msupdate.textStart0x0008_0003 |
SystemdiensteNur Display-Dienst und Treiber
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Nur Display-Dienst und Treiber | ||
System.protectionStatus() / protectionSetUp / protectionCheck / ownerChoices() | Geräteschutz und Besitzerwahl (system.protection). | ≤ 5 µsprotection.status0x0007_0001 |
System.openUSBController(_:) / openCANController(_:) / allocateDMA(_:) / waitForDevice(timeout:) | Treiber: Register, DMA-Speicher, Interrupts (device.usb, device.can). | ≤ 1 msdevices.open0x0009_0001 |
System.submitInput(kind: UInt64, code: UInt64, value: UInt64) | Eingabe-Ereignis liefern (input.provide). | ≤ 5 µsdisplay.submitInput0x0011_0006 |
Beispiel: Systeminformation, Einstellungen, ein Programm starten (apps/apicheck)
guard let info = System.info() else { fail("no system information"); return }
guard info.apiVersion == 1, info.version.count > 0, info.cpus >= 1 else { fail("system information looks wrong"); return }
let callcost: StaticString = "callcost"
_ = callcost.withUTF8Buffer { Apps.launch(Span(_unsafeElements: $0)) }Beispiel: Die letzten Abstürze auflisten (apps.launch)
var index = 0
while let crash = Apps.crash(at: index) {
var line = TextLine()
for i in 0..<crash.nameLength { line.append(byte: crash.name[i]) }
line.append(": ")
line.append(crash.reportPath)
System.log(line)
index += 1
}Zwischenablage
Text bis 64 KiB, eine für das ganze System. Jedes Programm mit Fenster darf kopieren; lesen darf es nur direkt
nachdem der Besitzer in diesem Programm „Einsetzen“ gewählt hat (der Display-Dienst gibt einen Lesezugriff innerhalb von zwei
Sekunden frei). Kein Programm kann die Zwischenablage beobachten. Das Standard-Bearbeiten-Menü
(addEditMenu) und InputField erledigen das von selbst.
Clipboard
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Kopieren | ||
static copy(_ text: Span<UInt8>) -> Bool | Text (UTF-8, höchstens 64 KiB) in die Zwischenablage. | ≤ 50 µsclipboard.copy0x0012_0001 |
Beispielaus apps/console (gekürzt) | ||
static var generation: UInt64 | Zähler, der mit jedem Kopieren wächst (die Mac-Brücke). | ≤ 2 µsclipboard.generation0x0012_0004 |
| Einsetzen | ||
static paste(into buffer: inout MutableSpan<UInt8>) -> Int | Liest in buffer; 0 wenn leer oder gerade nicht freigegeben. | ≤ 50 µsclipboard.paste0x0012_0002 |
Beispielaus apps/develop (gekürzt) | ||
| Freigeben (Dienst) | ||
static grant(process slot: Int) | Gibt Programm slot einen Lesezugriff (nur der Display-Dienst). | ohne Budgetclipboard.grant0x0012_0003 |
Beispielaus apps/display (gekürzt) | ||
Beispiel: Kopieren und Einsetzen im Editor (apps/develop)
switch id {
case EditMenu.copy, EditMenu.cut:
guard Editor.hasSelection else { return }
let bytes = Editor.selectedBytes
_ = bytes.withUnsafeBufferPointer { Clipboard.copy(Span(_unsafeElements: $0)) }
if id == EditMenu.cut { Editor.insert([]) }
case EditMenu.paste:
var buffer = [UInt8](repeating: 0, count: 64 * 1024)
var span = buffer.mutableSpan
let length = Clipboard.paste(into: &span)
_ = span
guard length > 0 else { return }
Editor.insert(Array(buffer[0..<length]))
default: break
}Kanäle & IPC
Mit ipc: Zwei Programme, die denselben Namen öffnen, teilen eine Seite mit einem Nachrichtenring in jede
Richtung (Nachrichten bis 256 Bytes). Wecken und Warten sind primitive Systemaufrufe (channelNotify, channelWait). Läuft die
Gegenseite auf einer anderen CPU, fragt receiveWaiting den Ring erst kurz ab – meist antwortet sie binnen
Mikrosekunden. So reden auch Roboterprogramme mit dem Aktor-Dienst (actuator) und Treiber untereinander (CAN-Rahmen).
Channel
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Öffnen | ||
init?(_ name: StaticString) | Kanal öffnen oder beitreten; nil ohne ipc. | ≤ 1 mschannels.open0x000A_0001 |
Beispielaus apps/ipcping (gekürzt) | ||
let handle: UInt64 | Nummer des Kanals. | |
| Senden | ||
send(_ message: Span<UInt8>) -> Bool | Nachricht einreihen (höchstens 256 Bytes); false bei vollem Ring. | |
Beispielaus apps/ipcping (gekürzt) | ||
send(_ frame: CANFrame) -> Bool | Einen CAN-Rahmen senden. | |
mutating notify() | Die Gegenseite wecken. | channelNotify (Systemaufruf 8) |
Beispielaus apps/arm (gekürzt) | ||
| Empfangen | ||
receive(into out: inout InlineArray<256, UInt8>) -> Int? | Älteste eingegangene Nachricht oder nil (wartet nicht). | |
Beispielaus apps/arm (gekürzt) | ||
receiveWaiting(into out: inout InlineArray<256, UInt8>) -> Int | Blockiert, bis eine Nachricht kommt (verlorene Weckrufe abgefangen). | channelWait (Systemaufruf 9) |
Beispielaus apps/ipcping (gekürzt) | ||
receivePolling(into out: inout InlineArray<256, UInt8>, microseconds: UInt64 = 30) -> Int? | Nächste Nachricht, wenn sie binnen µs kommt (nur bei Gegenseite auf anderer CPU). | |
Beispielaus apps/benchmark (gekürzt) | ||
wait(timeout: UInt64) | Schläft bis zum Weckruf oder Ablauf; kann früher zurückkehren. | channelWait (Systemaufruf 9) |
Beispielaus apps/develop (gekürzt) | ||
SharedPage · SharedRingRohe geteilte Wörter
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Rohe geteilte Wörter | ||
SharedPage: load / store / loadAcquire / storeRelease | Die Kanalseite Wort für Wort (geordnete Zugriffe). | |
SharedRing: load32 / store32 / loadAcquire32 / storeRelease32 / barrier | Eine Veröffentlichung Wort für Wort. | |
Beispiel: Hin und zurück über den Kanal "bench" (apps/ipcping)
// manifest: capabilities = system.log, ipc
guard var channel = Channel("bench") else {
System.log("ipcping: channel refused")
return
}
var reply = InlineArray<256, UInt8>(repeating: 0)
var message = InlineArray<64, UInt8>(repeating: 0x42)
for round in 0..<2_000 {
message[0] = UInt8(truncatingIfNeeded: round)
while !channel.send(message.span) { System.yield() }
channel.notify()
let count = channel.receiveWaiting(into: &reply)
guard count == 64, reply[0] == message[0] else { System.log("ipcping: wrong reply"); return }
}Text, Zeit & Mathematik
Ohne Heap braucht es feste Bausteine: TextLine ist eine Zeile fester Größe (160 Bytes), WallClock
rechnet Kalenderzeiten (Berlin), Maths liefert die Funktionen der Mathematik-Bibliothek für jedes Profil.
TextLine
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Anlegen | ||
TextLine() / TextLine(_ text: StaticString) | Leere bzw. vorbelegte Zeile. | |
| Anhängen | ||
append(_ text: StaticString) / append(_ other: TextLine) / append(byte: UInt8) | Anhängen. | |
append(decimal value: UInt64) / append(hex value: UInt64, minimumDigits: Int = 1) | Zahlen anhängen. | |
Beispielaus apps/ipcping (gekürzt) | ||
| Lesen | ||
count / subscript(index:) / forEachByte(_:) / capacity | Lesen. | |
WallClock · CivilTime
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Umrechnen | ||
WallClock.civil(unixMilliseconds: Int64) -> CivilTime | ms seit 1970 → Jahr, Monat, Tag, Stunde, Minute, Sekunde, Millisekunde. | |
Beispielaus apps/files (gekürzt) | ||
WallClock.berlinOffset(unixSeconds: Int64) -> Int64 | Versatz der Berliner Zeit (mit Sommerzeit). | |
WallClock.unixSeconds(_ time: CivilTime) -> Int64 / days(year:month:day:) | Rückrechnung. | |
| Ausgeben | ||
WallClock.appendDate(_ time: CivilTime, to line: inout TextLine) / appendTime(_:to:) | "2026-11-10" / "14:23:05" anhängen. | |
Beispielaus apps/files (gekürzt) | ||
Maths
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Maths | ||
sin, cos, tan, asin, acos, atan, atan2, sinCos | Trigonometrie. | |
Beispielaus apps/fpcheck (gekürzt) | ||
sqrt, hypot, pow, exp, log, log2, log10 | Wurzeln, Potenzen, Logarithmen. | |
floor, ceil, round, trunc, fmod, radians, degrees | Runden und Umrechnen. | |
pi, e | Konstanten. | |
Beispiel: Eine Zeile mit Datum bauen
var line = TextLine("Stand ")
let time = WallClock.civil(unixMilliseconds: Int64(System.wallClock))
WallClock.appendDate(time, to: &line)
line.append(" ")
WallClock.appendTime(time, to: &line)
line.append(", Winkel ")
line.append(decimal: UInt64(Maths.degrees(Maths.atan2(1, 1))))
System.log(line)Reflex (Develop)
Reflex ist die Sprache von Develop: Ein Ereignis rein, eine Reaktion raus. Ein Reflex-Programm ruft die
Shape-Module direkt beim Namen (robot.move, vision.detections) – ohne Swift. Jede Zeile nennt ihr
Modul; der Compiler sammelt daraus die Module und damit die Rechte, die beim Sichern als Programm in das signierte Manifest
kommen. Von Develop gebaute Programme bekommen höchstens: Protokoll, Fenster, Ton, Kamera und Erkennungen, Mikrofon, den
eigenen Ordner, Netz nur wenn erlaubt, den Roboter innerhalb seiner Grenzen über den Aktor-Dienst – nie Systemrechte.
Bausteine: state, on modul.ereignis(…) -> State, every 200 ms,
realtime every 1 ms (Develop beweist die Worst-Case-Zeit, höchstens 80 % der Periode), emit modul.aufruf(…)
für Effekte, func, screen { … }, test. Einheiten sind Teil der Typen (40°,
0.4 s, 880 Hz).
Ereignisse (on …)
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Programm | ||
on start / on stop | Programm startet / soll anhalten (letzte Gelegenheit zum Sichern). | Ereignissystem.start0x0002_0007 |
every 200 ms | Zeitgeber. | |
| Sehen & Hören | ||
on vision.detections(seen: Detections) | Neue Erkennungen von Video. | ≤ 10 µsvision.detections0x0021_0001 |
on microphone.loud(heard: Loudness) | Lauter als ein Pegel (Knall, blockierender Motor). | Ereignismicrophone.loud0x0031_0008 |
on microphone.voice(change: VoiceChange) | Sprechen begann oder endete. | Ereignismicrophone.voice0x0031_0009 |
on camera.frame(…) | Ein neues Kamerabild. | ≤ 10 µscamera.frame0x0020_0002 |
| Roboter | ||
on robot.moved(m: Motion) | Eine Bewegung endete (erreicht, begrenzt oder gestoppt). | Ereignisrobot.moved0x0101_0004 |
| Fenster | ||
on Name() (Knopf im screen) | Ein Knopf des Fensters wurde gedrückt. | Ereignisscreen.pressed0x0010_0003 |
Aufrufe & Effekte
| Aufruf | Was er tut | Shape-Aufruf · Zeitbudget |
|---|---|---|
| Roboter | ||
emit robot.move(joint:, to:, in:) | Gelenk bewegen (über die Grenzen des Aktor-Dienstes). | ≤ 20 µsrobot.move0x0101_0001 |
robot.state(joint:) | Zustand eines Gelenks (auch im Echtzeit-Handler, aus dem Ring). | ≤ 5 µsrobot.state0x0101_0002 |
arm.forward / arm.inverse / arm.joints | Kinematik des Arms. | ≤ 20 µsarm.inverse0x0102_0002 |
| Ton & Bild | ||
emit sound.tone(pitch:, length:) | Ein Ton. | ≤ 20 µssound.tone0x0030_0001 |
camera.toWorld(p) | Bildpunkt → Weltpunkt. | ≤ 2 µscamera.toWorld0x0020_0003 |
| Rechnen & Fragen | ||
signal.spectrum / filter / stats / sine | Spektren und Filter über Messreihen. | ≤ 500 µssignal.spectrum0x0040_0001 |
ask.claude(…) | Claude fragen (mit dem Schlüssel des Besitzers aus Settings). | ohne Budgetask.claude0x0050_0001 |
| Fenster | ||
screen { Title Text Big Button Slider Chart Picture Row Column } | Fenster als Beschreibung; Aussehen kommt vom System. | ≤ 200 µsscreen.show0x0010_0001 |
Beispiel: Klatschen hält den Arm an (apps/develop/examples/clap-stop.reflex)
state Listener {
bangs: Int32 = 0
lastBang: Time = 0
talking: Bool = false
}
/// A bang: hold the arm (every joint stays where it is) and beep.
on microphone.loud(heard: Loudness) -> Listener {
if isBang(level: heard.peak) and heard.taken - lastBang > 1 s {
emit robot.move(joint: 1, to: 0°, in: 2 s)
emit sound.tone(pitch: 660 Hz, length: 150 ms)
return self with { bangs: bangs + 1, lastBang: heard.taken }
}
return self
}
on microphone.voice(change: VoiceChange) -> Listener {
if change.speaking and not talking {
emit robot.move(joint: 5, to: 20°, in: 0.3 s)
}
return self with { talking: change.speaking }
}
func isBang(BangLevel { level: Float32 }) -> Bool { return level > -6 }
test "a clap is a bang" { expect isBang(level: -3) }Beispiel: Echtzeit-Handler, von Develop bewiesen (apps/develop/guide/6-realtime.reflex)
state Hold {
target: Angle = 30°
periods: Int32 = 0
}
realtime every 1 ms -> Hold {
let joint = robot.state(joint: 1)
if periods % 20 == 0 {
emit robot.move(joint: 1, to: joint.angle + step(gap: target - joint.angle).value, in: 20 ms)
}
return self with { periods: periods + 1 }
}
func step(Gap { gap: Angle }) -> Turn {
if gap > 1° { return Turn(value: 1°) }
if gap < -1° { return Turn(value: -1°) }
return Turn(value: gap)
}
shape Turn { value: Angle }Anhang: Systemaufrufe & Shape-Module
Unter der Swift-API liegen 14 primitive Systemaufrufe (enum SystemCall). Fast alles läuft über Nummer 13, den Shape-Aufruf: Anfrage und Antwort sind Shapes in fester Kodierung; die Aufruf-Kennung ist Modul << 16 | Aufrufnummer (z. B. files.open = 0x0003_0001). Der Kern prüft jede Anfrage gegen die Shape und die Rechte; das Budget ist die zugesagte längste Laufzeit. Quelle: shapes/*.shape.
SystemCallprimitive Systemaufrufe
| Name | Was er tut | Nummer |
|---|---|---|
exit | Programm beenden | 0 |
yield | Rechenzeit abgeben | 1 |
sleep | Schlafen (ns) | 2 |
wait | Auf Fenster/Dienst warten | 3 |
periodWait | Echtzeit: nächste Periode | 4 |
memoryGrow | Heap vergrößern | 5 |
workerWake | Worker-Thread wecken | 6 |
workerExit | Worker-Thread beenden | 7 |
channelNotify | Kanal: Gegenseite wecken | 8 |
channelWait | Kanal: warten | 9 |
deviceWait | Treiber: auf Interrupt warten | 10 |
surfaceNotify | Fenster: Dienst wecken | 11 |
inputReceive | Display-Dienst: Eingaben abholen | 12 |
shapeCall | Shape-Aufruf: alle Module unten | 13 |
coreModul 0x0001 · Gemeinsame Shapes aller Module: Problem, Punkt, Drehung, Pose, Pixelrechteck.
Keine Aufrufe – nur gemeinsame Shapes.
systemModul 0x0002 · Der Roboter selbst: Info, Zeit, Protokoll, Strom, Zufall, Prozessoren, Worker-Threads; Ereignisse start/stop.
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
system.info | InfoRequest → SystemInfo | ≤ 5 µs0x0002_0001 |
system.now | NowRequest → Now | ≤ 1 µs0x0002_0002 |
system.log | LogLine → Done | ≤ 5 µs0x0002_0003 |
system.power | PowerRequest → Done | ohne Budget0x0002_0004 |
system.readLog | LogRead → LogText | ≤ 20 µs0x0002_0005 |
system.processorTimes | InfoRequest → ProcessorTimes | ≤ 5 µs0x0002_0006 |
system.random | RandomRequest → RandomBytes | ≤ 5 µs0x0002_0009 |
system.adjustClock | ClockAdjust → Now | ≤ 5 µs0x0002_000A |
system.processor | ProcessorQuery → Processor | ≤ 5 µs0x0002_000B |
system.bootTimes | InfoRequest → BootTimes | ≤ 5 µs0x0002_000C |
system.realtime | RealtimeQuery → RealtimeStats | ≤ 2 µs0x0002_000D |
system.startWorker | WorkerStart → WorkerStarted | ≤ 1 ms0x0002_000E |
system.start event | Started | Ereignis0x0002_0007 |
system.stop event | Stopping | Ereignis0x0002_0008 |
filesModul 0x0003 · Dateien des Programms, Ordner, Volume und Snapshots.
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
files.open | OpenFile → FileOpened | ≤ 2 ms0x0003_0001 |
files.read | FileRead → FileData | ≤ 2 ms0x0003_0002 |
files.write | FileWrite → FileWritten | ≤ 2 ms0x0003_0003 |
files.close | FileRequest → Done | ≤ 5 ms0x0003_0004 |
files.list | ListFolder → FolderEntries | ≤ 2 ms0x0003_0005 |
files.info | FileQuery → FileInfo | ≤ 200 µs0x0003_0006 |
files.makeFolder | FileQuery → Done | ≤ 2 ms0x0003_0007 |
files.remove | FileQuery → Done | ≤ 2 ms0x0003_0008 |
files.rename | Rename → Done | ≤ 2 ms0x0003_0009 |
files.reveal | FileQuery → Done | ohne Budget0x0003_000A |
files.volume | VolumeQuery → VolumeInfo | ≤ 50 µs0x0003_000B |
files.snapshot | SnapshotQuery → SnapshotInfo | ≤ 50 µs0x0003_000C |
files.takeSnapshot | SnapshotQuery → SnapshotInfo | ≤ 50 ms0x0003_000D |
files.deleteSnapshot | SnapshotQuery → Done | ≤ 50 ms0x0003_000E |
files.restore | RestoreFile → Restored | ≤ 2 s0x0003_000F |
dbModul 0x0004 · SOFTrobotsDB: Datenbanken öffnen, schreiben, abfragen, Messreihen lesen.
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
db.open | OpenDatabase → DatabaseOpened | ≤ 2 ms0x0004_0001 |
db.write | TableWrite → Written | ≤ 1 ms0x0004_0002 |
db.query | TableQuery → TablePage | ≤ 5 ms0x0004_0003 |
db.describe | DatabaseRequest → Description | ≤ 1 ms0x0004_0004 |
db.close | DatabaseRequest → Done | ≤ 50 µs0x0004_0005 |
db.readSeries | SeriesRead → SeriesRecords | ≤ 5 ms0x0004_0006 |
netModul 0x0005 · Netzwerk: Namen, TCP, UDP, Lauschen (get/post geplant).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
net.get | Get → Response | ≤ 2 s0x0005_0001 |
net.post | Post → Response | ≤ 2 s0x0005_0002 |
net.resolve | Resolve → Resolved | ≤ 6 s0x0005_0003 |
net.connect | Connect → Connected | ≤ 5 s0x0005_0004 |
net.send | SocketSend → Sent | ≤ 1 s0x0005_0005 |
net.receive | SocketReceive → SocketData | ≤ 10 s0x0005_0006 |
net.close | SocketRequest → Done | ≤ 50 µs0x0005_0007 |
net.openDatagram | DatagramOpen → DatagramOpened | ≤ 5 s0x0005_0008 |
net.sendDatagram | DatagramSend → Done | ≤ 1 s0x0005_0009 |
net.receiveDatagram | DatagramReceive → DatagramData | ≤ 10 s0x0005_000A |
net.closeDatagram | DatagramRequest → Done | ≤ 50 µs0x0005_000B |
net.listen | Listen → Listening | ≤ 50 µs0x0005_000C |
net.accept | Accept → Accepted | ≤ 10 s0x0005_000D |
net.listenerInfo | ListenerRequest → ListenerInfo | ≤ 20 µs0x0005_000E |
net.closeListener | ListenerRequest → Done | ≤ 50 µs0x0005_000F |
programsModul 0x0006 · Das Programm selbst und die anderen: Dokument, Settings, Geheimnis, Start/Stopp, Listen, Abstürze, Bauen.
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
programs.document | DocumentRequest → HandedDocument | ≤ 5 µs0x0006_0001 |
programs.settings | SettingsRequest → SettingsData | ≤ 200 µs0x0006_0002 |
programs.saveSettings | SettingsData → Done | ≤ 2 ms0x0006_0003 |
programs.setSecret | SecretWrite → Done | ≤ 5 ms0x0006_0004 |
programs.hasSecret | SecretQuery → SecretPresence | ≤ 2 ms0x0006_0005 |
programs.ownSecret | SettingsRequest → SecretData | ≤ 2 ms0x0006_0006 |
programs.launch | ProgramName → Done | ≤ 50 ms0x0006_0007 |
programs.stop | ProgramName → Done | ≤ 50 µs0x0006_0008 |
programs.open | OpenDocument → Done | ≤ 50 ms0x0006_0009 |
programs.running | ProgramListQuery → RunningPrograms | ≤ 50 µs0x0006_000A |
programs.installed | ProgramListQuery → InstalledPrograms | ≤ 20 ms0x0006_000B |
programs.presentation | ProgramName → ProgramLook | ≤ 5 ms0x0006_000C |
programs.crashes | ProgramListQuery → CrashList | ≤ 20 µs0x0006_000D |
programs.usage | ProgramListQuery → UsageList | ≤ 100 µs0x0006_000E |
programs.signature | ProgramName → SignatureCheck | ≤ 50 ms0x0006_000F |
programs.build | BuildProgram → BuiltProgram | ≤ 3 s0x0006_0010 |
programs.signShare | SharePackage → ShareSignature | ≤ 20 ms0x0006_0011 |
programs.checkShare | SharePackage → ShareCheck | ≤ 20 ms0x0006_0012 |
programs.uninstall | Uninstall → Done | ≤ 3 s0x0006_0013 |
programs.leftovers | ProgramListQuery → Leftovers | ≤ 50 ms0x0006_0014 |
programs.deleteLeftover | LeftoverDelete → Done | ≤ 3 s0x0006_0015 |
protectionModul 0x0007 · Geräteschutz: Passwort für DATA, Sperre, Besitzerwahl (system.protection).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
protection.status | ProtectionQuery → ProtectionStatus | ≤ 5 µs0x0007_0001 |
protection.setUp | Password → RecoveryKey | ≤ 3 s0x0007_0002 |
protection.unlock | Password → Done | ≤ 3 s0x0007_0003 |
protection.verify | Password → Done | ≤ 3 s0x0007_0004 |
protection.choices | ProtectionQuery → OwnerChoices | ≤ 2 ms0x0007_0005 |
updateModul 0x0008 · Systemkopien A/B und Updates (system.update).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
update.status | UpdateQuery → UpdateState | ≤ 5 µs0x0008_0001 |
update.install | UpdatePackage → Done | ≤ 60 s0x0008_0002 |
update.textStart | StartLook → Done | ≤ 50 ms0x0008_0003 |
update.startSound | StartSound → Done | ≤ 50 ms0x0008_0004 |
devicesModul 0x0009 · Controller für Treiberprogramme (device.usb, device.can).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
devices.open | ControllerQuery → Controller | ≤ 1 ms0x0009_0001 |
devices.allocate | DMARequest → DMAMemory | ≤ 1 ms0x0009_0002 |
channelsModul 0x000A · Nachrichtenkanäle zwischen Programmen (ipc).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
channels.open | ChannelName → ChannelOpened | ≤ 1 ms0x000A_0001 |
screenModul 0x0010 · Fenster als Beschreibung von Views (Reflex).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
screen.show | ScreenView → Done | ≤ 200 µs0x0010_0001 |
screen.ScreenView effect | | Effekt0x0010_0002 |
screen.pressed event | Pressed | Ereignis0x0010_0003 |
screen.slid event | Slid | Ereignis0x0010_0004 |
screen.key event | KeyPress | Ereignis0x0010_0005 |
displayModul 0x0011 · Bildschirm und Fensterspeicher (ui.surface, display.own).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
display.open | DisplayQuery → ScreenInfo | ≤ 5 ms0x0011_0001 |
display.flush | PixelRect → Done | ≤ 200 µs0x0011_0002 |
display.screenshot | DisplayQuery → Done | ≤ 50 ms0x0011_0003 |
display.createSurface | DisplayQuery → SurfaceMemory | ≤ 5 ms0x0011_0004 |
display.surface | SurfaceQuery → SurfaceState | ≤ 20 µs0x0011_0005 |
display.submitInput | InputEvent → Done | ≤ 5 µs0x0011_0006 |
clipboardModul 0x0012 · Kopieren und Einsetzen.
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
clipboard.copy | ClipText → Done | ≤ 50 µs0x0012_0001 |
clipboard.paste | PasteRequest → ClipText | ≤ 50 µs0x0012_0002 |
clipboard.grant | PasteGrant → Done | ohne Budget0x0012_0003 |
clipboard.generation | PasteRequest → ClipGeneration | ≤ 2 µs0x0012_0004 |
dialogModul 0x0013 · Öffnen- und Sichern-Dialog des Systems (neu, 10.11.).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
dialog.ask neu | DialogAsk → DialogTicket | ≤ 1 ms0x0013_0001 |
dialog.result neu | DialogTicket → DialogResult | ≤ 20 µs0x0013_0002 |
dialog.questions neu | QuestionsRequest → DialogQuestions | ≤ 50 µs0x0013_0003 |
dialog.answer neu | DialogAnswer → Done | ≤ 5 ms0x0013_0004 |
cameraModul 0x0020 · Die Kameras des Roboters (device.camera, camera.provide).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
camera.list | CameraListRequest → CameraList | ≤ 20 µs0x0020_0001 |
camera.frame | FrameRequest → CameraFrame | ≤ 10 µs0x0020_0002 |
camera.toWorld | PixelPoint → WorldPoint | ≤ 2 µs0x0020_0003 |
camera.fromWorld | WorldPoint → PixelPoint | ≤ 2 µs0x0020_0004 |
camera.frame event | CameraFrame | Ereignis0x0020_0005 |
camera.ring | CameraRingQuery → CameraRingMap | ≤ 1 ms0x0020_0006 |
camera.provide | ProvideCamera → ProvidedCamera | ≤ 1 ms0x0020_0007 |
camera.push | PushFrame → Done | ≤ 500 µs0x0020_0008 |
camera.close | CameraRingQuery → Done | ≤ 1 ms0x0020_0009 |
visionModul 0x0021 · Was die Kameras sehen: Erkennungen, Farbmasken, Bewegung, Marker.
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
vision.detections | DetectionFilter → Detections | ≤ 10 µs0x0021_0001 |
vision.mask | ColorMask → Mask | ≤ 2 ms0x0021_0002 |
vision.motion | MotionRequest → Motion2D | ≤ 1 ms0x0021_0003 |
vision.markers | MarkerRequest → Markers | ≤ 3 ms0x0021_0004 |
vision.detections event | Detections | Ereignis0x0021_0005 |
soundModul 0x0030 · Ton über den Mischer des Systems (sound.play; tone/play/stop geplant).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
sound.tone | Tone → Played | ≤ 20 µs0x0030_0001 |
sound.play | Play → Played | ≤ 100 µs0x0030_0002 |
sound.stop | StopSound → Done | ≤ 20 µs0x0030_0003 |
sound.Tone effect | | Effekt0x0030_0004 |
sound.Play effect | | Effekt0x0030_0005 |
sound.open | SoundQuery → SoundRing | ≤ 1 ms0x0030_0006 |
sound.close | SoundQuery → Done | ≤ 5 µs0x0030_0007 |
sound.status | SoundQuery → SoundStatus | ≤ 5 µs0x0030_0008 |
sound.setVolume | MasterVolume → Done | ≤ 50 µs0x0030_0009 |
microphoneModul 0x0031 · Die Mikrofone des Roboters (device.microphone, microphone.provide).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
microphone.list | MicrophoneListRequest → MicrophoneList | ≤ 20 µs0x0031_0001 |
microphone.open | MicrophoneQuery → MicrophoneMap | ≤ 1 ms0x0031_0002 |
microphone.close | MicrophoneQuery → Done | ≤ 1 ms0x0031_0003 |
microphone.listeners | ListenersRequest → Listeners | ≤ 20 µs0x0031_0004 |
microphone.stopAll | StopListening → Done | ≤ 1 ms0x0031_0005 |
microphone.provide | ProvideMicrophone → ProvidedMicrophone | ≤ 1 ms0x0031_0006 |
microphone.push | PushSamples → Done | ≤ 200 µs0x0031_0007 |
microphone.loud event | Loudness | Ereignis0x0031_0008 |
microphone.voice event | VoiceChange | Ereignis0x0031_0009 |
signalModul 0x0040 · Messwerte über die Zeit: Spektrum, Filter, Statistik.
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
signal.spectrum | SpectrumRequest → Spectrum | ≤ 500 µs0x0040_0001 |
signal.filter | FilterRequest → Samples | ≤ 200 µs0x0040_0002 |
signal.stats | Samples → Stats | ≤ 50 µs0x0040_0003 |
signal.sine | SineRequest → Samples | ≤ 2 ms0x0040_0004 |
askModul 0x0050 · Claude fragen (Reflex-Skripte mit dem Schlüssel aus Settings).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
ask.claude | Question → Answer | ohne Budget0x0050_0001 |
safetyModul 0x0100 · Der Not-Halt (lesen frei, stop/release mit safety.stop).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
safety.state | SafetyQuery → SafetyState | ≤ 2 µs0x0100_0001 |
safety.stop | EmergencyStop → Done | ≤ 20 µs0x0100_0002 |
safety.release | SafetyQuery → Done | ≤ 20 µs0x0100_0003 |
robotModul 0x0101 · Gelenke bewegen; Sicherheitsgrenzen und Not-Halt gelten immer.
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
robot.move | Move → Motion | ≤ 20 µs0x0101_0001 |
robot.state | JointQuery → JointState | ≤ 5 µs0x0101_0002 |
robot.Move effect | | Effekt0x0101_0003 |
robot.moved event | Motion | Ereignis0x0101_0004 |
armModul 0x0102 · Der Arm als Ganzes: Vorwärts-/Rückwärtskinematik, Gelenke, Drehmoment.
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
arm.forward | Angles → ToolPose | ≤ 2 µs0x0102_0001 |
arm.inverse | Reach → Angles | ≤ 20 µs0x0102_0002 |
arm.joints | JointsRequest → JointList | ≤ 10 µs0x0102_0003 |
arm.torque | TorqueCommand → Done | ≤ 1 µs0x0102_0004 |
arm.TorqueCommand effect | | Effekt0x0102_0005 |
peersModul 0x0110 · Roboter in der Nähe (für später reserviert).
| Aufruf | Anfrage → Antwort | Zeitbudget · Kennung |
|---|---|---|
peers.list | PeersRequest → PeerList | ohne Budget0x0110_0001 |
peers.send | Envelope → Delivered | ohne Budget0x0110_0002 |
peers.received event | Envelope | Ereignis0x0110_0003 |
VollständigkeitAbgleich mit docs/api/app-api-1.txt
Alle 162 Typen der Liste kommen vor; rund 1000 der 1307 Deklarationen sind einzeln mit Namen genannt. Bewusst nur zusammengefasst (Felder in einer Zeile) oder weggelassen ist, was ein Programm nicht direkt aufruft:
SurfaceMessage-Konstanten (das Draht-Protokoll zwischen Surface und Display-Dienst) ·
Layout-Offsets von DetectionRing · Einzelfelder von Usage, SystemStatus, UpdateStatus,
ZipEntry, ProcessorInfo, FLACInfo, VP9Picture, UIDesign (Palette),
Chart-Farben, MatroskaID-Konstanten · Interna von Deflater (Tabellen, Huffman) und
HuffmanCode · die Ergebnis-Builder-Technik der Views (ViewBuilder, ViewList,
ListAnd, ListOf1) · Hilfsprotokolle ByteSource, ByteSink,
ZipDestination, ZipInput · Presentation.presentationIcon/-Bytes.
Umgekehrt zeigt diese Seite auch, was nicht in app-api-1.txt steht, weil tools/api/symbols.py
die Ordner nicht liest: File, Files, FileError, Database und Verwandte
(userland/shapes/), SecureConnection (uses-tls), Claude (uses-ai) – und das heute neue
Running.hiddenFromDock.
Nichts gefunden.