Функции (формулы) для Rapid SCADA

Просмотр 14 сообщений - с 16 по 29 (из 29 всего)
  • Автор
    Сообщения
  • #6341
    manjey73
    Участник

    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-й канал.

    • Этот ответ был изменен 4 года, 3 месяца назад от manjey73.
    #6650
    djbond07
    Участник

    Добрый день, manjey73! Попытался протестировать формулу TON, и вот что обнаружилось:
    1. ScadaServer Ругается на длинное тире в этой записи
    if (!q) ET = Ticks() — TonST[res];
    Изменил на обычный «-«, ошибка пропала. Это же относится к формуле TOF.
    А так формула работает, спасибо большое!

    #6655
    manjey73
    Участник

    Возможно длинное тире подставилось при копировании формулы сюда на страницу.
    Так как формулы вставляю как придется (в смысле из разных текстовых редакторов)

    Да, посмотрел, у меня в формуле короткое тире и это ошибка из-за копи паст произошла.

    Как писал выше, я стараюсь все входные и выходные переменные преобразовать в/из double внутри формул, тогда легче применять формулы непосредственно в SCADA. Что позволяет в используемой в канале формуле не писать таких вещей как Convert.ToInt64(vvv) и так далее. Уж лучше пусть конвертирование будет внутри формулы, если оно необходимо.

    • Этот ответ был изменен 4 года, 3 месяца назад от manjey73.
    #6657
    manjey73
    Участник

    Такая же ошибка есть и в TP

    #6671
    Mikhail
    Модератор

    С тире движок форума чудит.
    Вы имеете возможность вставлять код в теги <pre></pre>?

    #6686
    manjey73
    Участник

    Формулы для организации энергонезависимых переменных (запись и чтение из файла).
    Retain

    string 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.
    Так же можно инициализировать необходимые переменные при запуске сервера.

    • Этот ответ был изменен 4 года, 3 месяца назад от manjey73.
    #6692
    manjey73
    Участник

    Пример для Windows
    string pathRet = @"e:\Retain\retain.txt";

    Для меняющих переменных в процессе работы, но которым надо сделать сохранение лучше добавить наверное еще таймер и записывать не чаще х минут. Мне так кажется.
    Для конфигурационных переменных, которые меняются редко, можно использовать как есть.

    • Этот ответ был изменен 4 года, 3 месяца назад от manjey73.
    #6711
    Mikhail
    Модератор

    Идея по поводу хранения формул:
    Завести проект на Visual Studio, писать формулы внутри этого проекта и выложить проект на GitHub. Я могу поделиться проектом-заготовкой, которая имеет заглушки для стандартных формул Rapid SCADA.
    Смысл идеи — чтобы формулы точно не потерялись. А сюда выкладывать ссылки на них.

    #6718
    manjey73
    Участник

    Скиньте тогда шаблон, можно будет выложить по аналогии с OpenKP.
    А то я первоначальные формулы пару раз переписывал, ну и вообще видоизменял.

    • Этот ответ был изменен 4 года, 3 месяца назад от manjey73.
    #6721
    Mikhail
    Модератор

    Мне нужно подготовить проект для общего использования. Как выложу, напишу в эту тему.

    #6758
    Mikhail
    Модератор

    Проект для тестирования формул https://github.com/RapidScada/scada/tree/develop/ScadaServer/FormulaTester
    Обратите внимание, что он на данный момент в ветке develop. Примеры в коде говорят сами за себя. Используется функционал для запуска юнит-тестов Visual Studio.

    #8554
    KoliaMor
    Участник

    Таймер, по аналогии 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, время не сохраняется 
    
    • Этот ответ был изменен 3 года, 8 месяцев назад от KoliaMor.
    #8559
    Mikhail
    Модератор

    спасибо

    #19617
    sakhalin_Cat
    Участник

    спасибо

Просмотр 14 сообщений - с 16 по 29 (из 29 всего)
  • Вы должны авторизироваться для ответа в этой теме.