supergraph-api¶

главный модуль для работы с графами объектов FireFly и метаданными TypeDef
зависимости: static-eval, uuid и @turf/boolean-point-in-polygon

In [2]:
var {v4: uuid} = require('uuid');

// загрузка в браузер: 
//     <script src="lib/polyfill.min.js"></script>
//     <script src="lib/miscutils.js"></script> 
//     <script src="lib/supergraph.js"></script>

// загрузка в исходнике нода integra-node-red:
//     var { global } = this.context()
//     var { isEmpty, flatten } = global.get('miscutils')
//     var { queryPath, blankObject } = global.get('supergraph')

// загрузка в function ноде integra-node-red:
//     var {randomElem} = global.get('miscutils')
//     var { queryPath } = global.get('supergraph')

// загрузка в nodejs:
require("babel-polyfill")
Object.assign(global, require('./miscutils.js'))
Object.assign(global, require('./supergraph.js'))
'ok'
miscutils-api
miscutils v1.44
static-eval-api
static-eval v1.17
GRAPH-LOAD-API
GRAPH-CHANGE-API
GRAPH-PATH-API
FIELDS-SELECTION-API
TYPEDEF-UTILS
supergraph.js v1.62
Out[2]:
'ok'

getBase getType getEntity isItem isPin isLink isEvent getName getPosition nameOrId hasType hasTypeFrom graphContains blankObject loadGraph unloadGraph loadChanges isType getEnclosedText getCondition getExpression queryPolygon queryPath queryTree selectFields getTemplate updateTemplate

typedef compareTypeDefVersions queryEntityTypes queryEntityHeirs queryEntityDef queryPinItems queryPinLinks objectToChanges objectsToChanges graphToChanges changesToGraph

Соединение с сервером
Собственные функции
Еще примеры

getBase¶

возвращает базовый тип объекта FireFly, т.е. первую строку в списке типов types

In [2]:
[ getBase({ types: ['Item', 'IpVideoCamera'] }), getBase({}), getBase(null) ]
Out[2]:
[ 'Item', undefined, undefined ]

getType¶

возвращает собственный тип объекта FireFly, т.е. вторую строку в списке типов types

In [3]:
[ getType({ types: ['Item', 'IpVideoCamera'] }), getType({}), getBase(null) ]
Out[3]:
[ 'IpVideoCamera', undefined, undefined ]

getEntity¶

возвращает сущность объекта FireFly, т.е. базовый тип строчными буквами

In [4]:
[ getEntity({ types: ['Item', 'IpVideoCamera'] }), getEntity({}), getBase(null) ]
Out[4]:
[ 'item', undefined, undefined ]

isItem¶

проверяет является ли объект датчиком (итемом)

In [5]:
[isItem({ types: ['Item',  'IpVideoCamera' ] }), 
 isItem({ types: ['Pin',   'LogicInputPin' ] }), 
 isItem({ types: ['Link',  'LogicLink'     ] }), 
 isItem({ types: ['Event', 'StateEvent'    ] })]
Out[5]:
[ true, false, false, false ]

isPin¶

проверяет является ли объект разъемом (пином)

In [6]:
[isPin({ types: ['Item',  'IpVideoCamera' ] }), 
 isPin({ types: ['Pin',   'LogicInputPin' ] }), 
 isPin({ types: ['Link',  'LogicLink'     ] }), 
 isPin({ types: ['Event', 'StateEvent'    ] })]
Out[6]:
[ false, true, false, false ]

isLink¶

проверяет является ли объект связью (линком)

In [7]:
[isLink({ types: ['Item',  'IpVideoCamera' ] }), 
 isLink({ types: ['Pin',   'LogicInputPin' ] }), 
 isLink({ types: ['Link',  'LogicLink'     ] }), 
 isLink({ types: ['Event', 'StateEvent'    ] })]
Out[7]:
[ false, false, true, false ]

isEvent¶

проверяет является ли объект событием (эвентом)

In [8]:
[isEvent({ types: ['Item',  'IpVideoCamera' ] }), 
 isEvent({ types: ['Pin',   'LogicInputPin' ] }), 
 isEvent({ types: ['Link',  'LogicLink'     ] }), 
 isEvent({ types: ['Event', 'StateEvent'    ] })]
Out[8]:
[ false, false, false, true ]

getName¶

возвращает имя объекта т.е. значение ключевого параметра name

In [9]:
getName({ params: { name: { value: 'Датчик1' } } })
Out[9]:
'Датчик1'

getPosition¶

возвращает позицию объекта т.е. значение ключевого параметра position

In [10]:
getPosition({ params: { position: { value: 'POINT(50.22208228 53.2362814)' } } })
Out[10]:
[ 50.22208228, 53.2362814 ]

nameOrId¶

возвращает имя или идентификатор объекта, если имя не указано

In [5]:
[nameOrId({ id: 'ID1', name: 'Name1' }),
 nameOrId({ id: 'ID2' })]
Out[5]:
[ 'Name1', 'ID2' ]

hasType¶

возвращает функцию проверки типа объектов FireFly

In [11]:
var isStateEvent = hasType('StateEvent');
[isStateEvent({ types: ['Event', 'ZoneEvent'  ] }), 
 isStateEvent({ types: ['Event', 'StateEvent' ] })]
Out[11]:
[ false, true ]

hasTypeFrom¶

возвращает функцию проверки вхождения типа объектов в список типов FireFly

In [4]:
var isVideoCameraOrSensor = hasTypeFrom(['VideoCamera','Sensor']);
[isVideoCameraOrSensor({ types: ['Item', 'Zone'  ] }), 
 isVideoCameraOrSensor({ types: ['Item', 'Sensor' ] })]
Out[4]:
[ false, true ]

loadGraph¶

загружает граф FireFly в суперграф в памяти

In [12]:
loadGraph('cc467016-e940-4ce6-a5be-b14c9da2947f', { items: {}, links: {} })

unloadGraph¶

вызагружает граф FireFly из суперграфа в памяти

In [13]:
unloadGraph('cc467016-e940-4ce6-a5be-b14c9da2947f')

graphContains¶

проверяет, что аргумент является элементом графа, т.е. что имеет валидный тип и содержится в текущей копии суперграфа в памяти

In [11]:
var serverId = 'cc467016-e940-4ce6-a5be-b14c9da2947f';

var obj = { 
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    types: ['Item',  'IpVideoCamera' ],
    params: {  
        name: { value: 'Камера1' }
    }
};

var graph = { 
    items: { [obj.id]: obj },
    links: {},
};

console.log(graphContains(obj),graphContains({}),graphContains(null))

loadGraph(serverId, graph); console.log(graphContains(obj));

unloadGraph(serverId, graph); console.log(graphContains(obj));
false false false
true
false

blankObject¶

создает пустой объект FireFly при помощи, которого можно получить пакеты, которые следует отправить на сервер, для фактического создания объекта

In [15]:
var blank   = blankObject('cc467016-e940-4ce6-a5be-b14c9da2947f', 'item');
var updates = [
    blank.update('type', 'IpVideoCamera'),
    blank.update('name', 'Камера1'),
];
console.log(JSON.stringify(updates,null,2))
[
  {
    "operation": "create",
    "entity": "item",
    "server": "cc467016-e940-4ce6-a5be-b14c9da2947f",
    "owner": "cc467016-e940-4ce6-a5be-b14c9da2947f",
    "id": "a7ec22aa-7b48-4da6-96cb-eaa324feb41e",
    "params": {
      "type": {
        "value": "IpVideoCamera"
      }
    },
    "datetime": 0
  },
  {
    "operation": "update",
    "entity": "item",
    "server": "cc467016-e940-4ce6-a5be-b14c9da2947f",
    "owner": "cc467016-e940-4ce6-a5be-b14c9da2947f",
    "id": "a7ec22aa-7b48-4da6-96cb-eaa324feb41e",
    "params": {
      "name": {
        "value": "Камера1"
      }
    },
    "datetime": 0
  }
]

loadChanges¶

загружает пакеты с изменениями графа FireFly

In [16]:
loadGraph(serverId, graph); 
console.log(getName(obj), getPosition(obj), graphContains(obj));
var changes = [{
    operation: 'update',
    entity: 'item',
    server: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    owner: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    name: 'name',
    value: 'Камера2',
    datetime: epochtime()
},{
    operation: 'update',
    entity: 'item',
    server: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    owner: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    name: 'position',
    value: 'POINT(50.22205734252930 53.23633956909180)',
    datetime: epochtime()    
}];
loadChanges(changes);
console.log(getName(obj), getPosition(obj), graphContains(obj));
Камера1 [] true
Камера2 [ 50.2220573425293, 53.2363395690918 ] true

isType¶

проверяет что аргументя является валидным идентификатором и начинается с заглавной буквы, т.к. только так могут выглядеть имена типов объектов FireFly

In [17]:
[ isType(1), isType('item'), isType('Item'), isType('IpVideoCamera1') ]
Out[17]:
[ false, false, true, true ]

getEnclosedText¶

создает функцию достающую из переданного ей аргумента текст ограниченый сначала и с конца указанными строками, т.е. например проверяет, что текст обрамлен скобками и возвращает этот текст

In [18]:
console.log(getEnclosedText('(',')')('(123)'))
console.log(getEnclosedText('(',')')(' 123 '))
123
undefined

getCondition¶

проверяет, что переданный аргумент начинается с ?( и заканчивается ), а затем пытается скомпилировать текст посередине как выражение static-eval, и в случае успеха возвращает полученную функцию

In [19]:
var f = getCondition('?(x % 2 == True)');
f({ x: 123 })
Out[19]:
false

getExpression¶

проверяет, что переданный аргумент начинается с ( и заканчивается ), а затем пытается скомпилировать текст посередине как выражение static-eval, и в случае успеха возвращает полученную функцию

In [20]:
var f = getExpression('(x+1)');
f({ x: 123 })
Out[20]:
124

queryPolygon¶

функция для поиска элементов в графе находящихся внутри заданного полигона при помощи @turf/boolean-point-in-polygon

In [21]:
var area = 'POLYGON((50.22205734252930 53.23633956909180,50.22212782475368 53.23635106308247,50.22213308362630 53.23633408754193,50.22205542015872 53.23632573239626,50.22205734252930 53.23633956909180))';
var polygon = parsePolygon(area);
var items = queryPolygon(polygon);
console.log(items);
[
  {
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    types: [ 'Item', 'IpVideoCamera' ],
    params: { name: [Object], position: [Object] },
    serverId: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    pins: {},
    name: 'Камера2',
    position: [ 50.2220573425293, 53.2363395690918 ],
    search: [Function: queryPath],
    update: [Function (anonymous)],
    delete: [Function (anonymous)],
    get: [Function (anonymous)],
    refs: [Function (anonymous)],
    ref: [Function (anonymous)],
    parse: [Function (anonymous)],
    eval: [Function (anonymous)],
    time: 1662550230.95
  }
]

queryPath¶

Функция для поиска элементов в графе при помощи строки специального формата, первым аргументом функция принимает эту строку, а вторым список исходных элементов (если список пуст, то поиск происходит по всему графу).

Строка поиска связанных элементов состоит из набора команд разделенных символом /.

При этом каждая команда состоит из альтернатив разделенных символом |.

Механизм поиска связанных элементов по заданному элементу представляет собой конечный автомат, со следующими состояниями:

  • Граф (Graph) - принимает следующие команды:
  • Тип разъема - выбрать в графе все разъемы указанного типа.
  • Тип связи - выбрать в графе все связи указанного типа.
  • Тип элемента - выбрать в графе все элементы указанного типа.
  • Идентификатор элемента - выбрать в графе элемент с заданным идентификатором (идентификатор строго уникален).
  • Имя элемента - выбрать в графе элемент с заданным именем (за уникальностью имени должно следить клиентское приложение).
  • Элемент (Item) - принимает следующие команды:
  • Имя параметра - считать из параметра имя элемента, идентификатор или список идентификаторов элементов и перейти в них.
  • Имя разъема - перейти в указанный разъем на данном элементе.
  • Тип разъема - перейти во все разъемы указанного типа на данном элементе.
  • Тип связи - перейти во все связи указанного типа соединяющие разъем данного элемента.
  • Тип устройства - остаться в текущем устройстве, если его тип совместим с указанным, иначе остановить поиск.
  • Экземпляр (Instance) - принимает следующие команды:
  • Все тоже что и Item, т.к. является его наследником.
  • Имя класса - если данный экземпляр в иерархии наследования имеет предком указанный класс, то остаться в нем, иначе остановить поиск.
  • Связь (Link) - принимает следующие команды:
  • Имя параметра - считать из параметра имя элемента, идентификатор или список идентификаторов элементов и перейти в них.
  • Тип разъема - перейти в разъемы указанного типа, находящиеся на противоположной стороне от разъема, по которому алгоритм попал в данную связь.
  • Тип устройства - перейти в устройство указанного типа соединенного с противоположным концом данной связи.
  • Тип связи - остаться в текущей связи, если её тип совместим с указанным, иначе остановить поиск.
  • Разъем (Pin) - принимает следующие команды:
  • Имя параметра - считать из параметра имя элемента, идентификатор или список идентификаторов элементов и перейти в них.
  • Тип устройства - перейти в устройство указанного типа соединенного с противоположным концом связи этого разъема.
  • Тип связи - перейти во все связи указанного типа, присоединенные к данному разъему.
  • Событие (Event) - принимает следующие команды:
  • Имя параметра - считать из параметра имя элемента, идентификатор или список идентификаторов элементов и перейти в них.
  • Тип события - остаться в текущем событии, если его тип совместим с указанным, иначе остановить поиск.
  • Результат полученный по всем альтернативным веткам команды объединяется и выравнивается в плоский список.

  • Если по команде ничего не найдено, поиск по этой ветке останавливается.

  • Источником или корнем для следующей команды становятся все результаты полученные на предыдущем этапе.

  • Каждая команда так же может быть условием написанным с помощью безопасно-вычислимых выражений static-eval, заключенной между символами ? и ).


Computer/?(params.cpu_usage < 50)
  • Аналогично возможно возвращать рассчитанные значения вместо самого элемента графа, выражение в скобках вместо экземпляров компьютеров вернет загрузку процессора как число от нуля до единицы.

Computer/(params.cpu_usage/100)
  • Возможно также ограничить количество элементов в результате целым числом в качестве шаблона. Следующее выражение вернет только два компьютера.

Computer/2
  • Отдельный случай число один, такое выражение вернет скалярное значение, т.е. один компьютер, а не массив с одним элементом.

Computer/1
  • К параметрам элементов заданных в качестве источника функции queryPath можно обращаться в коде фильтров и селекторов при помощи префикса $, тогда как символ / в начале пути означает переход в корень графа.

Наконец возможен поиск подграфа при помощи многострочной строки поиска, где отступ от предыдущей строчки указывает на то, что поиск следует начинать от предыдущего результата.
В примере первая строчка ищется по всему графу. Вторая от результата первой. Третья тоже от результата первой, т. к. отступ не увеличился. Четвертая ищется от результата третьей, а пятая соответственно от четвертой.
Результатом queryPath будет массив элементов, где каждый элемент содержит результат поиска строки с соответствующим индексом.


ImportSource/1
        LogicLink/Binding/?(cron)/1
        ImportLink/Import
            classes/Class
                /Instance/?($name in classes)/2

  • Найти все видеокамеры:
"VideoCamera"
  • Найти все объекты не являющиеся видеокамерами:
"!VideoCamera"
  • Найти видеокамеру по идентификатору:
"2c5a5d58-5d04-4325-81c6-830a0661bb41/VideoCamera"
  • Найти видеокамеру по имени:
"Какая-то видеокамера/VideoCamera"
  • Найти все камеры, соединенные любым способом с двумя пространствами с указанными идентификаторами:
"1b63a2bd-733d-4788-9fb1-c987335eafcf|f62fe1da-a563-4c90-b13c-3d9eb95a5573/Space/Pin/VideoCamera"
  • Найти все видеокамеры подключенные к разъему logicOutput текущего устройства:
"logicOutput/VideoCamera"
  • Найти все видеокамеры находящиеся с текущим устройством в одном помещении:
"Pin/Space/Pin/VideoCamera"
  • Найти все видеокамеры соединенные любым способом с зоной, идентификатор которой содержится в параметре owner текущего события:
"owner/Zone/Pin/VideoCamera"
  • Найти все видеокамеры соединенные любым способом с зонами, идентификаторы которых содержатся в параметре trace_zones текущего подвижного объекта:
"trace_zones/Zone/Pin/VideoCamera"
  • Найти все сирены LowCurrentSirenMeta находящиеся с заданным элементом либо в одной зоне либо в одном пространстве:
"Link/Space|Zone/Link/LowCurrentSirenMeta"
  • Выбрать все компьютеры Computer с температурой процессора превышающей указанный порог:
"Computer/?(cpu_usage > 50)"
  • Выбрать все компьютеры Computer с температурой процессора превышающей указанный порог и вернуть вместо элементов значение температуры процессора первого из них:
"Computer/?(cpu_usage > 50)/(cpu_usage)/1"
  • Выбрать подграф управляющий импортом данных из внешней БД, и по паре примеров импортированных данных каждого типа:

var multipath = `
    ImportSource/1
        LogicLink/Binding/?(cron)/1
        ImportLink/Import
            classes/Class
                /Instance/?($name in classes)/2`;
var [source,binding,imports,classes,instances] = queryPath(multipath);
In [22]:
var camera = queryPath('IpVideoCamera/1');
camera
Out[22]:
{
  id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
  types: [ 'Item', 'IpVideoCamera' ],
  params: {
    name: {
      value: 'Камера2',
      datetime: 1662550230.95,
      status: undefined,
      version: undefined
    },
    position: {
      value: 'POINT(50.22205734252930 53.23633956909180)',
      datetime: 1662550230.95,
      status: undefined,
      version: undefined
    }
  },
  serverId: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
  pins: {},
  name: 'Камера2',
  position: [ 50.2220573425293, 53.2363395690918 ],
  search: [Function: queryPath],
  update: [Function (anonymous)],
  delete: [Function (anonymous)],
  get: [Function (anonymous)],
  refs: [Function (anonymous)],
  ref: [Function (anonymous)],
  parse: [Function (anonymous)],
  eval: [Function (anonymous)],
  time: 1662550230.95
}
In [9]:
var types = queryPath(`BaseObject/(types[1]+' '+name)`);
console.log(types.length, types);

var types = queryPath(`BaseObject/?(name)/(types[1]+' '+name)`);
console.log(types.length, types);

var types = queryPath(`BaseObject/!Plane/!Female/!Zone/!Sensor/(types[1]+' '+name)`);
console.log(types.length, types);

var types = queryPath(`BaseObject/Plane|Female|Zone|Sensor/(types[1]+' '+name)`);
console.log(types.length, types);
13 [
  'EnvMonitoringSubstanceSensor ',
  'EnvMonitoringWeatherSensor ',
  'IpVideoCamera Камера 109',
  'EnvMonitoringPost ',
  'Female Chica Flaca',
  'Plane Самолет 1',
  'Zone test143',
  'Zone Площадка за офисом',
  'BOLID_FireSensor BOLID_FireSensor1',
  'IpPTZVideoCamera PTZ Cam 211',
  'Zone Площадка перед складом-гаражом',
  'Zone Парковка и терасса (211)',
  'EnvMonitoringChimneyPost '
]
9 [
  'IpVideoCamera Камера 109',
  'Female Chica Flaca',
  'Plane Самолет 1',
  'Zone test143',
  'Zone Площадка за офисом',
  'BOLID_FireSensor BOLID_FireSensor1',
  'IpPTZVideoCamera PTZ Cam 211',
  'Zone Площадка перед складом-гаражом',
  'Zone Парковка и терасса (211)'
]
5 [
  'EnvMonitoringSubstanceSensor ',
  'IpVideoCamera Камера 109',
  'EnvMonitoringPost ',
  'IpPTZVideoCamera PTZ Cam 211',
  'EnvMonitoringChimneyPost '
]
8 [
  'Plane Самолет 1',
  'Female Chica Flaca',
  'Zone test143',
  'Zone Площадка за офисом',
  'Zone Площадка перед складом-гаражом',
  'Zone Парковка и терасса (211)',
  'EnvMonitoringWeatherSensor ',
  'BOLID_FireSensor BOLID_FireSensor1'
]

queryTree¶

Функция для поиска всех объектов соединенных с заданными на любом уровне дерева, т.е. всего поддерева, в которое входят заданные объеты. На каждом уровне поддерева для поиска используется указаный путь.

In [3]:
var am = randomElem(queryPath('AcuarioManager'));
console.log(getType(am), am.name)

var objs = queryTree('Link/Item', [am])
console.log(objs.length)
for(var obj of objs) console.log(getType(obj), obj.name||obj.id);
AcuarioManager 
3
AcuarioManager 02658aa6-d40f-4b30-92df-0ee3f392d5b4
Server 54103689-170d-4ec6-8f7e-4f4917ad671f
IntegraVideo7 d48951f6-1f7c-462d-a595-ceec424bf35e

selectFields¶

функция делающая выборку из javascript объекта по заданному шаблону или его зарегистрированному имени
по умолчанию зарегистрированы следующие шаблоны:

  • copy - пытается скопировать всю структуру как есть, игнорируя однако любые повторы во избежании зацикливания (подходит для любых структур)
  • deep - копирует основные атрибуты элемента в корень, а параметры и пины в соответствующие словари с сохранением структуры (подходит только для элементов графа)
  • shallow - копирует основные атрибуты элемента в корень, параметры и пины так же копирует в корень с сохранением структуры (подходит только для элементов графа)
  • flat - копирует основные атрибуты элемента в корень, так же копирует в корень значения параметров и пины (подходит только для элементов графа)
In [23]:
const shallowTemplate = {
    serverId: 'serverId',
    id:       'id',
    types:    'types',
    params: { '*': '{*}' },
    pins: {
        '*': { 
            types: { 1 : '{*.type}' },
            params: { '*': '{*.*}' },
            links: { 
                '*' : {
                    in:  { owner: { id: '[linkedItems]' } },
                    out: { owner: { id: '[linkedItems]' } },
                }
            },            
        }         
    }
};

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

если ключом является символ *, то выборку содержащуюся в его значении следует применить ко всем элементам родительского словаря

этот же символ в значении заменяется на соответствующее имя ключа словаря источника

params: { '*': '{*}' }

если атрибут копируется в корень целевого объекта, то в качестве значения достаточно указать имя целевого атрибута

serverId: 'serverId'

в фигурных скобках следует указывать путь к не корневому атрибуту целевого объекта

pins: { '*': { types: { 1 : '{*.type}' },

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

links: { '*' : { in: { owner: { id: '[linkedItems]' } },
In [24]:
var camera   = queryPath('IpVideoCamera/1');
var template = 'copy';
var template = 'deep';
var template = 'shallow';
var template = 'flat';
var template = getTemplate('shallow');
var view     = selectFields(template)(camera);
console.log(view);
{
  serverId: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
  id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
  types: [ 'Item', 'IpVideoCamera' ],
  name: {
    value: 'Камера2',
    datetime: 1662550230.95,
    status: undefined,
    version: undefined
  },
  position: {
    value: 'POINT(50.22205734252930 53.23633956909180)',
    datetime: 1662550230.95,
    status: undefined,
    version: undefined
  }
}

следующий пример показывает как при помощи выражений static-eval в тексте ключей шаблона можно изменить значение соответствующего ключа в результате

In [25]:
var obj = queryPath('IpVideoCamera/1')
var template1 = {    
    types: { 1: 'type' },
    id: 'id',
    params: {
        name: { value: 'fio' },
        '(Date(name.datetime))': 'fiodate',
    }
};
selectFields(template1)(obj)
Out[25]:
{
  type: 'IpVideoCamera',
  id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
  fio: 'Камера2',
  fiodate: 'Wed Sep 07 2022 11:31:02 GMT+0000 (Coordinated Universal Time)'
}

getTemplate¶

считывает текст именованного шаблона

In [26]:
var template = getTemplate('flat')
console.log(JSON.stringify(template,null,2))
{
  "serverId": "serverId",
  "id": "id",
  "types": "types",
  "params": {
    "*": {
      "value": "{*}"
    }
  },
  "pins": {
    "*": {
      "types": {
        "1": "{*.type}"
      },
      "params": {
        "*": {
          "value": "{*.*}"
        }
      },
      "links": {
        "*": {
          "in": {
            "owner": {
              "id": "[linkedItems]"
            }
          },
          "out": {
            "owner": {
              "id": "[linkedItems]"
            }
          }
        }
      }
    }
  }
}

updateTemplate¶

добавляет новый именованый шаблон

In [27]:
console.log(getTemplate('template1'))
updateTemplate('template1',template1)
console.log(getTemplate('template1'))
undefined
{
  types: { '1': 'type' },
  id: 'id',
  params: { name: { value: 'fio' }, '(Date(name.datetime))': 'fiodate' }
}

typedef¶

Типы элементов системы описываются на языке XML версии 1.0 с использованием utf-8 кодировки.

Считываются все xml файлы лежащие в каталоге typedef и объединяются в один большой документ, в котором могут присутствовать в произвольном количестве следующие теги:

  • enum - описание перечисляемого типа параметра.
  • item - описание устройства.
  • pin - описание разъема устройства.
  • link - описание соединения между разъемами.
  • event - описание события.
  • schema - схема json документа.

Корневой элемент каждого файла описания может называться произвольно например root.

Любой тег может содержать дополнительные атрибуты доступные в режиме исполнения. Дополнительный атрибут может иметь любое имя не совпадающее с именами служебных атрибутов описанных ниже. Имена дополнительных атрибутов так же не могут совпадать с именами тегов.

is¶

Теги enum, item, link, pin и event описывают конкретные элементы системы и их тела могут содержать произвольное количество тегов is, указывающих базовый тип элемента.

Тег is может содержать следующие атрибуты:

  • type - тип базового устройства. Обязательный атрибут.

Наследуемый тип получает все значения, параметры и разъемы базового.

enum¶

Тег enum содержит следующие атрибуты:

  • type - тип перечисления. Обязательный атрибут.

Тело тега enum содержит один или несколько тегов value, являющихся описанием конкретного значения перечисления.

Тело тега value содержит текстовое представление значения с которым связан данный элемент перечисления.

Так же тег value может содержать дополнительные атрибуты, например, в случае описания состояния устройства, следующие:

  • color - цвет состояния в html формате.
  • image - плоское изображение состояния.
  • model - 3D модель состояния.
  • schema - имя json схемы.
  • apiname - используется в Acuario Manager.

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

param¶

Теги item, link, pin и event описывают конкретные элементы системы и их тела могут содержать произвольное количество тегов param, описывающих параметры данного элемента.

Тег param может содержать следующие атрибуты:

  • type - тип параметра. Обязательный атрибут.
  • bool - булево значение bool;
  • int - целое знаковое число int8;
  • real - число с плавающей точкой float8;
  • guid - глобальный уникальный идентификатор uuid;
  • time - дата/время timestamp;
  • span - интервал времени interval;
  • text - строка text;
  • ltree - ветка иерархического дерева ltree;
  • list - список строк text[];
  • vector - список чисел с плавающей точкой float8[];
  • blob - бинарные данные bytea;
  • json - документ типа json json;
  • xml - документ типа xml xml;
  • point - точка в пространстве point;
  • polygon - полигон polygon;
  • args - если связано с перечисляемым типом при помощи атрибута enum, то список уникальных значений этого перечисления, иначе просто список строк text[];
  • name - имя параметра. Обязательный атрибут.
  • enum - тип перечисления связанного с данным параметром.
  • unit - единица измерения например second.
  • schema - имя параметра внутри данного типа содержащего описание json документа хранящегося в данном параметре в формате JSON Schema, либо имя типа схемы, описанной в теге schema. Имя параметра должно начинаться с маленькой буквы, а имя типа с большой. Имя параметра в этом атрибуте, в отличии от имени типа, реализует механизм динамической типизации настроек. В schema так же можно задавать строку поиска элемента queryPath, последней частью, разумеется, должно быть имя параметра.
  • regexp - регулярное выражение для поиска текста JSON Schema внутри параметра указанного в schema если пусто то используется весь текст параметра schema.
  • path - выражение формата JsonPath для поиска атрибута текста JSON Schema внутри json документа содержащегося в параметре указанном в schema если пусто то используется весь текст параметра schema.
  • editable - флаг обозначающий редактируемое поле используется в редакторах.
  • multiplier - множитель значения параметра используется в Acuario Manager.
  • isValue - флаг обозначающий, что параметр - это значение датчика используется в Acuario Manager.
  • oid - идентификатор в SNMP используется в Acuario Manager.
  • repeat - строковое значение задающее диапазон номеров имен дубликатов параметров вида 1..8. Вместо такого параметра создаются его копии с добавлением к имени индекса из диапазона. В других атрибутах и теле элемента строка {num} заменяется на этот индекс.
  • apiname - используется в Acuario Manager.

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

  • min - вспомогательный параметр указывающий минимальное допустимое значение для текущего параметра.
  • max - вспомогательный параметр указывающий максимальное допустимое значение для текущего параметра.
  • delta_abs - вспомогательный параметр указывающий допустимое отклонение параметра в абсолютных величинах.
  • delta_pct - вспомогательный параметр указывающий допустимое отклонение параметра в процентах.

Если тип параметра не указан то считается что это текстовый параметр.

Тело тега param может содержать текстовое представление значения поля по умолчанию.

Экземпляр элемента системы может содержать только одну копию одного и того же параметра.

Имена параметров in и out являются частично зарезервированными и не должны использоваться при описании типов соединений. Имена параметров name, state и stateargs, так же является частично зарезервированными, но должны быть описаны для каждого типа устройства.

Состояние элемента определяется двумя параметрами:

  • state - визуальное состояние элемента. Всегда имеет тип ltree. Должно быть связано с перечисляемым типом при помощи атрибута enum.
  • stateargs - дополнительные состояния элемента. Всегда имеет тип args. Должно быть связано с перечисляемым типом при помощи атрибута enum.

pin¶

Тег pin может содержать следующие атрибуты:

  • type - тип разъема. Обязательный атрибут.
  • abstract - булево значение, помечающее тип как абстрактный. Т.е. запрещающий создание экземпляров такого типа. По умолчанию считается равным false.
  • many - булево значение указывающее что из данного разъема может выходить несколько соединений. По умолчанию считается равным false.
  • must - булево значение указывающее что данный разъем обязательно должен быть соединен. По умолчанию считается равным false.

link¶

Тег link может содержать следующие атрибуты:

  • type - тип соединения. Обязательный атрибут.
  • abstract - булево значение, помечающее тип как абстрактный. Т.е. запрещающий создание экземпляров такого типа. По умолчанию считается равным false.
  • in - тип входного разъема. Обязательный атрибут.
  • out - тип выходного разъема. Обязательный атрибут.
  • color - цвет линка в редакторах.

item¶

Тег item может содержать следующие атрибуты:

  • type - тип устройства. Обязательный атрибут.
  • abstract - булево значение, помечающее тип как абстрактный. Т.е. запрещающий создание экземпляров такого типа. По умолчанию считается равным false.
  • color - цвет элемента по умолчанию в html формате.
  • image - плоское изображение элемента по умолчанию.
  • model - 3D модель состояния элемента по умолчанию.

Тело тега item может содержать произвольное количество следующих тегов:

  • pin - указывает тип разъема.

Тег pin может содержать следующие атрибуты:

  • type - тип разъема. Обязательный атрибут.
  • name - уникальное для устройства имя разъема. Используется для связывания с графическим отображением устройства. Разъемы для которых не указано имя считаются виртуальными. Если для разъема не указано имя, то его имя совпадает с его типом.
  • many - булево значение указывающее что из данного разъема может выходить несколько соединений. По умолчанию берется значение атрибута из базового класса.
  • must - булево значение указывающее что данный разъем обязательно должен быть соединен. По умолчанию берется значение атрибута из базового класса.
  • repeat - строковое значение задающее диапазон номеров имен дубликатов разъемов вида 1..8. Вместо такого разъема создаются его копии с добавлением к имени индекса из диапазона. В других атрибутах и теле элемента строка {num} заменяется на этот индекс.
  • index - идентификатор разъема на устройстве используется в Acuario Manager.

Внутренний для устройства разъем не может содержать дополнительных параметров.

event¶

Тег event может содержать следующие атрибуты:

  • type - тип события. Обязательный атрибут.
  • abstract - булево значение, помечающее тип как абстрактный. Т.е. запрещающий создание экземпляров такого типа. По умолчанию считается равным false.

schema¶

Тег schema может содержать следующие атрибуты:

  • type - тип схемы. Обязательный атрибут.

Тело тега schema должно содержать валидную схему json документа.

Замечания¶

В процессе парсинга первая буква всех имен типов переводится в верхний регистр, а первая буква всех имен параметров и разъемов в нижний.

Язык TypeDef позволяет описывать некий граф объектов и связи между ними, где основным элементом графа является item, который может иметь некоторое количество именованных разъемов pin, к которым в свою очередь могут быть подключены сзязи link. Таким образом элементы и item и link являются узлами графа и в конкретной реализации должны быть уникально идентифицируемы, тогда как pin идентифицируется своим владельцем item плюс своим уникальным именем. Элемент event так же должен быть уникально идентифицируем, однако не является узлом графа и сохраняется только в архиве изменений.

In [3]:
// загрузка типов в формате xml
const { JSDOM } = require("jsdom");
var xml  = fs.readFileSync('typedef.xml');
var typedefXml  = new JSDOM(xml, { contentType: 'text/xml' }).window.document;

// загрузка типов в формате json
var json = fs.readFileSync('typedef.json');
var typedefJson = JSON.parse(json);

compareTypeDefVersions¶

сравнивает две версии формата {major}.{minor}.{build}.{revision}
в текущей реализации сравниваются только последние компоненты {revision}

In [29]:
[compareTypeDefVersions('0.0.0.1400', '0.0.0.1411'),
 compareTypeDefVersions('0.0.0.1411', '0.0.0.1411'),
 compareTypeDefVersions('0.0.0.1411', '0.0.0.1400')]
Out[29]:
[ -11, 0, 11 ]

queryEntityTypes¶

возвращает полный список типов заданной сущности

In [30]:
var types = queryEntityTypes(typedefJson, 'item')
console.log(types.length, types.slice(0,5), '...', types.slice(-5))

var types = queryEntityTypes(typedefXml, 'item')
console.log(types.length, types.slice(0,5), '...', types.slice(-5))
1122 [
  'AbstractComputer',
  'AbstractTracker',
  'AccessControlCategory',
  'AccessController',
  'AccessItem'
] ... [
  'Zonus',
  'ZWaveDevice',
  'ZWaveRelaySwitch',
  'ZWaveServer',
  'ZwaveSwitch'
]
1122 [
  'AbstractComputer',
  'AbstractTracker',
  'AccessControlCategory',
  'AccessController',
  'AccessItem'
] ... [
  'Zonus',
  'ZWaveDevice',
  'ZWaveRelaySwitch',
  'ZWaveServer',
  'ZwaveSwitch'
]

queryEntityHeirs¶

возвращает полный список наследников для заданного типа заданной сущности

In [31]:
var types = queryEntityHeirs(typedefJson, 'item', 'VideoCamera')
console.log(types.length, types)

var types = queryEntityHeirs(typedefXml, 'item', 'VideoCamera')
console.log(types.length, types)
8 [
  'AnalogPTZVideoCamera',
  'AnalogVideoCamera',
  'EmergencyCallPoint',
  'IpPTZVideoCamera',
  'IpSpeedCam',
  'IpVideoCamera',
  'IpVideoCameraCarCount',
  'VideoTerminal'
]
8 [
  'AnalogPTZVideoCamera',
  'AnalogVideoCamera',
  'EmergencyCallPoint',
  'IpPTZVideoCamera',
  'IpSpeedCam',
  'IpVideoCamera',
  'IpVideoCameraCarCount',
  'VideoTerminal'
]

queryEntityDef¶

возвращает описание заданого типа заданой сущности в json формате FireFly

In [32]:
var def = queryEntityDef(typedefJson, 'event', 'StateEvent')
console.log(def)

var def = queryEntityDef(typedefXml, 'event', 'StateEvent')
console.log(def)
{
  attributes: { type: 'StateEvent' },
  is: [ 'Event', 'StateEvent', 'BaseEvent', 'ProcessingEvent' ],
  param: {
    position: { attributes: [Object], default: null },
    server: { attributes: [Object], default: null },
    owner: { attributes: [Object], default: null },
    event_status: { attributes: [Object], default: null },
    user: { attributes: [Object], default: null },
    event_comment: { attributes: [Object], default: null },
    alarm_info: { attributes: [Object], default: null },
    name: { attributes: [Object], default: null },
    itemtype: { attributes: [Object], default: null },
    version: { attributes: [Object], default: null },
    datetime: { attributes: [Object], default: null },
    state: { attributes: [Object], default: null }
  }
}
{
  attributes: { type: 'StateEvent' },
  is: [ 'Event', 'StateEvent', 'BaseEvent', 'ProcessingEvent' ],
  param: {
    position: { attributes: [Object], default: null },
    server: { attributes: [Object], default: null },
    owner: { attributes: [Object], default: null },
    event_status: { attributes: [Object], default: null },
    user: { attributes: [Object], default: null },
    event_comment: { attributes: [Object], default: null },
    alarm_info: { attributes: [Object], default: null },
    name: { attributes: [Object], default: null },
    itemtype: { attributes: [Object], default: null },
    version: { attributes: [Object], default: null },
    datetime: { attributes: [Object], default: null },
    state: { attributes: [Object], default: null }
  }
}

queryPinItems¶

возвращает список имен типов итемов которы могут быть соединены с заданым пином

In [8]:
console.log(queryPinItems(typedefJson, 'EgsInput'))
[
  'EgsOn',
  'EgsOnCreate',
  'EgsOnSchedule',
  'EgsOnStart',
  'EgsOnUpdate',
  'EgsScenario',
  'EgsScript',
  'EgsTrigger'
]

queryPinLinks¶

возвращает список имен типов итемов которы могут быть соединены с заданым пином

In [9]:
console.log(queryPinLinks(typedefJson, 'EgsInput'))
[ 'EgsLink' ]

objectToChanges¶

разбивает объект FireFly на массив пакетов при помощи которых этот объект можно создать

In [18]:
var obj = queryPath('/Item/1')
objectToChanges(obj)
Out[18]:
[
  {
    operation: 'create',
    entity: 'item',
    server: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    owner: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    datetime: 0,
    type: 'IpVideoCamera',
    types: [ 'Item', 'IpVideoCamera' ]
  },
  {
    operation: 'update',
    entity: 'item',
    server: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    owner: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    datetime: 0,
    type: 'IpVideoCamera',
    name: 'name',
    value: 'Камера1'
  }
]

objectsToChanges¶

разбивает список объектов FireFly на массив пакетов при помощи которых эти объекты можно создать

In [19]:
var obj = queryPath('/Item/1')
objectsToChanges([obj])
Out[19]:
[
  {
    operation: 'create',
    entity: 'item',
    server: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    owner: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    datetime: 0,
    type: 'IpVideoCamera',
    types: [ 'Item', 'IpVideoCamera' ]
  },
  {
    operation: 'update',
    entity: 'item',
    server: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    owner: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    datetime: 0,
    type: 'IpVideoCamera',
    name: 'name',
    value: 'Камера1'
  }
]

graphToChanges¶

разбивает граф объектов FireFly на массив пакетов при помощи которых этот граф можно создать

In [20]:
graphToChanges(graph)
Out[20]:
[
  {
    operation: 'create',
    entity: 'item',
    server: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    owner: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    datetime: 0,
    type: 'IpVideoCamera',
    types: [ 'Item', 'IpVideoCamera' ]
  },
  {
    operation: 'update',
    entity: 'item',
    server: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    owner: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    datetime: 0,
    type: 'IpVideoCamera',
    name: 'name',
    value: 'Камера1'
  }
]

changesToGraph¶

объендиняет массив пакетов с изменениями FireFly в графовую структуру

In [33]:
var changes = [{
    operation: 'create',
    entity: 'item',
    server: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    owner: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    type: 'IpVideoCamera',
    datetime: epochtime()
},{
    operation: 'update',
    entity: 'item',
    server: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    owner: 'cc467016-e940-4ce6-a5be-b14c9da2947f',
    id: '3998b9c7-0d56-4863-9633-7bec609aaf9a',
    name: 'name',
    value: 'Камера2',
    datetime: epochtime()    
}]

var graph = changesToGraph(changes)
console.log(JSON.stringify(graph,null,2))
{
  "items": {
    "3998b9c7-0d56-4863-9633-7bec609aaf9a": {
      "params": {
        "name": {
          "value": "Камера2",
          "datetime": 1662550295.729
        }
      },
      "pins": {}
    }
  },
  "links": {},
  "version": 0
}

Соединение с сервером¶

загрузим данные с реального сервера FireFly

In [46]:
Object.assign(global, require('./ffwamp.js'))
const WebSocket = require('ws');
const fetch     = require('node-fetch');
var url         = "ws://192.168.10.72:8301/ws";
var authorigin  = 'http://62.69.14.245:10000/auth';
var authid      = 'firefly';
var authpass    = 'firefly';

async function run(callback=(async () => {})) {
    var api = await connectToLocalServer(url, { authorigin, authid, authpass });
    var { serverId, getGraph, changeGraph, goodbye } = api;
    console.log('localServerId:', serverId);
    
    try {    
        var json  = await getGraph(serverId);
        var graph = JSON.parse(json);
        console.log('items:', Object.keys(graph.items).length);
        console.log('links:', Object.keys(graph.links).length);
        unloadGraph(serverId);
        loadGraph(serverId, graph);
        await callback(api);
    }
    catch(e) { throw e; }
    finally  { await goodbye(); }
}

run()
Out[46]:
Promise { <pending> }
OUTPUT: [1,"integra-s",{"authmethods":["integra-db"],"roles":{"caller":{},"subscriber":{}}}]
INPUT: [4,"integra-db",{}]
OUTPUT: [5,"116daf9d-9d05-4215-ad16-894264ae26ab",{}]
INPUT: [2,8707605780785845,{"authrole":"firefly.user","authmethod":"integra-db","authprovider":"integra-s","roles":{"broker":{"features":{"publisher_identification":true,"publisher_exclusion":true,"subscriber_blackwhite_listing":true}},"dealer":{"features":{"pro...
OUTPUT: [48,12,{"disclose_me":true},"com.integra.ember.call_stored",["server.get",[0]],{}]
INPUT: [50,12,{},["{\"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5\": {\"is_local\": true, \"status\": \"online\", \"position\": \"POINT(0 0)\", \"hash\": \"e2ba6b08-dc48-36d2-814a-af37201bdb38\", \"name\": \"Local server\", \"adm_level\": null, \"router_url\": \"localh...
localServerId: 5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5
OUTPUT: [48,13,{"disclose_me":true},"com.integra.ember.call_stored",["graph.get",["5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5",0]],{}]
INPUT: [50,13,{},["{\"graph_version\": 2498098, \"max_version\": 2498098, \"links\": {\"920e99d9-8449-457f-b2cc-9c2c7b04918b\": {\"params\": {\"out\": {\"version\": 2498098, \"value\": \"d79ec1a3-4ac1-4e37-916b-d59bbe8cfa2a.logicOutput\", \"datetime\": 166255044...
items: 37
links: 28
OUTPUT: [6,{},"wamp.close.goodbye_and_out"]
INPUT: [6,{},"wamp.goodbye.normal"]
(node:14058) UnhandledPromiseRejectionWarning: GOODBYE
(node:14058) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 5)

Собственные функции¶

каждый объект графа FireFly имеет так же набор удобных собственных функций

In [35]:
var toFlat = selectFields('flat');
var card = queryPath('Карточка1/1');

// поиск в графе при помощи queryPath, но начиная с владельца функции
console.log('search:', card.search('Link/Item').map(toFlat), '\n')

// создание пакета с изменением параметра
console.log('update:', card.update('state', 'alarm.alarm'), '\n')

// создание пакета с удалением владельца
console.log('delete:', card.delete(), '\n')

// безопасное получение параметра
console.log('get:', card.get('name').value, card.get('command').value, '\n')

// вызов выражения static-eval с владельцем функции в качестве контекста
console.log('eval:', card.eval('category+" "+name'), '\n')

// возвращает и кэширует javascript объект содержащийся в указаном параметре как json
// вторым аргументом parse можно указать специфический парсер, например parsePolygon
console.log('parse:', card.parse('regulations'), '\n')

// возвращает и кэширует FireFly объект идентификтор которого содержится в указаном параметре
console.log('ref:', toFlat(card.ref('value_250')), '\n')
console.log('refs:', card.refs('value_250').map(toFlat), '\n')

// проверить значение параметра функцией miscutils::areEqualValues
if(card.check('state', 'alarm.alarm')) {    
    
}
search: [
  {
    serverId: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    id: '579f29ac-5b8a-4b0c-936c-7afe3e78d240',
    types: [
      'Item',
      'IntegraAccidentCard',
      'BaseObject',
      'SpatialObject',
      'BaseCardEvent'
    ],
    incident_time: 1658926800,
    name: 'qqq',
    precedence: 50,
    executors: [],
    precedence_max: 100,
    altitude: -65535,
    time_created: 1658926800,
    regulations: {
      category: 'emergency_work',
      executants: [],
      regulations: [Object],
      name: 'qqq',
      areaColor: '#660000ff',
      lists: [],
      states: {},
      complex: false,
      interview: [Array],
      showPolygon: true,
      watching_services: [Array],
      toDo: [],
      additional: []
    },
    state: 'new_card',
    deadline: 1658930400,
    precedence_min: 0,
    position: 'POINT(0 0)',
    ignoreTimeout: 0,
    watching_services: [ 1 ],
    gpsContent: { type: 'GpsProviderIn' },
    positionContent: { type: 'PositionContent' },
    causeOf: { type: 'CauseOf' },
    logicOutput: { type: 'LogicOutput' },
    linkedItems: [
      '7c20f7f3-84ba-4655-ae99-f452b1629a3f',
      '579f29ac-5b8a-4b0c-936c-7afe3e78d240'
    ],
    logicInput: { type: 'LogicInput' },
    fileInput: { type: 'FileInput' },
    provoker_input: { type: 'Provoker_input' },
    person_input: { type: 'Person_input' },
    imageInput: { type: 'ImageInput' }
  }
] 

update: {
  operation: 'update',
  entity: 'item',
  server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
  owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
  id: '7c20f7f3-84ba-4655-ae99-f452b1629a3f',
  params: { state: { value: 'alarm.alarm' } },
  datetime: 0
} 

delete: {
  operation: 'delete',
  entity: 'item',
  server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
  owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
  id: '7c20f7f3-84ba-4655-ae99-f452b1629a3f',
  datetime: 0
} 

get: Карточка1 undefined 

eval: emergency_work Карточка1 

parse: { a: 1, b: 2 } 

ref: {
  serverId: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
  id: '862c2a0d-c553-4dab-a720-f370802a03c1',
  types: [ 'Item', 'Zone', 'BaseObject', 'SpatialObject' ],
  global: true,
  name: 'Зона 1',
  precedence: 50,
  is_water_zone: true,
  precedence_max: 100,
  altitude: -65535,
  trace_factor: 1,
  alarm_weight: 2,
  state: 'ok.normal',
  command: 'activate',
  precedence_min: 0,
  alarm_info: '{"alarm_factor":1,"alarm_precedence":0,"state_before":"alarm.alarm","state_after":"ok.normal","members":{"3d694a91-c6f8-423d-9c8b-17a005caf9b9":{"type":"IpPTZVideoCamera","state":"ok.normal","datetime":1659036334,"weight":1,"precedence":50,"importance":1,"alarm":false,"value":"ok.normal"},"b6c150c5-2402-4cbe-9730-a6fa3fdc2c83":{"type":"BOLID_VolumeSensor","state":"ok.normal","datetime":1659036337,"weight":1,"precedence":50,"importance":1,"alarm":false,"value":"ok.normal"},"af998922-1a8b-40af-afe9-c71b502bc018":{"type":"LowCurrentSirenMeta","weight":0,"precedence":50,"importance":1,"alarm":false},"cbfdb567-ea0f-4d24-b5db-9d135a467736":{"type":"StreetLighting","state":"ok.true","datetime":1659036328,"weight":0,"precedence":50,"importance":0,"alarm":false,"value":"ok.true"},"c2f58334-ebf4-4fff-b990-a7cae44ca463":{"type":"StreetLighting","state":"ok.true","datetime":1659036328,"weight":0,"precedence":50,"importance":0,"alarm":false,"value":"ok.true"}}}',
  position: 'POINT(0 0)',
  alarm_factor: 1,
  ignoreTimeout: 0,
  gpsContent: { type: 'GpsProviderIn' },
  positionContent: { type: 'PositionContent' },
  causeOf: { type: 'CauseOf' },
  logicOutput: { type: 'LogicOutput' },
  zoneOutputPin: { type: 'ZoneOutputPin' },
  linkedItems: [
    '3d694a91-c6f8-423d-9c8b-17a005caf9b9',
    '862c2a0d-c553-4dab-a720-f370802a03c1',
    'b6c150c5-2402-4cbe-9730-a6fa3fdc2c83',
    '862c2a0d-c553-4dab-a720-f370802a03c1',
    'af998922-1a8b-40af-afe9-c71b502bc018',
    '862c2a0d-c553-4dab-a720-f370802a03c1',
    'cbfdb567-ea0f-4d24-b5db-9d135a467736',
    '862c2a0d-c553-4dab-a720-f370802a03c1',
    'c2f58334-ebf4-4fff-b990-a7cae44ca463',
    '862c2a0d-c553-4dab-a720-f370802a03c1'
  ],
  logicInput: { type: 'LogicInput' },
  fileInput: { type: 'FileInput' },
  provoker_input: { type: 'Provoker_input' },
  person_input: { type: 'Person_input' },
  imageInput: { type: 'ImageInput' }
} 

refs: [
  {
    serverId: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    id: '862c2a0d-c553-4dab-a720-f370802a03c1',
    types: [ 'Item', 'Zone', 'BaseObject', 'SpatialObject' ],
    global: true,
    name: 'Зона 1',
    precedence: 50,
    is_water_zone: true,
    precedence_max: 100,
    altitude: -65535,
    trace_factor: 1,
    alarm_weight: 2,
    state: 'ok.normal',
    command: 'activate',
    precedence_min: 0,
    alarm_info: '{"alarm_factor":1,"alarm_precedence":0,"state_before":"alarm.alarm","state_after":"ok.normal","members":{"3d694a91-c6f8-423d-9c8b-17a005caf9b9":{"type":"IpPTZVideoCamera","state":"ok.normal","datetime":1659036334,"weight":1,"precedence":50,"importance":1,"alarm":false,"value":"ok.normal"},"b6c150c5-2402-4cbe-9730-a6fa3fdc2c83":{"type":"BOLID_VolumeSensor","state":"ok.normal","datetime":1659036337,"weight":1,"precedence":50,"importance":1,"alarm":false,"value":"ok.normal"},"af998922-1a8b-40af-afe9-c71b502bc018":{"type":"LowCurrentSirenMeta","weight":0,"precedence":50,"importance":1,"alarm":false},"cbfdb567-ea0f-4d24-b5db-9d135a467736":{"type":"StreetLighting","state":"ok.true","datetime":1659036328,"weight":0,"precedence":50,"importance":0,"alarm":false,"value":"ok.true"},"c2f58334-ebf4-4fff-b990-a7cae44ca463":{"type":"StreetLighting","state":"ok.true","datetime":1659036328,"weight":0,"precedence":50,"importance":0,"alarm":false,"value":"ok.true"}}}',
    position: 'POINT(0 0)',
    alarm_factor: 1,
    ignoreTimeout: 0,
    gpsContent: { type: 'GpsProviderIn' },
    positionContent: { type: 'PositionContent' },
    causeOf: { type: 'CauseOf' },
    logicOutput: { type: 'LogicOutput' },
    zoneOutputPin: { type: 'ZoneOutputPin' },
    linkedItems: [
      '3d694a91-c6f8-423d-9c8b-17a005caf9b9',
      '862c2a0d-c553-4dab-a720-f370802a03c1',
      'b6c150c5-2402-4cbe-9730-a6fa3fdc2c83',
      '862c2a0d-c553-4dab-a720-f370802a03c1',
      'af998922-1a8b-40af-afe9-c71b502bc018',
      '862c2a0d-c553-4dab-a720-f370802a03c1',
      'cbfdb567-ea0f-4d24-b5db-9d135a467736',
      '862c2a0d-c553-4dab-a720-f370802a03c1',
      'c2f58334-ebf4-4fff-b990-a7cae44ca463',
      '862c2a0d-c553-4dab-a720-f370802a03c1'
    ],
    logicInput: { type: 'LogicInput' },
    fileInput: { type: 'FileInput' },
    provoker_input: { type: 'Provoker_input' },
    person_input: { type: 'Person_input' },
    imageInput: { type: 'ImageInput' }
  }
] 

Еще примеры¶

создадим новый тестовый объект и выведем результат создания на экран

In [60]:
run(async ({ serverId, changeGraph }) => {
    var post1   = blankObject(serverId, 'item');
    var updates = [post1.update('type','MilitaryPost'), post1.update('name','Должность') ];
    var changes = await changeGraph(updates);
    loadChanges(changes);
    console.log(changes);
});
Out[60]:
Promise { <pending> }
OUTPUT: [1,"integra-s",{"authmethods":["integra-db"],"roles":{"caller":{},"subscriber":{}}}]
INPUT: [4,"integra-db",{}]
OUTPUT: [5,"2b791514-2472-409f-997b-c8018e53b573",{}]
INPUT: [2,140052119719672,{"authrole":"firefly.user","authmethod":"integra-db","authprovider":"integra-s","roles":{"broker":{"features":{"publisher_identification":true,"publisher_exclusion":true,"subscriber_blackwhite_listing":true}},"dealer":{"features":{"prog...
OUTPUT: [48,14,{"disclose_me":true},"com.integra.ember.call_stored",["server.get",[0]],{}]
INPUT: [50,14,{},["{\"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5\": {\"is_local\": true, \"status\": \"online\", \"position\": \"POINT(0 0)\", \"hash\": \"e2ba6b08-dc48-36d2-814a-af37201bdb38\", \"name\": \"Local server\", \"adm_level\": null, \"router_url\": \"localh...
localServerId: 5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5
OUTPUT: [48,15,{"disclose_me":true},"com.integra.ember.call_stored",["graph.get",["5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5",0]],{}]
INPUT: [50,15,{},["{\"graph_version\": 2498098, \"max_version\": 2498127, \"links\": {\"920e99d9-8449-457f-b2cc-9c2c7b04918b\": {\"params\": {\"out\": {\"version\": 2498098, \"value\": \"d79ec1a3-4ac1-4e37-916b-d59bbe8cfa2a.logicOutput\", \"datetime\": 166255044...
items: 35
links: 28
OUTPUT: [48,16,{"disclose_me":true},"com.integra.ember.put",[[{"operation":"create","entity":"item","server":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","owner":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","id":"55e848ed-64a6-4593-82d6-9db917033b66","params":{"type":{"value":"MilitaryPost"}},"datetime":0},{"operation":"update","entity":"item","server":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","owner":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","id":"55e848ed-64a6-4593-82d6-9db917033b66","params":{"name":{"value":"Должность"}},"datetime":0}]],{}]
INPUT: [50,16,{},[[{"status":null,"paramid":127563,"name":null,"server":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","entity":"item","value":null,"txid":23745929,"version":2498128,"owner":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","operation":"create","datetime":166255067...
[
  {
    status: null,
    paramid: 127563,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745929,
    version: 2498128,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'create',
    datetime: 1662550672,
    type: 'MilitaryPost',
    id: '55e848ed-64a6-4593-82d6-9db917033b66'
  },
  {
    status: null,
    paramid: 646599,
    name: 'categoryName',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745929,
    version: 2498129,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550672,
    type: 'MilitaryPost',
    id: '55e848ed-64a6-4593-82d6-9db917033b66'
  },
  {
    status: null,
    paramid: 646600,
    name: 'dateOfAppointment',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745929,
    version: 2498130,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550672,
    type: 'MilitaryPost',
    id: '55e848ed-64a6-4593-82d6-9db917033b66'
  },
  {
    status: null,
    paramid: 646601,
    name: 'groupName',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745929,
    version: 2498131,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550672,
    type: 'MilitaryPost',
    id: '55e848ed-64a6-4593-82d6-9db917033b66'
  },
  {
    status: null,
    paramid: 646602,
    name: 'militaryUnit',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745929,
    version: 2498132,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550672,
    type: 'MilitaryPost',
    id: '55e848ed-64a6-4593-82d6-9db917033b66'
  },
  {
    status: null,
    paramid: 646603,
    name: 'name',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745929,
    version: 2498133,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550672,
    type: 'MilitaryPost',
    id: '55e848ed-64a6-4593-82d6-9db917033b66'
  },
  {
    status: null,
    paramid: 646604,
    name: 'personalNumber',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745929,
    version: 2498134,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550672,
    type: 'MilitaryPost',
    id: '55e848ed-64a6-4593-82d6-9db917033b66'
  },
  {
    status: null,
    paramid: 646605,
    name: 'staffId',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745929,
    version: 2498135,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550672,
    type: 'MilitaryPost',
    id: '55e848ed-64a6-4593-82d6-9db917033b66'
  },
  {
    status: null,
    paramid: 646606,
    name: 'state',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745929,
    version: 2498136,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550672,
    type: 'MilitaryPost',
    id: '55e848ed-64a6-4593-82d6-9db917033b66'
  },
  {
    status: null,
    paramid: 127564,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'pin',
    value: null,
    txid: 23745929,
    version: 2498137,
    owner: '55e848ed-64a6-4593-82d6-9db917033b66',
    operation: 'create',
    datetime: 1662550672,
    type: 'MilitaryContainer',
    id: 'militaryContainer'
  },
  {
    status: null,
    paramid: 127565,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'pin',
    value: null,
    txid: 23745929,
    version: 2498138,
    owner: '55e848ed-64a6-4593-82d6-9db917033b66',
    operation: 'create',
    datetime: 1662550672,
    type: 'MilitaryContent',
    id: 'militaryContent'
  },
  {
    status: null,
    paramid: 127566,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'pin',
    value: null,
    txid: 23745929,
    version: 2498139,
    owner: '55e848ed-64a6-4593-82d6-9db917033b66',
    operation: 'create',
    datetime: 1662550672,
    type: 'MilitaryTempContainer',
    id: 'militaryTempContainer'
  },
  {
    status: null,
    paramid: 127567,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'pin',
    value: null,
    txid: 23745929,
    version: 2498140,
    owner: '55e848ed-64a6-4593-82d6-9db917033b66',
    operation: 'create',
    datetime: 1662550672,
    type: 'MilitaryTempContent',
    id: 'militaryTempContent'
  },
  {
    status: null,
    paramid: 127568,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'pin',
    value: null,
    txid: 23745929,
    version: 2498141,
    owner: '55e848ed-64a6-4593-82d6-9db917033b66',
    operation: 'create',
    datetime: 1662550672,
    type: 'LogicInput',
    id: 'logicInput'
  },
  {
    status: null,
    paramid: 127569,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'pin',
    value: null,
    txid: 23745929,
    version: 2498142,
    owner: '55e848ed-64a6-4593-82d6-9db917033b66',
    operation: 'create',
    datetime: 1662550672,
    type: 'LogicOutput',
    id: 'logicOutput'
  },
  {
    status: null,
    paramid: 646603,
    name: 'name',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: 'Должность',
    txid: 23745929,
    version: 2498143,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550672,
    type: 'MilitaryPost',
    id: '55e848ed-64a6-4593-82d6-9db917033b66'
  }
]
OUTPUT: [6,{},"wamp.close.goodbye_and_out"]
INPUT: [6,{},"wamp.goodbye.normal"]
(node:14058) UnhandledPromiseRejectionWarning: GOODBYE
(node:14058) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 6)

теперь найдем этот объект в графе

In [61]:
var post1 = queryPath('MilitaryPost/1')
post1
Out[61]:
{
  pins: {
    militaryContent: {
      types: [Array],
      name: 'militaryContent',
      id: '3d02e7cb-ce66-4c6f-ab2e-d7f782538352.militaryContent',
      params: {},
      search: [Function: queryPath],
      update: [Function (anonymous)],
      delete: [Function (anonymous)],
      get: [Function (anonymous)],
      refs: [Function (anonymous)],
      ref: [Function (anonymous)],
      parse: [Function (anonymous)],
      eval: [Function (anonymous)],
      time: 1662550669.962
    },
    militaryTempContent: {
      types: [Array],
      name: 'militaryTempContent',
      id: '3d02e7cb-ce66-4c6f-ab2e-d7f782538352.militaryTempContent',
      params: {},
      search: [Function: queryPath],
      update: [Function (anonymous)],
      delete: [Function (anonymous)],
      get: [Function (anonymous)],
      refs: [Function (anonymous)],
      ref: [Function (anonymous)],
      parse: [Function (anonymous)],
      eval: [Function (anonymous)],
      time: 1662550669.962
    },
    militaryContainer: {
      types: [Array],
      name: 'militaryContainer',
      id: '3d02e7cb-ce66-4c6f-ab2e-d7f782538352.militaryContainer',
      params: {},
      search: [Function: queryPath],
      update: [Function (anonymous)],
      delete: [Function (anonymous)],
      get: [Function (anonymous)],
      refs: [Function (anonymous)],
      ref: [Function (anonymous)],
      parse: [Function (anonymous)],
      eval: [Function (anonymous)],
      time: 1662550669.962
    },
    logicOutput: {
      types: [Array],
      name: 'logicOutput',
      id: '3d02e7cb-ce66-4c6f-ab2e-d7f782538352.logicOutput',
      params: {},
      search: [Function: queryPath],
      update: [Function (anonymous)],
      delete: [Function (anonymous)],
      get: [Function (anonymous)],
      refs: [Function (anonymous)],
      ref: [Function (anonymous)],
      parse: [Function (anonymous)],
      eval: [Function (anonymous)],
      time: 1662550669.962
    },
    logicInput: {
      types: [Array],
      name: 'logicInput',
      id: '3d02e7cb-ce66-4c6f-ab2e-d7f782538352.logicInput',
      params: {},
      search: [Function: queryPath],
      update: [Function (anonymous)],
      delete: [Function (anonymous)],
      get: [Function (anonymous)],
      refs: [Function (anonymous)],
      ref: [Function (anonymous)],
      parse: [Function (anonymous)],
      eval: [Function (anonymous)],
      time: 1662550669.962
    },
    militaryTempContainer: {
      types: [Array],
      name: 'militaryTempContainer',
      id: '3d02e7cb-ce66-4c6f-ab2e-d7f782538352.militaryTempContainer',
      params: {},
      search: [Function: queryPath],
      update: [Function (anonymous)],
      delete: [Function (anonymous)],
      get: [Function (anonymous)],
      refs: [Function (anonymous)],
      ref: [Function (anonymous)],
      parse: [Function (anonymous)],
      eval: [Function (anonymous)],
      time: 1662550669.962
    }
  },
  params: {
    state: { version: 2498070, value: null, datetime: 1662550419 },
    name: { version: 2498077, value: 'Должность', datetime: 1662550419 }
  },
  types: [ 'Item', 'MilitaryPost', 'Category', 'MilitaryCategory' ],
  serverId: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
  id: '3d02e7cb-ce66-4c6f-ab2e-d7f782538352',
  name: 'Должность',
  position: [],
  search: [Function: queryPath],
  update: [Function (anonymous)],
  delete: [Function (anonymous)],
  get: [Function (anonymous)],
  refs: [Function (anonymous)],
  ref: [Function (anonymous)],
  parse: [Function (anonymous)],
  eval: [Function (anonymous)],
  time: 1662550669.962
}

создадим тестовую метрику и выведем результат создания на экран

In [62]:
run(async ({ serverId, changeGraph }) => {
    var metric1 = blankObject(serverId, 'item')
    var updates = [metric1.update('type','Metric'), metric1.update('name','Metric1') ]        
    var changes = await changeGraph(updates);
    loadChanges(changes);
    console.log(changes);
});
Out[62]:
Promise { <pending> }
OUTPUT: [1,"integra-s",{"authmethods":["integra-db"],"roles":{"caller":{},"subscriber":{}}}]
INPUT: [4,"integra-db",{}]
OUTPUT: [5,"ef9a1d05-fd45-4221-b812-38d0baf4634f",{}]
INPUT: [2,6181946753485369,{"authrole":"firefly.user","authmethod":"integra-db","authprovider":"integra-s","roles":{"broker":{"features":{"publisher_identification":true,"publisher_exclusion":true,"subscriber_blackwhite_listing":true}},"dealer":{"features":{"pro...
OUTPUT: [48,17,{"disclose_me":true},"com.integra.ember.call_stored",["server.get",[0]],{}]
INPUT: [50,17,{},["{\"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5\": {\"is_local\": true, \"status\": \"online\", \"position\": \"POINT(0 0)\", \"hash\": \"e2ba6b08-dc48-36d2-814a-af37201bdb38\", \"name\": \"Local server\", \"adm_level\": null, \"router_url\": \"localh...
localServerId: 5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5
OUTPUT: [48,18,{"disclose_me":true},"com.integra.ember.call_stored",["graph.get",["5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5",0]],{}]
INPUT: [50,18,{},["{\"graph_version\": 2498143, \"max_version\": 2498143, \"links\": {\"920e99d9-8449-457f-b2cc-9c2c7b04918b\": {\"params\": {\"out\": {\"version\": 2498098, \"value\": \"d79ec1a3-4ac1-4e37-916b-d59bbe8cfa2a.logicOutput\", \"datetime\": 166255044...
items: 36
links: 28
OUTPUT: [48,19,{"disclose_me":true},"com.integra.ember.put",[[{"operation":"create","entity":"item","server":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","owner":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","id":"a08399ef-ca80-4321-8036-ee186622d786","params":{"type":{"value":"Metric"}},"datetime":0},{"operation":"update","entity":"item","server":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","owner":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","id":"a08399ef-ca80-4321-8036-ee186622d786","params":{"name":{"value":"Metric1"}},"datetime":0}]],{}]
INPUT: [50,19,{},[[{"status":null,"paramid":127570,"name":null,"server":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","entity":"item","value":null,"txid":23745931,"version":2498144,"owner":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","operation":"create","datetime":166255068...
[
  {
    status: null,
    paramid: 127570,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745931,
    version: 2498144,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'create',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  },
  {
    status: null,
    paramid: 646607,
    name: 'args',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745931,
    version: 2498145,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  },
  {
    status: null,
    paramid: 646608,
    name: 'binding',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745931,
    version: 2498146,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  },
  {
    status: null,
    paramid: 646609,
    name: 'description',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745931,
    version: 2498147,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  },
  {
    status: null,
    paramid: 646610,
    name: 'error',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745931,
    version: 2498148,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  },
  {
    status: null,
    paramid: 646611,
    name: 'kwargs',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745931,
    version: 2498149,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  },
  {
    status: null,
    paramid: 646612,
    name: 'name',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745931,
    version: 2498150,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  },
  {
    status: null,
    paramid: 646613,
    name: 'owner',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745931,
    version: 2498151,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  },
  {
    status: null,
    paramid: 646614,
    name: 'state',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745931,
    version: 2498152,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  },
  {
    status: null,
    paramid: 646615,
    name: 'value',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745931,
    version: 2498153,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  },
  {
    status: null,
    paramid: 646616,
    name: 'weight',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: null,
    txid: 23745931,
    version: 2498154,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  },
  {
    status: null,
    paramid: 127571,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'pin',
    value: null,
    txid: 23745931,
    version: 2498155,
    owner: 'a08399ef-ca80-4321-8036-ee186622d786',
    operation: 'create',
    datetime: 1662550683,
    type: 'SoftwareContent',
    id: 'softwareContent'
  },
  {
    status: null,
    paramid: 127572,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'pin',
    value: null,
    txid: 23745931,
    version: 2498156,
    owner: 'a08399ef-ca80-4321-8036-ee186622d786',
    operation: 'create',
    datetime: 1662550683,
    type: 'LogicOutput',
    id: 'logicOutput'
  },
  {
    status: null,
    paramid: 127573,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'pin',
    value: null,
    txid: 23745931,
    version: 2498157,
    owner: 'a08399ef-ca80-4321-8036-ee186622d786',
    operation: 'create',
    datetime: 1662550683,
    type: 'LogicInput',
    id: 'logicInput'
  },
  {
    status: null,
    paramid: 646612,
    name: 'name',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'item',
    value: 'Metric1',
    txid: 23745931,
    version: 2498158,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550683,
    type: 'Metric',
    id: 'a08399ef-ca80-4321-8036-ee186622d786'
  }
]
OUTPUT: [6,{},"wamp.close.goodbye_and_out"]
INPUT: [6,{},"wamp.goodbye.normal"]
(node:14058) UnhandledPromiseRejectionWarning: GOODBYE
(node:14058) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 7)

теперь найдем эту метрику в графе

In [63]:
var metric1 = queryPath('Metric1/1')
metric1
Out[63]:
{
  pins: {
    softwareContent: {
      types: [Array],
      name: 'softwareContent',
      id: 'ab4a7e0b-ddf2-417a-b5e5-98deb30d36ae.softwareContent',
      params: {},
      search: [Function: queryPath],
      update: [Function (anonymous)],
      delete: [Function (anonymous)],
      get: [Function (anonymous)],
      refs: [Function (anonymous)],
      ref: [Function (anonymous)],
      parse: [Function (anonymous)],
      eval: [Function (anonymous)],
      time: 1662550680.475
    },
    logicInput: {
      types: [Array],
      name: 'logicInput',
      id: 'ab4a7e0b-ddf2-417a-b5e5-98deb30d36ae.logicInput',
      params: {},
      search: [Function: queryPath],
      update: [Function (anonymous)],
      delete: [Function (anonymous)],
      get: [Function (anonymous)],
      refs: [Function (anonymous)],
      ref: [Function (anonymous)],
      parse: [Function (anonymous)],
      eval: [Function (anonymous)],
      time: 1662550680.475
    },
    logicOutput: {
      types: [Array],
      name: 'logicOutput',
      id: 'ab4a7e0b-ddf2-417a-b5e5-98deb30d36ae.logicOutput',
      params: {},
      search: [Function: queryPath],
      update: [Function (anonymous)],
      delete: [Function (anonymous)],
      get: [Function (anonymous)],
      refs: [Function (anonymous)],
      ref: [Function (anonymous)],
      parse: [Function (anonymous)],
      eval: [Function (anonymous)],
      time: 1662550680.475
    }
  },
  params: {
    state: { version: 2498086, value: null, datetime: 1662550432 },
    name: { version: 2498092, value: 'Metric1', datetime: 1662550432 }
  },
  types: [ 'Item', 'Metric', 'Software' ],
  serverId: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
  id: 'ab4a7e0b-ddf2-417a-b5e5-98deb30d36ae',
  name: 'Metric1',
  position: [],
  search: [Function: queryPath],
  update: [Function (anonymous)],
  delete: [Function (anonymous)],
  get: [Function (anonymous)],
  refs: [Function (anonymous)],
  ref: [Function (anonymous)],
  parse: [Function (anonymous)],
  eval: [Function (anonymous)],
  time: 1662550680.475
}

создадим линк соединяющий тестовый датчик и метрику и выведем результат создания на экран

In [64]:
run(async ({ serverId, changeGraph }) => {
    var link1 = blankObject(serverId, 'link');
    var updates = [
       link1.update('type','LogicLink'), 
       link1.update('in',post1.pins.logicInput.id),
       link1.update('out',metric1.pins.logicOutput.id),
    ];     
    console.log(updates);
    var changes = await changeGraph(updates);
    loadChanges(changes);
    console.log(changes);
});
Out[64]:
Promise { <pending> }
OUTPUT: [1,"integra-s",{"authmethods":["integra-db"],"roles":{"caller":{},"subscriber":{}}}]
INPUT: [4,"integra-db",{}]
OUTPUT: [5,"df6f5d67-51ac-4208-bcd4-dda8f154399c",{}]
INPUT: [2,4812682233207926,{"authrole":"firefly.user","authmethod":"integra-db","authprovider":"integra-s","roles":{"broker":{"features":{"publisher_identification":true,"publisher_exclusion":true,"subscriber_blackwhite_listing":true}},"dealer":{"features":{"pro...
OUTPUT: [48,20,{"disclose_me":true},"com.integra.ember.call_stored",["server.get",[0]],{}]
INPUT: [50,20,{},["{\"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5\": {\"is_local\": true, \"status\": \"online\", \"position\": \"POINT(0 0)\", \"hash\": \"e2ba6b08-dc48-36d2-814a-af37201bdb38\", \"name\": \"Local server\", \"adm_level\": null, \"router_url\": \"localh...
localServerId: 5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5
OUTPUT: [48,21,{"disclose_me":true},"com.integra.ember.call_stored",["graph.get",["5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5",0]],{}]
INPUT: [50,21,{},["{\"graph_version\": 2498158, \"max_version\": 2498158, \"links\": {\"920e99d9-8449-457f-b2cc-9c2c7b04918b\": {\"params\": {\"out\": {\"version\": 2498098, \"value\": \"d79ec1a3-4ac1-4e37-916b-d59bbe8cfa2a.logicOutput\", \"datetime\": 166255044...
items: 37
links: 28
[
  {
    operation: 'create',
    entity: 'link',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    id: '1b021cdc-32f4-47bf-b8d0-c1f5334fdf63',
    params: { type: [Object] },
    datetime: 0
  },
  {
    operation: 'update',
    entity: 'link',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    id: '1b021cdc-32f4-47bf-b8d0-c1f5334fdf63',
    params: { in: [Object] },
    datetime: 0
  },
  {
    operation: 'update',
    entity: 'link',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    id: '1b021cdc-32f4-47bf-b8d0-c1f5334fdf63',
    params: { out: [Object] },
    datetime: 0
  }
]
OUTPUT: [48,22,{"disclose_me":true},"com.integra.ember.put",[[{"operation":"create","entity":"link","server":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","owner":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","id":"1b021cdc-32f4-47bf-b8d0-c1f5334fdf63","params":{"type":{"value":"LogicLink"}},"datetime":0},{"operation":"update","entity":"link","server":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","owner":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","id":"1b021cdc-32f4-47bf-b8d0-c1f5334fdf63","params":{"in":{"value":"3d02e7cb-ce66-4c6f-ab2e-d7f782538352.logicInput"}},"datetime":0},{"operation":"update","entity":"link","server":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","owner":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","id":"1b021cdc-32f4-47bf-b8d0-c1f5334fdf63","params":{"out":{"value":"ab4a7e0b-ddf2-417a-b5e5-98deb30d36ae.logicOutput"}},"datetime":0}]],{}]
INPUT: [50,22,{},[[{"status":null,"paramid":127574,"name":null,"server":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","entity":"link","value":null,"txid":23745932,"version":2498159,"owner":"5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5","operation":"create","datetime":166255069...
[
  {
    status: null,
    paramid: 127574,
    name: null,
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'link',
    value: null,
    txid: 23745932,
    version: 2498159,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'create',
    datetime: 1662550691,
    type: 'LogicLink',
    id: '1b021cdc-32f4-47bf-b8d0-c1f5334fdf63'
  },
  {
    status: null,
    paramid: 646617,
    name: 'color',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'link',
    value: null,
    txid: 23745932,
    version: 2498160,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550691,
    type: 'LogicLink',
    id: '1b021cdc-32f4-47bf-b8d0-c1f5334fdf63'
  },
  {
    status: null,
    paramid: 646618,
    name: 'in',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'link',
    value: null,
    txid: 23745932,
    version: 2498161,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550691,
    type: 'LogicLink',
    id: '1b021cdc-32f4-47bf-b8d0-c1f5334fdf63'
  },
  {
    status: null,
    paramid: 646619,
    name: 'out',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'link',
    value: null,
    txid: 23745932,
    version: 2498162,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550691,
    type: 'LogicLink',
    id: '1b021cdc-32f4-47bf-b8d0-c1f5334fdf63'
  },
  {
    status: null,
    paramid: 646618,
    name: 'in',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'link',
    value: '3d02e7cb-ce66-4c6f-ab2e-d7f782538352.logicInput',
    txid: 23745932,
    version: 2498163,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550691,
    type: 'LogicLink',
    id: '1b021cdc-32f4-47bf-b8d0-c1f5334fdf63'
  },
  {
    status: null,
    paramid: 646619,
    name: 'out',
    server: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    entity: 'link',
    value: 'ab4a7e0b-ddf2-417a-b5e5-98deb30d36ae.logicOutput',
    txid: 23745932,
    version: 2498164,
    owner: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    operation: 'update',
    datetime: 1662550691,
    type: 'LogicLink',
    id: '1b021cdc-32f4-47bf-b8d0-c1f5334fdf63'
  }
]
OUTPUT: [6,{},"wamp.close.goodbye_and_out"]
INPUT: [6,{},"wamp.goodbye.normal"]
(node:14058) UnhandledPromiseRejectionWarning: GOODBYE
(node:14058) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 8)

можно протестировать то, что получилось, попробовав найти один датчик через связь с другим

In [66]:
metric1.search('LogicLink/MilitaryPost')
Out[66]:
[
  {
    pins: {
      militaryContent: [Object],
      militaryTempContent: [Object],
      militaryContainer: [Object],
      logicOutput: [Object],
      logicInput: [Object],
      militaryTempContainer: [Object]
    },
    params: { state: [Object], name: [Object] },
    types: [ 'Item', 'MilitaryPost', 'Category', 'MilitaryCategory' ],
    serverId: '5331e7ee-08aa-4655-90aa-fc1bd2ab3ba5',
    id: '3d02e7cb-ce66-4c6f-ab2e-d7f782538352',
    name: 'Должность',
    position: [],
    search: [Function: queryPath],
    update: [Function (anonymous)],
    delete: [Function (anonymous)],
    get: [Function (anonymous)],
    refs: [Function (anonymous)],
    ref: [Function (anonymous)],
    parse: [Function (anonymous)],
    eval: [Function (anonymous)],
    time: 1662550687.988
  }
]
In [4]:
system(`jupyter-nbconvert --output-dir=./static --to HTML docs-supergraph.ipynb`)
[NbConvertApp] Converting notebook docs-supergraph.ipynb to HTML
[NbConvertApp] Writing 885385 bytes to static/docs-supergraph.html

In [ ]: