Externe Steuerung (Kommandozeile & Named Pipe)¶
ReciPro-Makros lassen sich nicht nur aus dem eingebauten Editor ausführen, sondern auch von außerhalb der Anwendung. Dafür gibt es zwei Mechanismen:
| Mechanismus | Stil | Typischer Einsatz |
|---|---|---|
Kommandozeile (/m /o /x) |
Einmalig: ReciPro starten → Makro ausführen → (optional) beenden | Stapelverarbeitung, CI, geplante Aufgaben |
Named Pipe (ReciPro.Macro.v1) |
Interaktiv: Makros an ein laufendes ReciPro senden und die Ergebnisse empfangen | Python-/Jupyter-Skripte, Steuerrechner von Messgeräten, Integration mit anderer Software |
Beide führen das Makro mit derselben Engine aus wie der Editor, sodass die gesamte integrierte API zur Verfügung steht.
Ausführung über die Kommandozeile¶
ReciPro.exe /m <macro.mcr> [/o <result.txt>] [/x]
| Schalter | Bedeutung |
|---|---|
/m |
Führt nach dem Start die erste unter den Argumenten gefundene vorhandene *.mcr-Datei aus. |
/o <Datei> |
Quiet-Modus: Es werden keine Dialoge angezeigt; die print()-Ausgabe des Makros und ein etwaiger Fehler-Traceback werden in <Datei> geschrieben (UTF-8). Im Fehlerfall wird der Exit-Code des Prozesses auf 1 gesetzt. |
/x |
Schließt ReciPro nach dem Ende des Makros (für den Stapelbetrieb empfohlen — der Exit-Code wird dem Aufrufer erst zurückgegeben, wenn der Prozess endet). |
Ohne /o erscheinen Fehler als gewöhnliche Dialogfenster (nützlich beim Entwickeln des Makros). Mit /o läuft alles vollständig unbeaufsichtigt: Syntaxfehler, Laufzeitfehler, eine fehlende Makrodatei und sogar ein Fehlschlag beim Schreiben der Ergebnisdatei enden mit Exit-Code 1.
Beispiel: Batchdatei¶
ReciPro.exe /m C:\work\saed_series.mcr /o C:\work\result.txt /x
if errorlevel 1 (
echo Makro FEHLGESCHLAGEN:
type C:\work\result.txt
)
Beispiel: PowerShell¶
$p = Start-Process ReciPro.exe -ArgumentList '/m','C:\work\job.mcr','/o','C:\work\result.txt','/x' -Wait -PassThru
if ($p.ExitCode -ne 0) { Get-Content C:\work\result.txt }
Named-Pipe-Listener¶
Während ReciPro läuft, kann es über die Windows-Named-Pipe \\.\pipe\ReciPro.Macro.v1 Makros von anderen Programmen entgegennehmen. Ein Client schreibt ein Makro, ReciPro führt es aus, und der Client liest das Ergebnis zurück — ein einfacher Request/Response-Zyklus, der aus Python, PowerShell, C# oder allem funktioniert, was eine Datei öffnen kann.
Aktivieren¶
Der Listener ist standardmäßig aus. Aktivieren Sie ihn im Hauptfenster:
Optionen → Accept external macro commands (named pipe)
Die Einstellung bleibt über Sitzungen hinweg erhalten.
Sicherheitshinweis
Solange der Listener aktiv ist, kann jeder Prozess desselben Windows-Benutzers Makrocode innerhalb von ReciPro ausführen. Andere Benutzer (und andere Rechner) werden abgewiesen. Aktivieren Sie ihn nur, wenn Sie die externe Steuerung tatsächlich nutzen.
Protokoll¶
| Element | Spezifikation |
|---|---|
| Pipe-Name | \\.\pipe\ReciPro.Macro.v1 (lokaler Rechner, nur derselbe Benutzer) |
| Anfrage | Makro-Quelltext als UTF-8, abgeschlossen durch ein einzelnes NUL-Byte (0x00). Maximal 1 MiB, muss innerhalb von 30 s eintreffen. |
| Antwort | UTF-8-JSON {"output":"...","error":"..."}, danach schließt der Server die Verbindung (bis EOF lesen). |
output |
Alles, was das Makro nach stdout/stderr geschrieben hat (print() funktioniert hier, anders als im GUI-Editor). |
error |
Bei Erfolg leerer String; andernfalls der Python-Traceback, ein Syntaxfehler oder eine Protokollfehlermeldung. |
| Verbindungen | Eine Verbindung = ein Makro. Befehle werden nacheinander in ihrer Reihenfolge ausgeführt. |
| Zustand | Der Python-Namensraum wird mit dem Makro-Editor geteilt und bleibt zwischen Befehlen erhalten — in einem Befehl definierte Variablen sind im nächsten sichtbar. |
| Instanzen | Laufen mehrere ReciPro-Prozesse, lauscht nur der erste. |
Die Antwort wird gesendet, nachdem das Makro fertig ist; eine lange Simulation bedeutet daher lediglich eine lange Wartezeit auf Client-Seite — die Fertigstellung wird automatisch erkannt.
Python: minimales Beispiel¶
Die Client-Seite ist ganz normales CPython — numpy, pandas, Jupyter, alles ist möglich. Für die Pipe selbst sind keine Pakete nötig:
with open(r'\\.\pipe\ReciPro.Macro.v1', 'r+b', buffering=0) as f:
f.write('print(ReciPro.CrystalList.Count)'.encode('utf-8') + b'\0')
print(f.read().decode('utf-8')) # {"output":"68\r\n","error":""}
Python: eine wiederverwendbare Hilfsfunktion¶
Alle folgenden Beispiele verwenden diese kleine Funktion:
import json
PIPE = r'\\.\pipe\ReciPro.Macro.v1'
def recipro(code):
"""Führt ein IronPython-Makro in ReciPro aus und gibt dessen Ausgabe als str zurück."""
with open(PIPE, 'r+b', buffering=0) as f:
f.write(code.encode('utf-8') + b'\0')
res = json.loads(f.read().decode('utf-8'))
if res['error']:
raise RuntimeError(res['error'])
return res['output']
Python: eine CIF laden und ein SAED-Muster speichern¶
recipro('''
ReciPro.File.ReadCrystal(r"C:\\data\\rutile.cif")
ReciPro.DifSim.Open()
ReciPro.DifSim.Source_Electron()
ReciPro.DifSim.Energy = 200
ReciPro.Dir.ProjectAlongAxis(0, 0, 1)
ReciPro.DifSim.SaveAsPng(r"C:\\data\\rutile_001.png")
''')
Python: Stapelverarbeitung vieler CIF-Dateien¶
import glob, os
recipro('ReciPro.DifSim.Open(); ReciPro.DifSim.Source_Electron(); ReciPro.DifSim.Energy = 200')
for cif in glob.glob(r'C:\data\*.cif'):
png = os.path.splitext(cif)[0] + '_SAED.png'
recipro(f'''
ReciPro.File.ReadCrystal(r"{cif}")
ReciPro.Dir.ProjectAlongAxis(0, 0, 1)
ReciPro.DifSim.SaveAsPng(r"{png}")
''')
print('done:', cif)
Python: Kippserie¶
for i in range(10):
recipro(f'''
ReciPro.Dir.RotateAroundAxisInDeg(1, 0, 0, 1)
ReciPro.DifSim.SaveAsPng(r"C:\\data\\tilt_{i:02d}.png")
''')
Python: Reflexdaten nach pandas¶
ReciPro.DifSim.SpotInfo() liefert eine CSV-Zeichenkette; geben Sie sie mit print() aus und parsen Sie sie auf der Client-Seite:
import io, pandas as pd
csv_text = recipro('print(ReciPro.DifSim.SpotInfo())')
df = pd.read_csv(io.StringIO(csv_text))
print(df.head())
Python: der Zustand bleibt zwischen Befehlen erhalten¶
recipro('n = ReciPro.CrystalList.Count') # eine Variable definieren...
print(recipro('print(n * 2)')) # ...und in einem späteren Befehl verwenden
PowerShell¶
function Invoke-ReciProMacro([string]$Code) {
$pipe = [System.IO.Pipes.NamedPipeClientStream]::new('.', 'ReciPro.Macro.v1', [System.IO.Pipes.PipeDirection]::InOut)
$pipe.Connect(5000)
$bytes = [System.Text.Encoding]::UTF8.GetBytes($Code) + [byte[]]@(0)
$pipe.Write($bytes, 0, $bytes.Length); $pipe.Flush()
$ms = [System.IO.MemoryStream]::new(); $buf = New-Object byte[] 4096
while (($n = $pipe.Read($buf, 0, $buf.Length)) -gt 0) { $ms.Write($buf, 0, $n) }
$pipe.Dispose()
[System.Text.Encoding]::UTF8.GetString($ms.ToArray()) | ConvertFrom-Json
}
$r = Invoke-ReciProMacro 'print(ReciPro.Crystal.Name)'
$r.output # ausgegebener Text
$r.error # bei Erfolg leer
C¶
using System.IO.Pipes;
using System.Text;
using System.Text.Json;
static string ReciPro(string code)
{
using var pipe = new NamedPipeClientStream(".", "ReciPro.Macro.v1", PipeDirection.InOut);
pipe.Connect(5000);
var req = Encoding.UTF8.GetBytes(code + "\0");
pipe.Write(req, 0, req.Length);
using var ms = new MemoryStream();
pipe.CopyTo(ms);
var res = JsonSerializer.Deserialize<JsonElement>(ms.ToArray());
var error = res.GetProperty("error").GetString();
if (error!.Length > 0) throw new InvalidOperationException(error);
return res.GetProperty("output").GetString()!;
}
Console.WriteLine(ReciPro("print(ReciPro.CrystalList.Count)"));
Fehlerbehandlung¶
Schlägt das Makro fehl, enthält error den gewöhnlichen Python-Traceback und output das, was vor dem Fehler ausgegeben wurde:
{"output":"before error\r\n",
"error":"Traceback (most recent call last):\r\n File \"<string>\", line 2, in <module>\r\nNameError: name 'foo' is not defined"}
Protokollverstöße werden auf dieselbe Weise gemeldet (error beginnt mit Protocol error:): fehlender NUL-Abschluss, Anfrage über 1 MiB, kein gültiges UTF-8 oder eine Anfrage, deren Übertragung länger als 30 s gedauert hat.
Hinweise & Einschränkungen¶
- Die 30-Sekunden-Frist gilt nur für die Übertragung der Anfrage — die Ausführung des Makros darf beliebig lange dauern; die Antwort kommt, wenn sie beendet ist.
- Nicht-ASCII-Zeichen in der JSON-Antwort werden als
\uXXXXmaskiert (JSON-Standard); jeder JSON-Parser stellt sie wieder her. - Läuft ReciPro nicht (oder ist der Listener deaktiviert), schlägt der Verbindungsaufbau des Clients fehl oder läuft in einen Timeout — starten Sie zuerst ReciPro.
- Da alles im GUI-Thread abläuft, werden Befehle strikt nacheinander abgearbeitet; senden Sie möglichst keinen neuen Befehl, während aus einem anderen Skript noch ein sehr langer unterwegs ist.