Appearance
Логика и алармы
Логика объекта — конвейер «событие владельца → реакция/аларм → кейсы-условия → действия над целью».
Реакции (trigger) и алармы (alarms) хранятся на самом объекте-владельце в таблице project_objects. Реакция срабатывает при событии владельца, если выполнены условия-кейсы, и выполняет действия над объектами-целями. Аларм устроен так же, но его действия адресуются пользователю (текст, URL, мнемосхема, звук), а не объекту. Какие события, команды и параметры команд есть у типа — объявляют его декларации (см. страницы типов и Additional data команд).
Страницы
Additional data команд
Декларации типов, адаптерный конвейер, persistence-виды, projections и три вида значений: editor / persisted / wire.
Хранение и legacy
actionData (schemaVersion 1) — единственный persisted-формат; миграции BMSBUIL-1220 и BMSBUIL-1386.
Декларации событий/команд каждого типа — на его странице в группе «Объекты». Mnemoscheme не реализует контракт логики: событий и команд у неё нет.
Контракт владельца
Что тип объекта объявляет редактору логики и алармов:
ts
/**
* Контракт «логикоспособного» типа объекта: что тип объявляет редактору
* логики/алармов. Реакции и алармы хранятся на самом объекте-владельце
* (таблица project_objects), а их содержание набирается из деклараций ниже.
*/
export interface IObjectReactable<
TReaction extends ConcreteReaction = ConcreteReaction,
TStates extends string = string,
> {
/** реакции владельца: событие → условия-кейсы → действия над целями */
trigger: ConcreteReaction[];
/** связи объекта с драйверами шин */
driverLinks: IDriverLink[];
/** алармы владельца: событие + условия + приоритет + действия аларма */
alarms: IAlarm<ExtractReactionType<TReaction>>[];
/** лейблы событий, которые объект может породить (ключ — значение objectEvent реакции) */
readonly availableEvents: Record<ExtractReactionType<TReaction>, string>;
/** лейблы команд, доступных над объектом-целью (ключ — значение type действия) */
readonly availableActions: Partial<Record<ExtractActionType<TReaction>, string>>;
/** ключи специализированных адаптеров additional data по командам (см. registry адаптеров) */
readonly actionDataAdapters?: Partial<Record<string, ActionDataAdapterKey>>;
/** Параметры команд типа для редактора логики; есть не у всех — типы без
* параметризованных команд деклараций не экспортируют. */
readonly actionDeclarations?: IActionDeclarations;
/** поля объекта, доступные в кейсах реакций/алармов владельца */
readonly availableObjectFields: Record<string, string>;
/** поля объекта, доступные как поля кейса на мнемосхеме */
readonly availableMnemoFields: Record<string, string>;
/** поля объекта, связываемые с драйверами: лейбл + направления READ/WRITE */
readonly availableDriverLinkFields: TDriverLinkFieldDeclarations;
/** операторы сравнения, доступные для каждого поля кейса */
readonly availableOperators: Record<string, Partial<Record<LogicCase, string>>>;
/** значения-состояния поля State кейса (у mode-object — данные пользователя) */
readonly availableStates: Record<TStates, string>;
/** лейблы типов значений кейса: INT (число) / STATE (строка состояния) */
readonly availableStateLables: Record<StateCase, string>;
/**
* @anchor <additional_data_case_display>
* @description Возвращает дополнительные параметры для отображения и валидации условий логики и алармов.
* Используется UI компонентами (например, CaseItem.vue) и рантаймом алармов,
* чтобы понять, какие диапазоны, списки состояний или подсказки нужно показать для выбранного поля и типа проверки.
* @param field - Ключ поля (например, FieldCase.Value), за который отвечает условие.
* @param type - Тип условия (например, StateCase.INT или StateCase.STATE).
* @returns Объект с настройками UI (min/max, списки опций и т.п.); пустой объект — без ограничений.
*/
getCaseAdditionalData: (field: string, type: string) => Record<string, any>;
/** команда, выбранная по умолчанию при создании нового действия над целью этого типа */
defaultAction: ExtractActionType<TReaction>;
/** событие по умолчанию при создании новой реакции/аларма владельца */
defaultEvent: ExtractReactionType<TReaction>;
/** поле кейса по умолчанию при создании нового условия */
defaultObjectField: FieldCase;
defaultDriverLinkField: string;
/** оператор сравнения по умолчанию в новом кейсе */
defaultLogicCase: LogicCase;
/** тип значения (INT/STATE) по умолчанию в новом кейсе */
defaultStateLabel: StateCase;
defaultState: ExtractCaseType<TReaction>;
}Реакция и кейсы
ts
/**
* Реакция владельца: «при событии objectEvent, если выполнены кейсы cases,
* выполнить действия actions над объектами-целями». Хранится в trigger
* объекта-владельца; конкретные типы сужают строки через свои enum'ы.
*/
export interface IReaction<T extends string, U extends string, S extends string> {
/** идентификатор (uuid) */
id: string;
/** имя реакции в редакторе логики */
name: string;
/** событие владельца, запускающее реакцию (ключ из availableEvents владельца) */
objectEvent: T;
/** действия над объектами-целями; null — ещё не редактировались */
actions: IAction<U>[] | null;
/** условия срабатывания; null — реакция безусловная */
cases: ICase<S>[] | null;
}ts
/** Связка кейсов внутри одной реакции/аларма: первый кейс — START, далее AND/OR. */
export enum Operator {
START = "START",
AND = "AND",
OR = "OR",
}
/** Оператор сравнения значения поля кейса с caseValue. */
export enum LogicCase {
more = "more",
less = "less",
eq = "eq",
neq = "neq",
less_eq = "less_eq",
more_eq = "more_eq",
}
/** Тип значения кейса: INT — число, STATE — строка состояния. */
export enum StateCase {
INT = "INT",
STATE = "ENUM",
}
/** Общие поля объектов, доступные в кейсах; типы сужают своими enum'ами. */
export enum FieldCase {
State = "State",
Value = "Value",
LastClickCount = "LastClickCount",
Undefined = "",
}
/** Значение кейса: число (INT) или строка состояния (STATE). */
export interface ICaseValue<T> {
type: StateCase;
value: T;
}
export interface ICaseValueInt extends ICaseValue<number> {
type: StateCase.INT;
value: number;
}
export interface ICaseValueState extends ICaseValue<string> {
type: StateCase.STATE;
value: string;
}
/**
* Условие срабатывания реакции/аларма: поле объекта (objectField) в связке
* operator сравнивается по logicCase со значением caseValue.
*/
export interface ICase<T extends string> {
id: string;
/** место кейса в связке условий (START/AND/OR) */
operator: Operator;
/** объект, поле которого проверяется; пусто — сам владелец */
objectId?: string;
/** проверяемое поле (из availableObjectFields владельца или цели) */
objectField: FieldCase;
/** оператор сравнения с caseValue */
logicCase: LogicCase;
/** INT-кейсы хранят число, STATE — строку состояний; T — только строковая часть */
caseValue?: ICaseValue<T | number>;
}Действие
Действие — команда над объектом-целью. Параметры команды (если есть) живут в actionData; какие ключи попадают в values, определяет persistence поля команды.
ts
/**
* Действие реакции: команда `type` над объектом-целью `objectId`.
* Параметры команды (если есть) живут в `actionData`.
*
* Persisted additional data живет только в actionData (schemaVersion 1);
* формат legacy IAdditionalDataUI читает исключительно DB-миграция
* BMSBUIL-1220, рантайм его не использует.
*/
export interface IAction<T extends string> {
/** идентификатор (uuid) */
id: string;
/** команда из availableActions цели */
type: T;
/** объект-цель команды; undefined — цель ещё не выбрана */
objectId?: string;
/** значения полей команды; undefined — у команды нет параметров или они не заданы */
actionData?: IActionDataV1;
}ts
/**
* Persisted формат нового additional data pipeline.
*
* `actionData.values` — единственный persisted-формат;
* легаси-поле `data` удалено из БД миграцией BMSBUIL-1386
* (подъём значений из него — миграция BMSBUIL-1220).
*/
export interface IActionDataV1<
TValues extends Record<string, unknown> = Record<string, unknown>,
> {
/** версия формата; сейчас всегда 1 (гвард isActionDataV1) */
schemaVersion: 1;
/** значения полей команды в persisted-виде (какие ключи — решает FieldPersistence поля) */
values: TValues;
}ts
/**
* Что именно сохраняется в `actionData.values`.
*
* `literal` - сохраняем значение как есть.
* `root` - поле само является объектом (виджет-таймеры): его значения
* пишутся в корень `values`, без обёртки в ключ поля.
* `reference` - сохраняем stable id доменной сущности, а не ее index/name.
* `virtual` - поле нужно только редактору и не пишется в БД.
*/
export type FieldPersistence =
| {
kind: "literal";
valueKey: string;
}
| {
kind: "root";
}
| {
kind: "reference";
valueKey: string;
refType: string;
}
| {
kind: "virtual";
};Жизненный цикл действия
Инициализирует additional data при выборе action target или смене типа действия. Значения вычисляются из дефолтов команды (определения additional data цели), а не наследуются от предыдущей команды.
Отправка на контроллер
Преобразует действие из базы в формат контроллера, включая additional data. Additional data уезжает на контроллер только из actionData (values); экшены без actionData (созданные до его появления) синхронизируются с дефолтами команды. Перед отправкой значения прогоняются через zod-декларацию типа цели — битые значения не уходят на контроллер.
Алармы
ts
/** Тип действия при срабатывании аларма (что показать/открыть пользователю). */
export enum EAlarmActionType {
/** открыть URL из text */
URL = "url",
/** открыть выбранную мнемосхему (objectId) */
MNEMOSCHEME = "mnemoscheme",
/** показать текст из text */
TEXT = "text",
/** воспроизвести звуковой файл (audioId) */
AUDIO = "audio",
}
/**
* Действие аларма — реакция интерфейса на срабатывание. В отличие от
* IAction, адресуется не объекту-цели, а пользователю (портал/мнемосхема).
*/
export interface IAlarmAction {
id: string;
/** что сделать при срабатывании (см. EAlarmActionType) */
actionType: EAlarmActionType;
/** ссылка на выбранную мнемосхему или звуковой файл */
objectId?: string;
/** url и текст*/
text: string;
/** открывать в новой вкладке? */
newTab: boolean;
/** id файла из RepositoryAudio */
audioId: string;
}
/**
* Аларм владельца: «при событии objectEvent, если выполнены кейсы cases,
* выполнить действия actions». В отличие от реакции, несёт приоритет
* (шествие в журнале алармов) и порог срабатывания.
*
* TEvent — тип события объекта-владельца (сужается классами через
* IObjectReactable<ReactionX>; дефолт — широкое хранилище).
*/
export interface IAlarm<TEvent extends string = string> {
id: string;
/** имя аларма в редакторе */
name: string;
/** приоритет срабатывания (см. EAlarmPriority) */
priority: EAlarmPriority;
/** событие владельца, запускающее аларм (ключ из его availableEvents) */
objectEvent: TEvent;
/** описание, показываемое при срабатывании */
description: string;
/** условия срабатывания; null — аларм безусловный */
cases: ICase<string>[] | null;
/** действия при срабатывании */
actions: IAlarmAction[];
/** фильтр (мс): окно удержания аварийного условия до перехода аварии; 0–60000 */
threshold: number;
}
/**
* Приоритет аларма. Значения приходят из рантайма (IAlarmsFromRuntime):
* "0" — самый высокий (Emergency), "7" — самый низкий (Debug);
* точная семантика уровней определяется рантаймом, не этим репо.
*/
export enum EAlarmPriority {
Emergency = "0",
Alert = "1",
Critical = "2",
Error = "3",
Warning = "4",
Notice = "5",
Informational = "6",
Debug = "7",
}Алармы из хранилища (широкий IAlarm) → алармы владельца с его типом событий. Событие вне перечисления владельца — битые данные (смена перечислений между версиями, ручные правки БД): аларм исключается из домена с записью в лог.