COM: устройство отправляет данные только когда все меняется. Как с этим бороться? - PullRequest
0 голосов
/ 25 апреля 2020

Итак, я хочу подключиться к устройству через последовательный , который отправляет данные только тогда, когда все меняется с настройками на устройстве (измерительном устройстве). Я использую C# и. Net SerialPort.

Я могу читать данные, и это выглядит довольно хорошо. Но есть несколько проблем, с которыми я столкнулся.

Я реализовал свой метод чтения (ReadExistingData()), чтобы он постоянно использовал SerialDataReceivedEventHandler при появлении новых данных. К сожалению, когда я читаю его таким образом (возможно, из-за разного размера пакета), он будет читаться очень хаотично, и поэтому мне нужно «поймать» первый инициирующий байт (здесь это 0xA5). Это означает, что я всегда проверяю, является ли байт, который я только что получил, 0xA5, и если это так, я читаю остальные.

Но я чувствую, что таким образом я пропускаю некоторые команды, которые мое устройство посылает мне, и это почему я не могу правильно отображать данные, только время от времени.

Примечание: устройство отправляет устройству время и значение. Значение отсрочено и отчасти неточно, но время всегда точно на месте. Другие параметры, которые он посылает, всегда отклоняются и, кажется, не распознаются и, следовательно, вообще не изменяются.

Для отображения данных я использую консоль в целях тестирования, и вся конструкция, кажется, очень реагирует на выходные данные консоли.

Вот небольшой фрагмент кода:

class Device
{
    private int stdMessageLengthInBytes = 5;
    public DeviceData processedData;
    byte[] dataBuffer;

    int receivedMessages = 0;

    public Device()
    {
        // Initialize BaseClass (SerialPort derivative)
        this.port = new System.IO.Ports.SerialPort();

        // Initialize Device
        this.data = new byte[stdMessageLengthInBytes];
        this.processedData = new P8005_Data();

        dataBuffer = new byte[stdMessageLengthInBytes];
    }

    // Is supposed to read the data from serial port
    protected override void ReadExistingData()
    {
        // Check whether buffer is empty -> Try to catch a 0xA5
        if (dataBuffer[0] == 0x00)
        {
            port.Read(dataBuffer, 0, 1);
        }

        // If no 0xA5 was catched, restart
        if (dataBuffer[0] != 0xA5)
        {
            dataBuffer = new byte[stdMessageLengthInBytes]; // Reset buffer
            return;
        }

        // Read next byte -> Command byte
        port.Read(dataBuffer, 1, 1);

        // If command was empty, restart
        if (dataBuffer[1] == 0x00)
        {
            dataBuffer = new byte[stdMessageLengthInBytes]; // Reset buffer
            return;
        }

        // If its time that is communicated: Read 3 bytes
        if (dataBuffer[1] == 0x06)
        {
            // 4 ms delay seems to be needed otherwise it wont function correctly somehow
            System.Threading.Thread.Sleep(5);
            port.Read(dataBuffer, 2, 3);
            // Write buffer to actual raw data byte array
            this.data = dataBuffer;
            dataBuffer = new byte[stdMessageLengthInBytes]; // Reset buffer
        }

        // Otherwise: Just read 2 bytes
        System.Threading.Thread.Sleep(5); // Needed delay
        port.Read(dataBuffer, 2, 2);

        // Write buffer to actual raw data byte array
        this.data = dataBuffer;
        dataBuffer = new byte[stdMessageLengthInBytes]; // Reset buffer
    }

    // Method called by SerialDataReceivedEventHandler
    protected override void DataReceivedOnComPort(object sender, SerialDataReceivedEventArgs e)
    {
        bool valid = false;

        ReadExistingData(); // Read data from COM- Port

        lock (processedData)
        {
            switch (data[1]) // Check command byte
            {
                // Time (3 btyes)
                case (0x06):
                    processedData.currentTime = String.Format("{0:D2}:{1:D2}:{2:D2}", DecodeBcd(data[2]), DecodeBcd(data[3]), DecodeBcd(data[4]));

                    valid = true;
                    receivedMessages++;
                    break;

                // Value (2 bytes)
                case (0x0D):
                    double val = 0;
                    val += DecodeBcd(data[2]) * 100;
                    val += DecodeBcd(data[3]);
                    val /= 10;
                    processedData.currentValue = val;

                    valid = true;
                    receivedMessages++;
                    break;

                // ... here are various other hex- code that represent a command from the device (2 btyes)

                default:
                    valid = false;
                    break;
            }
        }

        // only to check when 
        if (valid)
        {
            Console.WriteLine("Received Valid Messages: {0}", receivedMessages);
            ConsoleOutput();
        }
    }
}

На заметку: Инициализация порта происходит в другом методе из базового класса и работает нормально.

Есть ли что я пропускаю? Как бороться с чем-то подобным? Есть ли какие-либо улучшения, которые помогут улучшить мою производительность? Я думал о потоке с блокировками, но я не думаю, что это решение как-то ... Или, может быть, все это просто проблема с консолью?


РЕДАКТИРОВАТЬ:

Я знаю, изменил мой код (как предложил @jdweng), чтобы я помещал все в буфер (в основном List<byte> mainBuffer. Затем я беру все байты в буфере всякий раз, когда это возможно, и работаю с ними, просматривая его для 0xA5. Когда один найден, я прочитайте команду и определите, как долго должно быть «сообщение» (время -> +3 байта, данные -> +2 байта, другое -> +1 байт). Затем я могу отработать эти сообщения (я поставил их в List<byte[]>) и определите мой вывод на экран.

Однако даже после аутсорсинга обработки сообщений и обработки сообщений я все равно, похоже, пропускаю некоторые сообщения, поскольку некоторые действия просто не зарегистрированы и имеют большую задержку, или моя обработка неверна. Я могу думать о том, что, поскольку я блокирую свои mainBuffer, возможно, некоторые данные не записываются в него.

Действительно ли это на этот раз критично? скую? Существует программное обеспечение, которое поставляется с устройством, и у него, похоже, нет таких больших проблем с задержкой и немного неправильными значениями ...

1 Ответ

1 голос
/ 26 апреля 2020

Поскольку у вас нет точных характеристик и / или ненадежного соединения (которое с последовательными данными следует ожидать), вам нужно синхронизировать c с 0xa5 в каждом сообщении. Я просто запускаю каждый байт, который вы получаете через анализатор, сохраняя состояние текущего полученного сообщения.

Убедитесь, что вы подтвердили свой ввод, так как есть куча вещей, которые могут go ошибаться, если вы запутались в последовательных данных. Например, если в других типах сообщений есть 0xa5, вы можете пропустить следующее сообщение. Во избежание этого я настоятельно рекомендую либо перейти к спецификациям, если это возможно, либо написать больше логи c на основе данных наблюдений.

private const int MESSAGE_LENGTH = 5;
private const int VALUE_COMMAND = 0x0D;
private const int VALUE_SIZE = 4;
private const int TIME_COMMAND = 0x06;
private const int TIME_SIZE = 5;
private byte[] _message = new byte[MESSAGE_LENGTH];
private int _messagePos = 0;
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    var data = new byte[_serialPort.BytesToRead];
    _serialPort.Read(data, 0, data.Length);
    foreach (var b in data)
    {
        _message[_messagePos] = b;
        if (_messagePos == 0 && b != 0xa5)
            continue;
        ++_messagePos;
        if (_messagePos > 2)    // if command byte present, process command of any size
            ProcessCommand(_message[1]);
    }
}

private void ProcessCommand(byte command)
{
    if (_messagePos == VALUE_SIZE && command == VALUE_COMMAND)
    {
        // parse value...
        _messagePos = 0;
    }
    else if (_messagePos == TIME_SIZE && _message[1] == TIME_COMMAND)
    {
        // parse time...
        _messagePos = 0;
    }
}
...