Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Не получили письмо с кодом активации?

Официальный форум Simple-Scada.

Просмотр сообщений

В этом разделе можно просмотреть все сообщения, сделанные этим пользователем.


Сообщения - pan2000

Страницы: 1 ... 7 8 [9] 10 11 ... 16
121
   Здравствуйте.

  Для "изменения" длины одиночной линии можно использовать экранирование части линии объектом с изменяемыми размерами (текст, ...) или динамическим изображением (уровень). В этом случае Ваша задача сводится к вычислениям и изменением свойств объектов Text1, Line1 и экранирующего объекта.
Пример во вложении.

122
   Здравствуйте.
В такой реализации аварийное сообщение о потере связи генерируется постоянно (в течении всего времени отсутствия связи).
Подскажите как реализовать однократное появление сообщения при потере связи и подобное сообщение когда связь восстановилась?
В качестве флажка однократного действия можно использовать свойство ping_plc.color
Код: (delphi)
begin
  if pingdata.Result then begin
    if ping_plc.color <> clgreen then <действия при восстановлении связи>;
    ping_plc.color := clgreen;
  end
  else begin
    if ping_plc.color <> clred then AddMessage (Now, mkAlarm, 'Отсутствие связи с контроллером', true, false);
    ping_plc.color := clred;
  end;
end.

123
Ваши вопросы / Re: конвертирование из BCD
« : 12 Сентября 2021, 09:45:28 »
   Здравствуйте.

  Конвертировать  данные из BCD-формата в десятичный можно последовательным суммированием BCD-цифр умноженных на десять в степени индекс разряда (справа налево, начиная с 0).
Большую сложность представляет получение BCD-цифр из исходных данных - нужно знать их формат представления. Это может быть последовательность байтов с одной цифрой в младший тетраде, двумя цифрами в каждом байте, по 4 цифры в одном 16-разрядном слове, по 8 в 32-разрядном.
Или сборке по отдельным BCD-цифрам произвольных переменных, например скрипт "Изменились переменные, перечисленные в скрипте":
Код: (delphi)
function Digit(const aVariable:TM_Variable; const BCDindex:integer; const DiGindex:integer): int64;
var i: int64;
    j: integer;
begin
  i := (aVariable.AsInt64 shr (BCDindex * 4)) and $F;   // извлечь BCD-цифру
  for j := 1 to DIGindex do i := i * 10;                // ее значение в соответствующим разряде десятичного числа
  Result := i;
end;

var ii: int64;
begin
// поразрядная сборка по набору переменных
  ii := Digit(QuadrupleDigit_1, 2, 0) +    // слово - 4 цифры
        Digit(DoubleDigit_2,    1, 1) +    // упакованный байт - 2цифры
        Digit(DoubleDigit_2,    0, 2) +    // упакованный байт - 2цифры
        Digit(SingleDigit_4,    0, 3) +    // байт - цифра в младшей тетраде
        Digit(SingleDigit_0,    0, 4) +    // байт - цифра в младшей тетраде
        Digit(EightDigit,       7, 5);     // двойное слово - 8 цифр

  Field21.Text := IntToStr(ii);       // вывод результата
end.

Во вложении пример для всех перечисленных способов представления BCD-числа.


124
   Здравствуйте.
Мы же и так можем узнать дату файла, зная имя.
Мой пример предполагает однократный запрос состояния службы с минимальным временем отклика, анализ наличия обновленного файла начинается с 0 секунды относительно запроса.

В Вашем случае это автомат состояний с периодом в 60 тактов, запрос выполняется в 0, а анализ во 2 тактах. Сам процесс получения данных асинхронный, и можно ожидать что он завершится за 2 секунды. Но если информация о состоянии файла обновится через 3 секунды и более, то возникнет ошибка.
Поскольку процесс получения состояния службы периодический и не связан с действиями оператора (как в примере), то анализ файла можно смело перенести до 55 такта (секунды).

Вывод названия параметра на разных языках ("Состояние" <-> "STATE") это, предположительно, отголосок локализации ОС. Я бы проверял строку на наличие подстрок ": 4  RUNNING" и ": 1  STOPPED".

И ещё одна особенность автомата при запуске проекта, с вероятностью 1/30 в течение первой минуты будет выдано сообщение "Ошибка получения статуса службы опроса модемов".
Для устранения этого можно использовать условие (начальное значение переменной - 0) :
Код: (delphi)
(if SecondOf(Now) = 2) and (StartTime.AsFloat > 1) then begin    //считываем файл для анализа через 2 секунды после создания и только после запуска скрипта

125
Вопрос: а зачем сканировать директорию на наличие файла, если мы точно знаем как он называется и можем открыть его напрямую?
Файл результата открывается как перезаписываемый - т.е. новый замещает старый. Узнать что файл обновился можно по изменению даты записи файла, она должна быть больше чем дата начала выполнения скрипта.

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

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

127
Ваши вопросы / Re: Межфазный уровень
« : 27 Августа 2021, 05:52:48 »
   Здравствуйте.
 
  Двухцветный уровень состоит из пары уровней с прозрачным фоном.
  Варианты двухцветного уровня для двух переменных А и В с одинаковым шкалами:
1. "А+В" - нижняя цветовая полоса в интервале [0, A], верхняя - (А, А+В]. Должно выполняться условие А+В <= максимум шкалы;
2. "A-B" - нижняя - [0, A-B], верхняя - (A-B, A]. Должно выполняться условие: В < A;
3. "А и В" - нижние границы = 0, верхние - A и В. На переднем плане переменная с меньшим значением. 

Примеры шкал во вложении.

128
   Здравствуйте.

Сброс таймера vrTimer в 0 по трем кнопкам Btn1, Btn2, Btn3 с управлением подсветкой кнопок К1, К2, К3.
Предполагается, что кнопки реагируют на нажатие только при завершению интервала контроля, т.е. когда кнопки мигают. Нажатия кнопок запоминаются в переменной состояние кнопок vrBtnState. Для разрешения реагирования на кнопки в любой момент времени достаточно удалить условие проверки состояния таймера: (vrTimer.AsInt < (Tmax - 2)).
После нажатия всех трех кнопок может выполняться проверка на правильный порядок нажатия 1-2-3 или 3-2-1.
Скрипт "Изменились переменные Btn1, Btn2, Btn3":
Код: (delphi)
const Tmax = 120 * 60;       // константа используется в двух скриптах - её можно перенести в глобальный модуль
begin
  if vrTimer.AsInt < (Tmax - 2) then exit;  // интервал контроля ещё не истек (рано кнопки нажимать). Для исключения контроля удалить оператор
  if not Variable.AsBool then exit;         // переход в false - игнорировать
  if Variable = btn1 then begin K1.Value := 29; vrBtnState.Value := SetBit(vrBtnState.AsInt, 0, true); end;    // запомнить нажатые кнопки
  if Variable = btn2 then begin K2.Value := 29; vrBtnState.Value := SetBit(vrBtnState.AsInt, 1, true); end;
  if Variable = btn3 then begin K3.Value := 29; vrBtnState.Value := SetBit(vrBtnState.AsInt, 2, true); end;

// СОСТОЯНИЕ КНОПОК - другой оператор должен быть закомментирован или удален
//  этот оператор контролирует порядок нажатия кнопок 1-2-3 или 3-2-1
  case vrBtnState.AsInt of
  2, 5: vrBtnState.Value := SetBit(vrBtnState.AsInt, 3, true);   // установить флаг ошибки порядка нажатия кнопок
     7: vrTimer.Value := 0;              // сброс таймера
    15: begin
          vrTimer.Value := 0;            // сброс таймера
          AddMessage(Now, mkWarning, 'Он шел на Одессу, а вышел к Херсону...(матрос-партизан Железняк)', true, false);
        end;
  end;
//   этот оператор контролирует факт нажатия всех кнопок
//  if vrBtnState.AsInt = 7 then vrTimer.Value := 0;              // сброс таймера

end.

Скрипт "Прошла секунда":
Код: (delphi)
const Tmax = 120 * 60;       // константа используется в двух скриптах - её можно перенести в глобальный модуль
begin
  vrTimer.inc(1, 0, Tmax);

  case vrTimer.AsInt of
  1: vrBtnState.Value := 0;       // сброс регистра нажатых кнопок
  2, Tmax - 1: begin
                 if not Getbit(vrBtnState.AsInt, 0) then K1.Value := 31;      // включать индикатор не нажатой кнопки
                 if not Getbit(vrBtnState.AsInt, 1) then K2.Value := 31;
                 if not Getbit(vrBtnState.AsInt, 2) then K3.Value := 31;
               end;
  Tmax: begin
          K1.Value := 29;     // выключить индикаторы кнопок
          K2.Value := 29;
          K3.Value := 29;
          vrTimer.inc( -2);
        end;
  end;
end.


Возможно ещё нужен контроль времени нажатия всех трёх кнопок.

Пример во вложении.

129
   Здравствуйте.

Сначала нужно получить начальные значения координат объекта из подстановки шаблона. Например, в шаблоне определен объект Field1 с основной переменной %Xcoord%, значение которой определяет смещение этого объекта относительно начального положения при запуске проекта.
Скрипт по событию "Инициализация шаблонного объекта":
Код: (delphi)
var Obj: TM_Object;
begin
  Obj := GetTemplateObject('Field1');
  if Obj = nil then exit;
  obj.Variable.LowAlarm := Obj.X;     // Х-координата исходного положения объекта хранится в нижней аварийной границе основной переменной
//  Obj.VariableEx.Value :=  Obj.X;    // при желании или необходимости для хранения можно использовать доп.переменную объекта
end.

Собственно перемещение объекта. Скрипт "Пользователь ввел значение в поле":
Код: (delphi)
begin
  with Sender as TM_Object do
    X := AsSingle + Variable.LowAlarm;    // начальная координата хранится в одном из свойств переменной
//    X := AsSingle + VariableEx.Value;    // начальная координата хранится в отдельной (дополнительной) переменной
end.

Перемещение объекта не ограничивается шаблоном или экраном, и при больших (абсолютных) перемещениях выходит за пределы экрана.
Пример во вложении.

130
    Здравствуйте.
... но разметить барграф нужно по сути только один раз ...
...  единственный способ применить этот скрипт к барграфу, который я нашел - это событие onDataChange ...
1. Если у барграфа не используется дополнительная переменная, то достаточно назначить скрипт разметки на событие по изменению доп. переменной, в качестве которой используется одна новая переменная. При ее инициализации (запуск проекта) для каждого барграфа будет однократно выполнен скрипт разметки.

2. Можно назначить скрипт разметки на событие по двойному клику мыши и однократно вызывать скрипт для всех барграфов при запуске проекта. Скрипт "Полностью запущен" (барграфы задаются перечислением или в цикле):
Код: (delphi)
var i: integer;
begin
  Bargraph1.OnDblClickEvent;
  Bargraph2.OnDblClickEvent;
  . . .
  Bargraph100500.OnDblClickEvent;
// или
  for i := 1 to 100500 do GetObjectByName('Bargraph' + IntToStr(i)).OnDblClickEvent;  // это только пример
end.

131
    Здравствуйте.

подскажите, возможно ли из скрипта получить доступ к массиву timeTrends[] временного тренда, размещенного в шаблонном окне?
Для управления видимостью трендов, расположенного в шаблонном окне объекта "Временные тренды", можно привязать подстановку управляющей переменной к основной или дополнительной переменной объекта.
Скрипт по изменению переменной:
Код: (delphi)
var i: integer;
begin
  with Sender as TM_TimeTrendViewer do
    for i := 0 to TimeTrendsCount -1 do TimeTrends[i].Visible := GetBit(AsInt,i);  // или NOT GetBit...
end.
.
Во вложении пример шаблонного окна с управлением видимостью трех трендов.

132
     Здравствуйте.

Изменить свойства объекта экземпляра шаблона можно только скриптом по событию изменения значения одной из двух переменных объекта. Для генерации события можно использовать пару переменных с начальными значениями 0 и 1. Одна переменная связывается с видимыми объектами, другая - со скрытыми (пара нужна для задания всех свойств экземпляра шаблона, событие - инициализация переменных при запуске проекта).
 
Варианты для управления видимостью объекта "Поле":
1. Дополнительная переменная свободна, скрипт управляет непосредственно видимостью объекта.
2. Дополнительная переменная занята, скрипт управляет видимостью экранирующего объекта по основной или дополнительной переменной.
Код: (delphi)
begin
  with Sender as TM_Object do Visible := VariableEx.AsInt = 0;  // инвертировать для варианта 2
end.

Во вложении пример для варианта 1 и еще несколько способов управления видимостью объекта экземпляра шаблона

133
Здравствуйте.

Необходимо явно указать преобразования типов.
Код: (delphi)
buffer := tempStringList[0][1] + UTF8ToString(UTF8LowerCase(UTF8Encode(buffer)));  // составляем буфер из первой буквы этой строки и имеющегося буфера в нижнем регистре - Ф + амилия - Фамилия

P.S. Цикл удаления 1-го символа фамилии можно заменить функцией UTF8Copy
Код: (delphi)
buffer := tempStringList[0][1] + UTF8ToString(UTF8LowerCase(UTF8Copy(UTF8Encode(tempStringList[0]), 2, tempStringList[0].Length - 1))); // Фамилия

134
Осталась вторая проблема -  это  откат времени после работы  2 или 3  одновременно вентилятора и переход в обычный режим. нашол один интересный факт если сделать тайм аут  приходяшего сигнала   Допустим приходит 2 потом 7  и между ними сделать 0 то все работает корректно (Возможно стоит поставить задержку на приходящий сигнал в 1 секунду да и всё или можно реализовать как то в скаде межу переключениями ?
По опыту работы с таймером можно отметить три его особенности:
1. Не пытаться повторно запустить таймер.
2. Новое начальное значение таймера можно установить только из состояния "Сброс".
3. Работающий таймер только пишет в таймерную переменную.
Все остальное это видимость принудительного изменения значения таймерных переменных на значения из файла при изменении состояния любого вентилятора.
Цитировать
Вообще операторы жалуются на сброс таймеров после рестарта пк или проекта
Это не сброс таймеров, а банальное переписывание нулевого значения из файла (было создано при первом запуске проекта).

Удалите из скриптов РАБОТУ С ФАЙЛОМ и объявите АВТОМАТИЧЕСКОЕ ВОССТАНОВЛЕНИЕ таймерных переменных в редакторе переменных.


Цитировать
Я так понимаю это мне нужно закинуть в корень папки Siple Scada 2 что бы открыть проэкт или как то по другому ?
Simple-Scada видит проекты, размещенные в папке Projects, которая размещена в папке пользовательских данных (выбирается во время установки Simple-Scada). Туда и нужно поместить разархивированную папку проекта.

Цитировать
...есть вариант  забирать время из контролера  и передавать на скаду... Правда не знаю как реализовать вытягивание битов  с 3 воздуходувок  через 1 тег...
Правильно получать значения времени наработки непосредственно из контроллера. Но у модуля SR3NET01 только по четыре шестнадцатиразрядных регистра для записи и для чтения. Т.е. всего-навсего по 64 бита. Можно увеличить количество путем страничного доступа к данным. Но это уже вопрос к автору программы и вычислительными ресурсами контроллера.

135
     Здравствуйте, Игорь32.

Сохранить значение переменной между сеансами работы сервера можно без записи/чтения в файл, а просто включив "Автоматическое восстановление" переменной.

Для таймера нежелательны повторные старты, пример универсального скрипта для объекта "Текст". Основная переменная - управление таймером. Событие "Изменилась значение основной переменной".
Код: (delphi)
var temp: TM_Variable;
begin
  with Sender as TM_Text do begin
    temp := VariableEx;

// проверка управления по принадлежности к группе, в Tag принадлежность к группе отмечена "единицами" в битах <код группы>,
// пример: для групп 4 и 7 (вентилятор 3) Tag = (2^4) + (2^7) = 16 + 128 = 144
    if ((1 shl AsInt) and Tag) = 0 then   
      TimerPause(temp)                    // установить таймер на паузу
    else
      if TimerGetState(temp) <> 1 then TimerStart(temp, temp.AsDateTime);     // не беспокоить работающий таймер
  end;
end.
Во вложении пример с шаблоном для вентилятора и просто группой объектов.

Страницы: 1 ... 7 8 [9] 10 11 ... 16