惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Cisco Blogs
T
Threat Research - Cisco Blogs
O
OpenAI News
AI
AI
GbyAI
GbyAI
Recent Commits to openclaw:main
Recent Commits to openclaw:main
量子位
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Google DeepMind News
Google DeepMind News
Forbes - Security
Forbes - Security
T
Troy Hunt's Blog
IT之家
IT之家
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Application and Cybersecurity Blog
Application and Cybersecurity Blog
小众软件
小众软件
博客园 - 叶小钗
S
Security @ Cisco Blogs
S
Secure Thoughts
H
Heimdal Security Blog
腾讯CDC
Webroot Blog
Webroot Blog
美团技术团队
T
Tenable Blog
D
DataBreaches.Net
AWS News Blog
AWS News Blog
G
GRAHAM CLULEY
酷 壳 – CoolShell
酷 壳 – CoolShell
S
Securelist
博客园 - 聂微东
Microsoft Security Blog
Microsoft Security Blog
Simon Willison's Weblog
Simon Willison's Weblog
Hacker News: Ask HN
Hacker News: Ask HN
aimingoo的专栏
aimingoo的专栏
人人都是产品经理
人人都是产品经理
Stack Overflow Blog
Stack Overflow Blog
WordPress大学
WordPress大学
Know Your Adversary
Know Your Adversary
C
Cybersecurity and Infrastructure Security Agency CISA
C
CERT Recently Published Vulnerability Notes
Schneier on Security
Schneier on Security
Cisco Talos Blog
Cisco Talos Blog
P
Palo Alto Networks Blog
阮一峰的网络日志
阮一峰的网络日志
The Cloudflare Blog
P
Privacy International News Feed
博客园 - 【当耐特】
H
Help Net Security
Scott Helme
Scott Helme
博客园_首页
D
Darknet – Hacking Tools, Hacker News & Cyber Security

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Обзор релиза 4.20 NodaLogic
Дмитрий Воронцов · 2026-06-17 · via Все публикации подряд на Хабре

Простой

14 мин

4

В июньском релизе много всего: удобная пакетная синхронизация узлов от внешней системы до устройства - "контракты", онлайн-обработчики событий, интеграция мультимедиа-элементов с s3-хранилищем, чтобы не писать ничего при обмене документов с картинками, печатные формы в веб- и мобильном клиенте, мощная система таймеров/воркеров и многое другое.

Удобная S3 обертка для мобильного и веб-клиента для работы с файлами и изображениями

UI-элементы в NodaLogic могут работать с физическими файлами мультимедиа и в целом все UI-элементы воспринимают абсолютные пути к файлам(физическим файлам на клиентах и сервере), но тогда самому как то нужно делать и синхронизацию. Но что, если просто сразу грузить на S3-совместимое хранилище, получать постоянную ссылку и работать с ней, как с идентификатором файла. Для устройства где файл появился такая ссылка – просто указывает на физический файл (не скачивает, он уже есть). Для устройства получателя – автоматически скачивает один раз, выполняя все необходимое (показ прогресс-бара, размещение). Такая ссылка удобна в том плане, что может быть прочитана где угодно напрямую, не нагружая бекенд, без дополнительных телодвижений по синхронизации.

Для этого в бекенде есть роут(/api/s3/upload-url), который работает с s3 (авторизация с S3 хранилищем не хранится на клиентах) . Клиент обращается к бекенду (серверу NodaLogic) получает временный токен на загрузку и в ответ получает публичную ссылку (s3 в режиме public). Если нужно сделать непубличным на чтение, то есть два варианта: ограничить доступ к бакету либо чтение сделать через бекенд (ключи доступа хранятся на бекенде, бекенд скачивает, хранит у себя, раздает по авторизации). Для второго варианта надо в настройках приложения включить s3backend proxy и выбрать соответствующие настройки. Надо сказать что публичные ссылки с органичением или без интереснее в случае с s3, в режиме проксирования теряются преимущества s3 хранилища(в режиме s3 клиенты напрямую работают с хранилищем, в ). В случае проксирования не скачивания роут также надо будет поменять под это (сейчас у него проксируется upload, не download).

Если вы хостите свои решения на nmaker, вам ничего дополнительно делать не нужно. Если вы скачали и развернули NodaLogic у себя то вам нужно будет прописать настройки s3 хранилища в app.py , переменная s3. Само s3-хранилище, соответственно также будет на вашей стороне.

Визуальные элементы с поддержкой S3

Все UI-элементы, связанные с изображениями, могут работать с S3 напрямую.

Все UI-элементы, связанные с изображениями, могут работать с S3 напрямую.

В элементах, работающих с изображениями достаточно указать флаг "s3":true чтобы можно было передавать в качестве источника не локальные имена файлов а s3-ссылки. Это удобно тем, что при передаче таких данных на другой клиент(другое устройство) не надо синхронизировать картинки – элементы сделают все сами. При первом открытии такой ссылки оно скачается (и покажет прогресс-бар) при последующих – будет брать уже локально с диска. Скачивание асинхронное, с крутящимся колесиком на каждой картинке. Если это галерея или слайдер - то скачивание каждой картинки будет асинхронно независимо и на каждой будет свой прогресс-бар.

Использование s3-тега в разметке

Использование s3-тега в разметке

Список элементов экрана, работающих с флагом s3

  • Picture

  • ImageSlider

  • MediaGallery

  • PhotoButton

  • GalleryButton

Активные элементы (камера и галерея) при этом понимают, что им надо упаковать не просто в файл, а сразу еще закачать на s3 и разместить ссылку. Т.е. например в MediaGallery будет помещен не массив файлов, а массив s3 ссылок

Функции для работы с s3 для Python и NodaScript

s3put(<абсолютное имя файла>) – помещает файл в s3 (синхронно)

s3get(ссылка) – скачивает файл по ссылке и возвращает путь

Пример (на NodaScript, но точно также будет для Python)

url = s3put(_data.photo); //синхронная выгрузка в s3, на выходе ссылка

path = s3get(url); //сохранение с s3, на выходе - путь

message("Загрузили в "+string(url));

Таймеры сервера и клиента

Таймеров может быть много разных

Таймеров может быть много разных

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

В таймере сразу надо указать где он будет запускаться – на сервере или на Android-клиенте.

Для Android можно указать галочку Worker это означает что таймер работает на воркере – особом механизме Андроид, обеспечивающим бесперебойное и энергоэффективное выполнение даже когда приложение выключено. У такого режима есть платформенное ограничение Андроида – не чаще раз в 15 минут. Зато работает, как я уже написал всегда - даже после перезагрузки.

Без галки Worker это обычные таймеры, их можно делать хоть каждую секунду. Они также как и воркеры будут рабоать вне контекста экрана узла, т.е. в фоне, но для NodaLogic это не проблема – UI команды отлично работают из фона – хоть по событиям с сервера, хоть с таймеров или еще каких то общих событий.

По серверным обработчикам все тоже самое, их можно делать на любой интервал. Выполняться они будут именно на сервере а не в веб-клиенте.

По выполнению таймера сразу выполняются обработчики, привязанные к нему (принцип тот же что и с общими событиями). Для сервера это может быть только python, для Андроид – любой движок. В data обработчика кладется переменная timer_id с ИД таймера, который вызвал событие, чтобы обработчик понимал что это за событие. Для python обработчиков, еще она же кладется в input_data.

Пакетная синхронизация данных внешняя система-сервер-клиенты через "Контракты"

Не смотря на то, что существует уже несколько способов донести узлы до устройств (Rooms, система мессенджинга), они все ориентированы на быструю доставку одиночных или небольшого числа узлов одновременно, нежели на пакетную передачу сотен тысяч узлов. Эту нишу закрывают «контракты». Это инструмент пакетной синхронизации узлов, ориентированный на большие загрузки через файлы с докачкой. Также контракты  берут на себя отслеживание изменений на устройствах через механизм подтверждений, т.е. другими словами отдают только то, что действительно еще не доставлено(изменилось) чтобы не качать лишние данные.

Контракты это система синхронизации и передачи узлов из внешней системы на сервер и клиенты с отслеживанием изменений. Внешняя система скидывает данные на URL и ни о чем не заботится, а сервер раздает это на устройства, принимает ack от устройств и не отправляет только те узлы, которые еще не приняты или содержат изменения.

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

Тут нет FCM или веб-сокета как в других подсистемах доставки NL, потому что это ориентировано больше на выкачивание больших пакетов через файлы.

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

 Контракт может принимать данные для конкретных классов, может данные сразу с упакованным классом (узлы без конфигураций или «самостоятельные узлы»). Со стороны клиента просто скидывается массив объектов вида [{“_id”:<внутренний id>,…}] а система сама их нормализует и превращает в удобоваримые документы системы - узлы.

Настройка контракта

Настройка контракта

Т.е. другими словами можно сказать так: внешняя система (например 1С) шлет на API контракта массив объектов когда ей это удобно. Эти объекты попадая на сервер, становятся классами, которые указаны в контракте (причем один объект может стать сразу несколькими узлами разных классов из разных конфигураций). Устройства подписываются на получение данных и периодически скачивают узлы, высылая подтверждение о получении. Если во внешней системе уже выгруженные данные изменятся и она выгрузит еще раз, то измененные узлы дойдут до всех подписанных клиентов.В целом, довольно простой механизм, ориентированный на групповую синхронизацию

Небольшая справка по видам синхронизации в NodaLogic чтобы понять какой механизм выбрать:

1)      Синхронизация узлов с устройствами через механизм Rooms. Работает как широковещательная подписка через либо WebSocket либо FCM и направлена прежде всего на быструю доставку. Узлы (полученные через API или собственные) регистрируются в комнате, и рассылаются устройствам, подключенным к комнате. Комнат может быть сколько угодно. Обращение через псевдонимы. Регистрация через команду _register. Одно устройство подключено к 1й группе. Есть механизм pending, хранение сообщений

2)      Синхронизация узлов через систему мессенджинга(https://habr.com/ru/articles/1034202/) – как в человекочитаемых чатах так и не отображаемая в чатах (обработчик-обработчик). Можно отправлять p2p , сообщение в группе, сообщение для конфигурации в целом. Также ориентирован на как можно более быструю доставку одного или нескольких узлов. Тут уже роутинг привязан к пользователям (и всем устройствам пользователя) , также хранение и все механизмы гарантированной доставки

3)      Синхронизация датасетов. Этот механизм по сути синхронизация целиком некоеего неизменяемого набора внешних данных. Т.е. по простому – выгрузили справочник в JSON и загрузили на устройстве при запуске. Целиком, без отслеживания изменений. Чтобы это шевелилось есть индексы. Датасеты привязаны к конфигурации. Кстати, при удалении конфигуаии все что с ней связано – подчищается, в т.ч. датасеты, а контракты не связаны с конфой. В целом раньше задача передачи справочников решалась через датасеты, сейчас есть выбор какой механизм использвоать.

4)      Контракты описанные в этой статье. В отличии от п.1 и п.2 – это пакетная передача узлов, ориентированная на большие данные. Но при этом не молниеносно-быстрая. Кроме того, сразу есть система отслеживания изменений. В отличии от п.3 – это узлы а не просто JSON, т.е. их можно менять, у них есть обработчики, интерфейс и т.д. Т.е. датасеты – это неизменяемые данные (только для ссылок), а тут обычные узлы.

 Для тогда чтобы организовать контракт и начать принимать в него данные надо:

  • Зайти в Контракты на сервере, создать новый контракт.

  • Выбрать классы в которые он будет доставлять данные

  • На устройстве зайти в контракты, добавить контракт, отсканировать QR код с сервера

  • Контракт готов для приема и синхронизации. Его API можно скопировать из карточки и использовать в своем решении

После того как контракт скачивается возникает общее событие onContractReceived на которое при необходимости можно повесить обработчик, в _data обработчика можно получить список id принятых узлов в виде массива в ключе "nodes", а в ключ "contract_uid" - uid контракта

Можно просмотреть узлы, полученные через контракт, в интерфейса мобильного приложения просто кликнув на него в Контрактах. В целом это обычные узлы. Их можно разместить в разделах интерфейса или не размещать (а только использовать в документах как ссылки или для поиска). Если это большой справочник, может и не стоит размещать в разделах.

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

Печатные формы на сервере, в веб- и мобильном клиенте

Пример печатной формы на веб-клиенте

Пример печатной формы на веб-клиенте

Добавилась подсистема печати. Она будет расширяться и эволюционировать в плане способов формирования печатных форм, сейчас заложены основы и сделан один из способов – через HTML макет, через Jinja.

Работает это так. Каждая печатная форма – это отдельный класс узла с видом PrintForm.

В нем задается

У каких узлов будет выводиться эта печатная форма в команде Печать. Это не обязательно узлы данных. В примере есть например «Печать этикеток» - это обработка, она собирает выборку узлов.

Макет печатной формы, в котором указываются переменные, которые будут взяты из data узла (напоминаю узел у нас это не документ, к которому прицеплена печатаная форма а узел-печатная форма). Откуда возьмутся поля в data узла – ниже.

По поводу макета могу сказать следующее: 1) любая LLM влет генерирует Jinja-HTML макеты по словесному описанию. Я ни одного сам не написал. Позже добавлю ИИ-генератор, но пока скопипаситить из чата – не слишком затруднительно 2) Я вот тут в 22м году еще для SimpleUI писал как вытаскивать формы из 1С для того же jinja-макета. Ничего не поменялось – до сих пор актуально. https://infostart.ru/1c/articles/1716745/ У 1С есть табличный редактор, у меня пока нет.

Обработчик. Когда из узла, к которому печатаная форма привязана выбирают ее (или запускают на печать удаленно) то у узла-печатной формы возникает событие onInput с listener = onStartForm на которое неплохо было бы повестить обработчик, который сформирует _data – данные, которые пойдут в печатную форму, т.е. в шаблон.

self._data["rows"] = [
        {
            "number": 1,
            "product": "Стеллаж металлический 100*40",
            "quantity": 2,
        },
        {
            "number": 2,
            "product": "Крышка контейнера 100*40",
            "quantity": 10,
        },
        {
            "number": 3,
            "product": "Контейнер пластиковый 500*180",
            "quantity": 5,
        },
    ]

В input_data такого обработчика передается – переменная _basement_data - это data узла, который вызвал форму печати. Ну и смысл этого обработчика положить в _data своего узла то, что будет напечатано, шаблон будет обращаться к переменным узла.

Важно! Это серверный обработчик. Про мобильный клиент – ниже.

Вот обработчик, который вызывается из узла – ячейки, по сути передает просто данные ячейки, чтобы их можно было вызвать из шаблона в переменну cell

basement_data = input_data["_basement_data"] #данные узла, из которого печатается форма
self._data["cell"] = basement_data

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

<div class="label">

  <div class="text">{{ cell.name }}</div>

  <img class="qr" src="{{ qr(cell.name) }}" alt="QR">

</div>
И вот что получилось

И вот что получилось

И для большего удобства также добавлен хелпер qr(string) который просто выводит qr.

Итого печатную форму можно распечатать на принтер или сохранить в PDF. Для PDF должен быть установлен модуль WeasyPrint. Не не стал его включать в requirements на GitHub по причине того, что для разных ОС он ставится по разному. Если вы его не поставите, то он просто выведет ошибку.

Печать на мобильных устройствах.

Не стал пока делать локальную печать через Jinja на мобильной платформе как это было на SimpleUI. На мой взгляд если что то актуально для мобильного устройства именно локально, то это печать через ZPL и прочие принтерные языки низкого уровня на мобильный принтер, а не PDF-документы. Это реализуется по-другому и макет тут не поможет.

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

Для этого надо включить кнопку Show print on mobile device и в тулбаре появляется команда печати с подключёнными печатными формами

Можно просто нажать предпросмотр, отправить на принтер, подключенный с мобильного или сохранить в PDF уже на мобильном устройстве.

А можно «отправить сразу на принтер». Это имеется ввиду на сетевой принтер с сервера. Т.е. мы с мобильного обращаемся к узлу печатной-форме, передаем ей наши данные _basement_data и говорим «сформируй и отправь сразу на принтер».

Но на какой принтер?

Есть два варианта:

1.      Самый простой – просто забить принтер в настройках мобильного приложения. Это может быть сетевой путь или имя для CUPS -принтера Линукс.

2.      А можно этот идентификатор (сетевой путь ил CUPS-имя) засунуть в QR и наклеить на принтер (физический). Тогда такой сценарий – в помещении несколько принт-станций, пользователь подходит, запускает печать, у него автоматом запускается скан QR принтера и сразу отправляется печать на этот принтер.

Настройки прямой печати задаются в приложении – тип принтера, порядок прямой печати.

Также из обработчиков можно прост отравлять запросы на роуты

POST /client/api/print-form/pdf - отправляет PDF

POST /client/api/print-form/print - прямая печать

Скрытый текст
curl -u "user@example.com:password" \

-X POST "https://server/client/api/print-form/pdf" \

-H "Content-Type: application/json" \

-d '{

"print_form_class": "config_uid$CellLabelsPrintForm",

"_data": {

"cells": [

{"name": "A-01"},

{"name": "A-02"}

]

}

}' \

--output labels.pdf 

 

RAW printer, по умолчанию

curl -u "user@example.com:password" \

 -X POST "https://server/client/api/print-form/print" \

 -H "Content-Type: application/json" \

 -d '{

   "print_form_class": "config_uid$CellLabelsPrintForm",

   "printer_name": "192.168.1.50:9100",

   "printer_type": "raw",

   "_data": {

     "cells": [

       {"name": "A-01"},

       {"name": "A-02"}

     ]

   }

 }'

 

curl -u "user@example.com:password" \

 -X POST "https://server/client/api/print-form/print" \

 -H "Content-Type: application/json" \

 -d '{

   "print_form_class": "config_uid$CellLabelsPrintForm",

   "printer_name": "Office_Printer",

   "printer_type": "cups",

   "_data": {

     "cells": [

       {"name": "A-01"},

       {"name": "A-02"}

     ]

   }

 }'

Предросмотр HTML и PDF

Добавлены на мобильном клиенте команды для предпросмотра PDF и HTML в отдельном окне. Если вы каким то образом из кода сделали печатный документ и хотите его показать пользователю чтобы он отправил его на принтер или просто сохранил то в NodaScript и Python добавлены команды

  • PreviewPDFFile(путь к файлу) - открывает PDF файл в экране предпросмотра

  • PreviewHTMLFile(путь к файлу) - открывает сохраненный HTML в экране предпросмотра

  • PreviewHTMLString(строка) - открывает HTML-строку в режиме предпросмотра

Теги

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

Итак в _data достаточно поместить массив тегов (массив на случай если тегов>1) в ключ "_tags":["my_tag1","my_tag2"]  и в обложке появятся теги внизу. Цвет тега генерится по md5-хешу (одинаковый алгоритм в мобильном и веб-клиенте), цвет текста подстраивается

Если не надо автоматический цвет то можно задать явно в формате _tags:[{"id":"mytag","color":"#FFF555"}]

Для веб-клиента можно включить галочку «Отображать облако тегов» и добавляется облако-фильтр по тегам. Для мобильного, я посчитал это неуместным(негде разместить).

В общем работает просто как фильтр.

Онлайн – обработчики

В мобильном клиенте можно на события повесить онлайн обработчик, вместо(или вместе) с другими видами локальных обработчиков – Python или NodaScript.

Работает это просто – в HTTP-запрос передается текущее состояние data узла (или общего события) которое вызывает обработчик, запрос отправляется на некий другой бекенд там эти данные анализируются, возможно отправляются новые данные, туда же в _data, и возможно отправляются команды UI (в массив commands), которые должен выполнить клиент при этом – вывести что то на экране, показать сообщение, открыть диалог и т.д.

Это несколько противоречит концепции NodaLogic где упор больше на самостоятельные, оффлайновые объекты-узлы, чтобы не быть привязанным к онлайну и не нагружать сервер лишними событиями. Но иногда может быть полезно залезть на сервер и сделать что то там напрямую и также это сделано для совместимости с SimpleUI.

В отличии от SimpleUI, где между клиентом и сервером путешествовал строковый стек команд и переменных вперемешку, теперь и объекта отправляется _data как есть т.е. в JSON-типизированном виде, больше не надо делать преобразования в строку и обратно. А команды отправляются отдельно, в массиве commands зато с аргументами. И что важно, это массив команд, т.е. очередность выполнения задает разработчик. Думаю, это более гибкая концепция. Более подробно о составе команд можно почитать тут https://nodalogic-txt-ru.readthedocs.io/ru/latest/http.html

Пример того, что отправляется и уходит

Пример запроса

{
  "_data": {
    "_id": "config_uid$Goods$123",
    "name": "Товар 001",
    "qty": 1
  },
  "input_data": {
    "barcode": "4601234567890"
  }
}

и то что возвращается с сервера:

{
  "_data": {
    "_id": "config_uid$Goods$123",
    "name": "Товар 001",
    "barcode": "4601234567890",
    "qty": 1,
    "status": "checked"
  },
  "_commands": [
    {
      "command": "SetTitle",
      "argument": ["Товар проверен"]
    },
    {
      "command": "Refresh"
    },
    {
      "command": "message",
      "argument": ["Штрихкод принят"]
    }
  ]
}

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

В настройках приложения обязательно указать Online handlers url (Буквально запрос пойдет на него +/<имя метода в событии> ) имя пользователя и пароль.

Текстовый и триграмм-индексы

В прошлых релизах у узлов появились хеш-индексы https://nodalogic-txt-ru.readthedocs.io/ru/latest/indexes.html. Зачем они вообще? Потому что NL по сути NoSQL без индексов о скорости можно забыть. Зато с индексами все летает. В новом релизе добавились text_index - по сути аналог SQL LIKE %expression% - вхождение подстроки.

И более интересный trigram_index он уже обеспечивает нечеткий поиск, причем на хороших скоростях что на мобильном клиенте, что на сервере.

Работает это для метода findByIndex() - также как и для хеш индекса. Просто в случае с триграммами это будет список похожих

И также триграмм индексы работают в поиске по узлам в разделах если в разделе есть узлы с подключенными триграмм-индексами.

Дополнения по части UI/UX

Надпись Text с обработчиком нажатия.

Кнопка занимает много места, надпись компактнее. У кнопки правда есть анимация нажатия, в общем есть выбор. Просто разместите clicked:true и можно отлавливать события

Таже в Text добавилось свойство underline – подчеркнутый текст наряду с bold и italic

Темная тема и переключатель тем и night-ключи

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

как-то задается цвет, добавлены _night-ключи – это цвет (как обычно в HEX) который будет использован для темной темы. Если он не задан, будет использован просто цвет либо цвет по умолчанию. В общем сейчас у любого элемента можно задать и цвет для светлой темы и отдельно цвет для темной темы например color и color_nigth