• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Доступ к функции из ассоциативного массива в языке Wiring

combasoft

New member
Здравствуйте.
Возможно ли в языке Wiring получить доступ к функции(т.е. вызвать функцию) из ассоциативного массива? Хотелось бы уйти от "if"-ов.
История появления вопроса :
хочу на базе esp создать устройство, которое может читать некоторые датчики и по некоторому набору правил рулить исполнительными устройствами. Правила в качества входной информации используют не только состояние датчиков, но и некоторые внутренние настройки, которые могу быть установлены из вне( по MQTT).
Структура данных и алгоритм работы мне видятся такими (далее - много текста :) ) :
Есть основной цикл. В нем опрашиваем RTC, смотрим сколько времени прошло с момента запуска "основой части программы". Если интервал времени не достиг еще некоторого заданного значения - уходим в начало цикла, иначе проваливаемся в ветку с основной частью.
а) Там мы запускаем процедуру, которая опрашивает датчики. Для этого есть некий список, который сопоставляет некоторую структуру данных в памяти с условным типом устройства и возможно какими-то его дополнительными характеристиками. Еще есть массив, сопоставляющий типы устройства (типа данных - String) с функцией, которая будет это устройство специфичным для него образом опрашивать. Это первое место, где понадобилось сопоставить какой-то тип данных и функцию. Результатом выполнения функции будет заполненная структура данных вида HashMap(String internalSensorName, *someDataStructure readedValue)
б) далее запускается функция, которая составляет карту значений - по какому датчику были изменения значений. Видимо для этого нужно иметь предыдущие считанные значения. На выходе ожидается что-то вроде HashSet(String internalSensorName)
в) далее дергается функция, которая читает данные, полученные на предыдущем шаге и по ассоциативному массиву вида HashMap(String internalSensorName, linkToFunction functionName) вызывает рулы, которые на базе некоторых настроек и некоторых значений датчиков обновляют внутренние переменные, которые сопоставлены с выводами контроллера, т.е. на выходе будет заполнена структура вида HashMap(String internalPinName, byte state)
г) потом дергается функция, которая пробегается по мапе из предыдущего шага и по мапе, вида HashMap(String internalPinName, byte pinNumber) и обновляет состояние выводов.

В callback функции клиента MQTT:
топики сопоставлены с переменными настроек рулов.
значение в топике - это новое значение переменной настроек.
а) смотрим все интересующие нас топики (мапа HashMap(String topicName, String internalSettingVariableName)), обновляем значения настроечных переменных.
б) определяем какие из них были изменены (на выходе - HashSet(String internalSettingVariableName))
в) смотрим мапу, в которой указаны от каких переменных какие рулы зависят:
HashMap(String internalSettingVariableName, HashSet( linkToFunction functionName)), на выходе выдаем
сет из функций рулов, которые нужно дернуть: HashSet( linkToFunction functionName)
г) пробегаемся по полученному сету, в результате обновляется HashMap(String internalPinName, byte state)
д) потом дергается функция, которая пробегается по мапе из предыдущего шага и по мапе, вида HashMap(String internalPinName, byte pinNumber) и обновляет состояние выводов.

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

В целом, система выглядит нормально для стационарного компа, а вот для esp у меня подозрение - поместится ли это все в нее. Реализацию мапы нашел. Сета - еще не искал, но наверное тоже есть.
С мапой можно реализовать contain(Object key), если условиться, что все реальные данные складывать начиная с индекса 1, тогда для несуществующего ключа функция indexOf(key) выдаст 0.

Если описанный "космический кораблик" на Wiring и esp не реализуем в принципе, я надеюсь, что вы мне подскажете альтернативные варианты решения проблемы)
 
Последнее редактирование:

combasoft

New member
зы.
таким относительно сложным решение я хотел достичь нескольких целей:
  • оптимизации времени выполнения (дергаем только необходимые функции)
  • расширяемость:
    • чтобы добавить датчик, нужно просто прописать его в массивах, при необходимост написать специфичную для него функцию считывания данных;
    • то же с рулами - два действия - пишем функцию, прописываем зависимости в массивах. все остальное не трогаем, если только например не хотим внедрить в существующие рулы зависимость от добавленного датчика либо добавленного рула (рул может не изменять сразу переменную состояния пина, а просто менять одну или несколько настроечных переменных)
 
Последнее редактирование:

Сергей_Ф

Moderator
Команда форума
@combasoft весь Ваш текст не осилил и до конца не понял, но ответ на первый вопрос - можно. Вайринг ничем от С++ принципиально не отличается.
 

nikolz

Well-known member
Вот посмотрите реализацию на луа:
---------------------
function cb() -- эта функция вызывается через заданное время и в ней исполняется опрос датчиков и отправка данных
--....
end
tmr.alarm(1,2000,1,cb) -- вызов cb через 2 секунды
---------------------
вот тут есть все нужные вам функции для работы с mqtt:
mqtt - NodeMCU Documentation
а также готовые драйверы для работы с датчиками
 

combasoft

New member
Я переосмыслил свой подход и понял, что слишком усложнил реализацию. В итоге, я отказался от идеи постройки космо-корабля ради "универсального" описания трёх-четырёх датчиков. Да здравствуют хардкод и здравый смысл (лень возиться, хочется результат побыстрее). На время я эту тему оставил. Но к теме ассоциирования функций с чем-либо я всё равно вернулся. На этот раз это конечный автомат для работы с разобранным на строки xml либо json - нужно будет заиметь мапу, в которой будут соотнесены состояние и действие, которое соответствует при переходе в состояние. Таблицу переходов практически "как есть" можно взять отсюда Конечный автомат (он же машина состояний) на чистом С . Разве что "signals" так же соотнести в мапе с параметрами из xml. Типа "рул освещения, параметр время старта/начала освещения" - это какой-нибудь номер 55. Попозже отпишу, что удалось сделать. В качестве выхлопа будет уход от switch-case, более легкая поддержка изменения логики работы. Меня не покидает чувство, что это велосипед) Беглый поиск по форуму выдал связку if-else. Т.е. код-шпагетти. На мой скромный взгляд - для заведомо несложной логики из трёх-четырёх сестояний - норм, а для чего-то посложнее - уже стоит осваивать вышеописанный подход. Все-таки код должен быть простым в поддержке(дебаг, добавление нового функционала), что б условно говоря любой школьник мог быстро разобраться в коде.
 

Юрий Ботов

Moderator
Команда форума
Если не секрет, ваш "основной" язык? А то человек, который "в С++ - ноль" уж больно храбро кидается вполне таки серьезной терминологией... Кстати...по поводу if/else... не проще использовать switch/case, а начинку упаковать в отдельные функции?
 

combasoft

New member
Джавист я.. хотя вот - "что в имени тебе моём"?)
Лет 6-7 назад что-то писал на С - простенькое, для себя. Это мне помогло сейчас - несколько уменьшило время на то, что б вспомнить как оно работать с указателями.
Насчет свитч/кейз - нормальное решение для небольшого количества состояний - я выше писал. Но хочется красоты и лаконичности, особенно когда количество состояний с десяток.
А что у меня за серьезная терминология в тексте?
 

nikolz

Well-known member
Я переосмыслил свой подход и понял, что слишком усложнил реализацию. В итоге, я отказался от идеи постройки космо-корабля ради "универсального" описания трёх-четырёх датчиков. Да здравствуют хардкод и здравый смысл (лень возиться, хочется результат побыстрее). На время я эту тему оставил. Но к теме ассоциирования функций с чем-либо я всё равно вернулся. На этот раз это конечный автомат для работы с разобранным на строки xml либо json - нужно будет заиметь мапу, в которой будут соотнесены состояние и действие, которое соответствует при переходе в состояние. Таблицу переходов практически "как есть" можно взять отсюда Конечный автомат (он же машина состояний) на чистом С . Разве что "signals" так же соотнести в мапе с параметрами из xml. Типа "рул освещения, параметр время старта/начала освещения" - это какой-нибудь номер 55. Попозже отпишу, что удалось сделать. В качестве выхлопа будет уход от switch-case, более легкая поддержка изменения логики работы. Меня не покидает чувство, что это велосипед) Беглый поиск по форуму выдал связку if-else. Т.е. код-шпагетти. На мой скромный взгляд - для заведомо несложной логики из трёх-четырёх сестояний - норм, а для чего-то посложнее - уже стоит осваивать вышеописанный подход. Все-таки код должен быть простым в поддержке(дебаг, добавление нового функционала), что б условно говоря любой школьник мог быстро разобраться в коде.
Позвольте пару слов без протокола.
Во-первых, любая микропроцессорная железка - это в общем случае конечный автомат.
Поэтому вы рассуждаете ни о чем.
Во-вторых, школьник разберется в том, чему его учили, но и то не любой.
Поэтому мечта о том, чтобы кухарка управляла государством, а любой школьник лепил конечные автоматы на микропроцессорах - это утопия.
Относительно ухода от switch-case к if/then/else - это в некоторых реализациях экономит память, так как case как правило, но не всегда, более затратный код.
Но это чисто технический вопрос и не имеет особо им заниматься пока не дойдете до оптимизации алгоритма, для создания которого еще надо поставить задачу.
успехов Вам в постановке задачи.
 

Юрий Ботов

Moderator
Команда форума
А что у меня за серьезная терминология в тексте?
Просто новички которые сюда забредают как правило не знают что такое map и hash, а словосочетание ассоциативный массив у некоторых вообще вызывает клинч сознания... Я (про себя) предполагал что вы из функциональщиков: lisp/sheme или F# например :)
case как правило, но не всегда, более затратный код.
Ассоциативный массив окажется в смысле кода еще более затратным :) но речь у автора не о компактности кода, а о понятности записи.

речь идет примерно о следующем (просто С):

struct sensor { char type[5], void (handler*)(struct sensor* s); char value; char prevvalue;}
struct sensor sensors[3] = {{"tem1",h1,0,0},{"bar1",h2,0,0},{"tem2",h1,0,0}};

...
for(int i = 0; i < 3; i++) sensors<i>.handler(&sensors<i>);


void h1(struct sensor* s) {s->prevvalue = s->value; s->value = interfacetosensor1();}
void h2(struct sensor* s) {s->prevvalue = s->value; s->value = interfacetosensor2();}

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

СОРРИ ОНО ЖРЕТ КВАДРАТНЫЕ СКОБКИ ВМЕСТЕ С СОДЕРЖИМЫМ заменю их на угловые.
 

combasoft

New member
успехов Вам в постановке задачи.
Давайте я распишу для вас и заодно для себя - может где что упустил. Итак) (много текста)

Общее описание:
Я хочу для начала просто помигать лампочкой по простому расписанию(светим "от" и "до"), которое можно как-то задавать из вне.
Система должна быть расширяемой, что бы можно было понадобавлять датчиков, правил исполнителных устройств. Это будет очередной "типа умный рассадный ящик" (на деле это будет очередной "самолет" с кучей ручек управления и исполнительных устройств, и что бы "это" взлетело, т.е. был какой-то выхлоп в виде полезного устройства, нужно еще осилить создание рулов с нужными настройками - за этим нужно будет ходить на форум агрономов или что-то вроде этого )

применительно к "посветить лампочкой по расписанию":
BR:
- т.е. система должна уметь как-то узнавать о времени,
- нужно иметь возможность задавать/считывать системное время
- в системе должно быть прописано некое правило, которое на основании данных о времени и данных о настройках правила будет дёргать одним из выходов контроллера.
- эти настройки хотелось бы как-то задавать/считывать
- смотреть "состояние" системы: считывать настройки рула, какое сейчас время по мнению системы и в каком состоянии управляющий выход.
- может быть в понятие "настройки" будет так же включен номер управляющего пина, но это уже скорее излишества)
- на время отладки хотелось бы иметь возможность управлять
нагрузкой в обход рула.

FR:
- т.к. нужно как-то работать со временем, в системе должно быть что-то типа DS3107, DS3231. Поэтому ищу либу, которая работает с этими видами "датчиков". Либу использую с "амперки".
- т.к. я хочу как-то там задавать "из вне" настройки системы, читать её "статус", то стоит обратить внимание на готовые решения подобного рода. Мне приглянулось решение на базе MQTT - нужно как-то определиться с протоколом передачи данных. Из распространенного есть XML и JSON. Последнее вроде бы более под силу МК с ограниченными ресурсами, так что останавливаюсь на нем. Нашел либу для работы с JSON. Человек ее аж в 4 захода писал (там что-то с динамическим выделением памяти в ранних версиях не ладилось). В итоге либу хвалит множество людей. Почему бы и мне ей не попользоваться) Home · bblanchon/ArduinoJson Wiki · GitHub

- составляю перечень команд, которые я хотел бы реализовать.
Три блока/вида команд: чтение/установка настроек рула, чтение/установка системного времени, управление управляющим)) выходом(включили, выключили ну или третий вариант -в соответствии с тем, что хочет рул).

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

На данный момент девайс шлет "телеметрию" на MQTT брокер, знает захардкоженные в if-else команды "сколько времени", "что там с управляющим выходом", "не свети".

Т.к. есть перечень параметров, с которыми буду работать, то есть и понимание какое наполнение будет у JSON и соответственно какой набор функций должен быть для обслуживания этого дела.

Для работы с толпой разобранных строк из JSON (param name, value) я как раз и хочу использовать автомат. Хотя тут наверное логика работы бесхитростная и подошел бы просто маппинг параметра на функцию-обработчик).

Есть небольшой задел на тему расширяемости:
я разнес код на классы, по хорошему надо бы оперировать только интерфейсами, что бы не зависеть от конкретной реализации и что бы можно было пробегать в цикле по коллекции рулов и единообразно их дёргать.
применительно к рулу например нужно иметь как минимум
doRule(); setRuleActive(), setRuleInactive(), а для классов, которые работают сенсорами - readSensor(); Еще хорошо бы заложить расширяемость по части правил, да еще что б их можно было "на лету" добавлять/удалять, но для этого как минимум потребуется определиться с их типами. Вот тут четкого понимания пока нет, да и рано наверное - нужно для начала нормально сделать "включать лампу по расписанию".

Пока объяснял - сам понял, что автомат пока не нужен) Во всяком случае необходимости именно в автомате нет, т.к. это же можно осуществить другим инструментом. А вот ассоциация функции с каким-то ключем - все же нужна. Это и есть другой инструмент)

Пожалуй, все же сделаю решение на автомате - на будущее, что б если вдруг потребуется какую сложную логику сделать - уже было чем. Например, что бы можно было в JSON не сваливать всё в кучу, а разнести по соответсвующим блокам, типа
{
"lighting rule" {
"param 1":"value";
}
"time sensor" {
"hour":"get";
}
}
 

nikolz

Well-known member
Давайте я распишу для вас и заодно для себя - может где что упустил. Итак) (много текста)

Общее описание:
Я хочу для начала просто помигать лампочкой по простому расписанию(светим "от" и "до"), которое можно как-то задавать из вне.
Система должна быть расширяемой, что бы можно было понадобавлять датчиков, правил исполнителных устройств. Это будет очередной "типа умный рассадный ящик" (на деле это будет очередной "самолет" с кучей ручек управления и исполнительных устройств, и что бы "это" взлетело, т.е. был какой-то выхлоп в виде полезного устройства, нужно еще осилить создание рулов с нужными настройками - за этим нужно будет ходить на форум агрономов или что-то вроде этого )

применительно к "посветить лампочкой по расписанию":
BR:
- т.е. система должна уметь как-то узнавать о времени,
- нужно иметь возможность задавать/считывать системное время
- в системе должно быть прописано некое правило, которое на основании данных о времени и данных о настройках правила будет дёргать одним из выходов контроллера.
- эти настройки хотелось бы как-то задавать/считывать
- смотреть "состояние" системы: считывать настройки рула, какое сейчас время по мнению системы и в каком состоянии управляющий выход.
- может быть в понятие "настройки" будет так же включен номер управляющего пина, но это уже скорее излишества)
- на время отладки хотелось бы иметь возможность управлять
нагрузкой в обход рула.

FR:
- т.к. нужно как-то работать со временем, в системе должно быть что-то типа DS3107, DS3231. Поэтому ищу либу, которая работает с этими видами "датчиков". Либу использую с "амперки".
- т.к. я хочу как-то там задавать "из вне" настройки системы, читать её "статус", то стоит обратить внимание на готовые решения подобного рода. Мне приглянулось решение на базе MQTT - нужно как-то определиться с протоколом передачи данных. Из распространенного есть XML и JSON. Последнее вроде бы более под силу МК с ограниченными ресурсами, так что останавливаюсь на нем. Нашел либу для работы с JSON. Человек ее аж в 4 захода писал (там что-то с динамическим выделением памяти в ранних версиях не ладилось). В итоге либу хвалит множество людей. Почему бы и мне ей не попользоваться) Home · bblanchon/ArduinoJson Wiki · GitHub

- составляю перечень команд, которые я хотел бы реализовать.
Три блока/вида команд: чтение/установка настроек рула, чтение/установка системного времени, управление управляющим)) выходом(включили, выключили ну или третий вариант -в соответствии с тем, что хочет рул).

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

На данный момент девайс шлет "телеметрию" на MQTT брокер, знает захардкоженные в if-else команды "сколько времени", "что там с управляющим выходом", "не свети".

Т.к. есть перечень параметров, с которыми буду работать, то есть и понимание какое наполнение будет у JSON и соответственно какой набор функций должен быть для обслуживания этого дела.

Для работы с толпой разобранных строк из JSON (param name, value) я как раз и хочу использовать автомат. Хотя тут наверное логика работы бесхитростная и подошел бы просто маппинг параметра на функцию-обработчик).

Есть небольшой задел на тему расширяемости:
я разнес код на классы, по хорошему надо бы оперировать только интерфейсами, что бы не зависеть от конкретной реализации и что бы можно было пробегать в цикле по коллекции рулов и единообразно их дёргать.
применительно к рулу например нужно иметь как минимум
doRule(); setRuleActive(), setRuleInactive(), а для классов, которые работают сенсорами - readSensor(); Еще хорошо бы заложить расширяемость по части правил, да еще что б их можно было "на лету" добавлять/удалять, но для этого как минимум потребуется определиться с их типами. Вот тут четкого понимания пока нет, да и рано наверное - нужно для начала нормально сделать "включать лампу по расписанию".

Пока объяснял - сам понял, что автомат пока не нужен) Во всяком случае необходимости именно в автомате нет, т.к. это же можно осуществить другим инструментом. А вот ассоциация функции с каким-то ключем - все же нужна. Это и есть другой инструмент)

Пожалуй, все же сделаю решение на автомате - на будущее, что б если вдруг потребуется какую сложную логику сделать - уже было чем. Например, что бы можно было в JSON не сваливать всё в кучу, а разнести по соответсвующим блокам, типа
{
"lighting rule" {
"param 1":"value";
}
"time sensor" {
"hour":"get";
}
}
Могу дать совет.
Так как у Вас есть опыт работы на джава, то рекомендую начать с луа.
Могу Вас заверить из собственного опыта, что все что Вы написали решается на луа , если знаете язык и основы работы сетей, очень быстро.
Для начала напишите на джава собственный терминал для компа.
Если действительно хотите сделать что-то вменяемое, то пишите сами,
а не используйте метод РЕКЛЕ (старый метод радиолюбителей "режу- клею").
Иначе будете очередным мечтателем на форуме.
 

combasoft

New member
@nikolz
Переход на Луа равно как и на другой любой инструмент, не снимает необходимости иметь архитектуру решения.
Вы же сами писали, что бы что-то сделать - нужно понимать чего ты хочешь достичь (постановка задачи и что от этого следует)
А имея точное представление что вы хотите достичь - в виде модели и списка FR-ов, вам особо без разницы каким инструментом вы это сделаете.
Включалку лампы по таймеру я почти доделал. Чего ради мне тратиться на переход на другой инструмент? - какие такие возможности он мне сумеет предоставить, которые будут выгодно смотреться в свете текущего инструмента? Расширяемость? - это зависит от архитектурного решения(модели) Готовое архитектурное решение? - так это только я сам и могу сделать и никакой супер-язык за меня этого не сделает)
Насчет "пишите сами". Написание велосипеда стоит моего времени. Вот честно, не вижу смысла на повторение пути по уже проторенной дорожке, т.е. когда уже есть готовое, а главное - протестированное решение. Вам конечно спасибо за советы, но я пока не вижу причин отклоняться от выбранного мною решения.
 

nikolz

Well-known member
@nikolz
Переход на Луа равно как и на другой любой инструмент, не снимает необходимости иметь архитектуру решения.
Вы же сами писали, что бы что-то сделать - нужно понимать чего ты хочешь достичь (постановка задачи и что от этого следует)
А имея точное представление что вы хотите достичь - в виде модели и списка FR-ов, вам особо без разницы каким инструментом вы это сделаете.
Включалку лампы по таймеру я почти доделал. Чего ради мне тратиться на переход на другой инструмент? - какие такие возможности он мне сумеет предоставить, которые будут выгодно смотреться в свете текущего инструмента? Расширяемость? - это зависит от архитектурного решения(модели) Готовое архитектурное решение? - так это только я сам и могу сделать и никакой супер-язык за меня этого не сделает)
Насчет "пишите сами". Написание велосипеда стоит моего времени. Вот честно, не вижу смысла на повторение пути по уже проторенной дорожке, т.е. когда уже есть готовое, а главное - протестированное решение. Вам конечно спасибо за советы, но я пока не вижу причин отклоняться от выбранного мною решения.
Вопрос выбора инструмента, конечно не отвечает на вопрос что делать.
Но луа - это алгоритмический язык, т е язык который позволяет быстро отладить предметный алгоритм не углубляясь в технические детали.
относительно "делать велосипед"
Не соглашусь с Вашим видением этой задачи.
Велосипед можно делать так: взять раму с установленной передачей и установить колеса. Это позволяет делать луа.
А можно делать так: взять подшипники и обод, покрышку спицы для колес, трубы для изготовления рамы, сварочный аппарат - и сделать велосипед с квадратными колесами - как душе угодно - это позволяет делать CИ
Можно купить готовый или еще проще взять на халяву коробку с надписью велосипед И потом пытаться понять как на этом ездить.
можно ...
Конечно выбор за Вами.
 
Сверху Снизу