Дальнейшее развитие проекта на TypeScript привело к появлению фреймворка под названием «Мирта» - постепенно добавляются различные модули, расширяющие стандартные возможности wb-rules.
Здесь и симулятор запуска на контроллере для юнит-тестов, и расширение устройств готовыми плагинами, и вспомогательные механизмы вроде debounce и throttle для предотвращения избыточных вызовов функций.
Использование проверенных паттернов и подходов улучшает качество кода и облегчает сопровождение. Поскольку базовая функциональность уже реализована, можно быстрее создавать новые проекты.
Сегодня рассмотрим один из таких элементов фреймворка - хранилище состояний. В целях обучения, базовая реализация модуля встроена в шаблон проекта (в Мирте хранилище представлено отдельным NPM-пакетом).
Источником вдохновения послужила Vue Pinia, с той лишь разницей, что воспроизвести на wb-rules 2.0 реактивность не представляется возможным в компактных объёмах кода и с сохранением высокой производительности - требуется поддержка Proxy.
В итоге, по аналогии была создана совместимая с wb-rules 2.0 структура, лишённая геттеров и действий, большей части вспомогательных методов. Остальное станет возможным при переходе движка на более современные стандарты ECMAScript (не ниже ES6).
Принцип инициализации тоже отличается - из-за множественных точек входа в виде отдельных скриптов, отсутствует глобальная сущность.
Общие данные в скриптах и модулях
Стандартный подход к использованию общей области памяти в модулях wb-rules заключается в расширении module.static дополнительными свойствами - в этом случае значения полей становятся общими для всех экземпляров модуля.
Однако, при работе с 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:
Пример определения хранилища в модуле:
Юнит-тесты хранилища (используются при разработке модуля Store для защиты от случайных отклонений в его поведении):