| OpenLab Studio | >= 1.30.0 |
| OpenLab SDK | >= 3.0.1 |
| Linguaggi | C# |
| Versione .NET | 8 |
Generale #
Questa guida spiega come creare delle porte custom per un Logic Block. La creazione di una porta custom è necessaria ad esempio per gestire più tipi di dati permessi per la porta o usare un editor custom, oppure per eseguire delle verifiche di sintassi o coerenza sui dati(ad esempio in base al tipo di dato scelto nell’editor).
Un esempio pratico è la porta Recive packet delimiter del logicblock SerialConnection, della stdlib. In questa porta sono permessi più tipi di delimitatore. Il pacchetto dati può essere delimitato in base alla lunghezza(numero di byte) o ad una stringa(ritorno a capo o una parola).
In questi casi dovremo verificare che i dati specificati immessi sia corretti prima di memorizzare il valore su questa porta, ad essempio, se come delimitatore si una la lunghezza, l’utente non dovrebbe poter specificare -1 come valore.
Per il setup dell’ambiente di sviluppo e installare il plugin seguire questa guida, il codice completo è reperibile su GitHub
L’esempio completo è disponibile su GitHub
Implemetare le basi #
Per creare una porta custom va estesa la classe LogicBlockPort
public class LogicBlockCustomPort : LogicBlockPort
{
public LogicBlockCustomPort(string name, Type[]? dataType, DataDirection dataDirection) : base(name, dataType, dataDirection){}
}
Al costruttore vengono passati:
- il nome della porta, che verrà visualizzato sul LogicBlock
- L’array con i tipi di dati permessi per questa porta
- La direzione dei dati(input, output, bidirectional)
Tipi di dati fissi #
E’ possibile far in modo che i tipi di dati siano fissi e non possano essere specificati quando si instanzia la porta. Nell’esempio sequente vengono specificati i tipi int,string e l’enum System.Windows.Input.Key. Quando si andrà ad instanziare la porta nel LogicBlock verrà passato null come tipi di dati.
public LogicBlockCustomPort(string name, Type[]? dataType, DataDirection dataDirection) : base(name, [typeof(string), typeof(int), typeof(System.Windows.Input.Key)], dataDirection){}
Usare tipi non base #
Quando si utilizzano tipi non base, ad esempio classi, enum, etc. questi vanno registrati in DataTypes. In questo caso abbiamo bisongo di registrare il tipo System.Windows.Input.Key che è un enum che fa riferimento hai tasti premuti sulla tastiera.
public LogicBlockCustomPort(string name, Type[]? dataType, DataDirection dataDirection) : base(name, [typeof(string), typeof(int), typeof(Key)], dataDirection)
{
IPluginsManager.commonsTypes.RegisterType("System.Windows.Input.Key", typeof(Key));
}
Per maggiori informazioni sul gestore dei dati guardare qui
Dati da e verso l’editor #
Quando viene aperto l’editor dei dati di una porta, questo, chiama il metodo getDataValueToEditor() che restituisce un istanza della classe DataEditorArgs con i quali vengono passati i dati dalla porta all’editor.
Quando l’editor deve salvare i dati nella porta chiama il metodo setDataValueByEditor() della porta, passandogli l’istanza della classe DataEditorArgs con i dati inseriti nell’editor.
Dopo di che, se non ci sono errori, l’editor chiama il metodo dataValueViewerFormatter() che scrive i dati memorizzati nella porta nel modo corretto secondo il tipo di dato e il valore.
Di default setDataValueByEditor() esegue il parse del valore in base al tipo di dato selezionato nell’editor. Gestisce i tipi di base(int,string,etc). Se il parse fallisce viene propagata l’eccezione che è stata lanciata all’interno di questo metodo.
DataEditorArgs contiene il valore passato dalla porta all’editor nella propprietà OldValue, mentre quando l’istanza arriva dall’editor il nuovo valore e il tipo scelto per il nuovo valore sono memorizzati rispettivamente in NewValue e Type.
In questo esempio verrà fatto l’override dei metodi setDataValueByEditor() e dataValueViewerFormatter() per verificare la correttezza dei dati in arrivo dall’editor e visualizzarli correttamente nella casella accanto alla porta.
override public void setDataValueByEditor(DataEditorArgs args)
{
try
{
if (args.NewValue == null)
{
throw new Exception("The value can't be null");
}
if (args.Type == typeof(int))
{
int val = int.Parse(args.NewValue);
if (val < 1)
{
throw new Exception("The value can't be < 1");
}
setData(val, args.OnDataChangeEventTempDisable, false);
}
else if (args.Type == typeof(string))
{
string val = ((string)args.NewValue);
if (val == "" || val == string.Empty)
{
throw new Exception("The value can't be empty");
}
setData(val, args.OnDataChangeEventTempDisable, false);
}
else if (args.Type == typeof(Key))
{
string val = ((string)args.NewValue).Trim();
if (val == "" || val == string.Empty || val.ToLower() == "null" || val == null)
{
throw new Exception("The value can't be empty or null");
}
// check if is insert string key of enum or integer value
int enumVal;
if(int.TryParse(val, out enumVal))
{
// if passe integer value
setData(Enum.Parse(typeof(Key),val,true), args.OnDataChangeEventTempDisable, false);
}
// Enum.GetName(typeof(Key), 1)
}
}
catch
{
throw;
}
}
public override string dataValueViewerFormatter()
{
if (data == null)
{
return "NULL";
}
else
{
if (settings.showDataTypeInDataValueViewer)
{
return data.GetType().GetElementType().Name;
}
else
{
if(data.GetType() == typeof(string))
{
return (string)data;
}
else if (data.GetType() == typeof(Key))
{
return "Key." + Enum.GetName(typeof(Key), (int)data);
}
else if(data.GetType() == typeof(int))
{
return ((int)data).ToString()+" Bytes";
}
else
{
return "Unknown";
}
}
}
}
Instanziare la porta #
Di seguito un esempio del codice da usare nel LogicBlock per instanziare la porta
public LogicBlockPortTutorial10(IPluginsManager _pluginsManager, IEventsManager _eventsManager, IWindowsManager _windowsManager, InstrumentDefinition instrumentDefinition) : base(_pluginsManager, _eventsManager, _windowsManager, instrumentDefinition)
{
logicBlockInfo = new Info();
LogicBlockCustomPort p = new LogicBlockCustomPort("Recive packet delimiter", null, LogicBlockPort.DataDirection.Input);
p.settings.horizontalPosition = LogicBlockPortDefaultSettings.Position.Left;
p.settings.onDataChangeOperation = LogicBlockPort.OnDataChangeOperations.None;
p.settings.onDataChangeOperationEditable = false;
p.settings.toStartOperation = LogicBlockPort.ToStartOperations.None;
p.settings.toStartOperationEditable = false;
p.settings.dataValueEditable = true;
p.settings.dataTypeEditable = true;
p.setData(1, true, false);
addPort(p);
}