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

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

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

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


Сообщения - pan2000

Страницы: [1] 2 3 ... 16
1
Ваши вопросы / Re: Мультиязычность проекта
« : 19 Октября 2026, 08:58:02 »
Почитываю иногда форум и до сих пор поражаюсь как изощрятся люди при решении простейших задач.

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

  Мультиязычная поддержка минимально должна распространяться на простые объекты, шаблоны (все экземпляры изменяются одинаковым образом), шаблонные окна и сообщения пользователя.
  Для описания мультиязычной поддержки используется текстовый файл. Первая строка содержит названия поддерживаемых языков. Далее следуют группы, включающие строку с именем и свойством объекта простого или входящего в шаблон или номер строки формата сообщения. Далее в группу входит несколько строк (да хоть 100, если не устанете их редактировать) с вариантами текста на различных языках. Простые объекты модифицируются непосредственно, объекты шаблона посредством единой строки-параметра, а сообщения просто заносятся в глобальный массив.
  Поддерживаются следующие классы/объекты TM_Control (Caption, Hint), TM_Window (Title), TM_Field и TM_Text (Text), TM_Button (States[].Caption), TM_ComboBox (Clear, AddItem()), TM_Table (Title, запись столбца, запись строки), шаблоны и шаблонные окна.
  Создание мультиязычной поддержки достаточно трудозатратно и содержит много рутины.
  Пример на 3-языка и примерно 1000 объектов во вложении. Для оценки временных характеристик большего проекта можно просто размножить содержимое текстового файла.
В проекте используется функция FormatStr для разделения сообщения на текстовую часть и подставляемые значения, по мотивам:
Реализовал свою простенькую функцию FormatStr.

2
"Если нельзя, но очень хочется, то можно"

 
Здравствуйте.
Так же хочется окно с текущими значениями переменных в режиме исполнения. В идеале видеть внешнии / внутрении переменные (теги), а так же значения глобальных/локальных переменных в скриптах. Мне кажется, это сильно упростит отладку проектов.

  Текущие значения внешних/внутренних переменных можно просматривать/изменять добавив к существующему проекту окно просмотра, пару-тройку скриптов, несколько внутренних переменных для хранения конфигураций окон и настроив вызовы единственного скрипта для объектов окна.
  Для выбора окна просмотра в нужной конфигурации используется выпадающий список окон (индекс или описание переменной хранения конфигурации). Самый первый элемент списка "Новое окно"- пустое. Заполнение окна происходит по нажатию кнопки "Обновить окно".
  Предусмотрены два режима работы с окном - изменение конфигурации ("Замочек открыт") и просмотр/изменение переменных ("Замочек закрыт"). В режиме изменения конфигурации выбор поля осуществляется нажатием ЛКМ, отмена выбора - повторным нажатием. Выбранное поле можно удалить (кнопка "Корзина"), а при отсутствии выбранного можно добавить новое поле(кнопка "+").
  Для выбора/изменения переменной и заголовка у выбранного поля появляется поле редактирования заголовка и селектор выбора переменной. Если у поля уже есть переменная, то селектор устанавливается именно на эту переменную.  По умолчанию заголовком является описание переменной, в случае длинного описания его можно заменить на более короткое изменив поле редактирования заголовка. Перезапись в заголовок поля по завершению ввода.  Выбор переменной по селектору завершает конфигурирование поля.
  Скрипт "Запуск проекта" содержит константы числа строк окна и размеры поля переменной. По мере добавления полей окно расширяется вниз, по достижении заданного числа строк окна, окно расширяется вправо.
  Изменение количества полей для переменных - при изменении количества необходимо обеспечить непрерывность индексов полей. Для объектов динамически изменяющих положение (поля переменных и редактирования заголовка, селектор выбора переменной) геометрия окна в редакторе не важна.

Последовательность действий добавления окна просмотра в проект:
1. Создать новое окно в проекте. Увеличить его размер, отключить и очистить заголовок. Выбрать и скопировать все объекты из окна примера (Ctrl/A - Ctrl/C). Вставить в окно проекта и сместить вверх-влево к границам окна. Включить заголовок (иначе в редакторе окно не переместить).
2. Перенести скрипты примера в отдельную группу отлаживаемого проекта.
3. Настроить вызовы универсального скрипта: "поля окна", "кнопки", "выпадающий список окон"  - OnClick, "поле редактирования заголовка" - OnDoneInput, "выпадающий список переменных" - OnDataChange.
4. Добавить в отдельную группу переменные конфигураций в необходимом количестве.
5. Настроить вызов окна просмотра, либо скрипт клавиатуры, либо объект с привязанным окном.
После отладки проекта можно удалить компоненты примера или отключить скрипты.

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



3
Ваши вопросы / Re: Расписание.
« : 04 Октября 2026, 00:31:41 »
  Здравствуйте.

  Интервалы расписания можно рассматривать как двумерный массив с фиксированным числом строк (день расписания) и переменным числом столбцов (интервал расписания). Оба индекса массива начинаются с 0. Из текущей даты функцией DayOfTheWeek(Now) можно получить день недели, но в диапазоне 1 - 7.
  Определить время перехода расписания в противоположное, относительно текущего, состояние можно по анализу интервалов. Для удобства границы интервалов считаются в секундах относительно начала суток вызова скрипта.
  Однако следует учитывать, что интервалы дня не упорядочены и могут перекрываться, т.е. анализ следует вести по всем интервалам расписания, а с учётом цикличности расписания - по сегодня дважды.
  Алгоритм заключается в движении по времени суток временной метки, начиная со времени вызова скрипта и соответствующей состоянию расписания на момент вызова. Все интервалы последовательно (а, для включенного состояния ещё и многократно) сравниваются с временной меткой, и по результатам принимается решение о перемещении метки. По достижении конца суток принимается решение о переходе к следующим суткам или завершении процесса.
  Алгоритм работает как при отсутствии интервалов в расписании, так и полностью "закрашенном" в любом порядке расписанием.
  Пример во вложении.

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

Существует скрипт который исполняется раз в секунду, данные накапливаются и выводятся в рапорт, но столкнулся с проблемой что запись в переменные накопления происходят не у всех.
Вопрос: это неправильно сделанный скрипт или что то еще - куда смотреть?

  Проверить на ошибки фазы выполнения проекта. См. журнал сервера.

  Сам скрипт можно проверить тестовыми последовательностями (константа - линейное нарастание результата, пила - весь диапазон входной переменной).

  При выполнении функции возможно изменение значения входной переменной. Значение входной переменной типа TM_Variable следует передавать как типизированное значение (формально параметр по значению передается локальной копией, однако при передаче объекта (экземпляра класса), внутри функции/процедуры работа идет со ссылкой на тот же объект, а не с копией его полей):
Код: (delphi)
procedure CalcWeightSum(FLeistung: single; var WeightSum, WeightDelta: TM_Variable);
begin
   // 1. Проверяем, входят ли текущие показания в допустимый диапазон
   if (0 <= FLeistung) and (FLeistung <= MaxLimit) then
. . .
end;

begin
   CalcWeightSum(FLeistung_BW_P10_1_db256.AsFloat, WeightSum_P10_1, WeightDelta_P10_1);
. . .
end;
или однократным присвоением локальной переменной:
Код: (delphi)
procedure CalcWeightSum(FLeistung: TM_Variable; var WeightSum, WeightDelta: TM_Variable);
var aFLeistung: single;
begin
   aFLeistung := FLeistung.AsFloat;
   // 1. Проверяем, входят ли текущие показания в допустимый диапазон
   if (0 <= aFLeistung) and (aFLeistung <= MaxLimit) then
. . .
end;

Дальнейшие проверки определятся уже из методики обнаружения отсутствия записи.


                                             Добавлено 26.09.2026.

P.S. Замечания по алгоритму скрипта и (в случае архивирования данных) использование функций для работы с архивом. Правило для вышедших за границы значений:
- принимает граничное значение. Архивирование переменной с применением фильтра "Фильтр мин. макс.", функции работы с архивом будут выдавать правильный результат;
- принимает предыдущее значение или игнорируется. Архивирование дополнительной переменной после соответствующей фильтрации. функции работы с архивом будут выдавать правильный результат;
- принимает предыдущее значение. В скрипте необходимо перенести суммирование за условное выражение, а само условное выражение оставить только для корректных значений;
- игнорируется. Как это сделано в скрипте. Появление игнорируемых входных значений приведёт к искажению результата в сторону уменьшения, т.к. [за период 3600 сек] суммируется меньшее число отсчетов. Т.е. для каждой переменной необходим счётчик числа корректных значений. Для правильного результата применяется повышающий коэффициент 3600/<значение счетчика>.

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

Во вложении карта памяти [Modbus] устройства РС83-В1 с журналом аварий на 100 событий. Структура ошибки очень похожа, но способ чтения совершенно другой.
1. Для начала нужно получить доступ к данным. Вам необходимо построить (пытая китайское устройство) или подсмотреть в китайском руководстве (цифры-то арабские) аналогичную карту.
2. Определить критерий, когда приняты все данные ошибки, особенно если линии связи с прибором медленные.
3. Из Вашего сообщения и документа на РС83-В1 можно предположить:
 - чтение журнала ведется из очереди аварий по одной аварии через буфер, который заполняется при записи в регистр (4000) смещения относительно начала очереди;
 - аварии последовательно заполняют очередь, и при её переполнении новая авария вытесняет первую аварию из очереди. Скорее всего для контроля этой ситуации используется возвращаемое значение "номер аварии" - порядковое число 0..65535;
 - в незаполненной очереди признаком конца очереди является авария с нулевыми данными;
 - полностью заполненная очередь создаёт еще одну ошибку: если между чтением двух аварий вклинится новая запись аварии-события, то произойдет сдвиг аварий в очереди, что нарушит всю последовательность чтения по номеру аварии.
4. Что ещё следует поискать в китайском устройстве и что, по моему мнению, крайне необходимо. так это удаление прочитанной ошибки из очереди, что даёт возможность (если не зевать с удалением ошибок) исключить переполнение очереди.
5. Самое простое китайское решение - НЕ ДЕЛАТЬ НИЧЕГО ВООБЩЕ, т.е. считать что есть только кольцевой буфер аварий, с началом индексации от самой старой сохранившейся аварии.  В этом случае алгоритм на основе анализа возвращаемого значения "номер аварии" пытается упорядочить прочитанные аварии в порядке последовательного возрастания номеров аварий.
  Пример из вложения содержит модель "Китайского устройства" (генерация ошибки/серии, очистка очереди) и окно получения данных (чтение ошибки по смещению, чтение списка аварий, очистка окна). Пример только для иллюстрации алгоритма, тщательно проверять его нет смысла пока не будут решены вопрос п.1 и п.2.
Для ввода повторного значения используйте лидирующий 0.

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

Возник вопрос: А если обработку поставить на скрипт "Изменилась переменная" и сделать флаги bool, которые завести как доппеременную и по ней менять цвет. Обработка будет быстрее или без разницы?
В данном случае это вопрос из серии "кто кого поборет: слон или кит?".
В первом приближении сравнивать времена обработки можно по количеству отдельных временных интервалов выполнения действия: как то вызов события, доступ к переменной (прямо, функция, метод), вычисления.
Скрипт "Изменилась переменная" (далее Скрипт, а вариант с событиями ячеек таблицы - Таблица):
- дополнительный вызов события при изменении флага (хотя ожидается, что это событие достаточно редкое), дополнительные переменные-паразиты под флаги, зависимость объема Скрипта/Скриптов от количества переменных;
- потенциальная возможность ошибок из-за необходимости явного перечисления имён в Скрипте. При соответствующим выборе имен переменных для Таблицы-шаблона достаточно только номера набора;
- выбор буриданова осла: один Скрипт со всем "легионом" входных переменных (а при большом количестве "массовки" незаметно подкрадётся ... ошибка) или множество индивидуальных Скриптов только со своими переменными. В первом случае, переменные по очереди пролазят через "бутылочное горлышко" параметра Скрипта и его выполнение начнется с селекции переменной (либо пачка операторов IF, либо CASE, причем следует ожидать, что с увеличением числа переменных пропорционально увеличится и среднее время выполнения Скрипта. Во втором - копирование и нудная правка скриптов. Вторая неприятность заключается в выборе всех необходимых для вычисления переменных (три тока), и тут начнётся шаманство с формированием имён для GetVariableByName(). В Таблице переменная скрыта за номером столбца и строки (своеобразный массив), в имени переменной нет необходимости, доступ к ней посредством метода GetCell(,).

ИТОГ: Таблица и Скрипт по количеству соизмеримых временных интервалов практически идут "ноздря в ноздрю", но ресурсов Скрипту/Скриптам потребуется больше: флаг-переменные и объём скрипта пропорциональный количеству переменных. И вишенка на торте - сравнение затрат и простоты при увеличении количества переменных.

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

1. По-видимому ячейка TM_TableCell лишь частично обладает свойствми TM_Control и TM_Object
2. Столбцы и строки таблицы всегда нумеруются с нуля. Если под заголовком понимается "Фиксированная строка", то это только сервисная расскраска таблицы, равно как и "Фиксированный столбец" и "Чередование".
3. Ячейка имеет свойство Variable, доступ к значению переменной ячейки: Cell.Variable.<тип значения>.
4. Обратится к значению другой ячейки можно только на уровне таблицы: <имя таблицы>.GetCell(<номер столбца>,<номер строки>).Variable.<тип значения> или <имя таблицы>.Columns[<номер столбца>].Cells[<номер строки>]...
Пример во вложении. Значения величин записываются непосредственно в ячейку. Можно выбрать один из двух скриптов, изменяющий цвет при авариях тока: все или только аварийные.

P.S. Изменение цвета шрифта не лучшая идея, по моему мнению более заметно изменение фона ячейки (причем для границ - стандартными средствами, без скрипта).

8
        Здравствуйте.
... в момент OnInit проверка (Sender is TM_Window) не проходит, да и свойства самого экземпляра еще толком не подтянулись.
К моменту события OnInit шаблонного окна все его объекты уже определены. Проблема состоит в том, что ссылка на экземпляр не приводится к типу TM_Window (возможно будет обновление-исправление). Однако работает ссылка как на владельца любого объекта окна.
Код: (delphi)
var
  idx : Integer;
  SaveButton : TM_Button;
begin
  if (TM_Window(GetTemplateObject('Flow1').GetOwner).Title <> 'Настройки ВКТ')then
    for idx := 0 to 2 do
      GetTemplateObject('Flow' + IntToStr(idx)).Visible := True;
end.
Цитировать
...эстетическая проблема...
эстетическая проблема должна исчезнуть после первого открытия окна, т.к. конфигурация объектов уже установилась.

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

Фиксированное положение окна не есть хорошо, если пользователь захочет посмотреть что же там за окном.

Артефакт есть следствие неправильной последовательности действий с окнами. Правильная последовательность: открыть новое окно, когда окно откроется и аккуратно перекроет предыдущее (событие окна OnShow), закрыть все прочие окна. Но для окна-формы, значения из которого запоминаются, эта последовательность не работает, т.к. окно автоматически закроется.

Для интерфейса настроек Вам необходимо выбрать характеристики окон (обычное - форма ввода), количество групп окон и возможность дальнейшего масштабирования проекта. Кроме того определить структуру имен объектов и переменных, что будет важно при использовании универсальных скриптов. (Я бы предложил вынести название агрегата в префикс: GeneralSettingPOU25 -> POU25_GeneralSetting или POU25_wndGeneralSetting, что по крайней мере соберёт в инспекторе все объекты в "кучку").

Во вложении пример для настройки с группой окон-форм двух агрегатов ПОУ25 и ПОУ26.
Дополнительные затраты для настроек ещё одного аналогичного агрегата:
- набор окон;
- объект/объекты начального вызова группы и скрипт вызова.
Для новой группы окон дополнительно ещё потребуются шаблон для переключения окон, скрипт его инициализации и скрипты работы с формой.

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

Во вложении пример интерфейса настроек на основе больших (размером в экран) оконных форм. Настройки одной формы образуют связанную группу данных, не подверженных влиянию других пользователей. Т.е. в форме желательно иметь максимальное количество настроек.
Интерфейс обеспечивает переключение между окнами и повторное открытие окна после действия "отправить форму".
Увы, но имеется артефакт переключения - между окнами "проскальзывает" изображения страницы.
 
P.S. можно уменьшить размер окон, но тогда по причине отсутствия механизмов перемещения окон или их фиксации, окна просто "разбегутся" по экрану

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

Для выделенной области сообщение формируется только при переходе значения переменной в эту область (включая границы), а изменение значения внутри области (включая границы) и выход из области не формирует сообщение. Выход из области добавляет время выхода в уже сформированное сообщение.
Цитировать
Мне важно уточнить, правильный ли я вывод делаю, что одна из границ в диапазоне "-55..-53" является не включённой, то есть [-55...-53)?
Дополню, что если ставить диапазон "-55..-55" то ничего не работает, что полагаю логичным.
Оба вывода неправильные, т.к. значения температуры задаются в вещественном виде от эмулятора, а границы - целыми числами.
Диапазон  "-55..-55" правильно формирует сообщение, хотя судя по "Основному руководству/Свойства состояний сообщения", установить одинаковые граничные значения "от" и "до" нельзя.
Судя по:
Цитировать
Через эмулятор я отправляю аналоговое значение "-53" - сообщение НЕ формируется на скаде.
Отправленное значение на самом деле содержит ненулевую дробную часть например, -52.99999ххх.

Цитировать
Отправляю "-54" - сообщение формируется на скаде.
Это правильно, т.к. вещественное "около -54" стало первым попаданием в область.

Цитировать
Отправляю "-55" - сообщение формируется на скаде.
Весьма неожиданно, т.к. для сообщения значение должно было выйти из области и войти в неё вновь (либо Вы ошиблись, отправив перед этим значение вне области, либо такое значение отправил эмулятор).

Цитировать
Для проверки отправляю "-56" - сообщение НЕ формируется на скаде.
Правильно, любое новое значение после формирования сообщения НЕ приводит к формированию сообщения.

Реальное значение температуры можно контролировать объектом "поле", предварительно изменив формат переменной для увеличения числа значимых цифр после запятой, например так: 0.############.

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

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

 Для построения таблицы гибридного отчёта можно использовать данные из системных таблиц Simple-Scada по следующему алгоритму:
- по таблице messages строится таблица с необходимыми для отчета колонками и добавлением колонок для переменных. Критерием отбора строк служит номер группы сообщений и временное окно построения;
- из таблицы отчета выбираются все временные метки и помещаются в динамический массив;
- по массиву в цикле выполняются получение значений переменных из архива по временной метке (ArchiveValueByTime) с последующей записью в таблицу отчёта.
Из-за использования архивной функции построение таблицы достаточно медленное. Однако данный алгоритм слабо связан с проектом и не требует дополнительных ресурсов БД.
Пример во вложении предельно упрощен. Секундный генератор переменных. Две кнопки событий и фиксированные имена переменных. Отбор только по номеру группы сообщений (можно предварительно почистить таблицы). Кнопка "Построить таблицу", визуализация результата в таблице Simple-Scada.

13
Ваши вопросы / Re: Иконки кнопок
« : 22 Июля 2026, 22:30:34 »
      Здравствуйте.

 Для однотипных кнопок можно использовать шаблон, по крайней мере нет необходимости в ручной связке кнопки и изображения, которая выполняется при инициализации экземпляра шаблона.
 Шаблон для битовой кнопки является универсальным, т.к. имеет две подстановки - имя переменной и номер бита. Пример во вложении.

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

Подскажите как в скрипте из события OnEnter объекта Page получить имя текущей подстраницы?
В скрипте OnEnter объекта Page нет возможности получить имя текущей подстраницы. При переходе на страницу (подстраницы ещё скрыты) однократно последовательно вызываются скрипты OnEnter и OnEnterSubpage текущей подстраницы, далее при переходам по подстраницам вызывается скрипт OnEnterSubpage.
Следовательно, достаточно поместить текст обработки имени текущей подстраницы в скрипт OnEnterSubpage и обеспечить однократность его выполнения при первом вызове. В универсальных скриптах для флажка однократности можно использовать свойство Tag страницы.
Скрипт OnEnter:
Код: (delphi)
begin
  Sender.Tag := 0;       // 0 - скрипт OnEnterSubpage ещё не вызывался
end.
Скрипт OnEnterSubpage:
Код: (delphi)
begin
  with TM_Subpage(Sender).OwnerPage do         // страница для текущей подстраницы
    if Tag = 0 then
    begin
      Tag := 1;    // однократность вызова скрипта при переходе по подстраницам
// действия по обработке имени текущей подстраницы

      Text1.Text := Text1.Text + 'ПЕРЕХОД: Page=' + Name + '    SubPage=' + TM_Subpage(Sender).Name + #10;

    end;
  Window1.ShowAll;
end.
Пример из вложения показывает список имён последних активных подстраниц перед переходом к другой странице.

P.S. Если Tag занят, то в качестве флага и имени подстраницы можно использовать строковую переменную. Для универсальности скриптов имя переменной должно быть связано с именем страницы, например так: vrSubPageName_<Имя страницы>.

15
Ваши вопросы / Re: Иконки кнопок
« : 24 Июня 2026, 13:21:37 »
      Здравствуйте.

  Если без своих иконок не обойтись, то можно использовать комбинацию объектов "изображение" (иконки - покадрово) поверх объекта "кнопка".
Взаимодействие осуществляется посредством общей переменной, ссылка на "кнопку" - свойство Hint объекта "изображение".
  Пример для кнопки с 5-ю состояниями, кнопки с 2-состояниями (более простые скрипты) и битовой кнопки во вложении.
Предварительно необходимо добавить файл иконок animals.png из папки ""User files" проекта в папку изображений "Pictures".

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