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

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

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

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


Сообщения - pan2000

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

...наверное, можно как то автоматически их обнулять...

  Кнопка/кнопки изменяют состояния по действиям оператора и изменению состояния установки. Пример для некоторой модели установки во вложении.
Модель установки на основе реверсивного счетчика, по значениям переменных SCi_pusk и SCi_stop формирует временные диаграммы сигналов состояния системы в переменной SCo_Kod__Sostoyanie_:
- бит 0: 1 - система стоит, 0 - работает;
- бит 5: 32 - жалюзи открываются/закрываются, 0 - в покое;
- бит 10: 1024 - двигатель включен, 0 - выключен.

1. Кнопка имеет четыре состояния: ПУСК, ОТКР., СТОП, ЗАКР. Основная переменная связана с вспомогательной переменной состояния кнопки, дополнительная с переменной состояния системы.
В состояниях ОТКР. и ЗАКР. кнопка недоступна, а переход к следующему состоянию определяется значением переменной состояния системы.
Скрипты по изменению основной и дополнительной переменных:
Код: (delphi)
begin
  with Sender as TM_Object do
  begin
    Enabled := not GetBit(AsInt, 0);  // недоступна в нечетных состояниях кнопки (ОТКР. и ЗАКР.)
// конверсия состояний кнопки в состояние управляющих переменных
    case AsInt of
    0: begin SCi_pusk.Value := false; SCi_stop.Value := false; end;
    1: begin SCi_pusk.Value := true; SCi_stop.Value := false; end;
//    2: begin SCi_pusk.Value := true; SCi_stop.Value := false; end;
    3: begin SCi_pusk.Value := false; SCi_stop.Value := true; end;
    end;
  end;
end.
Код: (delphi)
// обратная связь от установки
begin
  with Sender as TM_Object do
  begin
    case AsInt of
    1: if GetBit(VariableEx.AsInt, 10) then value := 2;       // от включения двигателя
//    3: if not GetBit(VariableEx.AsInt, 5) then value := 0;    // от закрытых жалюзи
    3: if GetBit(VariableEx.AsInt, 0) then value := 0;        // от состояния системы
    end;
  end;
end.

2. Пара двоичных кнопок с общим вектором состояния, Однако в этом случае скрипты становятся сложнее, а информативность ниже, т.к. допустимы только три состояния вектора (запрещено - доступны одновременно ПУСК и СТОП). 

17
   Здравствуйте.
Условие:
по сути, притенять кнопку нужно от момента нажатия, до значения переменной 1024. после этого она должная стать активной и перейти в состояние 2
предполагает наличие трёх состояний и, соответственно трех графических образов, причем собственно нажатие кнопки переведет её в следующее состояние.
Возможные варианты создания третьего притемненного состояние:
- при нажатии кнопка становится невидимой, графический объект (Текст, Кнопка или ...) размещенный непосредственно под кнопкой изображает притемненное состояние. По достижении условия кнопка вновь становится видимой;
- добавление третьего (притемненного) состояния к кнопке. в котором кнопка становится "Недоступной". По достижении условия кнопка переходит в состояние "Стоп" и вновь становится доступной;
- аналогично предыдущему варианту, но с использованием автомата состояний. Кнопка прикрыта сверху прозрачным объектом, управляющим автоматом с переходами по нажатию: "Пуск"->"Притемнение", "Притемнение"->"Притемнение", "Стоп"->"Пуск". По достижении условия автомат переходит в состояние "Стоп".
Притемненное состояние кнопки может использоваться для индикации промежуточных состояний (разумеется если они могут быть выделены):
ПУСК -> двигатель запускается -> жалюзи открывается -> что-то ещё случается -> СТОП
Примеры вариантов во вложении.

18
  Здравствуйте.
Последовательность действий для кнопки с инвертированием состояния:
- проверить что выбранная по клику ЛКМ ячейка это именно кнопка (варианты: переключение точно по кнопке или по строке);
- инвертировать бит переменной определяющий состояние кнопки (переменная привязана к дополнительной переменной ячейки);
- изменить индикацию кнопки-ячейки по состоянию бита.
Скрипт по событию OnCellClick:
Код: (delphi)
begin
  with Cell do
  begin
    if (Col <> 3) or (Row <> 1) then exit;                // клик ЛКМ не на кнопке - выход
    VariableEx.Value := InverseBit(VariableEx.Value, 0);  // инвертировать бит
    if GetBit(VariableEx.AsInt, 0) then                   // изменить индикацию кнопки-ячейки
    begin                                                 //  по текущему состоянию бита
      Text := 'Выкл';
      Color := clGreen;
    end
    else
    begin
      Text := 'Вкл';
      Color := clRed;
    end;
  end;
end.
В редакторе должны быть установлены цвет и текст ячейки в соответствии с начальным состоянием бита переменной.

При большом количестве кнопок проверка принадлежности ячейки к множеству кнопок становится весьма громоздкой. Для упрощения можно использовать определенные соглашения:
- дополнительная переменная есть только у кнопок;
- оператор case по выражению (Col * 10000 + Row) с возможностью выбора номера бита переменной (пример во вложении), и т.д.


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

 Можно получить выборку последовательным выполнением трёх запросов для временных общих табличных выражений с использованием оконных функций выбора предыдущего/последующего значений LAG и LEAD:
- выделение переходов значений value с фильтрацией по идентификатору переменной id и, возможно, по интервалу времени для timestamp. Поиск начинается с перехода 0->1, для первой строки в качестве предыдущего значения выбирается 0:

SELECT id,timestamp AS time1,value AS value1,LAG(value,1,0) OVER() AS value2 FROM trends_data WHERE id=1;

- формирование временных интервалов для значений value=1 и value=0:

WITH ttemp1 AS (SELECT id,timestamp AS time1,value AS value1,LAG(value,1,0) OVER() AS value2 FROM trends_data WHERE id=1) SELECT id,time1,LEAD(time1,1,' ещё не завершен') OVER() AS time2,value1,value2 FROM ttemp1 WHERE value1<>value2;

- прореживание интервалов только для значений value=1:

WITH ttemp2 AS (WITH ttemp1 AS (SELECT id,timestamp AS time1,value AS value1,LAG(value,1,0) OVER() AS value2 FROM trends_data WHERE id=1) SELECT id,time1,LEAD(time1,1,' ещё не завершен') OVER() AS time2,value1,value2 FROM ttemp1 WHERE value1<>value2) SELECT * FROM ttemp2 WHERE value1=1;


20
Ошибки / Артефакты функции ArchiveValueByTime
« : 02 Декабря 2024, 01:57:47 »
     Здравствуйте.

Значение функции ArchiveValueByTime отличается от значения из архива при одинаковой временной метке.

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

Управление:
- кнопка "ОСТАНОВИТЬ ЗАПИСЬ"/"ПРОДОЛЖИТЬ" - останов/продолжение счета последовательности (запись в архив по изменению переменной);
- кнопка "ЗАПОЛНИТЬ ТАБЛИЦУ" - запись в таблицу;
- кнопка "ВЫЧИСЛИТЬ ФУНКЦИЮ" - вычислить функцию по всем строкам;
- параметр "Сдвиг, сек" [-20..20] - сдвиг значения параметра функции.

Собственно тестирование:
- предварительно желательно очистить таблицы проекта и контролировать таблицу трендов средствами БД.
- запустить проект и подождать пока значение последовательности достигнет значения больше 32 (ожидаемая запись буферов), заполнить таблицу;
- вычислить функцию и/или одиночное вычисление/перевычисление функции по ЛКМ на строке;
- изменяя значение параметра "Сдвиг" можно добиться совпадения значений функции со значениями из таблицы. (магическое значение параметра равно -9). Магическое значение сохраняется и для последовательности с периодом 2 секунды.

Ещё есть предположение, что последнее значение (уже архивированное или в буфере) не требует сдвига.

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

Во вложении проект исправленный для чтения из CSV-файла, имена параметров обезличены от физического смысла и называются "Параметр для верхней строки" и "Параметр для первого столбца". 

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

  Двумерная калибровка включает следующие действия:
- чтение таблицы калибровки из файла в динамический массив при запуске проекта. Значений параметров температуры и давления хранятся столбце и строке с нулевым индексом;
- поиск для давления и температуры пары индексов интервала значений параметров, включающих давление и температуру;
- вычисление интерполированного значения из таблицы: совпадение индексов в обоих парах - выборка из таблицы, при одной совпадающей паре индексов - линейная интерполяция, индексы в обоих парах различны - двумерная интерполяция. Значения температуры и/или давления вне таблицы считаютcя соответствующими граничными значениями. 

Пример таблицы калибровки, комментарии строк показаны исключительно для описания структуры, в файле комментарии не допускаются:                                                           
фрагмент таблицы из  https://studfile.net/html/2706/401/html_WtevgQQelM.i4K9/img-S7y52D.png    // заголовок, игнорируется при загрузке файла
000   1.0   1.1   1.5   2.0   5.0  15.0          // 000 - исключительно для сохранения числа переменных в строке, переменные это значения давления для соответствующего столбца
 60 219.1 213.9 218.6 218.5 216.6 208.0     // первое число - значение температуры, далее значения энтальпии для соответствующих значений температуры и давления
 70 223.5 223.3 223.0 222.9 221.1 213.3     // разделитель чисел - пробел, пробелы перед числом игнорируются, для всех строк обязательно одинаковое количество чисел

Пример во вложении. Вывод отладочной информации позволяет изучить алгоритм проекта.

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

  Можно превратить шаблон в подобие объекта пользователя (или "сгруппированный объект"), характеризующегося поведением шаблона как единого целого. Условно можно разделить общие свойства шаблона на две части - не изменяющие геометрию составляющих объектов: перемещение, управление цветом, доступом и видимостью, так и изменяющие: масштабирование, поворот. Последних следует избегать, так как есть возможность (особенно для WEB-клиента) появление артефактов на картинке.
  Для начала нужно при инициализации шаблона связать объекты посредством последовательных ссылок в UserData, что позволит изменять объект без использования его переменных. Такая последовательность обезличена, можно использовать один универсальный скрипт. Аналогичную последовательность ссылок можно создать при запуске проекта и для группы обычных объектов.
  Управления свойствами шаблона осуществляется по изменению переменных, связанных с первым объектом последовательности (т.е. последовательность нужно начинать с объекта не использующего переменные проекта или с дополнительного объекта, предназначенного исключительно для управления свойствами шаблона).
  Во вложении пример циклического перемещения шаблона по Х-координате в пределах экрана. Для иллюстрации экзотических возможностей в пример добавлена простейшая операция зеркального отражения шаблона по Х (изменение знака ширины объекта). Однако, как и следовало ожидать, простота оборачивается артефактами картинки, особенно для WEB-клиента.



24
   Здравствуйте.
  Единственный слоёный объект по части раздачи окон - это кнопка вызова окна. Само окно ни с того ни с сего, [внушительно перебил неивестный,] не откроется. Да и в волчьем окне любой поросенок может быть поражен в правах доступа, равно как и наоборот.
  На мой взгляд, с точки зрения минимума затрат создания "картинки", слоёная структура превосходит вычислительно-модифицируемую, приемлимую для одноклиентских проектов (либо исскуственно сведенную к оным)
  Если есть возможность, то в имена объектов стопки можно добавить признак "стопочности", причем при удачном стечении обстоятельств вся стопка в инспектор объектов потянется за верхним. Кроме того в проект можно включить страницы с некоторыми пояснениями для продолжателей Вашего проекта.

P.S. Возможно несколько облегчит жизнь возможность ещё и переключение редактора с инспектором объектов в режим отдельного пользователя, обеспечивающий отображение его объектов и общих. Хотя ...

25
     Здравствуйте.
  Если открывать окно именно по событию OnClick, не используя свойство "Окно" объекта, то скриптом можно открыть нужное пользователю окно. В примере три группы пользователей - с индивидуальным окном, с монопольным окном и общим окном на группу.
  Монопольное окно исключает ситуацию, когда два пользователя "загибают" одну переменную в противоположные стороны, а третий с интересом наблюдает за ними (система коллективной безответственности им. Лебедя, Рака и Щуки).

26
   Здравствуйте.
...
1. Поля привязаны к переменным (привязываются при инициализации автоматически, примерно как в вашем скрипте при сбросе перебор полей, а не через подстановки)
2. Значение после сброса у каждого поля своё и берется их других переменных (т.е. дополнительная переменная уже занята полезным делом)
...
...либо пытается сделать рутину проще. Например, метод подстановки для шаблонов по сути означает, что имя переменной надо ввести вручную, а не выбрать из списка переменных, что сильно повышает вероятность ошибки ввода, найти которую потом будет сложно...
   Предпосылки для примера из вложения:
1. Все имена переменных и объектов имеют структуру <префикс>_<индекс от 1 до ...> и желательны непрерывные значения индекса;
2. У шаблона единственная подстановка - номер шаблона;
3. Объект связи должен иметь индекс совпадающий с номером шаблона и используется для передачи начальных имен переменной. Нужен для исключения ошибок редактирования, т.к. переменные выбираются из списка. Хотя по мне, так два имени вполне могут быть включены и в подстановки шаблона. Использование объекта оправдано при передаче чего-то из шаблона.
4. При инициализации шаблона выполняется несколько видов контроля - наличие объекта связи, наличие обоих переменных у объекта связи, наличие пар переменных с допустимыми префиксами.
5. Вид экземпляра шаблона настраивается по числу переменных.

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

  Ещё одним способом доступа к объектам шаблона является получение при инициализации шаблона цепочки ссылок на следующий объект начиная с объекта источника и передаче в приёмник ссылки на объект-источник.
  Примеры можно найти на форуме по поисковому запросу "Шаблон".

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

считаю вопрос закрыт тк мой скрипт криво, но справляется с уведомлениями.
Для однократной отправки сообщения необходим флаг "сообщение отправлено", причем в качестве оного можно использовать свойство Color объекта.
Вполне возможно, что дополнительно придется добавить гистерезис температуры для исключения отправки сообщений при "болтанке" температуры вблизи границы:
Код: (delphi)
const dT = 3;   // гистерезис температуры
begin
   if Sender is TM_Object then     // проверяем, что Sender это объект
     with Sender as TM_Object do   // приводим Sender к типу "TM_Object"
       if Color <> clRed then
       begin
         if AsSingle >= VariableEx.AsSingle then
         begin
           Color := clRed;
           AddMessage(Now, mkWarning, Variable.Description + ' превысила ' + Variable.AsStr + ' градуса ', True, True);
         end;
       end
      else
        if AsSingle < (VariableEx.AsSingle - dT) then Color := RGB(191,191,191);
end.
Для выполнения контроля при изменении границы можно добавить скрипт по изменению дополнительной переменной (границы):
Код: (delphi)
begin
    Sender.Color := RGB(191,191,191);     // обязательно выполнить сравнение с границей
    TM_Object(Sender).OnDataChangeEvent;
end.

Вполне рабочий вариант с использованием границ переменных.
{удалено, чтобы не повторять написанное Simple_Scada выше}

Во вложении два примера как со скриптами по изменению переменных, так и по нарушению границы. Как говорится, найдите 3 отличия.

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

  Запрос к БД должен "расыпать" один столбец данных исходной таблицы на три столбца данных, причем с использованием фильтрации по ID данных и по времени.
  На фильтр по времени существнно влияет как формируется временная метка записи в БД, либо от внешней архивируемой переменной, либо от внутренней по временным событиям scada-сервера.
  В примере из вложения фильтр по одиночному значению метки времени в пределах первой минуты часа/получаса. При отсутствии метки времени в пределах фильтрации, выдается сообщение "нет данных". ЛКМ на выбранной ячейке удаляет соответствующую строку тестовой таблицы.
  Предварительно строится временная таблица меток времени от начала недели с интервалом в 12 часов, далее формируется запрос на объединение таблицы меток и трех таблиц данных, полученных подзапросами.
 
  P.S. Иллюзия "игнорирования" - в разделе "Ваши вопросы" обнаружено всего 12 вопросов без ответов :), да и то многие из них предполагают отсутствие ответов. 

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

  Запрос к БД должен содержать фильтры, ограничивающие выборку данных только за одну предыдущую неделю с прореживанием данных.
  Простой фильтры предполагает, что метки времени попадают в первый минутный интервал периода измерения, т.е. минуты = 0 или 30, а секунды = 00..59:
 
Код: (delphi)
var aFirstDay: TDateTime;
begin
  aFirstDay := IncDay(DATE, - (6 + integer(DayOfTheWeek(Now))));                    // первый день пред. недели
  Table5.RunSQL('SELECT DATE_FORMAT(`t`,''%d.%m.%Y  %H:%i'')'                       // форматирование даты-времени
              + ',FORMAT(`T_3`,2),FORMAT(`T_6`,2),FORMAT(`T_7`,2) FROM `my_table` ' // форматирование температур
              + 'WHERE (MINUTE(`t`) = 0) '                                          // фильтр: минуты=0 или начало часа
              + 'AND ((HOUR(`t`) MOD 12) = 0) '                                     // фильтр: час=0 или час=12
              + 'AND (`t` >=' + MySQLDateTime(aFirstDay, dttHour)                   // фильтр: предыдущая неделя - начало
              + ') AND (`t` <' + MySQLDateTime(IncDay(aFirstDay, 7), dttHour)       //         предыдущая неделя - конец
              + ') ;',
              tsSaveFixRow);
end.
Во вложении пример построения исходной таблицы за две предшествующие и начало текущей недели. Построение таблицы результатов.

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