Найти в Дзене

Правила wb-rules и Мирта: хранилище состояний

Дальнейшее развитие проекта на TypeScript привело к появлению фреймворка под названием «Мирта» - постепенно добавляются различные модули, расширяющие стандартные возможности wb-rules. Здесь и симулятор запуска на контроллере для юнит-тестов, и расширение устройств готовыми плагинами, и вспомогательные механизмы вроде debounce и throttle для предотвращения избыточных вызовов функций. Использование проверенных паттернов и подходов улучшает качество кода и облегчает сопровождение. Поскольку базовая функциональность уже реализована, можно быстрее создавать новые проекты. Сегодня рассмотрим один из таких элементов фреймворка - хранилище состояний. В целях обучения, базовая реализация модуля встроена в шаблон проекта (в Мирте хранилище представлено отдельным NPM-пакетом). Источником вдохновения послужила Vue Pinia, с той лишь разницей, что воспроизвести на wb-rules 2.0 реактивность не представляется возможным в компактных объёмах кода и с сохранением высокой производительности - требуется по
Оглавление

Дальнейшее развитие проекта на TypeScript привело к появлению фреймворка под названием «Мирта» - постепенно добавляются различные модули, расширяющие стандартные возможности wb-rules.

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

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

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

Источником вдохновения послужила Vue Pinia, с той лишь разницей, что воспроизвести на wb-rules 2.0 реактивность не представляется возможным в компактных объёмах кода и с сохранением высокой производительности - требуется поддержка Proxy.

В итоге, по аналогии была создана совместимая с wb-rules 2.0 структура, лишённая геттеров и действий, большей части вспомогательных методов. Остальное станет возможным при переходе движка на более современные стандарты ECMAScript (не ниже ES6).

Принцип инициализации тоже отличается - из-за множественных точек входа в виде отдельных скриптов, отсутствует глобальная сущность.

Общие данные в скриптах и модулях

Стандартный подход к использованию общей области памяти в модулях wb-rules заключается в расширении module.static дополнительными свойствами - в этом случае значения полей становятся общими для всех экземпляров модуля.

GitHub - wirenboard/wb-rules: Rule engine for Wiren Board

Однако, при работе с TypeScript возникают сложности, связанные с типизацией - приходится расширять интерфейс module.static и бороться с распространением объявлений типов на module.static других модулей.

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

На этом принципе и работает модуль Store. Он задействует свой внутренний module.static и, при помощи экспортируемой функции defineStore, позволяет определить именованное состояние:

import { defineStore } from '@wbm/store'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 })
})

Передаваемый первым аргументом уникальный идентификатор определяет «ячейку» внутри централизованного хранилища. Второй аргумент представляет из себя объект конфигурации, задающий внутреннюю структуру этой ячейки хранения - поля и их типы, начальные значения.

Выполнение этой функции не приведёт к созданию хранилища. Чтобы с ним работать, нужно использовать полученную функцию-построитель:

const counterStore = useCounterStore()
counterStore.count += 10
log(counterStore.count) // 10
counterStore.$reset() // Возврат к начальному состоянию

➡️ Примечание: повторные вызовы use...Store() не приводят к дублированию - в пределах скрипта (как точки входа) и всех экземпляров подключенных в него модулей, построитель всегда возвращает один и тот же объект.

Сброс к начальному состоянию

Экземпляр хранилища обладает методом $reset() - он используется в случаях, когда нужно вернуть хранилище к изначальному состоянию.

Пример работы с экземпляром именованного хранилища
Пример работы с экземпляром именованного хранилища

Характеристики и возможности

Все глобальные данные wb-rules сосредоточены в едином хранилище, сегментированном по уникальным идентификаторам отдельных состояний. Это позволяет отслеживать изменения и согласовывать состояние разных частей проекта.

Простота доступа и обновления данных - любые модули и скрипты могут запрашивать необходимые данные из хранилища или обновлять их.

Вместе с тем, следует сделать важную оговорку: возможности wb-rules 2.0 ограничивают внутреннюю реализацию до «общего объекта» состояния.

Варианты развития

Безусловно, основополагающим направлением развития является дальнейшее приближение к концепциям Vue Pinia - встраивание реактивности, добавление геттеров и действий.

Поддержка энергонезависимой памяти

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

Отслеживание изменений через топики MQTT

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

Защита от переиспользования идентификаторов

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

Шаблон проекта на Github

Все изменения уже добавлены в экспериментальную ветку latest репозитория проекта на GitHub. Рекомендованная стратегия внедрения предполагает постепенное тестирование новых механизмов на отдельных участках правил wb-rules.

Пример работы с хранилищем состояния в скриптах wb-rules, объединённый с применением беспроводного пульта Moes ZS-SR-R01:

wb-rules-typescript/src/wb-rules/example_counter_1.ts at latest · wihome-dev/wb-rules-typescript

Пример определения хранилища в модуле:

wb-rules-typescript/src/wb-rules-modules/example-counter.ts at latest · wihome-dev/wb-rules-typescript

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

wb-rules-typescript/tests/wb-rules-modules/store.test.ts at latest · wihome-dev/wb-rules-typescript