Хочу парсить строки

Тема в разделе "Arduino & Shields", создана пользователем Vetrinus, 8 ноя 2015.

  1. Alex19

    Alex19 Гуру

    Вспомнил, что когда-то, что-то подобное делал на библиотеке Serial. Тогда только начинали учится с другом, вот это чудо, целиком - http://arduino.ru/forum/proekty/kontrol-peredachi-dannykh#comment-60939.

    Программа на удивление работает, работает иначе, чем Вам нужно, но там есть парсинг.
    Строки типа -
    ?NumberPacket(Номер пакета);XXX(Длина пакета);XXX(Тип операции);XXX(Параметр);XX(Тип данных);XXXXXXXXXX(Значение)!\n

    Залейте, и киньте строку ?0;14;7;0;1;1!, с передачей конца строки.

    Вот кусочки, той забавной программы.

    Получение данных.
    Код (C++):
    /// <summary>
    /// Получение данных с Serial порта
    /// </summary>
    void GetReceiveDataFromSerial()
    {
        // TODO: Добавить описание
        while(Serial.available())
        {
            /// <summary>
            /// Переменная хранящая последний полученный char
            /// </summary>
            char incomingChar = (char)Serial.read();

            if(incomingChar != newLine &&
                incomingChar != clearChar)
            {
                receiveCharArrayCOM[indexCharInArray] = incomingChar;
                indexCharInArray++;
            }
            else
            {
                receiveCharArrayCOM[indexCharInArray] = '\0';

                finishReceiveData = true;
            }
        }
    }
    Парсинг
    Код (C++):
    /// <summary>
    /// Парсинг полученных данных
    /// </summary>
    void ParseReceiveData()
    {
        // Завершено ли получения данных
        if (finishReceiveData)
        {
            // Получаем длину пакета
            receiveLength = strlen(receiveCharArrayCOM);
           
            // Пробегаемся по массиву хранящий пакет данных
            for (int i = 0; i < receiveLength; i++)
            {
                switch (receiveCharArrayCOM[i]) {
                    case beginChar:
                        // Получаем индекс символ начала пакета в пакете
                        indexBeginCharInPackage = i;
                        break;
                    case endChar:
                        // Получаем индекс символ конца пакета в пакете
                        indexEndCharInPackage = i;
                        break;
                    case breakChar:
                        // Получаем кол-во символов разделителя блоков данных в пакете
                        breakCharCountInPackage++;
                        break;
                    default:
                        // В зависимости от номера блока данных в пакете, заполняем переменные блоков
                        switch (breakCharCountInPackage) {
                            case 0:
                                // Номер пакета в системе
                                numberPacket += receiveCharArrayCOM[i];
                                break;
                            case 1:
                                // Длина пакета
                                lenPacket += receiveCharArrayCOM[i];
                                break;
                            case 2:
                                // Тип операции пакета
                                operationTypePacket += receiveCharArrayCOM[i];
                                break;
                            case 3:
                                // Тип операции пакета
                                parametrPacket += receiveCharArrayCOM[i];
                                break;
                            case 4:
                                // Тип данных параметра пакета
                                dataTypePacket += receiveCharArrayCOM[i];
                                break;
                            case 5:
                                // Значение параметра пакета
                                valuePacket += receiveCharArrayCOM[i];                  
                                break;
                        }
                        break;
                }
            }
         
            if (breakCharCountInPackage != 5)
            {
                ErroreDataReceived("2",1);

                return;
            }
           
            // Номер пакета в системе, Int
            receiveNumberIntPacket = numberPacket.toInt();

            // Номер пакета в системе, Int
            receiveLenIntPacket = lenPacket.toInt();

            // Тип операции пакета, Int
            receiveOperationTypeIntPacket = operationTypePacket.toInt();

            //SerialPrintLine(String(indexBeginCharInPackage));
            //SerialPrintLine(String(breakCharCountInPackage));
            //SerialPrintLine(String(indexEndCharInPackage));
            //SerialPrintLine(String(receiveLength));
            //SerialPrintLine(String(receiveLenIntPacket));

            // Проверка корректности полученного пакета
            if (indexBeginCharInPackage == 0 && breakCharCountInPackage == 5    // Индекс символа начала пакета, должен быть равен 0, кол-во блоков данных должно быть 5  
                    && indexEndCharInPackage == (receiveLength - 1)                // Индекс символа конца пакета, должен быть равен значению блока длины пакета - 1
                    && receiveLenIntPacket == receiveLength                        // Реальная длина пакета должна быть равна значению блока длины пакета
                    )
            {
                // Определяем тип операции пакета
                switch(receiveOperationTypeIntPacket)
                {
                    case OT_StartTPA:
                        // Старт ТПА

                        SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt());

                        // Устанавливаем состояние программы запущена
                        beginWorkTPA = true;
                        break;  
                    default:
                        int globalNumberPacketAndOne = globalNumberPacket;//globalNumberPacket + 1;
                        if (receiveOperationTypeIntPacket != OT_CheckReturnFromPC && receiveNumberIntPacket == globalNumberPacketAndOne)
                        {          
                            SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt());
                        }
                        else
                        {
                            if (receiveNumberIntPacket == globalNumberPacketAndOne)
                            {
                                switch(receiveOperationTypeIntPacket)
                                {
                                    case OT_StopTPA:
                                        // Стоп ТПА
                               
                                        SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt());
                                   
                                        //globalNumberPacket = 0;

                                        // Устанавливаем состояние программы остановлена
                                        beginWorkTPA = false;
                                        break;
                                }
                            }
                            else
                            {
                                ErroreDataReceived("3",0);
                            }
                        }      
                        break;
                }
            }
            else
            {
                ErroreDataReceived("4",0);
            }                                            
        }
    }
    Программа не оптимальна и сейчас бы и в голову не пришло делать на строках, но саму идею можно понять. Уже поздно, завтра если буду вопросы пишите, удачи!
     
    Vetrinus нравится это.
  2. Vetrinus

    Vetrinus Гик

    Код (C++):
    #define led 13
    String input_string = "";
    const String Led_off = "switch led off";
    const String Led_on = "switch led on";

    void setup() {
      Serial.begin(9600);
    }

    void loop() {
      while (Serial.available() > 0) {
        char c = Serial.read();
        if (c == '\n') { //считается, что команда завершена, если пришел символ перевода строки
          Serial.print("Input_string is: ");
          Serial.println(input_string);
          switch ( parse(input_string, Led_off, Led_on) ) {
            case 10:
              digitalWrite(led, LOW);
              Serial.println("Switching off is done");
              break;
            case 11:
              digitalWrite(led, HIGH);
              Serial.println("Switching on is done");
              break;
            case 9:
              Serial.println("invalid String");
              break;
          }
          input_string = "";
        } else {
          input_string += c;

        }
      }
    }

    byte parse(String input_string, const String Led_off, const String Led_on) {
      if (input_string.compareTo(Led_off) == 0) {
        return 10;
      }
      else if (input_string.compareTo(Led_on) == 0) {
        return 11;
      }
      else return 9;
    }
     
    upload_2015-11-12_9-38-2.png
    Только вот светодиод почему-то не отрабатывает..
     
  3. Vetrinus

    Vetrinus Гик

    Проблема решилась декларированием 13 порта как выхода)
     
  4. Alex19

    Alex19 Гуру

    Я Вас не правильно понял, подумал, что надо именно парсить строку, разбирать команды, параметры команд и т.д. Извините за мои пять копеек, удачи!
     
  5. Vetrinus

    Vetrinus Гик

    Да нет, ваши пять копеек вполне в тему. Но пока что у меня не горит практическое использование этого кода, поэтому написанное выше - только прототип, который потом будет дорабатываться и дорабатываться.
     
    Последнее редактирование: 12 ноя 2015
  6. Alex19

    Alex19 Гуру

    Раз так, вот упрощенный вариант.

    Код (C++):
    // Скорость Serial порта.
    #define SERIAL_SPEED 115200

    // Символ новой строки.
    #define NEW_LINE '\n'

    // Символ признак конца строки.
    #define CLEAR_CHAR '\0'

    // Символ начала пакета.
    #define beginChar '?'

    // Символ разделитель параметров пакета.
    #define breakChar ';'

    // Символ конца пакета.
    #define endChar '!'

    // Константа хранящая мак. размер массива хранящего пакет данных
    #define receiveSizeArrayCOM 40

    // Массив хранящий полученый пакет данных.
    char receiveCharArrayCOM[receiveSizeArrayCOM];

    // Переменная хранящая завершено ли получения данных.
    boolean finishReceiveData = false;

    // Переменная хранящая индекс текущего char в массиве хранящего пакет данных.
    int indexCharInArray;

    // Переменная хранящее номер символа начала пакета в пакете
    int indexBeginCharInPackage;

    // Переменная хранящее номер символа конца пакета в пакете
    int indexEndCharInPackage;

    // Кол-во символов разделителя блоков данных в пакете
    int breakCharCountInPackage;

    // Переменная хранящая ошибку длины полученных данных.
    boolean errorLengthReceiveData = false;

    // Массив хранящий команду.
    String receiveCommandCOM;

    // Массив хранящий аргумент команды.
    String receiveArgumentCommandCOM;

    /// <summary>
    /// Предварительная настройка.
    /// </summary>
    void setup ()
    {
      // Настраиваем Serial.
      Serial.begin(SERIAL_SPEED);
    }

    /// <summary>
    /// Основной цикл.
    /// </summary>
    void loop()
    {
      // Получение данных с Serial порта
      GetReceiveDataFromSerial();

      // Ошибка длинны данных.
      if (errorLengthReceiveData)
      {
        Serial.println("Error_0");
     
        // Очистка переменных получения данных, после получения данных.
        ClearReceiveDataVariable();

        // Обнуляем переменую errorLengthReceiveData.
        errorLengthReceiveData = false;
      }
      else if (finishReceiveData)
      {
        // Парсинг полученных данных.
        ParseReceiveData();
     
        Serial.print("Command - '");
        Serial.print(receiveCommandCOM);
        Serial.print("' Argument Command - '");
        Serial.print(receiveArgumentCommandCOM);
        Serial.println("'");
     
        // Очистка переменных получения данных, после получения данных.
        ClearReceiveDataVariable();
      }
    }

    /// <summary>
    /// Получение данных с Serial порта
    /// </summary>
    void GetReceiveDataFromSerial()
    {
      // Крутимся в цикле, до тех пор пока есть данные для чтения.
      while(Serial.available())
      {
        // Объявляем переменную и сохрянем 1 char нее.
        char incomingChar = (char)Serial.read();

        // Проверяем, если пришел символ новой строки или символ конца строки.
        if(incomingChar != NEW_LINE &&
          incomingChar != CLEAR_CHAR)
        {
          // Заполняем массив.
          receiveCharArrayCOM[indexCharInArray] = incomingChar;

          // Проверяем индекс массива.
          if ((indexCharInArray+1) < receiveSizeArrayCOM)
          {
            // Увеличеваем индекс массива.
            indexCharInArray++;
          }
          else
          {
            // Данных больше, чем можем вместить.
            // Очень коварная ситуация, если мы выдем за пределы массива, отладка будет очень тяжелая.
            errorLengthReceiveData = true;
            break;
          }
        }
        else
        {
          // Увеличеваем индекс массива.
          receiveCharArrayCOM[indexCharInArray] = '\0';

          // Взводим флаг, данные получены.
          finishReceiveData = true;
        }
      }
    }

    /// </summary>
    /// Парсинг полученных данных.
    /// </summary>
    void ParseReceiveData()
    {
      // Переменная хранящее реальную длину пакета, проверка после получения.
      // Получаем длину пакета.
      int receiveLength = strlen(receiveCharArrayCOM);

      // Пробегаемся по массиву хранящий пакет данных
      for (int i = 0; i < receiveLength; i++)
      {
        switch (receiveCharArrayCOM[i])
        {
        case beginChar:
          // Получаем индекс символ начала пакета в пакете
          indexBeginCharInPackage = i;
       
          // Идекс начального пакет не равен 0;
          if (indexBeginCharInPackage)
          {
            Serial.println("Error_1");
          }
          break;
        case endChar:
          // Получаем индекс символ конца пакета в пакете
          indexEndCharInPackage = i;
       
          // Идекс конца пакета равен receiveLength.
          if (indexEndCharInPackage == receiveLength)
          {
            Serial.println("Error_2");
          }
          break;
        case breakChar:
          // Получаем кол-во символов разделителя блоков данных в пакете
          breakCharCountInPackage++;
       
          // Разделителей больше одного.
          if (breakCharCountInPackage > 1)
          {
            Serial.println("Error_3");
          }
          break;
        default:
          switch (breakCharCountInPackage)
          {
          case 0:
            // Команда.
            receiveCommandCOM += receiveCharArrayCOM[i];
            break;
          case 1:
            // Аргумент команды.
            receiveArgumentCommandCOM += receiveCharArrayCOM[i];
            break;
          }
          break;
        }
      }
    }

    /// </summary>
    /// Очистка переменных получения данных, после получения данных.
    /// </summary>
    void ClearReceiveDataVariable()
    {
      indexCharInArray = 0;
      memset(receiveCharArrayCOM,CLEAR_CHAR,receiveSizeArrayCOM);

      indexBeginCharInPackage = indexEndCharInPackage = breakCharCountInPackage = 0;
      receiveArgumentCommandCOM = receiveCommandCOM = "";

      finishReceiveData = false;
    }
     
    Понимает команды типа: ?Команда;Аргумент команды!
    Пример: ?analogWrite;180!
    Вернет в Serial мониторе
    Вообще передача данных через Serial (UART), SPI и т.д., тема обширная и вариантов реализации очень много, со своими плюсами и минусами. На мой взгляд в идеале найти готовую библиотеку, в сети много вариантов, чем самому делать велосипед, в крайнем случае дописать ее.

    Вот они к примеру - http://playground.arduino.cc/Main/InterfacingWithHardware#Communication.

    Так я пришел к 3 варианту передачи данных по Serial (UART), взяв библиотеку от Multiwii, вот он - http://arduino.ru/forum/ishchu-ispo...a-podvodnogo-apparata-za-voznag#comment-93457. Правда пришлось его немного модернизировать. Его можно использовать если есть начальные познания в AVR и СИ.
     
    Последнее редактирование: 14 ноя 2015