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

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

Автор Тема: Вопросы по скриптам в Simple-Scada 2  (Прочитано 469086 раз)

pan2000

  • Постоялец
  • ***
  • Сообщений: 214
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1635 : 29 Июля 2025, 09:55:03 »
   Здравствуйте.

... но если авария уже была и появилась новая, то значение переменной не изменилось и скрипт открытия окна не выполнится пока мы не сбросим все аварии...
Возможны два варианта:
- использовать для скрипта открытия окна изменение переменной "Счетчик ошибок";
- повторно открывать окно при попытке закрыть окно с ошибками (подобно австралийцу, который пытается выбросить старый бумеранг).

В примере два варианта:
- вызов скрипта открытия окна по счетчику ошибок;
- вызов скрипта непосредственно по битам ошибок (для случая когда все ошибки доступны в окне).
Окно закрывается после сброса всех ошибок.
Второй вариант предпочтительней для случая большого количества окон (особенно шаблонных), т.к. скрипт "Изменились переменные" будет либо включать длинные списки переменных с какой-то логикой выборки переменных для конкретного окна, либо много-много отдельных скриптов.

Цитировать
...каждом выполнении скрипта "Изменились переменные" взвожу флаг в True ...
...данный скрипт будет выполняться и выполняться,...
Если это самое "True" есть переменная вызывающая скрипт, то вероятная причина зацикливания - отсутствие в первых строках скрипта команды выхода по "False" (признак - окно без ошибок открывается при запуске проекта).

Серега

  • Старожил
  • ****
  • Сообщений: 271
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1636 : 07 Августа 2025, 17:17:47 »
Добрый день!
Скрипт универсальный, вешается на тексты с разным значением текста, но значение текста может повторяться, на событие onClick.
Нажимаем на текст с инфо и стрелка показывает где это расположено.
Писать для каждого текста через Name объекта можно, работает. Но хотел подсократить объем кода.
Понятно что вызывающий скрипт объект TM_Control. Но мы как бы приводим его к типу TM_Text, а у него уже свойство .Text есть.
Можете объяснить почему данный скрипт вызывает ошибку 'Undeclared identifier "Text", ... ':
Код: (delphi)
  with (Sender as TM_Text) do
    shWhereProblem.Visible := True;
    if Sender.Text = 'A2.2' then
    begin
      shWhereProblem.X := 506;
      shWhereProblem.Y := 744;
    end;

Хотел бы услышать мнение разработчиков.
« Изменён: 07 Августа 2025, 17:24:58 от Simple-Scada »

Simple-Scada

  • Администратор
  • *****
  • Сообщений: 3193
    • Просмотр профиля
    • Simple-Scada
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1637 : 07 Августа 2025, 17:24:47 »
Если код не заключен в begin..end, то действие будет распространяться только на первую строку.
Также не нужно писать "Sender.Text", когда выше уже использовали "with (Sender as TM_Text) do", можно сразу обращаться к свойству Text.
Исправленный код:
Код: (delphi)
with (Sender as TM_Text) do
begin
  shWhereProblem.Visible := True;
  if Text = 'A2.2' then
  begin
    shWhereProblem.X := 506;
    shWhereProblem.Y := 744;
  end;
end;

Ещё можно делать так (проще для понимания):
Код: (delphi)
var
  aText: TM_Text;
begin
  aText := Sender as TM_Text;
  shWhereProblem.Visible := True;
  if aText.Text = 'A2.2' then
  begin
    shWhereProblem.X := 506;
    shWhereProblem.Y := 744;
  end;
end.

Серега

  • Старожил
  • ****
  • Сообщений: 271
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1638 : 07 Августа 2025, 17:39:03 »
Как то упустил из виду что для with тоже надо использовать begin..end. Все заработало.
2ой способ как то нагляднее. уже завтра попробую.
Спасибо!

OlegM

  • Новичок
  • *
  • Сообщений: 12
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1639 : 10 Октября 2025, 15:16:10 »
Добрый день.

Помогите, пожалуйста, разобраться со скриптом.
Реализовал свою простенькую функцию FormatStr.
Не всегда, но достаточно часто вижу в логах сервера, вот такую ошибку на этот скрипт: 'Ошибка в скрипте Utils в строке 192. Обращение к несуществующему объекту (017C0647 . Read of address 00000028)'.
Utils -- это мой глобальный модуль, а строка 192 -- это завершающий мою функцию `end;` .

Вот сама функция:
Код: (delphi)
// === Format String ===============================================================================
// =================================================================================================
// Подставляет в строку вместо %s элементы из массива.
// Может принимать элементы для подстановки разных типов (Variant).
// Каждый элемент для подстановки рассматривается внутри функции как тип Variant,
//   который затем приводится к строке через VarToStrDef().
// Экранирование процента делается так: '%%' -> '%'.


// Стандартная делфишная функция Format() не поддерживается.


// Пример:
// FormatStr('Процент: %s%%, значение: %s.', [85, 'Ok']);
// => 'Процент: 85%, значение: Ok.'


function FormatStr(const AString: string; const Args: array of Variant): string;
var
  I, ArgIndex: Integer;
  NextSeq: string;
  V: Variant;
begin
  Result := '';
  ArgIndex := 0;
  I := 1;


  while I <= Length(AString) do
  begin
    if (AString[I] = '%') and (I < Length(AString)) then
    begin
      NextSeq := Copy(AString, I, 2); // например: '%s', '%%', '%x'


      if NextSeq = '%%' then
      begin
        Result := Result + '%';
        Inc(I, 2);
      end
      else if NextSeq = '%s' then
      begin
        if (Length(Args) > 0) and (ArgIndex <= High(Args)) then
        begin
          V := Args[ArgIndex];
          Result := Result + VarToStrDef(V, '<unsupported>');
          Inc(ArgIndex);
        end
        else
          Result := Result + '<missing>';
        Inc(I, 2);
      end
      else // неизвестная последовательность -- оставляем просто '%'
      begin
        Result := Result + '%';
        Inc(I);
      end;
    end
    else
    begin
      Result := Result + AString[I];
      Inc(I);
    end;
  end;
end;
PS: Странно, что эта функция не поддерживается из коробки. Очень удобно ей пользоваться, например, при формировании sql-запросов с несколькими подстановками.

Вот код вызова, он иногода выполняется без ошибок, а иногда с этой ошибкой по памяти.
Но! Если перед этим кодом добавить строку для записи в виртуальную переменную любого значения, то всегда работает без ошибок.
Код: (delphi)
    Test.Value := 'Ok'; // С этой строкой FormatStr всегда работает без ошибок.
    GetVariableByName(FormatStr('Ch%s_PrgName',   [ChamberNo])).Value :=           tblPrograms.GetCell(Ord( colPrgName      ), SelRow).Text;
    GetVariableByName(FormatStr('Ch%s_BoardWood', [ChamberNo])).Value :=           tblPrograms.GetCell(Ord( colPrgBoardWood ), SelRow).Text;
    GetVariableByName(FormatStr('Ch%s_BoardThk',  [ChamberNo])).Value := StrToInt( tblPrograms.GetCell(Ord( colPrgBoardThk  ), SelRow).Text );
« Изменён: 13 Октября 2025, 14:05:28 от Simple Scada »

OlegM

  • Новичок
  • *
  • Сообщений: 12
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1640 : 13 Октября 2025, 14:21:55 »
Переделал на функцию без Variant: вместо %s функция подставляет только строки из массива.
Проблема осталась. По прежнему выбивает эту ошибку 'Ошибка в скрипте Utils в строке 262. Обращение к несуществующему объекту (017C0647 . Read of address 00000028)'.
Код: (delphi)
// === FormatStr ===============================================================================
// =============================================================================================
// Функция подставляет в строку вместо '%s' строки из массива.

function FormatStr(const S: string; const Args: array of string): string;
var
  I, ArgIndex: Integer;
  NextChar: Char;
begin
  Result := '';
  ArgIndex := 0;
  I := 1;

  while I <= Length(S) do
  begin
    if (S[I] = '%') and (I < Length(S)) then
    begin
      NextChar := S[I + 1];

      if NextChar = '%' then
      begin
        Result := Result + '%';
        Inc(I, 2);
      end
      else if NextChar = 's' then
      begin
        if ArgIndex < Length(Args) then
        begin
          Result := Result + Args[ArgIndex];
          Inc(ArgIndex);
        end
        else
          Result := Result + '<missing>';
        Inc(I, 2);
      end
      else
      begin
        // неизвестная последовательность — оставляем просто '%'
        Result := Result + '%';
        Inc(I);
      end;
    end
    else
    begin
      Result := Result + S[I];
      Inc(I);
    end;
  end;
end;
« Изменён: 13 Октября 2025, 19:16:14 от Simple_Scada »

OlegM

  • Новичок
  • *
  • Сообщений: 12
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1641 : 13 Октября 2025, 14:55:58 »
Если сделать через конкатенацию, то все выполняется без ошибок.
Код: (delphi)
    // Выполняется без ошибок.
    GetVariableByName('Ch'+ IntToStr(ChamberNo) +'_PrgName'  ).Value :=           tblPrograms.GetCell(Ord( colPrgName      ), SelRow).Text;
    GetVariableByName('Ch'+ IntToStr(ChamberNo) +'_BoardWood').Value :=           tblPrograms.GetCell(Ord( colPrgBoardWood ), SelRow).Text;
    GetVariableByName('Ch'+ IntToStr(ChamberNo) +'_BoardThk' ).Value := StrToInt( tblPrograms.GetCell(Ord( colPrgBoardThk  ), SelRow).Text );
« Изменён: 13 Октября 2025, 15:03:27 от Simple Scada »

Simple-Scada

  • Администратор
  • *****
  • Сообщений: 3193
    • Просмотр профиля
    • Simple-Scada
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1642 : 13 Октября 2025, 15:26:40 »
Явно как-то портится память (скорее всего связанная со строками). Можете выслать на support@simple-scada.com проект в котором воспроизводится ошибка?
« Изменён: 13 Октября 2025, 20:40:52 от Simple-Scada »

yurgin_757

  • Новичок
  • *
  • Сообщений: 5
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1643 : 03 Декабря 2025, 17:53:54 »
Здравствуйте!
...делаю тестовый проект, опрос МВ110-8А, 6 датчиков и два порта сухие контакты
Возникла потребность в коррекции данных приходящих от датчиков
применил скрипт:
Код: (delphi)
begin
  result.Value := t_1.Value + corr.Value;
end.
...он работает, но работает именно только с тем датчиком к которому привязан!
В связи с этим вопрос, как сделать универсальный скрип, а не жестко привязанный к ОПС переменной (в последующем их ожидается около 1000 шт)
« Изменён: 04 Декабря 2025, 11:06:37 от Simple_Scada »

Simple Scada

  • Глобальный модератор
  • *****
  • Сообщений: 266
    • Просмотр профиля
    • Simple-Scada
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1644 : 04 Декабря 2025, 13:34:23 »
Здравствуйте.

Можно использовать следующие способы:
1. Если требуется преобразовать значение из одной шкалы в другую, то можно использовать режим масштабирования переменной.
2.1. Создать внутреннюю переменную (в которую будем записывать результат вычисления) с именем, как у исходной переменной + приставка "_Corr". Например, для переменной "vrTemp" -> "vrTemp_Corr". Далее создать скрипт с типом события "Изменились переменные", добавить внешнюю переменную, изменение которой необходимо отслеживать, в список скрипта и написать такой скрипт:
Код: (delphi)
var
  aTemp_Corr: TM_Variable;
begin
  aTemp_Corr := GetVariableByName(Variable.Name + '_Corr');  // выполняем поиск переменной vrTemp_Corr
  if aTemp_Corr <> nil then                                  // если переменная существует, то
    aTemp_Corr.Value := Variable.AsInt + corr.AsInt;         // записываем в нее значение переменной vrTemp + значение переменной "corr"
end.
Теперь в переменную "vrTemp_Corr" будет записываться значение переменной "vrTemp" + значение переменной "vrTemp_Corr". Данный скрипт универсальный, если таких переменных несколько, то их все можно добавить в список скрипта и он будет выполняться для каждой переменной отдельно.
Данный пример подойдет для группы переменных с одинаковым значением корректировки.
2.2. Если для каждой внешней переменной будет уникальное значение корректировки, то для переменных корректировки также нужно задать однотипные имена, например "CorrValue_" + имя исходной переменной, и использовать следующий код скрипта:
Код: (delphi)
var
  aTemp_Corr, aCorr: TM_Variable;
begin
  aTemp_Corr := GetVariableByName(Variable.Name + '_Corr');  // выполняем поиск переменной vrTemp_Corr
  aCorr := GetVariableByName('CorrValue_' + Variable.Name);  // выполняем поиск переменной CorrValue_vrTemp
  if (aTemp_Corr <> nil) and (aCorr <> nil) then             // если переменные существуют, то
    aTemp_Corr.Value := Variable.AsInt + aCorr.AsInt;        // в переменную vrTemp_Corr записываем значение переменной vrTemp + значение переменной CorrValue_vrTemp
end.
3. Если эту переменную не требуется выводить в тренды, отчеты, генерировать по ней сообщения о нарушении границ и т.д., то можно использовать следующий способ: выводить значение переменной в компонент "Текст" (см. пример №5 по ссылке), с прибавлением необходимого значения. Для этого внешнюю переменную нужно указать в свойстве "Переменная", а переменную корректировки в свойстве "Доп. переменная", и на событие "OnDataChange" использовать следующий код:
Код: (delphi)
begin
  if Sender is TM_Text then                         // проверяем, что Sender это текст
    with Sender as TM_Text do                       // приводим Sender к типу "TM_Text"
      Text := IntToStr(AsInt + VariableEx.AsInt);   // записываем в текст значение основной переменной + значение доп. переменной
end.
Если необходимо выполнять скрипт после изменения значения переменной корректировки, то данный скрипт также необходимо назначить на событие "OnDataChangeEx".
« Изменён: 08 Декабря 2025, 18:26:54 от Simple_Scada »

yurgin_757

  • Новичок
  • *
  • Сообщений: 5
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1645 : 05 Декабря 2025, 06:03:41 »
Спасибо огромное, вариант 2.2 должен подойти я думаю, буду пробовать. А как бы в этот скрипт еще проверку качества добавить, на вроде того: if t_1.IsGoodQuality = False то vrTemp_Corr:=0;
..а скрипт 2.2 тоже имеет тип "изменилась переменная" ??
« Изменён: 05 Декабря 2025, 11:19:47 от Simple_Scada »

Simple Scada

  • Глобальный модератор
  • *****
  • Сообщений: 266
    • Просмотр профиля
    • Simple-Scada
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1646 : 05 Декабря 2025, 18:07:18 »
Здравствуйте.

Код: (delphi)
var
  aTemp_Corr, aCorr: TM_Variable;
begin
  aTemp_Corr := GetVariableByName(Variable.Name + '_Corr');  // выполняем поиск переменной vrTemp_Corr
  aCorr := GetVariableByName('CorrValue_' + Variable.Name);  // выполняем поиск переменной CorrValue_vrTemp
  if (aTemp_Corr <> nil) and (aCorr <> nil) then             // если переменные существуют, то
    if Variable.IsGoodQuality then                           // если переменная имеет хорошее качество, то
      aTemp_Corr.Value := Variable.AsInt + aCorr.AsInt       // в переменную vrTemp_Corr записываем значение переменной vrTemp + значение переменной CorrValue_vrTemp
    else                                                     // иначе (если переменная имеет плохое качество)
      aTemp_Corr.Value := 0;                                 // в переменную vrTemp_Corr записываем 0
end.

yurgin_757

  • Новичок
  • *
  • Сообщений: 5
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1647 : 07 Декабря 2025, 07:43:02 »
Спасибо! Работает...
Только вот как сделать чтобы переменная, в которую выводится значение реального тега + коррекция был в формате не целого числа а с двумя знаками после запятой.
...и что бы этот тег применялся по вводу коррекции, а не по изменению реальной переменной.
« Изменён: 07 Декабря 2025, 11:11:36 от yurgin_757 »

Simple_Scada

  • Администратор
  • *****
  • Сообщений: 1354
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1648 : Сегодня в 12:18:26 »
Цитировать
был в формате не целого числа а с двумя знаками после запятой.
Для этого переменной необходимо задать вещественный тип и настроить нужный формат. Также, в скрипте нужно использовать приведение значения переменной к вещественному типу (AsSingle).

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

Вообще, подобные вычисления следует производить на контроллере и передавать в скада-систему готовое значение, без необходимости его корректировки.

yurgin_757

  • Новичок
  • *
  • Сообщений: 5
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #1649 : Сегодня в 13:12:54 »
Цитировать
был в формате не целого числа а с двумя знаками после запятой.
Для этого переменной необходимо задать вещественный тип и настроить нужный формат. Также, в скрипте нужно использовать приведение значения переменной к вещественному типу (AsSingle).
Ага, спасибо это понял! ...сделал так:
Код: (delphi)
aTemp_Corr.Value := Variable.AsFloat + aCorr.AsFloat;

Цитировать
Если сделать, чтобы пересчет производился только по вводу коррекции, то потеряется смысл коррекции, т.к. значение итоговой переменной будет вычисляться только при изменении значения коррекции и затем при изменении исходной переменной не будет меняться.
...хммм, странно, в МастерСкаде было именно так

Цитировать
Вообще, подобные вычисления следует производить на контроллере и передавать в скада-систему готовое значение, без необходимости его корректировки.
...к сожалению данные берутся не из ПЛК или СПК а из аналоговых модулей МВ110-8А. В старой МастерСкаде ведётся паралельный съём данных так как оборудование "БигДайчмен" не даёт своих протоколов.

В любом случае ещё раз спасибо!
« Изменён: Сегодня в 13:29:15 от Simple Scada »