Стартовая страница › Форумы › Понять, как работает ПО › Использование формул › Простейшие программы на Rapid Scada
Помечено: Логика, программирование
- В этой теме 68 ответов, 4 участника, последнее обновление 7 лет, 10 месяцев назад сделано djbond07.
-
АвторСообщения
-
23.05.2016 в 17:54 #2144manjey73Участник
Пытаюсь реализовать функцию Toggle.
Принцип работы ее в ПЛК.
Это FB (Функциональный блок), для каждого функционального блока создается свой набор входных, выходных и внутренних переменных
VAR
edge : BOOL; (внутренняя переменная блока, тип bool, начальное значение false, меняет свое значение из кода блока и сохраняет его до следующего вызова блока, то есть при повторном вызове переменная будет равно true, при следующем false и так далее )
END_VARКак реализовать подобную переменную для формулы в Rapid SCADA ?
Так же необходимо создавать подобную переменную для каждого нового вызова функции…Попытки написать в формуле bool1 — в теле bool x = false;
bool2 — в теле bool q = false;
далее функция Toggle — тело функцииpublic bool Toggle(double clk)
{
if (clk>0 && !x)
q = !q;
x = q;
return q;
}И лыжи не едут…. Как объявить переменные, чтобы они сохраняли свои значения от вызова до вызова?
Вход clk в данном случае это извлечение 0 или 1 по битовой маске из канала.
в «Дорасчетном ТС» пытаюсь сделать функцию Toggle23.05.2016 в 18:29 #2146MikhailМодераторнужно объявить вне тела функции:
bool edge = false; bool Toggle() { edge = !edge; return edge; }
23.05.2016 в 23:28 #2147manjey73УчастникОт этого толка мало, использование Toggle для другой переменной приводит к хаосу, так как используются те же самые переменные, вынесенные за функцию.
Пока не могу сформулировать как это решить, но решать точно надо…
Иначе придется дублировать постоянно функции и переменные и еще не запутаться в этом.
Еще и период опроса каким-то образом влияет на качество работы функции.24.05.2016 в 08:46 #2149MikhailМодераторВ таком случае сформулируйте, пожалуйста, задачу более точно. Лучше всего без примеров кода, в обобщенном виде.
24.05.2016 в 09:48 #2151manjey73УчастникДобился, как мне кажется нужного результата, надо потестировать немного подольше.
Вариант 1. (с разбивкой на переменные с использованием разных функций, это позволяет код Toggle из ПЛК)
Канал «1» для отслеживания 0 и 1 используется функция GetBit(Val(x)) где x интересующий нас канал (применил ко времени, можно использовать просто входной канал где изначально 0 и 1)
Канал «2» используется функция Toggle
Канал «3» дополнительная переменная «edge» для контроля перехода от 0 к 1 но не наоборот.
Использование функций в каналах (про GetBit в 1-м канале написал)
Канал 2 — Toggle(Val(2), Val(1), Val(3))
переменная канал2 — изменение значения самого канала
переменная канал1 — вход CLK функции, отслеживание появления 1-цы
переменная канал3 — состояние дополнительной переменной, чтобы не было сброса при переходе от 1 к 0
Сама функция (формула)
bool Toggle (double val, double clk, double edg)
{
bool b = false;
bool q = false;
bool e = false;
if (clk>0) b = true;
if (val>0) q = true;
if (edg>0) e = true;
if (b && !e) q = !q;
return q;
}
Канал 3 — GetClk(Val(1))
Отслеживает состояние канала 1 и повторяет его но после выполнения функции канала 2
Сама функция (формула)
bool GetClk(double val)
{
bool q = false;
if (val>0) q = true;
return q;
}Данный вариант универсален, так как не требует каких-то манипуляций с повторным созданием функций и т.д. Вариант 2 даже приводить не буду, так как он не очень разумный пока. Оригинал функции из ПЛК
IF rst THEN // эту часть удалил, это вход ресет, пока он 0 то изменении входа clk
q := 0;// игнорируются, но можно и реализовать.
ELSIF clk AND NOT edge THEN
Q := NOT Q;
END_IF; // выполнение Toggle в реализации примера Вариант1 ограничено телом цикла
edge := clk; // после выполнения цикла выполняется присвоение дополнительной переменной отдельной формулойТеперь о думах в плане реализации на примере ПЛК, не знаю, возможно ли это реализовать в рамках Rapid SCADA.
На примерах. Даже если вынести переменную edge из вашего примера за тело функции она становится глобальной, такие переменные тоже нужны и тоже важны, но в рамках вызова функции повторно для другого канала будет так же использована она.
bool edge = false;
bool Toggle()
{
edge = !edge;
return edge;
}В рамках ПЛК переменная, указанная внутри кода функционального блока создается для каждого блока своя и позволяет сохранять свое значение от вызова до вызова.
Это реализовано объявлением переменной c указанием ее типа
VAR
edge : BOOL;
END_VAR
То есть при вызове Toggle1 (например для налала 2) будет создана переменная Toggle1_edge. При вызове той же функции Toggle но для канала 5 (например нам еще нужна такая функция) ПЛК создаст переменную Toggle5_edge
И каждый функциональный блок работает строго со своей переменной не записывая значение в чужую.Если переменную занести внутрь тела функции, то в рамках ПЛК это строго внутренняя переменная, которая не сохраняется а служит для внутренних расчетов функции.
bool Toggle()
{
bool edge = false;
edge = !edge;
return edge;
}Теперь вопрос о входных переменных функций — они всегда double, но даже если мы знаем, что в значении у нас будет либо 0 либо 1 C# не позволяет приводить их к bool простым способом — это не лечится ?
А то, если честно такой вариант мне аж глаза режет
bool q = false;
if (val>0) q = true;
Может можно как-то красивее делать ?Вот собственно хотелось бы иметь возможность обозначать переменные как
1 — Глобальные (с возможностью сохранения в энергонезависимую память в том числе, например в файл, если применительно к scada)
2 — только для вызываемой функции но с возможностью ее сохранения от вызова к вызову (пример объявления для ПЛК VAR переменная VAR_END
3 — внутренние впрочем и так реализованы и есть.Вот как-то так, потому что есть функции хорошие и полезные, которые можно было выдернуть из ПЛК разных производителей, чего только набор oscat.de для CoDeSys стоит, но вот с такими возможностями по переменным сложновато будет это реализовать.
24.05.2016 в 10:15 #2154manjey73УчастникЕще бы предложил добавить в базу данных дополнительную таблицу «Переменные» для редактирования ее в Администраторе.
По аналогии с ПЛК Siemens. так как раз есть ячейки памяти для отслеживания фротнов.
Только можно пойти чуть дальше, так как переменным можно было бы создавать разные типы данных.
Сейчас это можно сделать даже в таблице «Формулы» только получается не очень эстетично.Единственный момент, как реализовать обращение к данным переменным из самих формул.
Если мы запишем в таблице формул
Наименование — clk1
Исходный код — bool edge = false;
То это работает, но функция будет обращаться непосредственно к переменной edge а не к ее символьному представлению clk1 и насколько понимаю, данная переменная не сможет быть как входной так и выходной.
Ведь запись в C#
Функция (double val, int nvd, bool edg) предполагает только входные переменные ?24.05.2016 в 15:55 #2155MikhailМодератор1 — Глобальные (с возможностью сохранения в энергонезависимую память в том числе, например в файл, если применительно к scada)
Сейчас в файл сохраняются текущие значения каналов. Если переменную при первом обращении считать из канала, то получится реализация сохранения её значения при перезапуске сервера. Правда не очень удобно это делать самому, желательно иметь готовые средства.
2 — только для вызываемой функции но с возможностью ее сохранения от вызова к вызову (пример объявления для ПЛК VAR переменная VAR_END
Чтобы работать с разными каналами, для хранения каких-либо значений нужно заводить массивы или справочники Dictionary. Посмотрите, в базе по умолчанию уже есть примеры.
Красиво так:
bool q = val > 0;
24.05.2016 в 16:00 #2156MikhailМодераторСейчас поле Наименование в таблице формул имеет только информационное значение, на работу не влияет. Методы C# могут иметь выходные значения. Но функции, которые используются для формул входных каналов, должны только возвращать double и больше ничего. А внутри самих формул можно использовать любые функции, в том числе с выходными аргументами.
Да, работу с формулами хорошо бы модернизировать для удобства и добавить готовых средств для типовых задач. Лучше всего это делать в рамках конкретного внедрения.
25.05.2016 в 10:00 #2157manjey73УчастникВот если при модернизации работы с формулами получится сделать так, что переменные, указанные в скрипте формулы за пределами функции будут создаваться для каждого экземпляра вызова (привязка есть по номеру канала) то это снимет массу проблем и упростит задачу написания формул.
Запись Формулы:
bool edge = false; // вызвав из канала 5 эту формулу функция использует переменную, привязанную к каналу 5.
bool Toggle()
{
edge = !edge;
return edge;
}Если эту же функцию вызывать из канала 7, у нее своя такая же переменная и т.д.
Глобальные переменные в принципе создаются просто.
Новая формула Имя — Global
тело с перечислением переменных
bool x = false;
int y = 0;
int z = 125;
и так далее.
Даже можно писать функции с применением глобальных переменных.25.05.2016 в 13:32 #2160MikhailМодераторВ принципе можно реализовать специальные формулы, которые позволят привязывать переменные к входному каналу.
Использоваться они будут примерно так:int x = (int)GetVar(101, "x"); // 101 - номер канала SetVar(101, "x", 1.23); // 1.23 - устанавливаемое значение
25.05.2016 в 16:49 #2164manjey73УчастникВ смысле уже сейчас так будет работать или в будущем ?
25.05.2016 в 18:54 #2166MikhailМодераторИмею ввиду, что можно в таблицу формул уже сейчас добавить соответствующие реализации GetVar и SetVar и можно использовать.
Если есть конкретный проект, в который нужно внедрить, то можно за небольшую плату написать реализацию.25.05.2016 в 23:01 #2167manjey73УчастникКак такового проекта именно под подобные вещи нет, но если будет механизм локализовывать переменные формул(функций) для каждого отдельно взятого экземпляра при запуске, то это позволит проще писать формулы.
Ну и мог бы надергать разные из ПЛК-шных.27.05.2016 в 09:49 #2172MikhailМодераторDictionary<int, Dictionary<string, object>> SuperDict = new Dictionary<int, Dictionary<string, object>>(); object GetVar(int cnlNum, string varName, object defVal = null) { Dictionary<string, object> dict; if (SuperDict.TryGetValue(cnlNum, out dict)) { obj val; return dict.TryGetValue(varName, out val) ? val : defVal; } else { return defVal; } } void SetVar(int cnlNum, string varName, object val) { Dictionary<string, object> dict; if (!SuperDict.TryGetValue(cnlNum, out dict)) { dict = new Dictionary<string, object>(); SuperDict.Add(cnlNum, dict); } dict[varName] = val; }
Как-то так. Я не проверял, если есть ошибки, поправьте, пожалуйста.
27.05.2016 в 10:54 #2174manjey73УчастникСтрока 396, колонка 5: error CS0246: Не удалось найти имя типа или пространства имен «obj» (пропущена директива using или ссылка на сборку?)
Строка 397, колонка 12: error CS1502: Наиболее подходящий перегруженный метод для «System.Collections.Generic.Dictionary<string,object>.TryGetValue(string, out object)» имеет несколько недопустимых аргументов
Строка 397, колонка 42: error CS1503: Аргумент «2»: преобразование типа из «out obj» в «out object» невозможно
Для ознакомления с исходным кодом см. файл C:\SCADA\ScadaServer\Log\CalcEngine.cs
2016-05-27 10:54:40 <KRASNODAR-96><система><ERR> Нормальная работа программы невозможна.Добавил выше указанный код в формулу, получил ошибку.
Для меня сложновато все это… -
АвторСообщения
- Вы должны авторизироваться для ответа в этой теме.