This is topic Функции-блоки: как это работает in forum Языки программирования в TRACE MODE 6 / Algorithm Programming Languages at Форум TRACE MODE: техническая поддержка.
Здравствуйте, уважаемая техподдержка! Я понимаю, что Техно-языки ТМ6 являются глубоким переосмыслением языков МЭК в сторону сближения с С++. Учитывая, что идейным вдохновителем МЭК-языков можно считать Pasсal, получается весьма занятная сущность. Раньше я программировал на Техно-языках в духе С, и все было нормально. Теперь в образовательных целях я вынужден программировать в духе МЭК, с чем возник ряд вопросов, где ответ вроде «так лучше не работать» меня, как понимаете, не утешит.
Далее привожу ряд тезисов (для подтверждения или опровержения), а также вопросов.
Функция-блок это ни разу не функциональный блок в определениях МЭК (далее – ФБ), но скорее void-функция Си, которая просто может быть помещена на диаграмму Техно-FBD (впрочем, как и функция): при этом аргументы IN будут ножками слева, OUT — справа, IN/OUT — слева и справа (для удобства и во исполнение порядка вычисления). Однако внутренние переменные функции-блока (в IDE названы Вами просто переменными) не являются ни локальными переменными ФБ МЭК, ни static-переменными функции Си. Локальные переменные в ФБ являются частью экземпляра ФБ и доступны изнутри ФБ, а также иногда совне (но только для чтения) и сохраняют свое значение между вызовами программы. Static-переменные функции Си сохраняют свое значение между вызовами функции и доступны только изнутри нее. Переменные же функции-блока (что поначалу воспринимается большим откровением) являются глобальными(!), причем это написано в РП. Их количество в каждой функции-блоке не может превышать четырех (или это только для библиотечных?) Но главным свойством глобальных переменных является их видимость во всех сущностях программы, и действительно: можно обращаться к «внутренним» переменным функции-блока извне: записывать и читать их. К сожалению, напрямую обратиться к ним нельзя, подобно как можно обращаться к локальным переменным ФБ в МЭК (FB_1.var1 не прокатит), — нужно вызвать функцию-блок и только после этого можно будет обратиться через точку. Вопрос 1: зачем мне дозволено делать это, и как это следует использовать, ведь при каждом вызове функция-блок может менять свое состояние?
**************************************************** ФУНКЦИЯ_БЛОК(arg1, arg2, arg3).var1 = 5; VAR_PRG = ФУНКЦИЯ_БЛОК(arg1, arg2, arg3).var1; ***************************************************** FUNCTION_BLOCK ФУНКЦИЯ_БЛОК VAR_INPUT ARG1 : REAL; END_VAR VAR_OUTPUT ARG2 : REAL; END_VAR VAR_INOUT ARG3 : REAL; END_VAR VAR var1 : REAL; END_VAR ****************************************************** PROGRAM VAR_INPUT ARG1 : REAL; END_VAR VAR_INPUT ARG2 : REAL; END_VAR VAR_INPUT ARG3 : REAL; END_VAR VAR var_prg : SINT; END_VAR VAR var1 : INT; END_VAR ******************************************************* После этого переменная VAR_PRG имеет значение 5, несмотря на то, что тип var1 является бОльшим и неявное преобразование работать не должно.
Такое поведение замечеается в отладчике, когда:
1) в программе, вызывающей функцию-блок, есть локальная переменная, одноименная переменной функции блока (тип может не совпадать), иначе программа не компилируется, ругаясь на var1;
2) в функции-блоке не меняется значение соответствующей локальной переменной при обоих вызовах, иначе значение VAR_PRG тупо обнулится, что никак не зависит от значений по умолчанию переменных var1.
Не факт, что данные ограничения являются исключительными: полноценных тестов не проводилось.
При этом второй вызов функции-блока никак не обнаруживает изменения значения var1, однако VAR_PRG все равно каким-то магическим образом принимает то значение, которое мы помещаем прежде в переменную функции-блока, при этом одноименная локальная переменная программы, надо заметить, никак не реагирует.
Вопрос 2: переменные пользовательских функций-блоков имеют возможность сохранять значение между вызовами функции-блока, между вызовами программы? Сколько их может быть и как их задать, когда у меня значение переменных функции-блока всякий раз берется по умолчанию при ее вызове в отладчике, тогда как внутренние переменные библиотечных функций-блоков сохраняют свои значения?
Откровения о глобальности переменных функции-блока отчасти обуславливает некоторые вещи, что до того слыли не меньшими откровениями. Так, было обнаружено, что никаких экземпляров функций-блоков подобно экземплярам ФБ нет и быть не может, в чем сходство с void-функцией Си. Вообще, ФБ в МЭК — это упрощение и адаптация идеи классов в ООП под задачи АСУ ТП. В Техно-языках ТМ6 нам для этих целей даны аналоги структур из С++ и не стоит пытаться слепить функциональный блок в духе МЭК из функции-блока. Если Вы привыкли к идеи ФБ из МЭК, то, по-видимому, ничего не отсается, как сделать структуру с единственным методом с аргументами и вызывать его, дублируя аргументы при вызове в переменные-члены (кроме аргов INOUT), чтобы к ним потом можно было обращаться независимо, но все же лучше так не делать. Тем более что вызвать экземпляр структуры в FBD-диаграмме и представить ее в виде привычного блочка с ножками вряд ли удастся.
Таким образом, функция-блок глобальна и единственна во всей программе. Я пытался вызывать экземпляры функции-блока в Техно-FBD, наивно полагая, что они создаются самим фактом существования отдельного блочка на диаграмме, пока не понял, что на деле вызывается один и тот же объект. При этом экземпляры библиотечных функций-блоков (по крайней мере, на Техно-FBD-диаграмме) действительно создаются отдельные и их внутренние статические переменные независимы для отдельных экземпляров: тогда все же что такое библиотечный FBD-блок? Вы их даже по названию различаете: библиотечные FBD-блок и пользовательская функция-блок. Т.е. FBD-блок это больше про МЭК, а функция-блок про Си? (Вопрос 3.)
Итак, в РП написано:
Следующие функции (функции-блоки) могут быть вызваны в основной программе только однократно: содержащие глобальные переменные программы; содержащие FBD-блоки с внутренними переменными (см. Редактирование FBD-программ ). Другие функции могут вызываться в основной программе многократно.
Вопрос 4: как это утверждение РП понимать? Как функция может содержать глобальные переменные? Использовать — понимаю, но содержать? В РП сказано: В частности, глобальными являются переменные FBD- и LD-блоков; а переменные функций-блоков, они являются глобальными? Чем внутренние переменные FBD-блоков тогда отличаются от глобальных? Я в корне запутался. И что значит не могут быть вызваны? Я вызывал пользовательскую функцию-блок, состоящую из библиотечных FBD-блоков, в одной проге на Техно-FBD 3 раза. Смысл в том, что внутренние переменные соответствующих FBD-блоков в памяти занимали одно и то же место — это были одни и те же объекты в рамках различных вызовов моей функции-блока. Не могут вызываться и не следует — это разные вещи. Не могут — прога должна не компилиться, а она компилится.
Потом, с переменными-аргументами не все понятно. В ФБ МЭК они являются атрибутами ФБ и доступны вне его вызовов. INPUT — для чтения и записи, OUTPUT — только для чтения. Иногда также возможно читать локальные переменные ФБ. INOUTPUT при этом передается по ссылке и вообще не являются частью ФБ (можно передать адрес переменной INOUTPUT только при вызове ФБ). В функции-блоке у нас OUT и INOUT передаются по ссылке, и отличие их только на картинке FBD-диаграммы, и то формальное (ножки). Т.е. они частью функции-блока не являются и не имеют смысла вне ее вызова. Однако и об IN-аргументах, судя по всему имеет смысл говорить только в момент вызова, что указывает на сходство с параметром функции, передаваемом по значению, в идеологии Си. Подтвердите или опровергните это, пожалуйста (вопрос 5).
Премного благодарен!
Posted by АдАстра. Техподдержка (Участник № / Member № 4) on :
Здравствуйте, Alexander_!
По использованию поддерживаемых языков программирования МЭК мы можем с Вами обсудить вопросы работы и реализации. Остальные вопросы выходят за рамки.
1. При использовании FBD-блоков штатным является использование входов и выходов используемых блоков.
2. При использовании любых FBD-блоков (включая Пользовательские) значения переменных (входы/выходы и внутренние) являются глобальными, т.е. сохраняют свое значение между вызовами программы. По умолчанию, каждая переменная FBD-блока может сохранить одно свое значение между вызовами программы.
3. Библиотечный FBD-блок - это готовый FBD-блок, который можно сразу использовать. Пользовательский FBD-блок - это FBD-блок, содержимое которого разрабатывает Автор проекта, его необходимо разработать самостоятельно. Содержимое может быть разработано любыми поддерживаемыми языками, включая ST.
4. Уточните раздел Справочной Системы, в котором Вы прочитали данное описание.
5. Все довольно просто. Так как вопрос о FBD, то IN-аргументы (входа) являются началом программы и их можно записать. OUT-аргументы являются выходом программы и их можно читать. INOUT-аргументы являются и входными и выходными одновременно.
Между входными аргументами и выходными располагаются FBD-блоки со связями между собой.
Что бы было все максимально понятно, рекомендую ознакомиться в Справочной Системе с разделом Быстрый Старт - Часть вторая - Написание программ.
Posted by Alexander_ (Участник № / Member № 7778) on :
Добрый день! Мне и интересны прежде всего вопросы реализации. Можете объяснить это поведение:
Условия компиляции и работы описаны выше. При этом если var1 является аргументом функции-блока, то прога вылетает с ошибкой "Нарушение доступа".
Сегодня обнаружил, что если функция-блок на диаграмме Техно-FBD не выполняется (RUN=1, да-да: 1), то выходы блочка все равно передают значения в переменные, что к ним привязаны,(!) т.е. аргументы OUT являются частью блока и сохраняют свои предыдущие значения, но получить их можно только в Техно-FBD -- так?
Как мне обратиться к переменным или аргументам функции-блока на ST, не вызывая ее?
Как мне создать несколько экземпляров пользовательской функции-блока: один код -- несколько воплощений?
2. У меня пользовательская функция-блок, в ней внутренняя переменная -- счетчик вызовов: почему между вызовами функции-блока на ST, равно как и между вызовами программы при циклической отладке, (в отладчике) значение не сохраняется? Можете прислать пример функции-блока с внутренним счетчиком вызовов на ST?
4. Пункт РП "Операторы определения переменных" с. 41. и "Вызов ф. и ф.-б." с. 45 том 2.
Все еще надеюсь на внятное разъяснение. Если есть полноценная документация на Техно-языки, то мне больше ничего и не надо.