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

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

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

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


Сообщения - pan2000

Страницы: 1 ... 9 10 [11] 12 13 ... 16
151
      Здравствуйте!

Я добавил в пример один калибруемый счетчик. Сам алгоритм работы счетчика не изменился, только скрипт переведен в универсальный.
Калибровка в пределах (-50..50) определяется шкалой переменной величины калибровки. Если не нужны минус калибровки то скрипт можно урезать.
Относительно примера - я удаляю скомпилированные файлы, просто предварительно надо сохранить проект.

152
    Здравствуйте!

 Для правильной оценки изменения параметров нужно контролировать сам параметр, а не изменение визуальных свойств объекта (эффект суслика).
Например для объекта "Кнопка" с двумя состояниями - параметр изменение цвета в состоянии 1: при состоянии 1 изменение видно, а при 0 - нет, но оно есть.
Простейшее средство контроля - объект "Текст", при клике которого в его поле выводится значение контролируемого параметра, и, возможно ещё, дополнительная информация.

153
      Здравствуйте!

 Для правильной работы счетчика DiscreteInputCount0_8_GeneralQ можно применить коррекцию текущего значения счетчика на величину изменения DiscreteInputCount0_8 с момента последней коррекции. Для этого используется внутренняя переменная HoldCount0_8, запоминающая значение DiscreteInputCount0_8 в момент последней коррекции.
Сам скрипт может вызываться по событиям: изменение переменной (например, DiscreteInputCount0_8), по времени ("Прошла секунда") и т.д.
Пример скрипта, в предположении, что DiscreteInputCount0_8 представляет собой образ 32-разрядного счетчика в контроллере.
Код: (delphi)
var i, j: int64;
begin
  i := DiscreteInputCount0_8.AsInt64 and $FFFFFFFF;
  j := i - (HoldCount0_8.AsInt64 and $FFFFFFFF);       // добавка к "большому" счетчику со времени последней коррекции
  if j < 0 then j := j + 4294967296;                   // коррекция при переполнении 32-разрядного счетчика
  DiscreteInputCount0_8_GeneralQ.Value := DiscreteInputCount0_8_GeneralQ.AsInt64 + j;
  HoldCount0_8.Value := i;                   // запомнить текущее значение счетчика
end.
Скрипт установки счетчика в 0:
Код: (delphi)
begin
DiscreteInputCount0_8_GeneralQ.Value := 0;
HoldCount0_8.Value := DiscreteInputCount0_8.Value;
end.

Во вложении пример модели счетчика контроллера (изменение: прямая запись, генератор 1 Гц, +1, число 4294967290)
с возможностью отключения/подключения данных от ОРС-сервера и сохранением данных при перезапуске проекта.

К сожалению, я так и не понял алгоритм калибровки.

154
Ваши вопросы / Re: Работа с CSV файлами
« : 28 Июля 2020, 15:20:38 »
          Здравствуйте!

К сожалению, { вспомогательная процедура для обработки отдельной строки } не сможет правильно разобрать строку из CSV-файла экспорта переменных, содержащую пустые поля (и "пробелы").
Для разбора CSV-строки можно использовать функции из примера заполнения таблиц описанием переменных из CSV-файла:
Код: (delphi)
// выбор поля по индексу (произвольный выбор)
function GetField(const aStr: string; const n: integer): string;
var i, j: integer;
    aStr1: string;
begin
  j := 1; aStr1 := '';
  for i := 1 to Length(aStr) do begin
    if aStr[i] = ';' then begin
      if j = n then begin GetField := aStr1; exit; end;  // индекс выбора поля равен номеру выбранного поля
      aStr1 := '';                                       // новое поле
      j := j + 1;
    end
    else aStr1 := aStr1 + Astr[i];
  end;
  GetField := aStr1;     // пусто или последнее поле с отсутствующим разделителем ";"
end;

// последовательный выбор полей
function GetField2(const aStr: string; var n: integer): string;
var i: integer;
    aStr1: string;
begin
  aStr1 := '';
  for i := n to Length(aStr) do
    if aStr[i] = ';' then begin
      n := i + 1;                      // запомнить начало следующего поля
      GetField2 := aStr1;
      exit;
    end
    else aStr1 := aStr1 + Astr[i];
  GetField2 := aStr1;
end;

var aStr: string;
    i, j, k: integer;
begin
// открываем текстовый файл для чтения
 if TextFileOpen('MyCSV.csv', '', fomReset, fcpDefault) then begin
    TextFileReadLn;                                          // пропустить первую строку
    i := 0;
    with Table1 do
      while (not TextFileEOF) and (i < RowCount) do begin    // цикл с проходом по каждой строке текстового файла или до переполнения таблицы
        aStr := TextFileReadLn;
// функция GetField()
        for j := 1 to ColumnsCount do GetCell(j - 1, i).Text := GetField(aStr, j);

        for j := 0 to 3 do                // заполнение Table3 выбранными столбцами
          case j of
          0: Table3.GetCell(j, i).Text := GetField(aStr, 2);    // Имя переменной
          1: Table3.GetCell(j, i).Text := GetField(aStr, 3);    // Тип данных
          2: Table3.GetCell(j, i).Text := GetField(aStr, 8);    // Начальное значение
          3: Table3.GetCell(j, i).Text := GetField(aStr, 32);   // ID
          end;
// функция GetField2() - заполнение таблицы Table2 идентичной таблице Table1
        k := 1;     // индекс начала очередного поля в строке разбора aStr
        for j := 1 to ColumnsCount do Table2.GetCell(j - 1, i).Text := GetField2(aStr, k);

        i := i + 1;
      end;
    TextFileClose;
  end;
end.

155
    Здравствуйте!

Для временного контроля используются скрипты с событиями "Прошла секунда" и "Прошел час". Для работы проекта необходимы две переменные целочисленного типа: iButton - состояние кнопки и iTimer - таймер отсчета временного интервала.
Скрипт "Прошла секунда":
Код: (delphi)
const Tmax = 10;
begin
// таймер с прямым отсчетом и с перезапуском
  if iButton.AsInt <> 0 then begin
    iTimer.Inc(1);                                   // работает в пределах шкалы переменной iTimer, максимальное значение 12
    if iTimer.AsInt >= Tmax then iButton.Value := 0; // если таймер достиг конечного значения, то выключить кнопку
  end
  else iTimer.Value := 0;                            // установить начальное значение таймера
end.
Скрипт примера из вложения дополнительно содержит коды для таймера с обратным отсчетом и перезапуском и для таймера с обратным отсчетом и приостановом.
(перезапуск - установка таймера в начальное значение при выключении кнопки, приостанов - сохранение значения при выключении или включении кнопки, установка таймера в начальное значение только при достижении конечного.

156
  Здравствуйте!

Очевидно, что не выполняется сравнение строго с 1.
Правильное сравнение возможно для одинаковых типов. Например, значение типа Value может отличаться от значений типа AsBool для true. В примере можно посмотреть как изменяются значения переменной sen2 в зависимости от явного указания типа.
Код: (delphi)
//                                                                               результат для sen2 = true
  Text1608.Text := 'VariableEx.Value=' + IntToStr(VariableEx.value) + #10       //  -1
                 + 'VariableEx.AsInt=' + IntToStr(VariableEx.asint) + #10       //   1
                 + 'VariableEx.AsBool=' + IntToStr(integer(VariableEx.AsBool)); //   1

157
    Здравствуйте!

В Вашем случае всего четыре состояния, циклически изменяющихся: "Закрыто", "Открывается", "Открыто", "Закрывается" - вполне достаточно двух кнопок по четыре состояния, связанных одной переменной, в следующих парах для одного состояния:
0 - "Открыть" - "Закрыто";
1 - "Открывается" - "" (назовите состояние сами, в примере пустое поле);
2 - "Открыто" - "Закрыть";
4 - "" - "Закрывается".
Общая переменная привязана к основной переменной кнопок, индивидуальные переменные состояния привязаны к дополнительным переменным своих кнопок. Теги кнопок 0 и 2.
Скрипт "Изменилась основная переменная":
Код: (delphi)
begin
  with Sender as TM_Button do begin
    Enabled := Variable.AsInt = Tag;
    Open.Value := Variable.AsInt = 1;       // управляющая линия "Открыть"   
    Close.Value := Variable.AsInt = 3;      // управляющая линия "Закрыть"
  end;
end.
Скрипт "Изменилась дополнительная переменная":
Код: (delphi)
begin
  with Sender as TM_Button do if VariableEx.AsBool then Variable.Value := (Tag + 2) mod 4; // условие достижения крайнего положения = true
end.

Пример с моделью электромеханического клапана во вложении.

158
. . . Очень нужен, а то пол таблицы не видно.
Пока не добавлен горизонтальный скролл, можно воспользоваться примером реализации горизонтального скролла для широкой таблицы. Пример - 10 столбцов шириной по 400 с просмотром в окне шириной 1440. Одинаковый размер столбца и плавный движок (горизонтальный объект "Уровень") для скролла исключительно для простоты. Столбцы можно сделать разной ширины, а движок дискретным.

159
lipvasko, вот правильный вариант:
Код: (delphi)
TextFileWriteLn(UTF8ToString(DateTimeToStr(Now)) +' Создан новый файл ');
Выражение:
Код: (delphi)
UTF8ToString(DateTimeToStr(ADateTime: TDateTime): String)
подразумевает две конвертации: неявную String->UTF8 и явную UTF8->String, на мой взгляд проще явно указать тип компилятору:
Код: (delphi)
String(DateTimeToStr(Now)) + ' Создан новый файл ';

160
   Здравствуйте!

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

Миханик, очень похоже на отсутствие переменной привязанной к ячейке.

161
Но этот пример не совсем решает мою проблему. Во вложении примерный вид элемента управления как я его себе представляю.
     Здравствуйте!
Во вложении пример возможной реализации элемента управления.

162
Нужно отобразить 4ю кнопку "Неактивное"....
Здравствуйте!
Это кнопка с восьмью состояниями. Бонусом - индикация запрещенных комбинаций. При отсутствии оных - можно обойтись кнопкой с пятью состояниями. См. пример во вложении.

163
       Здравствуйте!

Возможным вариантом наименования переменных - использование суффикса, ссылающегося на набор переменных, например:
 <имя переменной>_<номер Модуля><номер Камеры из двух цифр, начиная с 1>.
В этом случае для работы с одним общим окном (пример с двумя объектами) достаточно универсального скрипта (события "Пользователь перешел на эту страницу", "Пользователь перешел на эту подстраницу", "Пользователь закрыл окно"):
Код: (delphi)
const ObjName: array [1..2] of string[10] = ('Field1', 'Button1');         //  связывание переменной с объектом окна
      VarName: array [1..2] of string[10] = ('Var1_', 'iButt1_');    //  для большого числа переменных удобнее двумерный массив (как в 3 окнах)
var i: integer;
begin
  for i := 1 to 2 do                // привязать реальные переменные к объектам окна
    GetObjectByName(ObjName[i]).Variable := GetVariableByName(string(VarName[i]) + IntToStr(Sender.Tag));

  GetPageByName('Set' + IntToStr((Sender.Tag div 100) * 100)).Tag := Sender.Tag;  // запомнить  набора для этой страницы (

  Window1.Tag := Sender.Tag;   // запомнить для восстановления окна после попытки закрыть "незакрываемое" окно.
  Window1.ShowAll;
end.
Существенным является определение тегов и название страниц/подстраниц:
- для подстраниц постоянное значение равное индексу набора;
- для страниц - начальное значение равное индексу первой подстраницы;
- для окна - начальное ноль, к переменным подключен первый набор;
- в данном примере страница - Set<номер Модуля>00, подстраница - Set<номер Модуля><номер Камеры>.

Добавление "новых" окон сводится к созданию нового набора переменных и добавлению подстраницы или страницы.


Вариант с 3 окнами - скрипт аналогичного вида с событием "Изменились переменные" для переменных "Вектор выбора подстраницы" (в примере iSelPage<номер Модуля>) у каждой страницы. По имени и значению переменной скрипт формирует индекс набора и далее привязывает переменные.

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

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

164
   Здравствуйте!

Синхронную подсветку строки в нескольких таблицах можно реализовать двумя способами:

- для таблиц с полосой прокрутки изменением цвета ячеек. Скрипт по событию "Пользователь кликнул ячейку":
Код: (delphi)
const nTables = 4;      // число таблиц с однотипными последовательно нумеруемыми именами
var i, j, k, l: integer;
begin
  i := Cell.Row;        // индекс новой строки
  j := Table1.Tag;      // индекс подсвеченной строки. Начальное значение -1
  if i = j then exit;
  Table1.Tag := i;      // хранение индекса подсвеченной строки (можно и в виртуальной переменной).

  for l := 1 to nTables do
    with GetTableByName('Table' + IntToStr(l)) as TM_Table do begin
      if j >= 0 then for k := 0 to ColumnsCount - 1 do GetCell(k, j).Color := $6D6D6D;  // удалить подсветку
      for k := 0 to ColumnsCount - 1 do GetCell(k, i).Color := clMediumTurquoise;       // подсветить
    end;
end.
В данной реализации теряется исходная информация о цвете ячейки. Есть несколько вариантов восстановления цвета - вычисление по индексам, подсветка части ячеек и т.д.

- для таблиц без полосы прокрутки - позиционированием подсвечивающего объекта над/под таблицей. Скрипт для двух таблиц:
Код: (delphi)
const Y5 = 128; Y6 = 176;      // Y-координаты верхней строки таблиц  (Свойство Y у обоих таблиц = 88 ! )
var i: integer;
begin
  i := Cell.Row;        // индекс новой строки
  Text1.Y := Y5 + i * Table5.RowHeight;
  Text2.Y := Y6 + i * Table6.RowHeight;
end.
Объект над таблицей блокирует доступ к таблице, под - требует наличие прозрачности у таблицы (либо торчащие "уши" объекта шире таблицы).
Для выделения строки контуром можно применить объект "Линия" над таблицей.
Примеры во вложении.

Предложения - см.
Замечания и пожелания по результатам использования объекта "Таблица":

 Раз уж зашла речь о таблице, то могу добавить следующие пожелания (некритично, обходится посредством скриптов, но лучше перевести эти действия в Simple-Scada):

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

2. Форматирование "вправо,  ..." для строк, выходящих за размер соответствующего столбца.
Условно, урезанием строки слева, но несколько проблематично для не моноширинного шрифта.

165
Ваши вопросы / Re: Список он же ComboBox V2
« : 08 Июня 2020, 23:17:26 »
    Здравствуйте!

 Исключить дублирующие записи можно следующим SQL-запросом:
Код: (delphi)
RunSQL('SELECT `ProdName` FROM `archive` GROUP BY `ProdName`;', ComboBox3, 10);
Сравнение запросов с и без исключения дублирующих записей в проекте из вложении.

Страницы: 1 ... 9 10 [11] 12 13 ... 16