# kkormsvr

Модуль управления процессом многокомпонентного дозирования. Предназначен для замены старого приложения `kkormrcpsvr2.exe`. Данный модуль требует включения в задачу.

## Поддерживаемые модули дозаторов

promauto.gelios.dozkkmc

## Основные отличия от kkormrcpsvr2.exe

- Опрос дозаторов и управление процессом дозирования выполняют независимые друг от друга модули. Добавление нового типа дозатора в `peripherial` не требует изменения программного кода модуля `kkormsvr`.
- Возможность размещаться как на сервере (старый вариант), так и в составе ПЛК с использованием сервера в качестве удаленного СУБД. Работа на одной аппаратной платформе с ПЛК позволяет отказаться от дискретных сигналов управления дозаторами без ущерба общей скорости дозирования.
- Хранение оперативных данных в базе данных, т.е. там же где и хранятся все остальные данные. Старый вариант использовал для сохранения оперативных данных обычные файлы, что в случае сбоя может привести к рассинхронизации. Другой минус старого варианта при использовании на борту ПЛК - это частые операции записи в файл, что также является потенциально опасным.
- В ситуации когда на линии несколько дозаторов, а по рецепту используются не все, то в неиспользуемые дозаторы не посылается нулевой рецепт, как это делал `kkormrcpsvr2`. Также `Kkormsvr` для выполнения задачи не требует наличия связи с неиспользуемыми дозаторами.
- Работа одной транзакцией на цикл, что гарантирует целостность данных и положительно влияет на производительность приложения.
- Возможность отключения, включения и перезагрузки `kkormsvr` без прерывания выполнения задачи дозирования.
- При потери связи с удаленным СУБД восстановление происходит автоматически без прерывания выполнения задачи дозирования.
- Состав и именование тегов полностью обновлено. Новые теги кроме текущего состояния процесса также позволяют видеть названия рецептов и продуктов, выполнять сброс и приостановку работы линий.

При работе с удаленными модулями дозаторов использовать протокол jrbustcp!

Например, в jrobo 1 работает `roboplant` логика и выполняется опрос всех устройств ввода/вывода (`peripherial`), в т.ч. и многокомпонентных дозаторов, а также работает модуль jrbustcp в режиме `server`. В jrobo 2 работает модуль `kkormsvr`, взаимодействуя с локальной, либо удаленной базой данных, а теги по дозаторам поставляются при помощи модуля jrbustcp в режиме `client`. Использовать связку `rpsvrtcp` и `rpclient` недопустимо, т.к. данный протокол не поддерживает передачу тегов типа `LONG`.

Структура базы данных осталась прежней Модель базы данных. При миграции старой системы на `kkormsvr` НЕЛЬЗЯ работать непосредственно со старой базой как есть, а необходимо создать новую и сделать импорт.

## Конфигурация

    plugin.kkormsvr:

      module.kksvr:
        database: db

        lines:
          - linenum:  1
            name:     "Линия 1"

            dosers:
              - name:       "d1"
                bind:       "D001"
                grouplevel:  0

                storages:
                  1: 101, Бункер 101
                  2: 102, Бункер 102
                  3: 103, Бункер 103

              - name:       "d2"
                storages:
                  1: 201
                  2: 202
                  3: 203

## Параметры

Параметр | Умолчание | Описание
--- | --- | ---
database | `db` | Модуль базы данных (database)
lines | `` | Список линий
dosers | `` | Список дозаторов, принадлежащих линии
storages | `` | Список бункеров/питателей, принадлежащих дозатору
lines
linenum | `0` | Номер линии
name | `Line<linenum>` | Название линии
dosers
name | `` | Имя дозатора. Используется для формирование имен тегов
bind | `<name>` | Имя peripherial-модуля многокомпонентного дозатора
grouplevel | `0` | Указывает принадлежность дозатора к группе с заданным уровнем опережения выполнения циклов. Отсчет уровня от нуля
storages
<addr> |  | Формат записи: `stornum [, storname]`, где addr - адрес питателя в дозаторе (число) stornum - уникальный номер бункера (число), соответствующего питателю дозатора storname - название бункера (текст). Если не указывать, то будет `Бункер <stornum>`

## Теги

Имя тега | Тип данных | Доступ | Описание
--- | --- | --- | ---
kksvr.connected | BOOL |  | Подключение к базе данных.
Линия 1
kksvr.Line1.CycleCnt | INT |  | Количество выполненных полных циклов.
kksvr.Line1.CycleReq | INT | rw | Заданное количество циклов.
kksvr.Line1.State | INT |  | Состояние линии: 0 - остановлено 1 - работа 2 - работа приостановлена 3 - ошибка: новая задача не распределилась между дозаторами 4 - ошибка: один из дозаторов не принял задачу
kksvr.Line1.TaskId | INT |  | `TaskId` текущей задачи из таблицы `KK_TASK`. Если задача отсутствут, равно 0.
kksvr.Line1.Receipt | STRING |  | Название рецепта текущей задачи.
kksvr.Line1.Reset | BOOL | rw | 1 - Сброс линии.
kksvr.Line1.Suspend | BOOL | rw | 1 - Приостановить работу линии. 0 - возобновить.
Линия 1 - Группа 0
kksvr.Line1.Group0.CycleCnt | INT |  | Количество выполненных циклов.
kksvr.Line1.Group0.State | INT |  | Состояние группы: 0 - остановлено 1 - подача дозаторам команды "Старт цикла". Ожидание готовности к установке задачи. 2 - установка задачи 3 - дозирование 4 - ошибка установки задачи одним из дозаторов. Какой именно дозатор - смотреть тег дозатора SendTask на отрицательное значение.
Линия 1 - Группа 1
kksvr.Line1.Group1.CycleCnt | INT |  | —\\—
kksvr.Line1.Group1.State | INT |  | —\\—
Линия 1 - Дозатор d1
kksvr.d1.CurStorNum | INT |  | номер `stornum` текущего бункера
kksvr.d1.CurStorName | STRING |  | название `name` текущего бункера
kksvr.d1.CurProduct | STRING |  | название текущего загружаемого продукта
kksvr.d1.Error | INT |  | Текущая ошибка работы с дозатором (не путать с кодом ошибки самого дозатора): 0 - ошибки нет 1 - LINK - теги дозатора не найдены (проверить, есть ли теги, правильность параметра ''bind'') 2 - DISCONNECT - нет связи 3 - CRC - данные не достоверны 4 - TASKDIFFER - заданный вес в дозаторе отличается от заданного по рецепту
kksvr.d1.ReqWeightZero | INT |  | =1, если суммарный заданный вес равен нулю, иначе =0
kksvr.d1.Product1 | STRING |  | Название продукта, заданного по рецепту.
kksvr.d1.ReqWeight1 | LONG |  | Заданный вес по рецепту.
kksvr.d1.Product2 | STRING |  | —\\—
kksvr.d1.ReqWeight2 | LONG |  | —\\—
kksvr.d1.Product3 | STRING |  | —\\—
kksvr.d1.ReqWeight3 | LONG |  | —\\—
Линия 1 - Дозатор d2
kksvr.d2.CurStorNum | INT |  | —\\—
kksvr.d2.CurStorName | STRING |  | —\\—
kksvr.d2.CurProduct | STRING |  | —\\—
kksvr.d2.Error | INT |  | —\\—
kksvr.d1.ReqWeightZero | INT |  | —\\—
kksvr.d2.Product1 | STRING |  | —\\—
kksvr.d2.ReqWeight1 | LONG |  | —\\—
kksvr.d2.Product2 | STRING |  | —\\—
kksvr.d2.ReqWeight2 | LONG |  | —\\—
kksvr.d2.Product3 | STRING |  | —\\—
kksvr.d2.ReqWeight3 | LONG |  | —\\—

Теги старой версии (kkormrcpsvr2)

## Группы

Процесс приготовления комбикормовой смеси может происходить в несколько этапов.

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

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

Параметр `grouplevel` определяет на сколько циклов дозирования группа может «обгонять» цикл дозирования линии.

В группу целесообразно объединять ДОЗАТОРЫ ОДНОВРЕМЕННО РАЗГРУЖАЕМЫЕ В ОДИН СМЕСИТЕЛЬ.

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

## Дозирование вне задачи

Во время выполнения задачи каждое изменение значения суммарного счетчика дозатора (другими словами - отвес) фиксируется в базе данных в соответствующих таблицах с ссылкой на задачу и рецепт, по которому работает задача. Если же счетчики меняют значение, когда линия остановлена и выполняемых задач нет, то в этой ситуации создается задача с пустым рецептом, и уже к ней привязывается отвес. Пустой рецепт создается автоматически с названием «ДОЗИРОВАНИЕ ВНЕ ЗАДАЧИ (РУЧНОЕ)» и kk_receipt.id равным 0.

Также при фиксировании отвеса вне задачи указывается продукт, который в данный момент назначен соответствующему бункеру. Если продукт не указан, создается «НЕИЗВЕСТНЫЙ ПРОДУКТ» c kk_product.id равным 0.

## Переходный импорт данных

Для перехода со старого архиватора `kkormrcpsvr2` на новый требуется перенести данные из старой базы данных в новую, создаваемою автоматически модулем `kkormsvr`. Описание действий по шагам:

- создать два модуля `database` для подключения к новой (не существующей) и старой (существующей) базам данных.

      plugin.database:
        module.db:
          type:        firebird
          dbname:      /database/kkorm_fb30.fdb

        module.dbold:
          type:        firebird
          dbname:      /database/old_kkorm_fb30.fdb

      plugin.kkormsvr:
        module.kksvr:
          database:  db

      plugin.task:
        module.taskmain:
          modules:
            - db
            - kksvr

- запустить систему и выполнить команды:

      kksvr:import dbold
      reload kksvr

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

## Удаление старых записей

В таблицах `KK_TASK`, `KK_TASKCONTENT`, `KK_EXECUTE`, `KK_OUTPUT` и `KK_RASHOD` данные сохраняются без циклической перезаписи, и таким образом постоянно накапливаются. Для удаления ненужных данных используется команда `w:sweep <year>`. Пример использования:

    kksvr:sweep 2016

Данная команда, посылаемая модулю с именем `kksvr`, удаляет все записи старше 2016 года включительно.