API Serial Port¶
Introduzione
Le API descritte di seguito sono esposte dall’oggetto Porta seriale e servono a implementare le logiche del protocollo di comunicazione di uno o più PLC non supportati nativamente da UNIQO.
API disponibili
Di seguito i metodi disponibili:
public void Open();
public void Close();
public byte[] ReadBytes(uint count);
public char[] ReadChars(uint count);
public byte ReadByte();
public char ReadChar();
public string ReadLine();
public byte[] ReadBytesUntil(string delimiter);
public char[] ReadCharsUntil(string delimiter);
public void WriteBytes(byte[] buffer);
public void WriteChars(char[] buffer);
public void WriteLine(string text);
public void CancelRead();
Di seguito le proprietà C# per leggere/scrivere le variabili dell’oggetto e per modificare il funzionamento di alcuni metodi:
public string PortName { get; set; }
public uint BaudRate { get; set; }
public byte DataSize { get; set; }
public ParityType Parity { get; set; }
public StopBitsType StopBits { get; set; }
public FlowControlType FlowControl { get; set; }
public TimeSpan Timeout { get; set; }
public string NewLine { get; set; } = "\n";
Esempio di configurazione protocollo in modalità richiesta/risposta¶
Di seguito un esempio di un NetLogic, contenuto nell’oggetto Porta seriale, che utilizza la modalità richiesta/risposta per leggere ogni 300 millisecondi il valore di un registro di un PLC Modbus.
Con il metodo WriteBytes
si invia il comando attraverso la seriale e con il metodo ReadBytes
successivo si attende una risposta dal PLC. Se il PLC non risponde entro il tempo indicato dalla proprietà Timeout
viene generata una eccezione e la lettura/scrittura fallisce.
Scarica il progetto di esempio da qui
public class RuntimeNetlogic1 : BaseNetLogic
{
private SerialPort serialPort;
private PeriodicTask periodicTask;
public override void Start()
{
serialPort = (SerialPort)Owner;
periodicTask = new PeriodicTask(Read, 300, Owner);
periodicTask.Start();
}
private void Read()
{
try
{
ReadImpl();
}
catch (Exception ex)
{
Log.Error("Failed to read from Modbus: " + ex);
}
}
private void ReadImpl()
{
serialPort.WriteBytes(Serialize());
var result = serialPort.ReadBytes(3);
if ((result[1] & 0x80) == 0)
{
result = serialPort.ReadBytes((uint)(result[2] + 2));
Log.Info(Deserialize(result));
}
else
{
Log.Error("Failed to read from Modbus");
}
}
private byte[] Serialize()
{
var buffer = new byte[]
{
0x01, // UnitId
0x03, // Function code
0x00, // Starting address
0x00,
0x00, // Quantity Of Registers
0x01,
0x84, // CRC
0x0a
};
return buffer;
}
private ushort Deserialize(byte[] buffer)
{
var first = (ushort)buffer[1];
var second = (ushort)(buffer[0] << 8);
return (ushort)(first | second);
}
}
Esempio di configurazione protocollo in modalità evento¶
Di seguito un esempio di un NetLogic, contenuto nell’oggetto Porta seriale, che utilizza la modalità a evento per leggere i dati inviati in maniera asincrona da un dispositivo. Il metodo OnClick
, collegato all’evento di pressione di un pulsante, permette di interrompere la lettura corrente e di avviarne una nuova.
Per utilizzare questa modalità, la proprietà Timeout
dev’essere impostata a 0, così facendo ReadBytes
risulterà bloccante fintanto che non arrivano sulla seriale i dati richiesti. È possibile interrompere questa lettura con il metodo CancelRead
oppure con il metodo Close
. In entrambi i casi viene generata un’eccezione ReadCanceledException
. Per effettuare una chiusura pulita del NetLogic è necessario chiamare il metodo Close
nella Stop
del NetLogic stesso per terminare eventuali operazioni di lettura pendenti.
Scarica il progetto di esempio da qui
.
public class RuntimeNetlogic1 : BaseNetLogic
{
private SerialPort serialPort;
private LongRunningTask task;
public override void Start()
{
serialPort = (SerialPort)Owner;
serialPort.Timeout = TimeSpan.FromMilliseconds(0.0);
task = new LongRunningTask(Run, Owner);
task.Start();
}
[ExportMethod]
public void OnClick()
{
// Cancel current read
serialPort.CancelRead();
task.Cancel();
// Start new read
task = new LongRunningTask(Run, Owner);
task.Start();
}
private void Run()
{
while(!task.IsCancellationRequested)
{
try
{
// Block until 3 bytes arrive on serial
var result = serialPort.ReadBytes(3);
foreach (var b in result)
Log.Info(String.Format("0x{0:x2}", b));
}
catch (ReadCanceledException ex)
{
// In case of read canceled, exit from the loop
Log.Info(ex.Message);
return;
}
catch (Exception e)
{
Log.Error(e.Message);
}
}
}
public override void Stop()
{
// Explicit call to Close to cancel pending read (if any)
serialPort.Close();
task.Cancel();
}
}