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

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

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

TeNQ

  • Постоялец
  • ***
  • Сообщений: 199
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #15 : 04 Февраля 2016, 15:49:28 »
Написал простой скрипт, может кому-нибудь понадобится.
Задача. Я ввожу в поле ввода секунды, должен произойти пересчет, и в OPC должны уйти миллисекунды.
Ограничение ввода от 0 до 20 секунд.
Как это сделать.
Имеется переменная OPC: Имя Time_vibro, тип Integer, шкала от 0 до 20000, формат вывода 0 в которой будет хранится значение в миллисекундах.
Я создаю виртуальную переменную, например, a1. Тип integer, шкала от 0 до 20. Формат вывода 0. В ней будет хранится промежуточное значение в секундах.
Создаю поля ввода Field1 для ввода секунд. Переменная для этого поля будет а1. Также у него есть событие OnDoneInput. Оно будет вызвано по окончанию ввода в поле. Cоздаю следующий скрипт.
Код
Procedure Field1_OnDoneInput(Sender: TM_Control);
begin
  Time_Vibro.Value := Field1.ValueAsInt  * 1000;
end. 
Что он делает. Допустим мы ввели в поле секунд 2. Это значение будет выведено в Field1.ValueAsInt.  Умножением на 1000 переведу секунды в миллисекунды и сохраню результат в переменную OPC.
Все работает, но. Так как виртуальная переменная не хранит результат после закрытия скады, то при перезапуске скады, мы в поле ввода увидим 0. Хотя какое-то значение записано в Time_Vibro.Value.
Значит нужно считать его из OPC в поле ввода секунд с обратным преобразованием.
Я это сделал так.
Создал вспомогательное поле ввода Field2. Сделал его недоступным и невидимым. Переменной для этого поля выбрал Time_Vibro. И использовал событие OnDataChange.
Код
Procedure Field2_OnDataChange(Sender: TM_Control);
begin
  Field1.Value := Time_Vibro / 1000
end.
При загрузке скады произойдет сравнение значение переменной Time_Vibro в клиенте скады и значение переменной Time_Vibro в OPC сервере. И если будет обнаружено различие вызовется событие OnDataChange. Миллисекунды переведем в секунды делением и полученное значение присвоим полю ввода секунд.
Так можно преобразовывать любые значение при вводе, а также дополнив скрипт OnDoneInput проверять введенное значение на соответствие нужным условиям и соответственно принимать какие то действия.
« Изменён: 04 Февраля 2016, 18:05:09 от TeNQ »

TeNQ

  • Постоялец
  • ***
  • Сообщений: 199
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #16 : 04 Февраля 2016, 17:25:43 »
Чуть посложнее.
Имеется функция генерирующая программно ШИМ. Есть два поля ввода. Ввод в скаде в секундах, передаем в контроллер в мс. Длительность импульса и период следования. Длительность импульса не должна превышать период следования, функция этого не переварит. Следовательно нужно ввести проверку на ввод пользователя и сделать следующее:
1) Если введенная длительность импульса(с) не превышает период следования(мс), то преобразуем в мс и передаем контроллеру.
2) Если превышает, то делаем длительность импульса(мс) равной периоду следования(мс). Получаем постоянный сигнал на выходе.
Код: (delphi)
Procedure Field43_OnDoneInput(Sender: TM_Control);
var
  Vvod_s: Integer;
  Time_ms: Integer;
  Period_ms: Integer;
begin
  Vvod_s    := Field43.ValueAsInt;
  Time_ms   := D450.ValueAsInt;
  Period_ms := D451.ValueAsInt;

  if Vvod_s < Period_ms/1000 then
    D450.Value  := Vvod_s * 1000
  else
    D450.Value  := Period_ms;
end.
Vvod_s - введенное в поле значение длительности импульса в сек. (Вирт. переменная)
Time_ms - преобразованное значение длительности импульса в мс (Переменная OPC сервера)
D450.Value оно же, но для присвоения.
Period_ms - преобразованное значение периода следования в мс (Переменная OPC сервера)
« Изменён: 26 Мая 2018, 18:01:48 от Simple-Scada »

Simple-Scada

  • Администратор
  • *****
  • Сообщений: 2761
    • Просмотр профиля
    • Simple-Scada
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #17 : 04 Февраля 2016, 21:23:19 »
Отличное и неожиданное решение задач! Но, т.к. в первой задаче у вас прямая зависимость между значением переменной на OPC-сервере и значением отображаемом в поле, то здесь можно было бы обойтись и вовсе без скриптов. Решение первой задачи без скриптов выглядит так:

- добавляем новую переменную с OPC-сервера которая хранит значение времени в миллисекундах;
- ставим этой переменной смещение запятой = -3;
- ставим формат переменной = 0;
- ставим шкалу для этой переменной 0-20000;

Готово! Теперь можно добавить объект Поле в проект и связать эту переменную с ним. Таким образом мы увидим в Поле значение в секундах, а на контролере оно будет храниться и передаваться в миллисекундах.

Этой задачей Вы навели нас на мысль о том, что нужно добавить объектам Поле и Уровень два свойства: "Визуальный минимум" и "Визуальный максимум" (такие свойства были в старой версии скады, но мы их убрали). С их помощью можно было бы работать с переменными в любой собственной шкале, даже без прямой зависимости.
« Изменён: 04 Февраля 2016, 22:17:50 от Simple-Scada »

TeNQ

  • Постоялец
  • ***
  • Сообщений: 199
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #18 : 04 Февраля 2016, 23:33:20 »
Цитировать
- добавляем новую переменную с OPC-сервера которая хранит значение времени в миллисекундах;
- ставим этой переменной смещение запятой = -3;
- ставим формат переменной = 0;
- ставим шкалу для этой переменной 0-20000;
Красиво. Но мне все равно нужна была проверка.

У меня  есть проблема. Есть два поля ввода. К ним привязаны переменные
D442 Тип Integer Шкала мин 0 макс 1 Формат 0 Сдвиг 0 - Привязано к Field28
D443 Тип Integer Шкала мин 0 макс 1 Формат 0 Сдвиг 0
Суть в чем, эти переменные отвечают за очередность выбора.
0 - первая компонента
1 - вторая компонента
Одновременно они не могут быть первыми или вторыми (допустим) Соответственно, если я ввожу 1 в первое поле мне нужно автоматически ввести 0 во второе. И также если я ввожу 0, то 1 во второе. Ручного ввода во второе поле пока не касаюсь.
Создаю простейший скрипт.

Код
Procedure Field28_OnDoneInput(Sender: TM_Control);
begin
if field28.Value = 1 then D443.Value  := 0 else D443.Value  := 1;
end.     
И он не работает. Я ввожу в поле Field28 1 и получаю 1 во втором; 0 и 0; 2(огр. до 1) = 1 и 0. Продолжаю вводить 1, получаю 0; ввожу 0, получаю 0. Вообщем я этого никак не могу понять.
Заменяю переменные на виртуальные того же типа. Все ок. 1 и 0; 0 и 1; 2(огр. до 1) = 1 и 0.
Оставляю привязанной к первому полю виртуальную переменную, а кто второму OPC переменную D443. Все ок.
Привязываю к первому опять OPC переменную и опять ерунда. Переменные при обмене не пересекаются, в контролере тоже не влияют друг на друга. Что это может быть.
Очень не хватает отладчика, где можно пошагово посмотреть, что происходит на исполнении скрипта и значения переменных во время исполнения.
« Изменён: 05 Февраля 2016, 09:13:01 от TeNQ »

Simple-Scada

  • Администратор
  • *****
  • Сообщений: 2761
    • Просмотр профиля
    • Simple-Scada
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #19 : 06 Февраля 2016, 00:31:46 »
Скрипт не работает, потому что он выполняется по событию OnDoneInput. Т.е. сразу же после того как на клиенте пользователь ввёл значение в поле, скрипт проверяет значение переменной, связанной с этим полем и в зависимости от этого значения меняет переменную другого поля. Ошибка состоит в том, что внешняя переменная не меняет свое значение моментально. После того, как клиент ввел новое значение в поле, значение переменной не изменяется! Новое значение сначала отправляется на сервер, чтобы он записал его в OPC-тег. И только после того, как значение успешно записалось в OPC-тег - клиент получает новое значение переменной. Т.е. на момент выполнения вашего скрипта, значение "field28.Value" всё ещё неизменно, несмотря на то, что пользователь ввел в поле новое значение. А вот с виртуальными переменными всё работает. Ведь им не требуется время на запись, они всегда в памяти и меняются моментально и на момент события OnDoneInput виртуальная переменная уже имеет введенное пользователем значение.

Simple_Scada

  • Администратор
  • *****
  • Сообщений: 922
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #20 : 06 Февраля 2016, 17:27:10 »
Еще один небольшой пример.

Рассмотрим случай, когда нам нужно сделать мигание объекта при определенном значении связанной с ним переменной. Для этого:
1. В свойстве "Переменная" объекта выбираем нужную нам переменную.
2. В инспекторе объектов переходим на вкладку События и дважды кликаем по событию OnDataChange (изменение значения переменной связанной с объектом).
3. Пишем скрипт, например для изображения:

Код: (delphi)
begin
  { отключаем мигание установив цвет мигания «без цвета» - таким образом не нужно будет
    отдельно отключать мигание отдельно для каждого условия ни-же. }
  Image1.FlashColor := clNone;
  case Image1.ValueAsInt of             // если значение переменной равно:
    1: Image1.Color := clRed;           // 1 - изменить цвет на красный.
    2:
    begin
      Image1.Color := clNone;           // 2 – изменить цвет на «без цвета» - для того, чтобы мигание было только зеленым цветом, а не с красного на зеленый.   
      Image1.FlashColor := clGreen;     // и включаем мигание зеленым цветом.
    end;
    3: Image1.Color := clGreen;         // 3 - меняем цвет на зеленый.
  end;
end.
Все объекты в проекте, для которых применяется мигание будут мигать синхронно - по умолчанию с частотой 1 секунда. Управление частотой мигания будет доступно в следующих обновлениях.
« Изменён: 26 Мая 2018, 10:21:25 от Simple-Scada »

Simple_Scada

  • Администратор
  • *****
  • Сообщений: 922
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #21 : 06 Февраля 2016, 17:33:55 »
Рассмотрим работу с файлами для ситуации, когда нам нужно сохранить различные данные в файл, а потом загрузить их в различные объекты.
Создадим две кнопки – для сохранения в файл и для загрузки из файла. В событии OnClick первой кнопки напишем скрипт для сохранения в файл, например:
Код: (delphi)
begin
  if FileOpen('Hello.sbm') then  // открыть если существует или создать файл Hello.sbm (расширение вы можете использовать любое)
  begin
    FileWriteString(Field1.ValueAsStr);      // записать в файл строку из поля
    FileWriteInteger(Field2.ValueAsInt);     // записать в файл целое число из поля
    FileWriteSingle (Field3.ValueAsFloat);   // записать в файл вещественное число из поля
    FileWriteString (Text1.Text);            // записать в файл содержимое текста
    FileWriteDateTime(Now);                  // записать в файл текущую дату и время
    // закрывать файл не обязательно, Simple-Scada сделает это автоматически. 
  end;
end.

В событии OnClick второй кнопки напишем скрипт для чтения данных из файла, например:
Код: (delphi)
begin
// нужно считывать все данные в той последовательности, в которой мы их записывали
  if FileOpen('Hello.sbm') then    // открыть файл если существует
  begin
    Field1.Value := FileReadString;     // считать в переменную поля строку
    Field2.Value := FileReadInteger;    // считать в переменную поля целое число
    Field3.Value := FileReadSingle;     // считать в переменную поля вещественное число
    Text2.Text   := FileReadString;     // считать в текст строку
    Text3.Text   := DateTimeToStr(FileReadDateTime);// считать в текст время и дату
  end;
end.

Теперь по нажатию первой кнопки данные будут сохраняться в файл, а по нажатию второй загружаться из файла.



Работа с файлами будет доступна после сегодняшнего обновления.
« Изменён: 26 Мая 2018, 10:21:57 от Simple-Scada »

Al_Kuz

  • Новичок
  • *
  • Сообщений: 30
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #22 : 08 Февраля 2016, 13:29:17 »
Здравствуйте! Не получается работа с файлом из окна. Файл создается, но записи и чтения не происходит. Со страницы запись/чтение в норме. Может, что не так делаю, проект прилагаю.

Pr_Alex

  • Новичок
  • *
  • Сообщений: 6
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #23 : 08 Февраля 2016, 13:54:23 »
Добрый день!
Возможно ли в скриптах динамически создавать объекты, что-то типа:

Код: (delphi)
procedure TForm1.Button1Click(Sender: TObject);
var
   Button : array [1..20] of TButton;
begin
  for i:=1 to 20 do
  begin
    Button[i]:=TButton.Create(Form1);
    Button[i].Parent:=Form1;
    Button[i].Left:=80;
    Button[i].Top:=80;
    Button[i].Name:='Buttons'+IntToStr(i);
  end;
end;
...и обращаться по имени к объектам, типа:
Код: (delphi)
var
  i:integer;
begin
  for i:=1 to 20 do
    (FindComponent('Edit'+IntToStr(i)) as TEdit).Text:='0';
end;
« Изменён: 26 Мая 2018, 10:22:23 от Simple-Scada »

Simple-Scada

  • Администратор
  • *****
  • Сообщений: 2761
    • Просмотр профиля
    • Simple-Scada
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #24 : 08 Февраля 2016, 17:29:57 »
Не получается работа с файлом из окна. Файл создается, но записи и чтения не происходит.
Спасибо, сейчас займемся этим вопросом.

Возможно ли в скриптах динамически создавать объекты, что-то типа:
Нет, это запрещено по многим причинам (из соображений быстродействия, безопасности в коде, работы по сети и др.). Если вы хотите, чтобы 20 кнопок появились после выполнения скрипта, то их обязательно нужно предварительно создать в редакторе и, к примеру, сделать скрытыми выключив свойство "Видимость". А скриптом придется просто включать видимость, но для каждой кнопки это придется делать отдельно:

Код
begin
  Button1.Visible := True;
  Button2.Visible := True;
  ...
  Button20.Visible := True;
end;

А как в примере с FindComponent можно было бы осуществить, если мы добавим такую функцию в скрипты. Правда такая функция будет значительно медленнее, чем обращение к объекту по имени напрямую, ведь придется искать её вреди всех объектов проекта. Поэтому подобные скрипты нежелательны и по возможности их лучше избегать. Мы подумаем ещё по поводу FindComponent.
« Изменён: 08 Февраля 2016, 17:35:30 от Simple-Scada »

TeNQ

  • Постоялец
  • ***
  • Сообщений: 199
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #25 : 09 Февраля 2016, 19:34:18 »
OnExit – скрипт выполнится при выходе из проекта.
А что считается выходом из проекта? В версии 1 можно было настроить кнопку по которой осуществляется выход. А во 2 я такой опции не нашел. По ESC из проекта не выйдешь как раньше, я просто жму ALT+F4. При этом у меня либо скрипт исполняется неверно, либо событие не происходит. И я пока не пойму почему.

Simple-Scada

  • Администратор
  • *****
  • Сообщений: 2761
    • Просмотр профиля
    • Simple-Scada
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #26 : 09 Февраля 2016, 21:13:14 »
Выходом считается реальное завершение работы проекта на сервере. Сейчас мы планируем также добавить события OnInitClient и OnExitClient, которые будут выполняться  по запуску/выходу из клиента.

Цитировать
В версии 1 можно было настроить кнопку по которой осуществляется выход. А во 2 я такой опции не нашел.
Да, мы забыли её включить. Сейчас по Shift+ESCAPE осуществляется выход.

Al_Kuz

  • Новичок
  • *
  • Сообщений: 30
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #27 : 15 Февраля 2016, 09:44:53 »
По работе с файлами. Часто для работы оборудования требуются некие параметры. Набор таких параметров или рецептов создается пользователем и хранится в файле. Вот мыслю, как это сделать имеющимися в S-S2 возможностями. Если в файл записывать только данные числового типа, то каждая запись группы параметров имеет фиксированную длину в байтах, но пользователю нужен не только номер набора параметров или рецепта, но и название (имя). Строчные данные имеют разную длину, поэтому не одинаковую длину будет иметь и группа параметров. Как организовать поиск и считывание из файла нужных мне параметров?

Поскольку синтаксис скриптов S-S2 похож на синтаксис Дельфи, то подсмотрел в справке как там работают напрямую с файлом. Было бы не плохо научить S-S2 компиль cоздавать типизированные файлы, а так же писать и читать переменные типа record.

Simple-Scada

  • Администратор
  • *****
  • Сообщений: 2761
    • Просмотр профиля
    • Simple-Scada
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #28 : 15 Февраля 2016, 10:44:08 »
Цитировать
Строчные данные имеют разную длину, поэтому не одинаковую длину будет иметь и группа параметров. Как организовать поиск и считывание из файла нужных мне параметров?
Допустим Вы записываете в файл следующим образом:

Код: (delphi)
begin
  if FileOpen('MyFile.sfl') then
  begin
    FileWriteInteger(10);              // первое целое число
    FileWriteString('Привет, мир!');   // строка
    FileWriteInteger(20);              // второе целое число
    // ... и т.д.
  end;
end;
При чтении, чтобы добраться до второго числа в файле, Вы должны выполнить чтение первых двух параметров в той же последовательности.

Код: (delphi)
var
  I: Integer;
  S: UTF8String;
begin
  if FileOpen('MyFile.sfl') then
  begin
    I := FileReadInteger;    // первое целое число
    S := FileReadString;     // строка
    I := FileReadInteger;    // второе целое число
    // ... и т.д.
  end;
end;
Для случая, когда в файле хранятся строки (т.е. динамические данные), только так. Т.е. чтобы переместиться в файле на длину строки - нужно её считать. Как мы поняли Вы хотите хранить в одном файле несколько рецептов, а затем, при необходимости, загружать нужный рецепт из файла. А так как рецепт содержит строки, быстро перейти к нужному рецепту в файле не получается. Нужно будет читать рецепты до него, как в примере выше. Если это так, то здесь можно просто хранить рецепты в разных файлах и всегда читать нужный рецепт, без лишних данных.

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

Работу с типизированными файлами возможно добавим позже. Но нужно понимать, что типизированные файлы обычно не несут ничего хорошего, кроме удобного перемещения по файлу. Ведь для того, чтобы хранить в таком файле, к примеру строки и числа, придется всё записывать в виде строк, что вынуждает конвертировать числа в строку перед записью в файл, и наоборот, строку в число при чтении из файла.
« Изменён: 26 Мая 2018, 10:22:53 от Simple-Scada »

Al_Kuz

  • Новичок
  • *
  • Сообщений: 30
    • Просмотр профиля
Re: Вопросы по скриптам в Simple-Scada 2
« Ответ #29 : 15 Февраля 2016, 23:33:07 »
Цитировать
можно просто хранить рецепты в разных файлах и всегда читать нужный рецепт, без лишних данных.

Цитировать
Если хранить их нужно строго в одном файле, то можно предложить ещё одно решение. Перед сохранением рецепта в файл, можно сохранять в него и длину рецепта (т.е. суммарную длину всех параметров рецепта), как целочисленное значение.
Эти варианты я рассматривал. В первом варианте нужно создать может быть полсотню файлов и не понятно как искать
нужный файл. Во втором - можно только дополнять файл, если отредактировать нужный рецепт, то все последующие будут не доступны для чтения. Идеальный вариант как в Дельфи:

Код: (delphi)
type
TRecipe = record //Создаем структуру типа запись
  number:integer;
  name  :string[16];
  weight:integer;
 end;
 
var
 Recipe: TRecipe; //переменная записи
 RecipeF: file of TRecipe; //файловая переменная

begin
    Recipe.number:= Field1.Value;//присвоение значений
    Recipe.name:= Field2.Value;
    Recipe.weight:= Field3.Value;
       
  write(RecipeF,Recipe); //записать данные в файл. Каждая запись имеет строго определенную длину.
end. //Как то так. Вы и без меня знаете.
« Изменён: 26 Мая 2018, 10:23:39 от Simple-Scada »