Архитектура приложения

Тема в разделе "Arduino & Shields", создана пользователем railmisaka, 10 июл 2017.

?

Как вам идея?

  1. Нормальный подход, если, конечно, хватит памяти

    1 голосов
    100,0%
  2. Плохой подход

    0 голосов
    0,0%
  3. Спорное решение

    0 голосов
    0,0%
  1. railmisaka

    railmisaka Гик

    Приветствую всех!

    Я новичок по части Arduino, но у меня есть опыт разработки приложений (по большей части на языках высокого уровня). Вероятно, именно поэтому я написал слово "приложение" в заголовке.

    Возможно, я ошибся веткой/форумом. В таком случае, просьба не кидаться тапками, а просто направить куда нужно. Спасибо.

    Я понимаю, что программы пишутся по-разному, в зависимости от платформы и языка. Но, тем не менее, меня смутил первый же скетч:
    Код (C++):
    void loop()
    {
    digitalWrite(13, HIGH);
    delay(1000);
    digitalWrite(13, LOW);
    delay(1000);
    }
    Конкретно, мне не нравится вызов delay(). Такой подход вынуждает все системы ждать, пока мы моргаем светодиодом.
    Возможно, иногда это вполне приемлемо и оправдано, но иногда требуется, чтобы несколько систем работали сообща. В этом случае, такой подход не будет работать.

    Да, есть TimedAction. Это позволит нам запускать несколько задач одновременно. В основном, это решает проблемы.

    Но мне больше нравится несколько другой подход (в целом, думаю, TimedAction реализован примерно также).

    Мое решение основано на функции millis().
    Идея в том, чтобы в цикле обновлять состояния всех систем, на основе времени, прошедшего с предыдущего обновления. Не блокировать поток исполнения вызовами delay(), постоянно наращивать время исполнения операций (короче говоря, интегрирование).

    Скорее всего, я изобретаю велосипед. Пардон, просто не нашел ответов на вопросы в гугле, да и хочется с кем-нибудь обсудить свои мысли.

    Весь код далее написан "на коленке" и не претендует на то, чтобы быть конечным вариантом. Прошу не относиться к нему слишком строго, это исключительно для демонстрации концепции (честно говоря, я даже не компилировал его xD ).

    Файл Parallel.h
    Код (C++):
    class Parallel
    {
    private:
        unsigned long prevTime;
        unsigned long stepTime;
     
    public:
        Parallel();
     
    public:
        void loop();
     
        unsigned long getDeltaTime();
    };
    Файл Parallel.сpp
    Код (C++):
    #include "Parallel.h"

    Parallel::Parallel()
    : prevTime(0)
    {
    }

    void Parallel::loop()
    {
        unsigned long tmp = millis();
        stepTime = tmp - prevTime;
        prevTime = tmp;
    }
     
    unsigned long Parallel::getDeltaTime()
    {
        return stepTime;
    }
    Создаем один единственный экземпляр этого класса (а лучше вообще сделаем его синглтоном, если это возможно). Метод Parallel::loop() вызываем в каждом цикле Arduino.
    Таким образом мы всегда будем знать, сколько времени длился предыдущий цикл обновления.

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

    Файл Watering.h
    Код (C++):
    class Parallel;

    class Watering
    {
    private:
        Parallel *parallel;
        boolean started;
     
        boolean inProcess;
        unsigned long currentProcessTime;
        unsigned long fullProcessTime;
     
    public:
        Watering(Parallel *p, unsigned long time);
     
    public:
        void loop();
     
        void start();
    };
    Файл Watering.cpp
    Код (C++):
    #include "Watering.h"
    #include "Parallel.h"

    Watering::Watering(Parallel *p, unsigned long time)
    :     parallel(p),
        started(false),
        inProcess(false),
        currentProcessTime(0),
        fullProcessTime(time)
    {
    }

    void Watering::loop()
    {
        if(inProcess)
        {
            if(!started)
            {
                // enable pump
            }
            else
            {
                processTime += parallel->getDeltaTime();
                if(processTime > fullProcessTime)
                {
                    inProcess = false;
                    // sisable pump
                }
            }
        }
    }

    void Watering::start()
    {
        inProcess = true;
    }

    Основной файл:
    Код (C++):
    #include "Parallel.h"
    #include "Watering.h"

    Parallel parallel();
    Watering watering(&parallel, 10000);

    boolean button_clicked()
    {
        // return true, if button clicked
    }

    void setup()
    {
    }

    void loop()
    {
        if(button_clicked())
        {
            watering.start();
        }
     
        parallel.loop();
        watering.loop();
    }
    (Мне кажется, код достаточно простой, но если нужно, я напишу комментарии).

    Я пока не в курсе, чем точно отличается язык программирования arduino от C++, но если это возможно, я бы сделал абстрактный класс, пронаследовал от него мой мой класс Watering и прочие подобные классы.

    Еще я бы создал диспетчер задач, которые бы инкапсулировал в себе обновления всех классов, типа Watering (вызов loop()).

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

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

    Всем спасибо за внимание )
     
    Tomasina и arkadyf нравится это.
  2. Unixon

    Unixon Оракул Модератор

    TL;DR
    Отформатируйте в Wiki, просигнальте @acos :)

    Ничем, абсолютно, кроме базовой библиотеки, которая не есть часть языка.
     
  3. rkit

    rkit Гуру

    На ардуино портирована freertos, где есть все, что нужно для многозадачности.
     
    arkadyf нравится это.
  4. DetSimen

    DetSimen Guest

    arkadyf нравится это.
  5. qwone

    qwone Гик

    Вот еще один вариант http://arduino.ru/forum/programmirovanie/klassy-arduino-po-qwone-dlya-chainikov
    ПС: Попробуйте написать несколько рабочих программ в вашем стиле. А потом скажете ваши ощущения.
    ППС: Ваш метод он очень сырой, но если отшлифовать, то будет понятен не всем.
     
    arkadyf нравится это.
  6. railmisaka

    railmisaka Гик

    Простите, не понял что значит отформатировать в вики.

    Спасибо за ответ! Обязательно попробую!
    Я только все время боюсь, что не хватит памяти именно из-за этого оверхеда. Пока программы маленькие все, конечно, хорошо. С другой стороны, надо и о структуре кода заботиться.

    Если я правильно понял, то первое это как раз что то типа TimedAction.
    Про второе - я пока не думал, как организовать межклассовое взаимодействие, и очередь сообщений проверенный способ, но вот пихать в очередь сообщения про время исполнения мне не кажется очень хорошей идеей (может быть не совсем правильно понял вашу идею).

    Очень похоже на то что предлагаю я, только у меня еще есть один единый класс отвечающий за время.
    На счет понятен не всем - ну, в целом, тут нет ничего сложного. Да и я бы не стал писать что-то очень сложное с точки зрения структуры, потому что боюсь оверхеда. Думаю, буду пытаться искать компромисные решения по удобству/затратам памяти.
    А вот ваш совет по поводу написать пару программ приму. Тем более, насколько я понял, подобный подход все же используют и это не самая плохая идея.
     
  7. Unixon

    Unixon Оракул Модератор

    Хотел сказать, что в принципе сойдет за статью в Wiki, если немного отшлифовать и соответственно оформить.
     
  8. Unixon

    Unixon Оракул Модератор

  9. railmisaka

    railmisaka Гик

    Эм.. просто вы написали TL;DR. И мне казалось что в вики стоит оформлять только то, что представляется нормальным подходом. А я как раз спрашивал о праве такого подхода на существование.
     
  10. Unixon

    Unixon Оракул Модератор

    Прочитал по диагонали, увидел нормальный код. :)
     
  11. railmisaka

    railmisaka Гик

    Код писался на коленке не особо задумываясь как именно это должно выглядеть, а только как это может выглядеть. Для статьи надо доработать. Ну и если делать что-то вроде фреймворка, то нужно предусмотреть способы взаимодействия таких классов (как у меня описано). Мне нравится очереди сообщений, как у @DetSimen, но, вероятно, я бы сделал по-другому.
     
  12. Unixon

    Unixon Оракул Модератор

    Вот и скооперируйтесь.