Serial Port APIs

Introduction

The APIs described below are shown by the serial Port object and are used to implement the communication protocol logic of one or more PLCs not natively supported by UNIQO.

Available APIs

Below are the available methods:

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

Below are the C# properties to read/write the object variables, and to change the operation of some methods:

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

Example of protocol configuration in request/response mode

Below is an example of a NetLogic contained in the serial Port object that uses the request/response mode to read the value of a Modbus PLC register every 300 milliseconds.

With the WriteBytes method, the command is sent via the serial port, and with the subsequent ReadBytes method, it waits for a response from the PLC. If the PLC does not respond within the time indicated by the Timeout property, an exception is generated and the read/write fails.

Download the example project from here

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

Example of protocol configuration in event mode

Below is an example of a NetLogic contained in the serial Port object that uses the event mode to read data sent asynchronously from a device. The OnClick method, linked to the button pressing event, makes it possible to stop the current reading and start a new reading.

To use this mode, the Timeout property must be set to 0; in this way, ReadBytes will be blocking as long as the requested data do not arrive on the serial port. It is possible to stop this reading with the CancelRead method, or the Close method. In both cases a ReadCanceledException exception is generated. To carry out a clean close of the NetLogic, the Close method in the NetLogic Stop must be called to terminate any pending read operations.

Download the example project from here.

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();
    }
}