[Globals]
(IniScript=<path_to_file>{.py})
(StyleSheet=<path_to_file>)
(Icon=<path_to_file>)
(LegacyName=<legacy_code_string>)
(Font=<string>)
(Geometry=<string>)
InterfaceSize=<n>
[Interface_(i=0..n-1)]
Name=First Interface
ConfigFile=<path_to_file>{.xml}
BusDriver=<path_to_file>{(.dll|.so)|.py}
UiFile=<path_to_file>{.ui}
(Plugin=<path_to_file>{(.dll|.so)|.py})
(CurrentLogPath=<path>)
(IniScript=<path_to_file>{.py} )
[Tools]
(Plugin_0=<path_to_file>{(.dll|.so)|.py}
L'applicativo, una volta determinata la sessione,
in fase di inizializzazione carica per ogni interfaccia una serie di
files:
- database ICD [ConfigFile]
- driver di comunicazione [BusDriver]
- interfaccia grafica [UiFile]
- plugin [Plugin]
- script python [IniScript]
I Plugin opzionali *plugin raccolti nella cartella
$(LeoPlatSimHomeDir)/options vengono automaticamente inclusi nella
sessione, altri possono essere specificati nella sezione Tools del file
.INI.
Per essere riconosciuti tali plugin devono, oltre a rispettare
la struttura software basata su SDK dedicato, avere la dicitura
"<nome>Plugin.<estensione>"
con:
- <nome> : testo libero
- <estensione> : py ( plugin Python )
oppure dll ( Windows ) o so ( Linux )
ConfigFile
E' un file che descrive la composizione dei messaggi di
interscambio tra il simulatore ed il target.
Utilizza un formalismo XML le cui regole di composizione sono tracciate
dal documento DTD:
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE ICDHandler [<!ELEMENT ICDHandler
(HiLevel,LowLevel,Mapping)>
<!ELEMENT HiLevel (Message+)>
<!ELEMENT Message (Field+)>
<!ELEMENT Field (Values*)>
<!ELEMENT Values (Value+)>
<!ELEMENT Value (#PCDATA)>
<!ATTLIST ICDHandler
Name CDATA #REQUIRED
Version CDATA
#REQUIRED
Endianness
(rawbyte|bigendian16|bigendian32|littleendian16|littleendian32)
"rawbyte"
Ui
(all_tab|all_vertical|all_horizontal|by_iotype) "by_iotype"
>
<!ATTLIST Message
Id CDATA #REQUIRED
Name CDATA #REQUIRED
Width CDATA #REQUIRED
IOType (tx|rx)
#REQUIRED
uSecPeriodTime CDATA
#IMPLIED
>
<!ATTLIST Field
Name CDATA #REQUIRED
Width CDATA #REQUIRED
Offset CDATA
#REQUIRED
Min CDATA #IMPLIED
Max CDATA
#IMPLIED
Weight CDATA "1"
OffsetValue CDATA "0"
Type
(spare|boolean|short|ushort|int|uint|long|ulong|double|float|string|enum)
#REQUIRED
Unit CDATA #IMPLIED
Display
(binary|decimal|hexadecimal) "decimal"
Default CDATA
#IMPLIED
Encoding
(sign_module|bcd|binary|=<formula>) "binary"
Resolution CDATA
"0.000001"
Ui
(default|entry|spinbox|combobox|radiobox|checkbox|listbox|slider|progressbar|knob)
"default"
>
<!ATTLIST Value
Name CDATA #REQUIRED
>
]>
Notes:
Encoding Formula
The string must contains a mathematic expresion.
The followings variables can be used:
- min : Field Min
- max : Field Max
- lsb : Field Weight
- off : Field OffsetValue
- len : Field Width
The following C math functions are also supported:
- abs (calls to fabs)
- acos, asin, atan, atan2, cos, cosh, sin, sinh, tan, tanh
- ceil, exp, floor
- ln (calls to log)
- log (calls to log10 by default, see below), log10
- pow, sqrt
Also, the following constants are available:
BusDriver
E' un file, tipicamente una libreria dinamica
(.dll, .so,... ), con il quale si construisce un canale di
comunicazione tra l'applicativo ed il target.
E' dipendente dal protocollo utilizzato ( RS232, Socket , GPIB, Arinc
429, MIL 1553 ....)
Puo' far riferimento a file di configurazione esterni.
Puo' ottenere informazioni relazionate al database ICD tramite
i nodi XML `LowLevel` e `Mapping
`, i quali sono lasciati a libera implementazione.
UiFile
E' un file testuale con il quale di definisce l'interfaccia
grafica dell'interfaccia. E' un file con sintassi xml derivante dal
framework Qt. Puo' essere modificato sia manualmente che tramite
strumenti della toolchain Qt ( per es. Designer ).
Plugin
E' un file, tipicamente una libreria dinamica
(.dll, .so,... ), con il quale vengono definite azioni
specifiche dell' interfaccia come automatismi , sincronizzazione tra
messaggi, extra funzionalita'. Si basa su una struttuta definita e
riconoscibile dall'applicativo ( vedi SDK e tecniche Plugin )
IniScript
E' uno script python che viene eseguito
in fase di inizializzazione dell'interfaccia
Caratteristiche
Per
quanto concerne le caratteristiche funzionali del simulatore, esse sono
soggette alle versioni rilasciate nel tempo per fissare problemi o
introdurre funzionalita' richieste dal cliente.
Attualmente sono implementate le seguenti:
| Resivione |
Nota |
| 103017 |
Possibilita' di configurare il BusDrver e i Plugin
direttamente con file python |
| 102810 |
Portabilita' verso python 3.x. |
| 49055 |
Aggiunta gestione expressioni matematiche sull encoder |
| 47733 |
Implementato iniScript |
| 47207 |
Allo startup LeoPlatSim carica, sottoforma di plugin,
tuttle le librerie presenti nei seguenti path:
- <installation path>
- <appdata>/LeonardoCompany/<build>/options/
seguendo la regole <name>Plugin.<suffix>
( es "plotterPlugin.dll" ) |
Funzionalita`
di Base
uPlatSim.exe --help
Usage: uPlatSim.exe [options]
LeonardoCompany Platform Simulator
Options:
-?, -h,
--help
Displays this help.
-v,
--version
Displays version information.
-s, --session <name> Set the
platform application session to run
-a, --appdata <dir> Set
the application data to dir
-b, --batch
<file> Run python script in
batch mode
-d, --debug
<file> Log debug
information into file
-i,
--cli
Run interactive cli python console instead of gui
-I, --ini <inifile> Manage
session by using ini file
-P, --python
<dir> Set python home
environment
L'applicativo puo essere utilizzato fondamentamente in 3
diversi modi:
- Attraverso l'interfaccia grafica ( normale utilizzo
dell'ambiente da parte di un utilizzatore )
- Eseguendo uno script da riga di comando ( utilizzabile per
es. all'interno di procedure shell/DOS )
- Command line ( attivazione della sola modalita' python
senza ambiente grafico [ per smanettoni ] )
Argomenti
globali
Tutte le modalita' di utilizzo prescindono dalla definizione
di alcuni argomenti globali di seguito descritti
Session
Una sessione e' il modo con cui si identifica un'
implementazione del simulatore di piattaforma.
Leonardo Platform Simulator e' pensato per avere una singola
installazione core ( eseguibile e librerie ) e una o piu' sessioni
dispobili. Genericamente l'applicativo cerca tale sessione /
identificativo in una determinata area applicativa dell' utente gestita
da sistema operativo
| Sistema Operativo |
Variabile |
| Windows |
%APPDATA%\LeonardoCompany |
| Linux |
$HOME/.config/LeonardoCompany |
L'identificativo passato come valore dell'argomento --session
o in forma abbreviata -s individua il file .ini
avente tale nome.
appdata
Con questo argomento opzionale si indica, attraverso il suo
valore, l'area applicativo nella quale cercare il file .ini con nome
passato dall'argomento session.
Utile per diversificare/versionare distribuzioni di
implementazioni o nel caso di versione portable.
Ini
Analogamente alla sessione il valore dell'argomento --ini
o in forma abbreviata -I individua univocamente il
file .ini ed utilizzandone la directory come area applicativa.
Python
Leonardo Platform Simulator utilizza la distribuzione Python
individuata dalle variabili di ambiente PYTHONHOME e
PYTHONPATH per poter usufruire dei moduli python
dispobili e non integrati nell'applicativo ( solo i built-in ).
Alternativamente si puo' indicare quale python usare
valorizzando tale argomento.
Modalita'
grafica
E la modalita' utlizzata per default . Di seguito le opzioni
di chiamata:
uPlatSim.exe --session=<name> [
--python=<dir> ] [ --appdata=<dir> ]
[--debug=<file>]
uPlatSim.exe --ini=<inifile> [
--python=<dir> ] [--debug=<file>]
Sono integrate due visualizzazioni. La prima (GUI
mode) presenta le interfacce attraverso elementi
interattivi ( entry , combobox, checkbuttons,... ), mentre la seconda (Python
Mode) gestisce la parte di interfaccia a python.
GUI
Mode

Nella seguente modalita' l'applicativo esegue le seguenti operazioni:
- vengono rapresentate in forma grafica tutte le
interfacce presentate dal file INI
- vengono eseguiti gli scripts di inizializzazione
( globali e di interfaccia )
L'utente ha la possibilita' di accedere ai campi di tutti i messaggi
implemenati per ogni singola interfaccia sia
- modalita grafica : accesso diretto tramite oggetti grafici
interattivi
- tramite console python : console python interattiva
- tramite il lancio di uno o piu' scripts: caricamento di
files ( o liste di files ) ed escuzione con generazione automatica di
un file di log nella stessa cartella dello script
Ogni interfaccia e` contraddistinta da un tab indicante il
nome dell’ interfaccia ( quello relativo all’ attributo name
del documento ICD (.xml) { Nella figura per es. troviamo le interfacce
AEROFLEX-IFF45TS e TIC-T760 }.
comando
e controllo { controllo interfaccia / bus }
Questa sezione gestisce il bus in termini di :
- Start :attivazione del traffico
- Stop :terminazione del traffico
- Configure: gestione dei parametri di interfaccia del bus
indicatore
messaggi
Indicatore dei messaggi:
- Tx = trasmessi
- Rx = ricevuti ,
- Err = errati (Tx eRx )
iniettore
errori di protocollo
Permette di comunicare al bus l’inserzione di tipologie di
errore di protocollo al fine di testarne i possibili effetti sul target.
gestione log
Permette di registrare e salvare su file il traffico dati in base a
tipologie di filtraggio:
- tutti i messaggi,
- solo i messaggi errati,
- solo i messaggi modificati
Su Windows per default i files sono salvati in %USERPROFILE%\Documents.
gestione
scrittura ed invio dei dati
Questa sezione permette di gestire l’invio o la richiesta di
ricezione di messaggi a seconda della tipologia del messaggio.
A seguito di una modifica di uno o piu’ campi associati a uno o piu
messaggi, si possono attuare le seguenti azioni:
Commit
Changes
TUTTE le modifiche sono salvate ed inviate al bus che ne
gestisce la trasmissione secondo le modalita’ del caso.
Revert
Changes
Vengono rigettate le modifiche e ripristinati i dati relativi
all’ultima modifica.
Commit
on the Fly
Le modifiche dei campi saranno processate direttamente.
Invio
di messaggi non modificati
Un messaggio non periodico puo’ essere inviato anche quando
questo non ha subito modifiche.
Il comando e’ attivo quando non vi sono modifiche pendenti.
Di seguito la sequenza:
- selezionare nella combobox associata al comando Resend
Msg il messaggio
- cliccare sul button avente come testo il nome del messaggio
( ogni click corrisponde ad un invio )
richiesta
ricezione messaggio
Per un messaggio non periodico in lettura puo’
esserne richiesto l’aggiornamento forzato ( quando questo e’ gestito
dal protocollo )
Il comando e’ attivo quando non vi sono modifiche pendenti.
Di seguito la sequenza:
- selezionare nella combobox associata al comando Resend
Msg il messaggio
- cliccare sul button avente come testo il nome del messaggio
( ogni click corrisponde ad una richiesta di lettura )
Python
Mode
La modalita' python e' rivolta principalmente ad attivita' di test e
validazione del target, dove si desidera proceduralizzare seguenze di
operazioni di input/output du una o piu interfacce in modo da poterle
rendere riproducibili ed indipendenti dall'operatore.
Leonardo Platform simulator integra al suo interno un motore
python contenente un modulo addizionale ( denominato interpreter
) che permette l'interoperabilita' con i messaggi caricati
tramite ICD. Sono pertanto disponibili, oltre ai moduli python
standard, API per:
- la lettura/scrittura di messaggi
- il controllo del bus
- l'esecuzione di funzionalita' fornite dai plugins
NB: Leonardo
Platform NON contiene
al suo interno una distribuzione Python ma solamente l'interprete;
questo significa che non sono contenute librerie. L'host deve essere
munito di distribuzione python ( 2.7.X.X ) compatibile con
l'architettura dell'applicativo ( 32/64 bit )
Sono presentate per default due tab denominate:
- Python Script #0
- Terminal #0
Altri terminali python possono essere aggiunti. Ogni
terminale e' una istanza python indipendente dalle altre.
controlli
Python
Da sx a ds:
- aggiungi un python script alla lista di esecuzione (
alternativamente il file puo' essere trascinato consentendone il
caricamento )
- carica una lista di scripts (.lst)
- salva la lista corrente su file (.lst)
- pulisci la finestra di output
- lancia l'esecuzione della lista di scripts
- abortisci l'esecuzione della lista di scripts
- apri un nuovo terminale python
Tali funzionalita' sono
disponibili anche da menu Edit > Python
Python
Script #0

Questa tab e' predisposta per gestire la parte di scripting
python. In questa sezione si possono aggiungere / eliminare scripts,
salvare / caricare una lista di scripts ed eseguirli tramite i suddetti
controlli.
La parte destra (output)
della pagina visualizza l' output derivante dall'esecuzione degli
scripts.Ogni esecuzione produce un file di log avente radice pari al
nome dello script file ( dirname e basename ) ed estensione .log
L'applicativo si presenta
come una console a riga di comando python. Non visualizza interfacce
grafiche.
In questa modalita'
l'applicativo esegue le seguenti operazioni:
- vengono eseguiti gli scripts di inizializzazione ( globali
e di interfaccia )
- viene eseguito un singolo processo di interprete python
interattivo
Esempio
Utilizzo la sessione test1
in modalita cli ed eseguo comandi python relativi al modulo interno
'interpreter'
Batch
uPlatSim.exe --batch <file>
--session=<name> [--python=<dir> ] [
--appdata=<dir> ] [--debug <file>]
In questa modalita' e' possibile eseguire un singolo file di script
python direttamente da command line senza l'apertura dell' interfaccia
grafica.L'applicativo esegue le seguenti operazioni:
- vengono eseguiti gli scripts di inizializzazione ( globali
e di interfaccia )
- viene eseguito lo script passato da argomento
Tale esecuzione ridirige su
standard
output le comunicazioni risultanti dall'esecuzionde dello
script e
non viene
effettuato il salvataggio su file ( come avviene
in modalita' grafica ).
L'applicativo termina con valore:
- 0: in caso di esecuzione dello script terminata con
successo
- numero diverso da 0 : in caso di errore durante
l'esecuzione dello script oppure con il valore
specificato dall'istruzione python sys.exit([arg])
Esempio
Utilizziamo la sessione 'Test1'
individuata dal file Test1.ini
e lo script python (Test.1.py).
Log di un traffico dati
Ogni interfaccia di una determinata sessione permette di
salvare su file il traffico dati in ingresso ed uscita.
La registrazione [ log ] puo' essere attivata/disattivata nei seguenti
modi:
- tramite Python
- logStart(self, level, dirName)
- logStop()
- tramite Gui:
- utilizzanto il tasto "Start Logging" per
avviare (* un led rosso lampeggiante in prossimita' del tasto indica
l'attivita' di registrazione in corso)
- utilizzando il medesimo tasto (ma con testo "Stop
Logging" se in fase di registrazione ) per terminare
Il file viene salvato per default nella cartella Documenti dell'utente
( in base al sistema operativo ), o nella cartella specificata
dall'utente a seconda del tipo di chiamata alla funzione ( da Gui
tramite tasto opportuno e da python tamite argomento ) e il
nome e' determinato dalla seguente regola:
<nome
interfaccia>__<anno>_<mese>_<giorno>_<ora>_<minuto>_<secondo>.log
Contenuto del file di log
#
[TimeTag] [Flag] [MsgId] [IO] (...)
(TIME)
(FLAG)[<ID>] [<IO>]
(DATA0) ... (DATA-N)
...
Con:
- TIME : tempo espresso in
<Hour>.<Mim>.<Sec>.<mSec>
- FLAG : [-|E|I] [ Normal ]; E [ Errore ]; I [ Info ]
- ID : <digit> identificativo
messaggio ( tipicamente utlizzato quello dell'ICD )
- IO : [Tx/Rx] Tx - se trasmissione ; Rx -
se ricezione
- DATA : <carattere/parola> expresso in
formato esadecimale
|
Plugins
Come si e' detto il simulatore di piattaforma e' in grado di
supportare funzionalita' particolari la dove l'interfaccia lo richieda,
oppure aggiungere strumenti di utilizzo generale.
Questa funzionalita' e' gestita tramite il concetto dei
plugin, ovvero librerie caricate dinamicamente le quali rspettano una
struttura dati nota all'applicativo.
Le regole applicabili per la realizzazione di un plugin sono
descritte nel documento 'plugin' incluso nell'installazione.
Gli oggetti applicabili al plugin sono sostanzialmente di 3
tipi:
- elementi selezionabili ( eventualmente da multiple opzioni
) { per esempio abilitare/disabilitare una certa funzionalita' estra
tramite una checkbox }
- elementi richiamanti dialogs (solo GUI ) o azioni dirette
riferite ad un certo istante
- elementi richiamanti funzionalita' python ( solo python )
L'utilizzo dei suddetti plugin tramite GUI gli
elementi sono raggiungibili da menu/Plugins/<nome
interfaccia>/. Gli elementi possono appartenere a
sottomenu.
L'utilizzo tramite Python e' gestito dall oggetto interpreter.PyPluginHandler
ottenuto dall'interfaccia tramite API getPlugonHandler()
>>>dir(interpreter.PyPluginHandler)
['__class__', '__del__', '__delattr__', '__dict__', '__doc__',
'__format__', '__getattr__', '__getattribute__', '__hash__',
'__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'__swig_destroy__', '__swig_getmethods__', '__swig_setmethods__',
'__weakref__', 'doAction', 'getActionHelp', 'getChecked',
'isCheckable', 'names', 'setChecked']
con isCheckable(n) [Boolean] : ritorna True se
l'elemento passato come argomento e' del tipo selezinabile
getChecked(n) [Boolean] :ritorna True se
l'elemento e' selezionato
setChecked(n,Boolean): imposta l'elemento
selezionabile con valore passato da argomento
doAction(self, arg2, dict_args=None,
dict_retval=None) con arg2 nome della funzione, dict_args=Tupla
nome/valore dei parametri in input, dict_ret_val=Tupla nome/valore dei
parametri in output
Funzionalita`
avanzate
Modificare
layout di interfaccia
Per layout di una interfacia si intende la visualizzazione
degli oggetti grafici i quali rappresentano i campi di tutti
i messaggi descritti nel relativo file di ICD.
Tipicamente ogni campo di un dato messaggio viene visualizzato con tre
elementi grafici:
- oggetto nome : contenente il testo indicativo
il nome del campo
- oggetto valore : contenente il valore [ editabile
se in scrittura ] del campo
- oggetto unita di misura : conenente il testo indicativo
l'unita' di misura [ se presente ] del campo
Il layout di una interfaccia e' descritto da un
file .ui gestito direttamente dalla piattaforma Qt (
http://doc.qt.io/archives/qt-4.8/designer-using-a-ui-file.html)
il quale viene caricato in fase di avviamento dell'applicativo.
Tale file e' specificato ne file .INI relativo alla sessione tramite
chiave
Interface_
<n>
/UiFile dove
n e' un numero progressivo che determina l'interfaccia.
E' possibile modificare il layout:
- direttamente modificando a mano il file ( che e' in formato
XML )
- tramite Designer.exe ( Qt ) che e'
fornito a corredo dell'applicativo nella directory stessa.
- da applicativo tramite Menu->Edit->Interface->[Nome
Interface]
L'applicativo gestisce le seguenti modifiche del layout:
- modifiche posizionali [ organizzazione
degli elementi tramite gestore geometrici di vario tipo
(verticali / orizzonati / a matrice) ... ]
- modifiche delle proprieta' caratteristiche
dell' elemento [ per es stile di visualizzazione di
una combobox ]
- aggiunta/sostituzione di elementi collegabili ( se
compatibili ) ad un campo di un messaggio.
- Un dato messaggio puo' essere
rappresentao da uno o piu' elementi grafici i quali sono
collegati al campo tramite il valore della porprieta' ObjectName
il quale deve seguire il formalismo seguente:
"___MESSAGEID____ <id>
_ <field_name>
___FIELDTAG___ <obj_text>
"
- <id> :
e' l'identificativo numerico del messaggio
- <field_name>:
e' il nome del campo normalizzato ( i caratteri
[:punct:] sono sostituiti da _ )
- <obj_text>
: testo libero che differenzia l'oggetto ( es: "numeric1"
)
Utilizzare
uno script Python come Bus
Leonardo Platform Simulator e' in grado di utilizzare uno script Python
per agire come gestore dei dato di basso livello ( denominato BusDriver
).
Per essere compatibile con l'applicativo tale script deve utilizzare
come riferimento il modulo 'proxybusdriver' presente nella cartella
dell'eseguibile.
Tale modulo permette di creare un canale di comunicazione univoco con
l'applicativo consentendo di interfacciarsi con il
database con la quale si intende operare ( messaggi e campi ).
Per creare un istanza di busDriver sono necessarie le seguenti
operazioni:
- Instanziare o ridefinire i metodi della classe
python fornita dal modulo python (proxybusdriver)
- Basandosi su una classe padre definita in python
(proxybus)
con la quale viene gestita la parte di connessione al simulatore si
possono finalmente definire le funzionalita' per gestire lo scambio
dati. Distinguiamo:
- costanti:
- __proxy_icdinterface__
: srtinga identificativa dell interfaccia
- __proxy_icdxmlfile__
: path del file xml ( configurazione )
- __proxy_identifier__
: identificatore dell'istanza proxybus
- metodi richiamabili direttamente da python:
- onBusError
: da utilizzare per passare al livello superiore (
il simulatore ) un errore di gestione dati
- onClock
: notificare al simulatore la
periodicita' ( in modo da sincronizzare eventuali automatisci
di varia natura )
- onReceive
: passare al livello superiore l'avvenuta
ricezione ( codificata ) di dati proveniente
dal dispositivo
collegato
- callback python per implementare funzionalita'
predefinite dal simulatore:
- setOnErrorInjectionListCallback
: implementare l'evento proveniente dal
simulatore che definisce una lista di errori iniettabili sul canale di
comunicazione
- setSendCallback
: implementare l'evento proveniente dal
simulatore di invio dati verso il dispositivo collegato
- setStartCallback
: implementare l'evento proveniente dal
simulatore di start della counicazione
- setStopCallback
: implementare l'evento proveniente dal
simulatore di stop della counicazione
- setUsClockPeriodCallback
: implementare l'evento proveniente dal
simulatore che imposta il tempo di clock ( espresso in microsecondi )
- il modulo dispone di una
classe ProxyBusDriver
che 'incorpora' proxybus rendendo piu facile la parte di
programmazione python ( associazioni di callback, uso di
safe-threading,
... )
Esempio
Si vuole stabilire una connesione di tipo seriale tra un
apparato (TDK-LAMBDA-40-38) e il simulatore utilizzando la libreria
python PySerial.
Dato l'ICD:
<ICDHandler
Name='TDK-LAMBDA-40-38' Ui='all_vertical'
Version='89873786-A8FF-49A9-AF2E-0184193BE6CE' >
<HiLevel>
<Message Id='1' Name='OUT' Width='8' IOType='TX'
>
<Field Name='ENABLE' Width='8' Offset='0' Type='ENUM'
Display='DECIMAL' Default='OFF'
>
<Values>
<Value Name="OFF" >0x00</Value>
<Value Name="ON">0x7F</Value>
</Values>
</Field>
</Message>
<Message Id='2' Name='VOLTAGE' Width='16' IOType='TX'
>
<Field Name='VOLTAGE' Width='16' Offset='0' Min='0.00'
Max='40.00' Type='DOUBLE' Weight='0.01' Precision='0.01'
Default='28.00' Unit='Volt' />
</Message>
<Message Id='3' Name='CURRENT'
Width='16' IOType='TX' >
<Field Name='CURRENT' Width='16' Offset='0' Min='0.00'
Max='38.00' Type='DOUBLE' Weight='0.01' Precision='0.01'
Default='10.00' Unit='Amp' />
</Message>
<Message Id='4'
Name='TDK-LOOPBACK' Width='48' IsPeriodic='FALSE' IOType='RX' >
<Field Name='REMOTE-ENABLE' Width='8' Offset='0' Type='ENUM'
Display='DECIMAL' Default='OFF'
>
<Values>
<Value Name="OFF" >0x00</Value>
<Value Name="ON">0x7F</Value>
</Values>
</Field>
<Field Name='OUT-ENABLE' Width='8' Offset='8' Type='ENUM'
Display='DECIMAL' Default='OFF'
>
<Values>
<Value Name="OFF" >0x00</Value>
<Value Name="ON">0x7F</Value>
</Values>
</Field>
<Field Name='MEAS-VOLTAGE' Width='16' Offset='16' Min='0.00'
Max='40.00' Type='DOUBLE' Weight='0.01' Precision='0.01' Default='0'
Unit='Volt' />
<Field Name='MEAS-CURRENT' Width='16' Offset='32' Min='0.00'
Max='38.00' Type='DOUBLE' Weight='0.01' Precision='0.01' Default='0'
Unit='Amp'
/>
</Message>
</HiLevel>
<LowLevel>
<Config Port="COM1"
/>
</LowLevel>
<Mapping>
<Map
Id="1">out</Map>
<Map
Id="2">voltage</Map>
<Map
Id="3">current</Map>
<Map
Id="4">response</Map>
</Mapping>
</ICDHandler>
il codice python per realizzare il driver sara' il seguente:
import
proxybus
# proxy
python fornita da ProxyPythonBusDriver
from
proxybusdriver import
ProxyBusDriver
# per
importare ProxyBusDriver
import
serial
#
carico la
seriale
import time
import struct
import re
import sys
import
xml.etree.ElementTree as et
# mi serve
per parsare il file xml e costruire le mappature
class
serialTDKLambdaPowerSupply(ProxyBusDriver):
def
__init__(self):
ProxyBusDriver.__init__(self)
# costruttore della classe padre
self.name = "Serial TDK-LAMBDA Power
Supply"
self.usclockperiod = 500000
# deternimo la durata del periodo
di clock
self.response_id = None
self.s=serial.Serial(None, 9600,
# instanzio l'oggetto seriale
della libreria python
timeout=0,
parity=serial.PARITY_NONE)
self.s.setWriteTimeout(10.0)
self.opened = False
self.query_err_reg_exp=re.compile('C0[1-4]')
self.eval_tx = {
"remote" :
self.set_remote,
"out"
: self.set_out,
"voltage" : self.set_voltage,
"current" : self.set_current
}
self.resolution = { 'voltage' : 0.01,
'current' : 0.01 }
self.icd_map={}
self.parseICD()
####
# re-implemento il metodo della classe padre
open_comm
####
def
open_comm(self):
self.opened = False
try:
self.s.open()
except:
print 'failed to open ....'
self.opened = self.s.isOpen()
if self.opened is True:
reg_exp=re.compile('.*LAMBDA,\s*GEN40\-38')
self.s.read(255)
self.ps_set('RMT REM')
self.ps_set('ADR 06')
ident = self.ps_query('IDN?')
if ident is None or reg_exp.match(ident) is False:
self.raiseError('No valid IDN reveived',3)
self.close_comm()
else:
self.raiseError('cannot open serial port %s' %self.s.port,3)
return self.opened
####
# re-implemento il
metodo della classe padre close_comm
####
def
close_comm(self):
self.ps_set('RMT LOC')
try:
self.s.close()
except:
self.raiseError(sys.exc_info(),3)
self.opened = False
####
# Utilizzo questo metodo per dedurre:
# - la
porta seriale da utilizzare
# - la
corrispondenza tra msg_id e tipo di dato da decodificare in
lettura/scrittura
####
def
parseICD(self):
xml=''
with open(self.icdxmlfile) as fileinput:
for line in fileinput:
xml += line.lower()
root = et.fromstring(xml)
config = root.find('./lowlevel/config')
if config is not None: self.s.port = config.attrib.get('port')
for i in root.findall('./mapping/map'):
key= int(i.attrib.get('id'))
val= i.text
self.icd_map[key] = val
if val == 'response' :
self.response_id = key
print self.icd_map
####
# re-implemento il metodo della
classe padre send
####
def
send(self, m_id, m_data, m_err):
ret = False
self.acquireLock()
try:
s_func = self.icd_map.get(m_id,"not_found")
ret=self.eval_tx.get(s_func,self.not_found)(m_data)
# in base al
mapping viene richiamata il methodo corrispondente
except:
self.raiseError(sys.exc_info(),3)
finally:
self.releaseLock()
return ret
####
#
re-implemento il metodo della classe padre iteration ( routine che
viene eseguita da un thread ogno uSec definiti come da Clock )
# in questo caso ogni clock voglio
leggere dati dalla periferica ...
####
def
iteration(self):
reg_exp="(\w+)=([a-zA-Z0-9\.]+)\;?\s"
out = { 'REMOTE' : None,
'OUT' : None,
'VOLTAGE' : None,
'CURRENT' : None
}
d = self.ps_query("RMT?")
if d[:3] == 'LOC': out['REMOTE']=0x00
elif d[:3] == 'REM': out['REMOTE']=0x7F
d = self.ps_query("OUT?")
if d[:3] == 'OFF': out['OUT']=0x00
elif d[:2] == 'ON': out['OUT']=0x7F
out['VOLTAGE'] = self.ps_query("MV?")
out['CURRENT'] = self.ps_query("MC?")
#print
out
valid=True
for el in out.values():
valid =(False if el == None else
valid)
if valid and self.response_id:
_rmt = int(out['REMOTE'])
_out = int(out['OUT'])
_vlt =
float(out['VOLTAGE'])/self.resolution['voltage']
_cur =
float(out['CURRENT'])/self.resolution['current']
x=struct.pack("bbhh",_rmt,_out,_vlt,_cur)
#impacchetto
il messaggio
self.forward_msg(self.response_id,x)
#invio il
messaggio codificato al simulatore
else:
print
"invalid"
def
ps_set(self,cmd):
r = self.ps_query(cmd)
#print "recv::",r
ret = (True if r[:2] == 'OK' else False )
return ret
def
ps_query(self,cmd):
try:
n=self.s.write(cmd+'\r')
except:
self.raiseError(sys.exc_info(),3)
raise
#print 'sent',n
time.sleep(0.1)
resp = self.s.read(512)
#print
cmd,'->',resp
if self.query_err_reg_exp.match(resp): resp=None
#print cmd,'->',resp
return resp
def
set_remote(self,data):
ret = False
remote = 'LOC'
if len(data) == 1:
_bytes=struct.unpack('b',data)
if _bytes[0] == 0x7F: remote = 'REM'
ret=self.ps_set("RMT %s" %remote)
return ret
def
set_out(self,data):
ret =
False
out = 'OFF'
if len(data) == 1:
_bytes=struct.unpack('b',data)
if _bytes[0] == 0x7F: out = 'ON'
ret=self.ps_set("OUT %s" %out)
return ret
def
set_voltage(self,data):
ret = False
#print len(data)
if len(data) == 2:
_bytes=struct.unpack('h',data)
voltage=float(_bytes[0])*self.resolution['voltage']
ret=self.ps_set("PV %.2f" %voltage)
return ret
def
set_current(self,data):
ret = False
if len(data) == 2:
_bytes=struct.unpack('h',data)
#print _bytes
current=float(_bytes[0])*self.resolution['current']
ret=self.ps_set("PC %.2f" %current)
return ret
def
not_found(self,data):
print "what?"
return False
me
= serialTDKLambdaPowerSupply()
#!!!!!
IMPORTANTE : instanzio l'oggetto
Utilizzare
uno script Python come Plugin
Leonardo Platform Simulator e' in grado di utilizzare uno script Python
per ampliare le funzionalita' di base. Tale script si basa sulla classe
ProxyPluginObject offerta dal modulo python 'proxypluginobjectmod'.
Tale classe permette di creare un canale diretto di comunicazione con
l'applicativo offrendo le stesse possibilita' di un plugin scritto in
cpp e compilato
con l'ausilio dell'SDK dedicato alla creazione dei plugin ossia:
- Creazione del menu dedicato ( e sottomenu ) con elementi richiamabili
e selezionabili (
addAction
)
- Gestione degli eventi menu (
onPluginAction
)
- Controllo del flusso degli eventi dell'applicativo (
consumeICDEvent )
- Gestione del database ( lettura scrittura dei campi / messaggi dell
ICD )
addAction
In quanto plugin l'applicativo crea una voce nel menu ( per
es. fooPlugin ). Aggiungendo azioni quente vengono associate
al menu
( e conseguentemente registrate anche dall'interprete python ) in base
ai criteri descritti dagli argomenti di seguito elencati:
@param action_id : (Integer)
l'identificativo con il quale l'azione plugin viene registrata
@param action_name : (String)
la stringa con la quale viene plubblicata sull'
applicativo
@param action_type : (Integer)
il tipo di azione ( voce diretta / sottomenu / elemento selezionabile /
insieme di elementi selezionabili
ma esclusivi
@param action_parent : (Integer)
l'identificativo del menu padre ( se esistente ) { vedi sottomenu }
onPluginAction
Questa funzione agisce da callback ed e' invocata dall' applicativo in
conseguenza della sezione, da parte dell'utente, di una
determinata azione associata al plugin. Per individuare
univocamente l'azione invocata si utilizza il paramento action_id
precedentemente utilizzato nella creazione dell'azione.
Una volta individuata tale azione non resta altro che indirizzare lo
script verso la / le funzioni sclete per tale azione.
I parametri del metodo sono :
@param action_id : (Integer)
l'identificativo con il quale l'azione plugin viene registrata
Per esempio, aggiungo un' azione diretta denominata 'click
me' con id 7777 e preparo un metodo onClickMe.
Avro' un elemento grafico nella sezione menu denominato appunto
'click me'.
Alla selezione di tale elemento verra` evocata la funzione
onPluginAction con
il parametro 7777 dal quale lo script python capira` che proprio tale
elemento e' stato selezionato.
A questo punto potro` finalmente eseguire la funzione dedicata, ossia
onClickMe.
consumeICDEvent
Questa funzione agisce da callback ed e' invocata dall' applicativo in
conseguenza di azioni riferibili al database (
lettura/scrittura/aggiornamento/evento clock) o allo stato
dell'applicativo ( start/stop/error ... ). L'utilita` della decodifica
di tali eventi risiede nella possibilita' di sincronizzare operazioni
sulla base di ebneti esterni al plugin ( tipicamente vogliamo
effettuare un aggiornamento di una serie di dati ciclicamente
utilizzando come periodo il clock fornito dall'applicativo
Esempio
import proxyplugin
import sys
import ICDEVENTS
import time
events={}
class Test2ProxyHandler(ProxyHandler):
def __init__(self):
super().__init__()
def start(self):
super().start()
print(self.__class__,dir(self) )
print('Hello, I\'m',self.name)
print(self.interface.getMessageNames())
def consumeICDEvent(self,event_id):
if event_id == ICDEVENTS.ICD_EVENT_ONUPDATE: pass
elif event_id == ICDEVENTS.ICD_EVENT_ONBUSCLOCK:
...
else:
print('event ',events.get(event_id,event_id))
class Test2PluginObject(ProxyPluginObject):
def __init__(self):
#here i specify the factory class to implements the icd handler interfaces
ProxyPluginObject.__init__(self,"ExamplePluginObject",Test2ProxyHandler)
self.addAction(300,'Menu',ActionTypes['MENU'])
self.addAction(301,'Load file',ActionTypes['NORMAL'],300)
def start(self):
"""
for each handlers built perform start operation
"""
print ( self.handlers_dict )
for k,v in self.handlers_dict.items():
print('starting ',k)
v.start()
return True
def onPluginAction(self,action_id):
global root
if action_id == 301:
ret=self.openFileDialog('open file','.','CSV File ( *.csv);;')
print(ret)
if ret is None:
self.messageBox('Operation aborted' ,'foo',2,3)
else:
self.messageBox('Operation Ok' ,'foo')
if __name__ == "__main__":
#build a dictionary for event_id translation
gen = (i for i in dir(ICDEVENTS) if i.startswith("ICD_EVENT"))
for e in list(gen): events[eval( "ICDEVENTS.{}".format(e) )]=e
print(events)
print(dir(ProxyHandler))
print(dir(ProxyPluginObject))
me = globals().get('me')
#if me is not None: del me
me = Test2PluginObject()