Простейшие программы на Rapid Scada

Просмотр 15 сообщений - с 46 по 60 (из 69 всего)
  • Автор
    Сообщения
  • #2144
    manjey73
    Участник

    Пытаюсь реализовать функцию 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 по битовой маске из канала.
    в «Дорасчетном ТС» пытаюсь сделать функцию Toggle

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

    нужно объявить вне тела функции:

    bool edge = false;
    
    bool Toggle()
    {
      edge = !edge;
      return edge;
    }
    
    #2147
    manjey73
    Участник

    От этого толка мало, использование Toggle для другой переменной приводит к хаосу, так как используются те же самые переменные, вынесенные за функцию.

    Пока не могу сформулировать как это решить, но решать точно надо…
    Иначе придется дублировать постоянно функции и переменные и еще не запутаться в этом.
    Еще и период опроса каким-то образом влияет на качество работы функции.

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

    В таком случае сформулируйте, пожалуйста, задачу более точно. Лучше всего без примеров кода, в обобщенном виде.

    #2151
    manjey73
    Участник

    Добился, как мне кажется нужного результата, надо потестировать немного подольше.
    Вариант 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 стоит, но вот с такими возможностями по переменным сложновато будет это реализовать.

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

    Еще бы предложил добавить в базу данных дополнительную таблицу «Переменные» для редактирования ее в Администраторе.
    По аналогии с ПЛК Siemens. так как раз есть ячейки памяти для отслеживания фротнов.
    Только можно пойти чуть дальше, так как переменным можно было бы создавать разные типы данных.
    Сейчас это можно сделать даже в таблице «Формулы» только получается не очень эстетично.

    Единственный момент, как реализовать обращение к данным переменным из самих формул.
    Если мы запишем в таблице формул
    Наименование — clk1
    Исходный код — bool edge = false;
    То это работает, но функция будет обращаться непосредственно к переменной edge а не к ее символьному представлению clk1 и насколько понимаю, данная переменная не сможет быть как входной так и выходной.
    Ведь запись в C#
    Функция (double val, int nvd, bool edg) предполагает только входные переменные ?

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

    1 — Глобальные (с возможностью сохранения в энергонезависимую память в том числе, например в файл, если применительно к scada)

    Сейчас в файл сохраняются текущие значения каналов. Если переменную при первом обращении считать из канала, то получится реализация сохранения её значения при перезапуске сервера. Правда не очень удобно это делать самому, желательно иметь готовые средства.

    2 — только для вызываемой функции но с возможностью ее сохранения от вызова к вызову (пример объявления для ПЛК VAR переменная VAR_END

    Чтобы работать с разными каналами, для хранения каких-либо значений нужно заводить массивы или справочники Dictionary. Посмотрите, в базе по умолчанию уже есть примеры.

    Красиво так:

    bool q = val > 0;
    
    #2156
    Mikhail
    Модератор

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

    Да, работу с формулами хорошо бы модернизировать для удобства и добавить готовых средств для типовых задач. Лучше всего это делать в рамках конкретного внедрения.

    #2157
    manjey73
    Участник

    Вот если при модернизации работы с формулами получится сделать так, что переменные, указанные в скрипте формулы за пределами функции будут создаваться для каждого экземпляра вызова (привязка есть по номеру канала) то это снимет массу проблем и упростит задачу написания формул.

    Запись Формулы:
    bool edge = false; // вызвав из канала 5 эту формулу функция использует переменную, привязанную к каналу 5.
    bool Toggle()
    {
    edge = !edge;
    return edge;
    }

    Если эту же функцию вызывать из канала 7, у нее своя такая же переменная и т.д.

    Глобальные переменные в принципе создаются просто.
    Новая формула Имя — Global
    тело с перечислением переменных
    bool x = false;
    int y = 0;
    int z = 125;
    и так далее.
    Даже можно писать функции с применением глобальных переменных.

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

    В принципе можно реализовать специальные формулы, которые позволят привязывать переменные к входному каналу.
    Использоваться они будут примерно так:

    int x = (int)GetVar(101, "x"); // 101 - номер канала
    SetVar(101, "x", 1.23); // 1.23 - устанавливаемое значение
    
    #2164
    manjey73
    Участник

    В смысле уже сейчас так будет работать или в будущем ?

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

    Имею ввиду, что можно в таблицу формул уже сейчас добавить соответствующие реализации GetVar и SetVar и можно использовать.
    Если есть конкретный проект, в который нужно внедрить, то можно за небольшую плату написать реализацию.

    #2167
    manjey73
    Участник

    Как такового проекта именно под подобные вещи нет, но если будет механизм локализовывать переменные формул(функций) для каждого отдельно взятого экземпляра при запуске, то это позволит проще писать формулы.
    Ну и мог бы надергать разные из ПЛК-шных.

    #2172
    Mikhail
    Модератор
    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;
    }
    

    Как-то так. Я не проверял, если есть ошибки, поправьте, пожалуйста.

    #2174
    manjey73
    Участник

    Строка 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> Нормальная работа программы невозможна.

    Добавил выше указанный код в формулу, получил ошибку.
    Для меня сложновато все это…

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