Добрый день всем!
Нравятся мне не просто рабочие программы и алгоритмы, но еще и Красивые.. И задумался я над реализацией красивой и лаконичной 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 параметров.

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