Структура конфигурации

Конфигурация – это JSON-файл, который задает поведение системы на клиенте и сервере. На сервере – конфигурация это по большей части – набор классов узлов. Узлы хранятся и выполняются по мере обращения к ним со стороны клиентов и внешней системы. А для мобильного клиента в конфигурации задаются многие другие свойства.

Какие разделы есть в конфигурации

  • Общие свойства (Название конфигурации, поставщик, версия и т.д.)

  • Разделы. То, на каких вкладках в интерфейсе располагаются узлы и процессы. Подробнее в разделе Мобильный клиент

  • Классы. Структура классов данных – и серверных и клиентских. Подробнее в разделе Классы

  • Общие события. События, возникающие в системе вне классов узлов, т.е. в приложении в целом. Подробнее в подразделе «Общие события мобильного клиента»

  • Разделы с python-обработчиками для классов и общих событий. Это python-файлы, преобразованные в base64-формат, хранящиеся в структуре конфигурации. Доставляются на целевое устройство вместе с обновлением конфигурации и преобразуются в исполняемые временные файлы.

  • Датасеты. Хранение и доставка справочников и ссылок внешней системы на устройство. Подробнее в разделе Датасеты

  • Серверы – раздел для регистрации псевдонимов серверов, использующихся в функциях API

Описание формата файла

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

Общие свойства конфигурации

«name» – имя конфигурации

«uid» – ИД установки на сервере, через который организуется доступ к объектам конфигурации для API и хранение

«url» – полный адрес хостинга, с которого клиенты будут скачивать конфигурацию(конфигурация изначально может быть попасть на клиент с файла, но для обновления он будет обращаться по этому адресу)

«content_uid» – уникальный ID конфигурации независимо от ИД уставновки. Одна и та же конфигурация может быть развернута под разными uid, но content_uid останется тем же

«vendor» имя поставщика конфигурации

«version» – номер версии.

«NodaLogicFormat» – версия формата. Анализируется приложением. Если приложение не поддерживает этот формат, будет предложено обновить приложение

«NodaLogicType»: «ANDROID_SERVER» – константа

Обработчики

«nodes_handlers» и «nodes_server_handlers» – base64 закодированные файлы обработчиков. Они должны быть оформлены по определенному стандарту – верхняя часть содержим импорты и константы, которые автоматически генерируются системой. Далее идут классы и пользовательский код. В current_module_name прописывается url инстанса, в current_configuration_url – путь к API инстанса. Т.е. поля uid и url

Для андроид это:

from nodesclient import RefreshTab,SetTitle,CloseNode,RunGPS,StopGPS,UpdateView,Dialog,ScanBarcode,GetLocation,AddTimer,StopTimer,ShowProgressButton,HideProgressButton,ShowProgressGlobal,HideProgressGlobal,Controls,SetCover,getBase64FromImageFile,convertImageFilesToBase64Array,saveBase64ToFile,convertBase64ArrayToFilePaths,UpdateMediaGallery
from android import *
from nodes import NewNode, DeleteNode, GetAllNodes, GetNode, GetAllNodesStr, GetRemoteClass, CreateDataSet, GetDataSet, DeleteDataSet,to_uid, from_uid
from com.dv.noda import DataSet
from com.dv.noda import DataSets
from com.dv.noda import SimpleUtilites as su
from datasets import GetDataSetData

# Константы конфигурации
current_module_name="296f962e-bd0d-4b32-8ac8-3bc9a1162a56"
current_configuration_url="http://nmaker.pw/api/config/296f962e-bd0d-4b32-8ac8-3bc9a1162a56"
_data_dir = su.get_data_dir(current_module_name)
_downloads_dir = su.get_downloads_dir(current_module_name)


from nodes import Node

Для server это:

from nodes import Node

Классы

«classes» – массив классов

Каждый класс должен присутствовать и в массиве classes и также быть в обработчике, в зависимости от того где он будет выполняться. Либо в «nodes_handlers» либо в «nodes_server_handlers»

Класс имеет ключи:

  • «name» – имя, оно же идентифкатор класса. Должно быть python-совместимым (так как на этот класс в python также существует класс)

  • «section_code» и «section» – код раздела и название раздела (из раздела конфигурации sections) к которому отностится класс

  • «has_storage» – автосохранение данных при вводе в поля ввода

  • «display_name» – отображаемое имя, елси не используется «обложка»

  • «cover_image» – обложка в формате разметки, таком же как для экранов и люых мест где используется разметка в Андроид- клиенте

  • «hidden» – класс (если это класс-процесс) будет скрыт для отображения в интерфейсе)

  • «class_type» – доступно либо «custom_process» – узел-процесс, существующий в единственно экземпляре, создается вместе с загрузкой конфигурации либо «data_node» – узел данных, т.е. объект данных создаваемый или загружаемый в систему

«methods» – массив методов класса. Методы должны быть описаны и в массиве и в обработчиках.

Ключи метода:

  • «name» и тоже самое в «code» (для совместимости)– имя метода

  • «source»: «internal»

  • «engine» : либо «android_python» либо «engine»: «server_python» в зависимости от того, где планируется выполнять метод

  • «events» – массив событий, которые возникают в системе – открытие формы, действия пользователя

Ключи события:

  • «event» – может быть «onShow» (при открытии формы узла), «onResume» ,»onInput» – любое событие ввода от пользователя или внешнее событие

  • «listener» – фильтр по «listener». События ввода как правило содержат дополнительное поле «listener», уточняющее событие. Например, id нажатой кнопки. Если не использовать «listener» то все события будут попадать на этот обработчик

  • «actions» – массив действий, который надо выполнить на событие. На одно и то же событие можно повесить несколько действий, но обычно оно одно.

Ключи действия:

  • «action» -«run»(синхронное выполнение), «runasync» – асинхронное выполнение, «runprogress» – синхронное с прогрессбаром.

  • «source» : «internal»

  • «method» : имя метода из массива «methods»

  • «postExecuteMethod» – имя метода, который может быть выполнен после окончания выполнения основного метода. Актуально для асинхронного выполнения и выполнение с прогрессбаром.

Помимо прописывания в classes класс узла также необходимо разместить в обработчиках, используя то же имя и те же имена методов и соблюдая определенный формат описания – класс родитель, метод __init__.

Для Андроид пример:

class MyClass(Node):
   def __init__(self, modules, jNode, modulename, uid, _data):
       super().__init__(modules, jNode, modulename, uid, _data)

   """Class MyClass"""

   def Open(self, input_data=None):
       self.Show(
         [
           [{"type":"Input","id":"input1","caption":"input 1","value":"@input1"}],
           [{"type":"Button","id":"button1","caption":"Get result"}]
         ]
       )

       return True,{}
   def Input(self, input_data=None):
       toast(self._data["input1"])

       return True,{}

Для сервера:

class MyClass(Node):

   def __init__(self, node_id=None, config_uid=None):
       super().__init__(node_id, config_uid)

Также при оформлении методов класса нужно придерживаться единого формата , описанного в разделах Классы и Общие обработчики. Это параметры метода и структура возвращаемого кортежа. Для методов класса:

def Input(self, input_data=None):

       return True,{}

Для общих обработчиков:

def onStartConfiguration(input_data):

 return True,{}

Секция Разделы

«sections» – массив разделов

«name» – отображаемое имя раздела

«code» – ИД раздела

«commands» – список команд в виде списка через запятую <Заголовок команды>|<ид команды>. При нажатии на кнопку генерируется общее событие onStartMenuCommand в котором в качестве параметра передается ИД команды

Псевдонимы серверов

Если на клиенте используется такая конструкция как RemoteClass (по сути обертка вокруг API сервера) то надо завести хотябы один сервер с «is_default»: true. Если еще есть сервера то они также используются тут. В таком случае в функциях используются их псевдонимы. В случае одного сервера, псевдоним можно не использовать.

«servers» – массив псевдонимов серверов

Ключи объекта сервера:

  • «alias» – псевдоним сервера, который используется в фунциях работы с удаленными серверами

  • «url» – адрес сервера

  • «is_default» – признак сервера по умолчанию

Раздел «Датасеты»

«datasets» – массив объектов типа «Датасет»

  • «name» – имя датасета, по которому к нему обращаться в прикладном решении

  • «hash_indexes» – список ключей записей для hash-индексов в виде массива. Пример: [

    «barcode», «article»

    ]

  • «text_indexes» – список полей полнотекстового поиска в виде массива. Пример: [

    «name»

    ]

  • «view_template» – шаблон отображения записи датасеты в поле ввода формы. Пример: «{name}, {article}»

** «api_url» – прямой url доступа к датасету. Откуда он будет грузится. Обязательно поле. Принцип формирования видно на примере.

Пример конфигурации целиком

{
   "name": "New configuration",
   "server_name": "",
   "uid": "296f962e-bd0d-4b32-8ac8-3bc9a1162a56",
   "url": "http://nmaker.pw/api/config/296f962e-bd0d-4b32-8ac8-3bc9a1162a56",
   "content_uid": "1dd8f18c-fd58-4c12-b610-0595fe573429",
   "vendor": "Dmitry Vorontsov",
   "nodes_handlers": "base64 encoded python file",
   "nodes_handlers_meta": null,
   "nodes_server_handlers": "base64 encoded python file",
   "nodes_server_handlers_meta": null,
   "version": "00.00.01",
   "NodaLogicFormat": "1.1",
   "NodaLogicType": "ANDROID_SERVER",
   "last_modified": "2025-12-02T13:39:08.917962+03:00",
   "provider": "Dmitry Vorontsov",
   "classes": [
       {
           "name": "MyClass",
           "section": "Documentation samples",
           "section_code": "Documentation",
           "has_storage": true,
           "display_name": "MyClass",
           "cover_image": "[[\"MyClass example\"]]",
           "class_type": "custom_process",
           "hidden": false,
           "methods": [
               {
                   "name": "Open",
                   "source": "internal",
                   "engine": "android_python",
                   "code": "Open"
               },
               {
                   "name": "Input",
                   "source": "internal",
                   "engine": "android_python",
                   "code": "Input"
               }
           ],
           "events": [
               {
                   "event": "onShow",
                   "listener": "",
                   "actions": [
                       {
                           "action": "run",
                           "source": "internal",
                           "server": "",
                           "method": "Open",
                           "postExecuteMethod": ""
                       }
                   ]
               },
               {
                   "event": "onInput",
                   "listener": "",
                   "actions": [
                       {
                           "action": "run",
                           "source": "internal",
                           "server": "",
                           "method": "Input",
                           "postExecuteMethod": ""
                       }
                   ]
               }
           ]
       }
   ],
   "datasets": [
       {
           "name": "goods",
           "hash_indexes": [
               "barcode",
               "article"
           ],
           "text_indexes": [
               "name"
           ],
           "view_template": "{name}, {article}",
           "autoload": false,
           "created_at": "2025-12-01T12:01:01.072524",
           "updated_at": "2025-12-01T12:01:01.072554",
           "api_url": "http://nmaker.pw/api/config/296f962e-bd0d-4b32-8ac8-3bc9a1162a56/dataset/goods/items",
           "item_count": 0
       }
   ],
   "sections": [
       {
           "name": "Documentation samples",
           "code": "Documentation",
           "commands": ""
       }
   ],
   "servers": [
       {
           "alias": "main",
           "url": "https://nmaker.pw",
           "is_default": true
       }
   ]
}