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

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

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

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


Сообщения - pan2000

Страницы: [1] 2 3 ... 15
1
     Здравствуйте.

Фантазия-набросок без гарантий (ошибки всегда возможны, да и излишества разные) на Вашу тему.
Проект включает две части: запись данных в БД и просмотр результатов:
- набор тележек, на каждую помещается партия материала, т.е. вводится номер с контролем уникальности номера. При первом перемещении тележки в камеру в таблицу учета партий записывается время начала, номер партии и прочее. Далее оператор перемещает тележку по камерам.  Считается, что при помещении в камеру партия моментально приобретает температуру камеры, а при извлечении моментально охлаждается. Что и записывается в БД.
С каждой тележкой связана таблица содержащее номер партии, метку времени и температуру(запись в таблицу по изменению). По завершении процесса испытаний в таблицу учета записывается время окончания;
- просмотр результатов заключается в установке минимальной и максимальной температур контроля и числа равномерно распределённых зон контроля температуры (всё записываются в таблицу БД). Для выбора номера партии используется выпадающий список номеров-комментариев. После выбора номера-комментария происходит заполнение таблицы интервалов времени по температурным зонам и строится гистограмма распределения температур.
Для тестирования в камере 1 температура принимает 2 значения с периодом 2 сек, в камере 2 температура изменяется раз в секунду случайным образом в диапазоне от 0 до 100. Данные первой тележки (номер партии только для иллюстрации и ограничен шкалой до 200, температура) архивируются стандартным образом.

Можно установить период изменения температуры в камере в любых "попугаях", в том числе и по 10 минут, возможно есть смысл за это время считать среднюю температуру по камере. При необходимости в таблицу учета можно записывать все "телодвижения" тележки с возможностью просмотра данных по отдельным камерам.

Управление:
"Захват" тележки кликом ЛКМ:
- блокируется доступ к пустым тележкам для других клиентов;
- вызывается окно редактирования номера-комментария;
- после ввода номера выполняется его проверка на уникальность по таблице учёта;
- после успешной проверки появляется кнопка "ДОБАВИТЬ", при нажати которой тележка выбирается (зелёный бордюр) и получает номер партии, окно закрывается;   
- далее кликом ЛКМ по месту назначения тележки либо перемещают её в камеру, либо освобождают, перемещая на область "Завершить" .
Перемещение тележки: выбор тележки кликом ЛКМ, для перемещения клик ЛКМ по месту назначения. Допустимые переходы указаны стрелками.
Построение таблицы температурных зон - всё очевидно, вариации таблицы позволяют детальнее рассмотреть некоторый диапазон температур.
Построение гистограммы времени нахождения в температурных зонах, не работает на WEB-клиентах:
- выбор номера партии из выпадающего списка и построение гистограммы;
- повторное построение по кнопке "ОБНОВИТЬ",
- флажок "Хвост: обрезан/до сейчас" - последний незавершенный временной интервал игнорируется или считается до текущего времени;
- флажок "Показать превышение" включает в гистограмму данные о превышении максимума таблицы температурных зон.
Кнопка "Eº" редактирование комментария выбранной партии. Запись по завершению ввода в строку редактирования. Отказ - повторное нажатие кнопки.   
Клик ЛКМ по столбику диаграммы - вызов окна детализации параметров этого столбика.

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

Нет необходимости в дублировании значения кнопки в свойство "Тег":
Код: (delphi)
    if AsInt = States[0].Value then  // если значение переменной кнопки равно значению,
      Color := {Tag //} clGreen      //   указанному в свойстве "Значение"(т.е именно она и нажата последней), то изменить цвет кнопки на зеленый
    else                             //   иначе
      Color := $6D6D6D;     //  не совсем понятно что делает SetInitialColor с кнопкой;               //   восстановить исходный цвет кнопки
Что касается исходного цвета кнопки, то оператор SetInitialColor более универсален, чем явное присвоение цвета (но не для кнопки).
Если есть необходимость в различном цвете нажатой кнопки, то для задания цвета можно использовать свойство "Тег".

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

Относительно возможности масштабирования/перемещения большой карты с объектами:
- операторы не смогут работать с картой в привычном режиме - захватил мышкой и потащил, если что не так видно, то колёсико покрутил;
- большое количество объектов не масштабируемых корректно/в принципе, в т.ч. и трубопроводы. А в WEB-клиенте и того больше, включая текст и значение поля, т.е. WEB-клиенты практически не поддерживаются;
- сложность редактирования для больших карт, выходящих за пределы разрешенной области редактирования (при крупном масштабе около 4000х2300, в примере изображение карты 10000х10000), определяемой как откат изображения назад при попытке сдвинуть его за границу этой области. Для редактирования дальних областей выбор всех объектов со сдвигом в нужную сторону, причем одно ошибочное действие может развалить всю картинку;
- значительная трудоёмкость проектирования, например изображение трубопровода, которое состоит из последовательности изображений трубы, уголка и тройника (для реализма ещё и фланец со сдвоенным фланцем). Правда есть и положительная сторона - можно прокладывать под любыми углами. Аналогично линии, резервуары, клапаны и т.д.;

Для перемещения карты можно использовать пару ползунков-уровней или четыре узкие кнопки по сторонам экрана. Для масштабирования - две кнопки уменьшения и увеличения масштаба.
Можно сократить количество перемещений посредством предустановленного списка объектов для быстрого перехода. Объект выбирается по названию/описанию и позиционируется по центру экрана в масштабе 1.00. К быстрому переходу можно добавить/удалить ограниченное число меток-объектов (вид "кольцо", в редакторе всегда можно добавить простым копированием). Выбор метки из списка осуществляется по её комментарию, заданному перед постановкой метки.
Пример во вложении, текстуры проекта в папке "Карта_с_объектами_на_главной_странице", расположенной в папке "User files" проекта. Предварительно папку целиком необходимо скопировать/перенести в папку "Pictures"

4
Ваши вопросы / Re: Элемент Флажок
« : 18 Ноября 2025, 13:51:01 »
   Здравствуйте.

 Можно построить вектор состояния действий, в котором каждый бит соответствует одному действию и биты упорядочены справа-налево в порядке следования действий.
Пока порядок не нарушен, у каждого бита = 0 все "левые" соседи будут равны 0, что проверяется обычным сравнение целых чисел - значением вектора и 2 в степени номер бита + 1.
Скрипт изменилась основная переменная объекта "Флажок":
Код: (delphi)
begin
  with Sender as TM_CheckBox do
    if (not Checked) and (AsInt >= Setbit(0, BitNumber + 1, true)) then // флажок не установлен, но есть "левые" установленные биты
       Text1.Text := Text1.Text + Caption + '  пропущен' + #10;
end.
Уменьшить количество вызовов предыдущего скрипта на одно нажатие флажка (по числу флажков) можно до одного скрипта по изменению переменной (с потерей простого доступа к свойствам флажка):
Код: (delphi)
begin
 var i: integer;
    b: boolean;
begin
  with Sender as TM_Text do
  begin
    Text := '';
    i := 0;
    b := AsInt <> 0;
    while b and (i < 31) do
    begin
      b := AsInt >= Setbit(0, i + 1, true); // признак наличия "левых"
      if (not GetBit(AsInt, i)) and b then  // флажок не установлен, но есть "левые" установленные биты
        Text := Text + 'пропущен этап ' + IntToStr(i + 1) + #10;
      i := i + 1;
    end;
  end;
end.
Пример во вложении.

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

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

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

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

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

Для многокадрового изображения возможные варианты: изменение свойства FlashColor по номеру кадра или управлением дополнительными кадрами по номеру и таймеру.
См. пример из вложения. Перед первым вызвом редактора необходимо скопировать файл индикатора из "User Files" проекта в "Pictures" сервера.
Более изощренный способ - стопка из трёх изображений: одно статическое для устойчивых состояний заслонки (открыта, закрыта, промежуточное) и два с анимацией и/или миганием для переходных состояний (открывается, закрывается).

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

Для цветового индикатора подходит объект "фигура", цвето-текстового "Кнопка" или пара "Фигура" - "Текст".
Собственно сам способ реализации индикатора зависит от количества переменных определяющих состояние индикатора: менее трех - объект с изменяемыми свойствами, более - стопка объектов с управлением видимостью.
В примере три варианта индикатора:
- простая последовательность [0-4];
- пара регистров "Управление" и "Состояние" с битовыми полями;
- четыре отдельных булевых переменных, индикатор - шаблон.

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

Добрый день. Вопрос в следующем:
Хочу реализовать выбор режима работы по расписанию или по кнопке. Думаю реализовать с помощью метода OnActive.
При наличии ручного режима для управления ОРС-переменной хорошо подходит кнопка с двумя состояниями. Кнопку можно настроить либо на запись двух значений, либо на изменение одиночного бита.
А расписание уже просто управляет этой кнопкой, недоступной/невидимой.  В примере с двумя кнопками - "РУЧНОЕ-АВТОМАТ" и "ВЫКЛ.-ВКЛ." приведены два вида шаблонного окна - кнопки в окне или кнопки на странице.
Правила функционирования кнопок:
- кнопка "ВЫКЛ.-ВКЛ." непосредственно управляет ОРС-переменной, либо пара значений, либо одиночный бит. Для каждой пары шаблонное окно уникально, для одиночных бит достаточно одного шаблонного окна (для кнопок в окне +1 подстановка "номер бита");
- кнопка "РУЧНОЕ-АВТОМАТ" управляет видимостью или доступностью кнопки "ВЫКЛ.-ВКЛ." (для кнопок на странице может быть индикатором состояния ОРС-переменной);
- В состоянии "АВТОМАТ" расписание управляет кнопкой "ВЫКЛ.-ВКЛ." по значению основной переменной, к которой (по ID и имени проекта) собственно и привязана таблица интервалов этого расписания. В случае двух режимов использовать событий OnActivate и OnDeacnivate нецелесообразно, т.к. для правильного изменения значения ОРС-переменной при переключении режимов нужно знать текущее состояние расписания (внутри/вне интервала) и, следовательно придется его восстанавливать по событиям (что собственно и есть значение основной переменной).

Особенности варианта с кнопками на странице (поскольку приходится связывать реальные и виртуальные объекты, а очень желательно обойтись без уникальных скриптов для каждого), причем достаточно всего одного шаблонного окна:
- создать кнопку управления режимами "РУЧНОЕ-АВТОМАТ" с основной переменной и без событий;
- создать кнопку управления ОРС-переменной "ВЫКЛ.-ВКЛ." с соответствующим управлением ОРС-переменной и без событий;
- в подстановке шаблонного окна "кнопка_режима</>кнопка_переменной" записать имена кнопок через знак "наклонная черта";
- в подстановке шаблонного окна "переменная_расписания" указать уникальную переменную.
Связи (цепочка ссылок на объекты) устанавливаются при инициализации конкретного экземпляра шаблонного окна.

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

... Заказчик попросил, чтобы при возникновении критических аварий, всплывало окно с аварийным оповещением ...
Всплывающее окно может содержать гораздо больше информации чем сообщение. В примере индикатором аварий выступает вектор аварий у которого каждая авария связана с определенным битом.
При возникновении любой аварии открывается окно с мигающей рамкой (в примере для всех клиентов, т.к. авария это серверное событие, но для многопользовательского проекта окно должно быть связано именно с пользователем и необходимо селективное открывание окна, например только для первого присутствующего из списка пользователей). 
Каждой из трёх аварий соответствует сообщение о причинах аварии и кнопка "Подтверждение" для сброса бита аварии.
Окно закроется только после подтверждения всех аварий.
Возможны дополнительные действия/сообщения при некоторой комбинации аварий, например при всех трех.

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

Для начала нужно определить в какое состояние переходит вентилятор при отключении расписания (сохраняет текущее, выключен), а дальше два варианта:
- перевод расписания в неактивное состояние с последующей блокировкой его редактирования;
- блокировка формирования управляющих сигналов без блокировки редактирования.
Во вложении пример с кнопкой переключения режимов редактирование-работа, причем в режиме редактирования блокируются управляющие сигналы, сохраняется состояние вентилятора и разрешается режим ручного управления вентилятором.
Так что если оператор включит редактирование при работающем вентиляторе и просто уйдет на обед, то вентилятор продолжит дуть :-\

11
   Здравствуйте.
...записывает себе в базу данных очень маленькие значения переменных. ... Приходится удалять их вручную...
Т.е. речь идет от отбрасывании заведомо недопустимых значений.

Если найти причину образования некорректных данных поступающих в скада-систему не получится, то можно попробовать отфильтровать некорректные значения переменной... в настройках переменной включить Фильтр мин. макс.
Судя по описанию и интерактивной картинке фильтра в редакторе, "Фильтр мин. макс" не отбрасывает недопустимое значение, а конвертирует его в минимум/максимум.
На картинке зеленым цветом показано поведение переменной при условии игнорирования недопустимых значений. Хорошо видно, что площадь под кривой заметно различается для фильтра и при отбрасывании значения.
В этом случае отчет-то может и будет строится, а вот его корректность ещё придется доказать (понятно, что недопустимые значения весьма редкие, и отчет скорее всего не пострадает, но "не аккуратненько").

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

Для начала нужно убедиться, что от ОРС-сервера не поступают ошибочные данные. Индикация наличия ошибочных - скрипт "Изменились переменные":
Код: (delphi)
// ОТЛОВ МИНИМАЛЬНЫХ ЗНАЧЕНИЙ
begin
  with Variable do
// выбор минимального порога
//    if (Value <= LowAlarm) and (not IsFirstChange) then  // индивидуальный
    if (Value <= 5) and (not IsFirstChange) then       // общий

      AddMessage(Now, mkAlarm, Name + '=' + AsStr, True, True);
end.
При наличии ошибок желательно их ликвидировать как явление.
И как последнее средство - введение фильтра минимальных значений перед архивированием. Т.е. переменная разделяется на входную (не архивируемую), по изменению которой происходит контроль величины и выходную (архивируемую), перезаписываемую из входной при прохождении контроля.
При условии, что имена входной и выходной переменной отличаются только суффиксом у выходной переменной - скрипт по событию "Изменились переменные":
Код: (delphi)
// ФИЛЬТР МИНИМАЛЬНЫХ ЗНАЧЕНИЙ
begin
  with Variable do
// выбор минимального порога
//    if (Value > LowAlarm) and (not IsFirstChange) then
    if (Value > 5) and (not IsFirstChange) then

      GetVariableByName(Name + '_out').Value := Value;
end.
См. пример.

13
Ваши вопросы / Re: Вопрос по рассписанию.
« : 29 Апреля 2025, 02:20:35 »
   Здравствуйте.

  Поскольку нет аппаратного условия сброса управляющих сигналов, то остается формировать длительности управляющих сигналов сервером Simple-Scada.
Скрипт по событию "Текущее время вошло в установленный интервал объекта "Расписание"":
Код: (delphi)
begin
  SCI_Pusk.Value := true;
  RunDelay(3000, 1);
end.
Скрипт по событию "Текущее время вышло из установленного интервала объекта "Расписание"":
Код: (delphi)
begin
  SCI_Stop.Value := true;
  RunDelay(3000, 2);
end.
Скрипт по событию "Выполнена задержка":
Код: (delphi)
begin
  case DelayEventData.Tag of
  1: SCI_Pusk.Value := false;
  2: SCI_Stop.Value := false;
  end;
end.
Пример во вложении.

14
Ваши вопросы / Re: Границы переменных
« : 25 Апреля 2025, 00:57:48 »
   Здравствуйте.

Появление баннера при нарушении "Верхней аварийной" границы и пропадание по выходу из зоны нечувствительности:

1.Если для прочих нужд проекта не используется "Верхняя предупредительная" граница, скрипт "Нарушена граница":
Код: (delphi)
begin
  Shape5.Visible := Variable.GetValueZone = 2;
end.
, в вариациях задания зоны нечувствительности:
- "Только аварийные" границы - сама "Зона нечувствительности";
- "Аварийные и предупредительные" границы - либо нижняя граница зоны нечувствительности задается "Верхней предупредительной" границей при "Зоне нечувствительности"=0, либо "Зоной нечувствительности" при "Верхней предупредительной"="Верхней аварийной".

2. Без использования стандартной процедуры контроля границ, скрипт "Изменилась переменная":
- с нулевой зоной:
Код: (delphi)
begin
  Shape5.Visible := Variable.AsFloat >= Variable.HighAlarm;
end.
- с ненулевой зоной:
Код: (delphi)
const hist = 4.00;  // величина зоны в единицах измерения переменной
begin
  if (Variable.AsFloat >= Variable.HighAlarm) then Shape5.Visible := true;
  if Shape5.Visible and (Variable.AsFloat < (Variable.HighAlarm - hist)) then Shape5.Visible := false;
end.
.
Вариант 1 более предпочтительный, т.к. и скрипт проще, и событий меньше. Примеры для некоторых вариантов во вложении.

15
Ваши вопросы / Re: Работа с таблицами
« : 16 Апреля 2025, 22:12:31 »
    Здравствуйте.

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

Замечание по скрипту обработки SQL-запроса - к сожалению окончание скрипта не видно, но если у условия "if DataSet.IsEmpty = false then" нет альтернативы "else" с очисткой таблицы, то группа тележек, умудрившаяся выкатиться, полностью освободив помещение в течение последней минуты, может и не удалиться из таблиц формы.
Можно обрезать "жирок" со скриптов:
Код: (delphi)
begin
  // запрашиваем первые до 20 строк
  RunSQL('SELECT numberCart,timeCart1,timeCart2 FROM cart1 LIMIT 20;', Table1, 3);
  // запрашиваем остаток строк
  RunSQL('SELECT numberCart,timeCart1,timeCart2 FROM cart1 LIMIT 20 OFFSET 20;', Table2, 3);
end.
Код: (delphi)
var aRow, i: integer;
    aColor: cardinal;
begin
  if DataSet.Tag = 3 then
    with DataSet.Sender as TM_Table do
      for aRow := 0 to 19 do
        if not DataSet.EOF then
        begin
          if DataSet.Fields[2].AsInt > dryingTime1.AsInt then aColor := clGreen
          else aColor := clRed;
          for i := 0 to DataSet.FieldCount - 1 do
          begin
            GetCell(i, aRow).Text := DataSet.Fields[i].AsStr;
            GetCell(i, aRow).Color := aColor;
          end;
          DataSet.Next;
        end
        else
          for i := 0 to 2 do
          begin
            GetCell(i, aRow).Text := '';
            GetCell(i, aRow).Color := clNone;
          end;
end.

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

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

Страницы: [1] 2 3 ... 15