Правильные методы реализации функционального клиентского доступа к асинхронному последовательному интерфейсу?

Это вопрос не для новичков.

Я реализую функциональный интерфейс для асинхронного последовательного устройства, подключенного к порту Serial1 на mega.

Чтобы обеспечить ясность, я хочу реализовать ряд функций таким образом:

    {param} CallInterface({param1},{param2},...) {
        sendRequestPacket();
        ....{await Response or timeout}....
        return Response;
     }

Очевидно, что когда мы "ожидаем ответа", мы блокируем операционный поток и должны разрешить доступ к дополнительному процессору потока, чтобы получить и проанализировать ответ.

На многопоточной платформе это было бы тривиально реализовать с помощью семафоров и других многопоточных методов, но на Arduino, AVR решение, возможно, менее очевидно или нет.

Итак, вопрос в том, каков наилучший и наиболее эффективный способ реализации такого шаблона на платформе Arduino?

, 👍-1

Обсуждение

Очевидно, что когда мы "ждем ответа", мы блокируем операционный поток... на самом деле это не очевидно... вы можете использовать неблокирующий код, @jsotola

Трудно сказать, что является “лучшим и наиболее эффективным”, не зная точных требований вашего проекта. Однажды я столкнулся с подобной ситуацией, когда мне нужно было, чтобы мой код был неблокирующим. Затем я добавил параметр “callback” в "CallInterface", на который должен быть доставлен ответ, и метод "update ()" к объекту, реализующему интерфейс, чтобы он перехватил ответ и доставил его в обратный вызов., @Edgar Bonet

@jsotola не могли бы вы проследить за этим примером неблокирующего шаблона кода, который будет работать на платформе Arduino?, @Mark

@EdgarBonet Спасибо Эдгар, да, это аналогичная модель Async Javascript / Node. Первоначальные впечатления таковы, что мне нужно будет реализовать массив обратного вызова в коде получателя, чтобы захватить все потенциальные запросы на эти данные, которые должны быть выполнены, когда и если ответ поступит. Единственная проблема, которую я предвижу, заключается в том, что между запросом и ответом нет никакой "связи", кроме того факта, что ответ происходит вскоре после запроса., @Mark

Невозможно связать конкретный запрос с конкретным ответом, кроме как блокировать запросы до тех пор, пока не будут выполнены все ожидающие запросы или не наступит тайм-аут., @Mark

один из параметров вызова может быть переадресован в обратный вызов. или с помощью лямбд вы можете фиксировать параметры, @Juraj


1 ответ


0

Типичный способ справиться с такими вещами - иметь одну "главную" функцию, которая управляет всем, используя конечный автомат неблокирующим способом.

Эта функция может содержать всю логику для выполнения каких-либо действий, или же она может быть проще и просто обрабатывать базовую коммуникацию, а затем передавать детали функции обратного вызова, указанной в вызове функции действия.

Таким образом, поток может быть:

  1. Вы вызываете функцию, которая просто устанавливает вещи:
  • Он хранит подробную информацию о запросе, который должен быть сделан
  • Он записывает адрес функции "обратного вызова".
  1. Вы многократно выполняете свою функцию "run" из loop().
  • Он посылает команду цели
  • Он получает и анализирует ответ, сохраняя его где-то внутри
  • Когда ответ завершен он выполняет функцию обратного вызова передавая ей ответ

Не все из 2 будет происходить на каждой итерации loop(), поэтому напишите его неблокирующим способом (используя .available() и т. Д.) И вызовите обратный вызов только тогда, когда все будет завершено.

Таким образом, вы можете настроить цепочку событий:

  1. Вы отправляете запрос 1 с обратным вызовом A.
  2. Обратный вызов A отправляет запрос 2 с обратным вызовом B.
  3. Обратный вызов B отправляет запрос 3 с обратным вызовом C.

... и так далее ...

В качестве примера вот как может выглядеть элемент управления модемом:

Modem.command("AT+CREG?", testRegistration);
>>> send         AT+CREG?
<<< store        +CREG: 0,2
<<< store
<<< identify     OK
--- Execute testRegistration(true, "+CREG: 0,2\n\n");

Или, может быть, если что-то пойдет не так:

Modem.command("AT+CROG?", testRegistration); // Ой, опечатка...
>>> send         AT+CROG?
<<< identify     ERR
--- Execute testRegistration(false, "");
,