Aller au contenu

Contrôle externe (ligne de commande et tube nommé)

Les macros ReciPro peuvent être exécutées non seulement depuis l'éditeur intégré, mais aussi depuis l'extérieur de l'application. Deux mécanismes sont disponibles :

Mécanisme Style Usage typique
Ligne de commande (/m /o /x) Ponctuel : démarrer ReciPro → exécuter une macro → (éventuellement) quitter Traitements par lots, CI, tâches planifiées
Tube nommé (ReciPro.Macro.v1) Interactif : envoyer des macros à un ReciPro en cours d'exécution et recevoir les résultats Scripts Python/Jupyter, PC de pilotage d'instrument, intégration avec d'autres logiciels

Les deux exécutent la macro avec le même moteur que l'éditeur ; toute l'API intégrée est donc disponible.


Exécution en ligne de commande

ReciPro.exe /m <macro.mcr> [/o <result.txt>] [/x]
Option Signification
/m Exécute au démarrage le premier fichier *.mcr existant trouvé parmi les arguments.
/o <fichier> Mode silencieux : aucune boîte de dialogue n'est affichée ; la sortie print() de la macro et toute trace d'erreur sont écrites dans <fichier> (UTF-8). En cas d'échec, le code de sortie du processus vaut 1.
/x Ferme ReciPro à la fin de la macro (recommandé pour un usage par lots — le code de sortie n'est renvoyé à l'appelant qu'à la fin du processus).

Sans /o, les erreurs apparaissent sous forme de boîtes de dialogue ordinaires (utile pendant le développement de la macro). Avec /o, l'exécution est entièrement automatique : erreurs de syntaxe, erreurs d'exécution, fichier de macro introuvable et même échec d'écriture du fichier de résultat se terminent tous par le code de sortie 1.

Exemple : fichier batch

ReciPro.exe /m C:\work\saed_series.mcr /o C:\work\result.txt /x
if errorlevel 1 (
    echo Echec de la macro :
    type C:\work\result.txt
)

Exemple : 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 }

Écouteur de tube nommé

Pendant son exécution, ReciPro peut accepter des macros provenant d'autres programmes via le tube nommé Windows \\.\pipe\ReciPro.Macro.v1. Un client écrit une macro, ReciPro l'exécute, puis le client relit le résultat — un simple cycle requête/réponse utilisable depuis Python, PowerShell, C#, ou tout ce qui sait ouvrir un fichier.

Activation

L'écouteur est désactivé par défaut. Activez-le depuis la fenêtre principale :

Options → Accept external macro commands (named pipe)

Le réglage est conservé d'une session à l'autre.

Note de sécurité

Tant qu'il est activé, tout processus s'exécutant sous le même utilisateur Windows peut exécuter du code de macro dans ReciPro. Les autres utilisateurs (et les autres machines) sont rejetés. Ne l'activez que lorsque vous utilisez réellement le contrôle externe.

Protocole

Élément Spécification
Nom du tube \\.\pipe\ReciPro.Macro.v1 (machine locale, même utilisateur uniquement)
Requête Code source de la macro en UTF-8, terminé par un unique octet NUL (0x00). 1 Mio maximum, doit arriver en moins de 30 s.
Réponse JSON UTF-8 {"output":"...","error":"..."}, puis le serveur ferme la connexion (lire jusqu'à EOF).
output Tout ce que la macro a écrit sur stdout/stderr (print() fonctionne ici, contrairement à l'éditeur graphique).
error Chaîne vide en cas de succès ; sinon la trace Python, l'erreur de syntaxe ou un message d'erreur de protocole.
Connexions Une connexion = une macro. Les commandes sont exécutées une à une, dans l'ordre.
État La portée Python est partagée avec l'éditeur de macros et persiste entre les commandes — les variables définies dans une commande sont visibles dans la suivante.
Instances Si plusieurs processus ReciPro tournent, seul le premier écoute.

La réponse est envoyée après la fin de la macro ; une simulation longue signifie donc simplement une longue attente côté client — la détection de fin est automatique.

Python : exemple minimal

Le client est du CPython ordinaire — numpy, pandas, Jupyter, tout est possible. Aucun paquet n'est nécessaire pour le tube lui-même :

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 : une fonction utilitaire réutilisable

Tous les exemples suivants utilisent cette petite fonction :

import json

PIPE = r'\\.\pipe\ReciPro.Macro.v1'

def recipro(code):
    """Exécute une macro IronPython dans ReciPro ; renvoie sa sortie imprimée sous forme de str."""
    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 : charger un CIF et enregistrer un cliché SAED

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 : traitement par lots de nombreux fichiers CIF

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 : série d'inclinaisons

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 : données de taches vers pandas

ReciPro.DifSim.SpotInfo() renvoie une chaîne CSV ; affichez-la avec print() et analysez-la côté client :

import io, pandas as pd

csv_text = recipro('print(ReciPro.DifSim.SpotInfo())')
df = pd.read_csv(io.StringIO(csv_text))
print(df.head())

Python : l'état persiste entre les commandes

recipro('n = ReciPro.CrystalList.Count')   # définir une variable...
print(recipro('print(n * 2)'))             # ...et l'utiliser plus tard

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      # sortie imprimée
$r.error       # vide en cas de succès

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)"));

Gestion des erreurs

Lorsque la macro échoue, error contient la trace Python habituelle et output ce qui avait été imprimé avant l'échec :

{"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"}

Les violations de protocole sont signalées de la même manière (error commence par Protocol error:) : terminateur NUL manquant, requête de plus de 1 Mio, UTF-8 invalide, ou requête ayant mis plus de 30 s à arriver.

Remarques et limites

  • Le délai de 30 secondes s'applique uniquement au transfert de la requête — l'exécution de la macro peut durer indéfiniment ; la réponse arrive à la fin.
  • Les caractères non ASCII de la réponse JSON sont échappés en \uXXXX (JSON standard) ; tout analyseur JSON les restitue.
  • Si ReciPro n'est pas lancé (ou si l'écouteur est désactivé), l'ouverture/la connexion du client échoue ou expire — démarrez d'abord ReciPro.
  • Comme tout s'exécute sur le thread de l'interface, les commandes sont mises en file et exécutées strictement une à une ; évitez d'envoyer une nouvelle commande pendant qu'une très longue est en cours depuis un autre script.

Voir aussi