Стартовая страница › Форумы › Понять, как работает ПО › Использование формул › Функции (формулы) для Rapid SCADA
- В этой теме 94 ответа, 8 участников, последнее обновление 1 месяц, 1 неделя назад сделано Algomus.
-
АвторСообщения
-
30.06.2017 в 09:50 #6341manjey73Участник
R_Trig появление импульса (лог 1) в канале по фронту (переход от 0 к 1) контролируемого сигнала
Можно отловить модулем автоматического управления, визуализация бывает не показывает.________________________________________________________________
int[] RtrigN = new int[1];
bool[] RtrigM = new bool[1];
double Rtrig (double clk)
{
bool q = Val(CnlNum) > 0;
bool c = clk > 0;
int res = Array.IndexOf(RtrigN, CnlNum);
if (res == -1)
{
res = RtrigN.Length;
Array.Resize(ref RtrigN, res+1);
Array.Resize(ref RtrigM, res+1);
RtrigN[res] = CnlNum;
RtrigM[res] = false;
}q = c && !RtrigM[res];
RtrigM[res] = c;
return Convert.ToDouble(q);
}
_________________________________________________________________Запись в формуле аналогично F_trig
Собственно применение, канал 187 — использование формулы — Rtrig(Val(186)) — 186 — номер контролируемого сигнала. Модулем ловим 187-й канал.
- Этот ответ был изменен 6 лет, 9 месяцев назад от manjey73.
17.07.2017 в 09:56 #6650djbond07УчастникДобрый день, manjey73! Попытался протестировать формулу TON, и вот что обнаружилось:
1. ScadaServer Ругается на длинное тире в этой записи
if (!q) ET = Ticks() — TonST[res];
Изменил на обычный «-«, ошибка пропала. Это же относится к формуле TOF.
А так формула работает, спасибо большое!17.07.2017 в 12:43 #6655manjey73УчастникВозможно длинное тире подставилось при копировании формулы сюда на страницу.
Так как формулы вставляю как придется (в смысле из разных текстовых редакторов)Да, посмотрел, у меня в формуле короткое тире и это ошибка из-за копи паст произошла.
Как писал выше, я стараюсь все входные и выходные переменные преобразовать в/из double внутри формул, тогда легче применять формулы непосредственно в SCADA. Что позволяет в используемой в канале формуле не писать таких вещей как Convert.ToInt64(vvv) и так далее. Уж лучше пусть конвертирование будет внутри формулы, если оно необходимо.
- Этот ответ был изменен 6 лет, 8 месяцев назад от manjey73.
17.07.2017 в 12:49 #6657manjey73УчастникТакая же ошибка есть и в TP
18.07.2017 в 16:34 #6671MikhailМодераторС тире движок форума чудит.
Вы имеете возможность вставлять код в теги <pre></pre>?19.07.2017 в 16:38 #6686manjey73УчастникФормулы для организации энергонезависимых переменных (запись и чтение из файла).
Retainstring pathRet = @"/home/pi/scada/ScadaServer/Config/retain.txt"; Dictionary<int, double> DictRet = new Dictionary<int, double>(); public double Retain (double ret) { DictRet[CnlNum] = ret; return ret; }
Для применения на Windows в случае организации записи файла текущих данных current.dat на виртуальный диск для SSD дисков указать путь в формуле на соответствующий диск. Пример: string pathRet = @»e:\Retain\retain.txt»;
Ну или любая существующая папка, которую вы сделаете для указанного файла.
Наличие папок не проверяется, только наличие файла.Пример применения формулы в базе входных каналов. Retain(Val(150))
В канале управления может стоять SetVal(150, Cmd)В самом младшем входном канале применяется формула Load_Retain
Запись в канале LoadRet()bool initRet = false; double LoadRet() { if (!initRet) { if (System.IO.File.Exists(pathRet)) { string[] RetLoad = System.IO.File.ReadAllLines(pathRet, Encoding.UTF8); for (int i = 0; i < RetLoad.Length; i++) { SetVal(Convert.ToInt32(RetLoad[i].Substring(RetLoad[i].IndexOf("[")+1, RetLoad[i].IndexOf(", ")-(RetLoad[i].IndexOf("[")+1)),10),Convert.ToDouble(RetLoad[i].Substring(RetLoad[i].IndexOf(", ")+2,RetLoad[i].IndexOf("]")-(RetLoad[i].IndexOf(", ")+2)))); } initRet = true; } } return Convert.ToDouble(initRet); }
Выполняется один раз при старте сервера
В самом последнем канале используется формула Save_Retain
Запись в формуле канала SaveRet()double SaveRet() { string[] RetSave = new string[DictRet.Count]; bool eq = false; for (int i = 0; i < DictRet.Count; i++) { RetSave[i] = Convert.ToString(System.Linq.Enumerable.ElementAt(DictRet, i)); } if (!System.IO.File.Exists(pathRet)) { System.IO.File.WriteAllLines(pathRet, RetSave, Encoding.UTF8); return 0; } else { string[] RetOld = System.IO.File.ReadAllLines(pathRet, Encoding.UTF8); eq = System.Linq.Enumerable.SequenceEqual(RetOld, RetSave); if (!eq) { System.IO.File.WriteAllLines(pathRet, RetSave, Encoding.UTF8); } } return Convert.ToDouble(eq); }
Путь на Raspberry указываете свой в первой формуле.
Пример файла retain.txt[450, 23]
[451, 51]
[452, 51,5]
[453, 46]Первая цифра — номер канала, вторая — значение канала.
Запись файла происходит при изменении переменной, указанной как Retain.
Так же можно инициализировать необходимые переменные при запуске сервера.- Этот ответ был изменен 6 лет, 8 месяцев назад от manjey73.
19.07.2017 в 16:52 #6692manjey73УчастникПример для Windows
string pathRet = @"e:\Retain\retain.txt";
Для меняющих переменных в процессе работы, но которым надо сделать сохранение лучше добавить наверное еще таймер и записывать не чаще х минут. Мне так кажется.
Для конфигурационных переменных, которые меняются редко, можно использовать как есть.- Этот ответ был изменен 6 лет, 8 месяцев назад от manjey73.
20.07.2017 в 16:29 #6711MikhailМодераторИдея по поводу хранения формул:
Завести проект на Visual Studio, писать формулы внутри этого проекта и выложить проект на GitHub. Я могу поделиться проектом-заготовкой, которая имеет заглушки для стандартных формул Rapid SCADA.
Смысл идеи — чтобы формулы точно не потерялись. А сюда выкладывать ссылки на них.20.07.2017 в 21:41 #6718manjey73УчастникСкиньте тогда шаблон, можно будет выложить по аналогии с OpenKP.
А то я первоначальные формулы пару раз переписывал, ну и вообще видоизменял.- Этот ответ был изменен 6 лет, 8 месяцев назад от manjey73.
21.07.2017 в 19:41 #6721MikhailМодераторМне нужно подготовить проект для общего использования. Как выложу, напишу в эту тему.
26.07.2017 в 16:33 #6758MikhailМодераторПроект для тестирования формул https://github.com/RapidScada/scada/tree/develop/ScadaServer/FormulaTester
Обратите внимание, что он на данный момент в ветке develop. Примеры в коде говорят сами за себя. Используется функционал для запуска юнит-тестов Visual Studio.22.02.2018 в 23:42 #8554KoliaMorУчастникТаймер, по аналогии TON, TOF. Только на выходе выдает время включения. Отдельный вход сброса TimRst().
// Tim int[] TimNum = new int[1]; long[] TimST = new long[1]; long[] TimAcc = new long[1]; bool[] TimFlag = new bool[1]; public double Tim(double TimIn, int mode) { bool Tim_in = TimIn > 0; int res = Array.IndexOf(TimNum, CnlNum); if (res == -1) { res = TimNum.Length; Array.Resize(ref TimNum, res+1); Array.Resize(ref TimST, res+1); Array.Resize(ref TimAcc, res+1); Array.Resize(ref TimFlag, res+1); TimNum[res] = CnlNum; } if (!Tim_in) { TimFlag[res] = false; TimST[res] = 0L; if((mode&1)==0) TimAcc[res] = 0L; } else { if (!TimFlag[res]) { TimFlag[res] = true; TimST[res] = Ticks(); } else { TimAcc[res] += Ticks() - TimST[res]; TimST[res] = Ticks(); } } if((mode&2)==0) { long Ret = ((mode&4)==0) ? TimAcc[res] : TimAcc[res]/1000; double Div = Math.Pow(10, (mode&0x70)>>4); return Convert.ToDouble(Ret)/Div; } else { DateTime RetTim = new DateTime(2018, 1, 1, 0, 0, 0); return EncodeDate(RetTim.AddMilliseconds(TimAcc[res])); } } public double TimRst(double InRst,int Chanel) { int res = Array.IndexOf(TimNum, Chanel); if (res != -1) { if(InRst!=0)TimAcc[res] = 0L; } return 0; } // Example : // 130, ДТИ, Фл = Tim(Val(101),0x05), Фт = D // 101 - включение // 131, ДТИ, Фл = TimRst(Val(102),130) // 102 - сброс (для (mode&1) > 0) // mode bits: // 0: 0 - без сохранения, 1 - с сохранением, сброс по Фл.TimRst -> при пропадании входа // 1: 0 - вывод double, 1 - вывод TimeData в формате double // 2: 0 - вывод double в мсек, 1 - вывод double в сек // 3: не используется // 4-6: делитель числа double: 0 - на 1, 1 на 10, 2 на 100 и т.д. до 10^7 // 7: не используется // Example : Tim(Val(101),0x30) - вывод времени в мс в формате D.DDD, время не сохраняется
- Этот ответ был изменен 6 лет, 1 месяц назад от KoliaMor.
24.02.2018 в 13:06 #8559MikhailМодераторспасибо
27.05.2021 в 16:30 #19617sakhalin_CatУчастникспасибо
12.02.2024 в 13:11 #31025AlgomusУчастникПрошу помощи. Моих навыков не хватает. Как эту формулу превратить в генератор импульсов в один цикл.
Таймер задержки на включение Tonint[] TonNum = new int[1];
long[] TonST = new long[1];
bool[] TonFlag = new bool[1];
public bool Ton(double TonIn, long TonPT)
{
long ET = 0L;
bool q = Val(CnlNum) > 0;
bool ton_in = TonIn > 0;int res = Array.IndexOf(TonNum, CnlNum);
if (res == -1)
{
res = TonNum.Length;
Array.Resize(ref TonNum, res+1);
Array.Resize(ref TonST, res+1);
Array.Resize(ref TonFlag, res+1);
TonNum[res] = CnlNum;
}if (!ton_in)
{
q = false;
TonFlag[res] = false;
TonST[res] = 0L;
}
else
{
if (!TonFlag[res])
{
TonFlag[res] = true;
TonST[res] = Ticks();
}
else
{
if (!q) ET = Ticks() — TonST[res];
}
if (ET >= TonPT) q = true;
}
return q;
}- Этот ответ был изменен 1 месяц, 2 недели назад от Algomus.
-
АвторСообщения
- Вы должны авторизироваться для ответа в этой теме.