Показано с 1 по 10 из 10

Тема: Динамический вызов функций либо FB, и все-таки...

  1. #1

    Question Динамический вызов функций либо FB, и все-таки...

    Добрый день всем!
    Нравятся мне не просто рабочие программы и алгоритмы, но еще и Красивые.. И задумался я над реализацией красивой и лаконичной FSM (final state machine) для ПЛК, и, в частности, plc63.
    Уж больно красиво выглядит решение на C на любые контроллеры от arduino до siemens.
    А case и IF многочисленные - "не вштыривают".

    И вот к чему пришел (синтаксис объявление и деклараций не полностью приведен - скопировал только суть, размер массивов значение не имеет - это демонстрационный пример , все "с запасом"
    пример реального использования динамического вызова не привожу - чтобы не усложнять вопрос - там все тривиально - в зависимости от события(=индекс_массива) выбирается следующее состояние FSM):
    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    объявим на глобальном уровне тип struct:
    type................
    struct stSTATE:
    iActionHandler: int;
    iActionHandlerPTR: dword;
    arTransitionTable: array[1..10] of int;
    end_struct;
    end_type;.......................

    объявлены FB fbNORMAL и fbSTBY с одинаковыми ДЕЙСТВИЯМИ "run" (в них для целей отладки по одному оператору типа fVar1test := true; fVar2test := true;

    глобальные переменные:
    arStates: array [1..10] of stSTATE;
    fInit: bool;

    Ну и PLC_PRG:

    PROGRAM PLC_PRG
    VAR
    iThisStateHandler : INT;
    fbSNORMAL: fbNORMAL;
    fbSSTBY: fbSTBY;
    fbInst : POINTER TO fbSTBY ;
    END_VAR

    IF NOT fInit
    THEN fInit := init(); (*проверка на холодный старт, если ДА - инициализировать значения. В init код такой: arStates[1].iActionHandler := INDEXOF(fbSTBY); arStates[2].iActionHandler := INDEXOF(fbNORMAL); но далее не используется.*)
    arStates[1].iActionHandlerPTR := ADR(fbSSTBY); (*для простоты вынес СЮДА - получаем адрес экземпляра блока и сохраняем в массив состояний для состояния 1 в dword*)
    arStates[2].iActionHandlerPTR := ADR(fbSNORMAL); (*для простоты вынес СЮДА - получаем адрес экземпляра ДРУГОГО блока и сохраняем в массив состояний для состояния 2 в dword*)
    END_IF;
    fbInst := arStates[1].iActionHandlerPTR; (*а вот и мясо с изюминкой - получим указатель на FB для ПЕРВОГО состояния *)
    fbInst^.run(); (*и посмотрим, выполнится ли он*)
    fbInst := arStates[2].iActionHandlerPTR; (*а вот и мясо с изюминкой - получим указатель на FB для ВТОРОГО состояния *)
    fbInst^.run(); (*и посмотрим, выполнится ли он*)

    (*iThisStateHandler := arStates[1].iActionHandler;*) (*а это "игры" с индексами POU*)

    (*IF iThisStateHandler <> 0 THEN И ЧТО-БЫ ТАКОЕ С ИНДЕКСОМ СДЕЛАТЬ ?!

    END_IF*)
    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    Интересно то, что код прекрасно компилируется без ошибок и предупреждений и в симуляторе выполняется, вот только
    ВТОРОЙ вызов fbInst^.run(); ВЫПОЛНЯЕТ RUN ДЛЯ БЛОКА fbSSTBY !! то есть для первого блока, чей адрес сохранен в arStates[1].iActionHandlerPTR,
    а не для arStates[2].iActionHandlerPTR как можно было бы предположить.
    При этом я готов был выслушать от компилятора жалобу на то, что fbInst := arStates[2].iActionHandlerPTR; присваивает указателю адрес на на тот инстанс, на который объявлен указатель,
    но жалобы не последовало. Удивительно, arStates[1].iActionHandlerPTR и arStates[2].iActionHandlerPTR содержат РАЗНЫЕ адреса! Это видно при отладке, однако ж - run вызывается от первого FB.

    Трудно понять логику - для чего есть прекрасный оператор INDEXOF если результат его выполнения нельзя использовать (в примере выше код для использования indexof закомментирован) ?!
    Поиск аналогичных вопросов и ответов привел на forum.codesys.com, и там, о чудо, об использовании CAA_Callback.lib как обертки вокруг CB_CallFunctionByIndex
    Но вот беда - и эта либа только для Codesys 3...А PLC63 "весь под codesys 2".

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    Итак, вопрос:
    - каким образом программно можно вызвать функцию, либо метод - действие FB, определенные при компиляции ? То есть , имея переменную, как индекс, выбирать из массива указатели/... на FB или функции и динамически вызывать их ?
    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    Вообще выбор plc63M продиктован необычайной удобностью вкупе с дисплеем, нормированными преобразователями физ величин на борту, понятными алгоритмами и тд. Да и ЦЕНОЙ.
    Ну и 3 выхода минимум 0..10в нужны для управления сервами.
    Переложил бы задачу на ПЛК1хх какой нибудь, но только ради CODESYS v3.5 это делать "религия не позволяет". Да еще и дисплей тянуть дополнительный для визуализации 3-5 параметров.

    Есть ли какие-нибудь соображения на этот счет, уважаемые коллеги и разработчики ?

  2. #2
    Пользователь Аватар для capzap
    Регистрация
    25.02.2011
    Адрес
    Киров
    Сообщений
    10,373

    По умолчанию

    мне вообще не заметно где здесь динамическое выполнение, ладно бы использовалась SysMemAlloc

    второе, а почему не написать fbInst := ADR(fbSSTBY);

    третье
    ПЛК1хх какой нибудь, но только ради CODESYS v3.5
    такое невозможно впринципе, а не из-за Вашей религии
    Bad programmers worry about the code. Good programmers worry about data structures and their relationships

  3. #3

    По умолчанию

    Цитата Сообщение от Валенок Посмотреть сообщение
    Коротко - тута обратного вызова нету. Совсем
    Видимо придется смириться. Пока не надоест до конца будут if then писать

    Цитата Сообщение от capzap Посмотреть сообщение
    мне вообще не заметно где здесь динамическое выполнение, ладно бы использовалась SysMemAlloc

    второе, а почему не написать fbInst := ADR(fbSSTBY);
    Что касается первого, я же написал, что сам код динамического вызова "в процессе машины состояний" не приведен, тут я лишь написал САМ вызов, чтобы показать что не работает как хотелось бы.
    Сама идея проста: в массиве лежат адреса вызова fb (или функций), индекс массива = коду события (или наоборот - как душе угодно), тогда:
    адрес_функции := массив[код_события или код_следующего состояния или структура.массив[код_следующего_состояния] ] , ну вот так упрощенно человеческим языком
    Ну и далее call (*адрес_функции). Очень близко к этому делаю в C. Бесконечный цикл вызова состояний состоит из 4 строк, обработчик состояния - одна функция/процедура. Модифицировать код - проще некуда (я давно перестал писать с нуля, беру старый проект и переименовываю состояния, если, конечно, суть задачи описывается автоматным программированием). Код события обычно "вычисляю-перевожу" из событий клавиатуры, таймеров и тд в некий int
    В C использую enum, как код состояния и индекс массива, и как код события (отдельный enum конечно).

    ко-второму: так именно так и написано, только в начале выполнения (при инициализации) в массив состояний заносятся адреса FB, который потом будут выбираться и вызываться через индексы, то есть
    fbInst := arStates[1].iActionHandlerPTR как раз и получает ADR(...)
    На этапе программирования мы же не знаем адреса FB, поэтому в начале выполнения заполняем массив состояний адресами вызываемых FB.

    Вот смотрю, и думаю, а что интересно будет, если fbInst объявить тоже как DWORD ?... вечером проверю.

    А невозможно ЧТО (про плк1ххх) ? В CS3 есть даже наследование и "полиморфизм" (https://help.codesys.com/api-content...alling-methods).
    Изумительный пример и образец. Такой подход , только в жесткой форме hardcoding'ка, использую давно.
    Попробовал в теории в режиме симуляции - работает. Очень удобно и, главное, элегантно так получается. А значит правильно работать будет.
    Последний раз редактировалось dorofeevms; 11.03.2019 в 10:27.

  4. #4
    Пользователь
    Регистрация
    11.01.2009
    Адрес
    Кострома
    Сообщений
    3,241

    По умолчанию

    1. А что мешает использовать цикл For и выход из него Exit, для обработки больших массивов и структур?

    2. Указатели в программе использовать не нужно.
    Прямой и косвенной адресации вполне достаточно.
    Это одно из нарушений правил стандартов MISRA.

    3. Возможно Вам понравится:
    https://habr.com/ru/company/hexlet/blog/303160/

  5. #5
    Пользователь Аватар для capzap
    Регистрация
    25.02.2011
    Адрес
    Киров
    Сообщений
    10,373

    По умолчанию

    Цитата Сообщение от dorofeevms Посмотреть сообщение
    Видимо придется смириться. Пока не надоест до конца будут if then писать


    Что касается первого, я же написал, что сам код динамического вызова "в процессе машины состояний" не приведен, тут я лишь написал САМ вызов, чтобы показать что не работает как хотелось бы.
    ...
    ко-второму: так именно так и написано, только в начале выполнения (при инициализации) в массив состояний заносятся адреса FB, который потом будут выбираться и вызываться через индексы, то есть
    fbInst := arStates[1].iActionHandlerPTR как раз и получает ADR(...)
    На этапе программирования мы же не знаем адреса FB, поэтому в начале выполнения заполняем массив состояний адресами вызываемых FB.
    так Вы все же динамически выделяете память под нужное ПОУ или все они объявляются изначально в окне объявлений. Если, первое, то тогда с чего решили что начальная точка будет одна и та же каждый цикл как и в первый скан?

    ПЛК1хх поддерживают только КДС2.3
    Bad programmers worry about the code. Good programmers worry about data structures and their relationships

  6. #6

    По умолчанию

    Цитата Сообщение от capzap Посмотреть сообщение
    так Вы все же динамически выделяете память под нужное ПОУ или все они объявляются изначально в окне объявлений. Если, первое, то тогда с чего решили что начальная точка будет одна и та же каждый цикл как и в первый скан?

    ПЛК1хх поддерживают только КДС2.3
    Извиняюсь, capzap, не совсем верно понял первый Ваш вопрос и уточнение, поэтому отреагировал не вполне адекватно.
    Как я делаю (или точнее - хочу сделать):
    - создаю экземпляр FB (fbSNORMAL: fbNORMAL; fbSSTBY: fbSTBY; ).
    - получаю и сохраняю адрес
    - в ходе исполнения беру сохраненный адрес и вызываю метод fbInst^.run();
    (это то я даже проверил и "оно" работает)

    Индекс тоже можно получить на стадии инициализации, куда его только потом запихать не нашел. И этот путь "Закомментировал".
    В чистом Си (при программировании для ПЛК или МК) классы (и ООП в целом) не использую, там я вместо FB использую функции void*... (Объявляю прототип, позже объявляю, сохраняю указатель в массиве и т.д.)
    Тут с использованием функций в интересующем русле вообще тупик, поэтому и зашел со стороны FB, которые "появляются в памяти" и у которых есть действия.

    По поводу ПЛК1хх - промашка моя вышла, Вы правы. Тем более обиднее, и 63й и 73й для задачи подходят.

    И, видимо, придется махнуть рукой и делать как делал. Интересно, а вообще либа для CS2.3 существует, которая делает нечто вроде CB_CallFunctionByIndex в либе CAA_Callback.lib?...
    За ссылку про программистов НАСА спасибо Понравилось, видел раньше.
    Боюсь показаться слишком самоуверенным, но я не очень уважаю программистов НАСА и вообще... пишущих на клавиатурах без ЙЦУКЕН.
    Я там был (в том числе, в ЦУП Хьюстон кстати, но не в отделе разработки ПО), и своими глазами Видел, Кто пишет код для НАСА, "нефтянки" и иже с ними
    Последний раз редактировалось dorofeevms; 11.03.2019 в 12:29.

  7. #7
    Пользователь Аватар для capzap
    Регистрация
    25.02.2011
    Адрес
    Киров
    Сообщений
    10,373

    По умолчанию

    Цитата Сообщение от dorofeevms Посмотреть сообщение
    - в ходе исполнения беру сохраненный адрес и вызываю метод fbInst^.run();
    (это то я даже проверил и "оно" работает)
    вызываете действие, а сами ФБ выполняются или только объявлены
    Bad programmers worry about the code. Good programmers worry about data structures and their relationships

  8. #8

    По умолчанию

    Цитата Сообщение от capzap Посмотреть сообщение
    вызываете действие, а сами ФБ выполняются или только объявлены
    Вызываются только действия. Я полагаю, что компилятор организует и выделяет память для fb при объявлении, для действий где-то же он место выделяет.
    Есть мысли, что если вызвать блок поведение будет другим ? Или сделать что-то вроде "инициализации" блока , вызвав его код (добавить конечно) ?
    Можно попробовать вызывать сам FB (типа (fbInst^)() - на что-то страшное похоже ... ). А вообще интересно очень что получится... эх, cs нет на работе....
    У меня по ходу обсуждения появилась еще затея ... Ради эксперимента вечером покопаюсь еще.

  9. #9

    По умолчанию

    Может кому интересны результаты извращений...
    Проделал все что в голову пришло, не работает. Во вложении экспорт проекта.
    Пока будут думать над обходным решением вопроса. Ибо такой расклад меня пока не устраивает,
    хочу динамический выбор вызываемого fb.....
    Вложения Вложения

  10. #10

    По умолчанию

    2Валенок, понял - нету здесь про void* - это я пытался объяснить алгоритм, который пытаюсь тут реализовать, может проще было фрагмент кода сразу привести... Ясен пень, общего там ничего. Все что вы привели в пример - в export тест-примера, все именно так и работает, как сказали. Спасибо всем откликнувшимся!
    Вопрос исчерпан.

Похожие темы

  1. Ответов: 8
    Последнее сообщение: 16.08.2016, 08:01
  2. Всё-таки как правильно работать с файлами?
    от Андрей Шатохин в разделе ПЛК1хх
    Ответов: 24
    Последнее сообщение: 17.02.2016, 16:39
  3. ЕКОН-134 Подводный камень или решение все-таки есть?
    от EFrol в разделе Сетевые технологии
    Ответов: 1
    Последнее сообщение: 15.10.2015, 10:17
  4. codesys 3.5 График! трасировка либо тренд
    от ChernovR в разделе СПК1хх
    Ответов: 4
    Последнее сообщение: 05.07.2015, 18:17
  5. Переменные в отладке функций.
    от Edik_Ponomarenko в разделе ПЛК1хх
    Ответов: 10
    Последнее сообщение: 30.12.2011, 11:01

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •