| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- const {
- isCancelError,
- loadGroupsFromMessageFile,
- saveGroupsToChat
- } = require('./persistence.js')
- const {
- mergeImportedGroups
- } = require('./import-merge.js')
- const {
- completeStructInstanceGroups,
- parseStructDefinition
- } = require('./struct-completion.js')
- const storageAccessService = require('../storage-access/service.js')
- const storageAccessIo = require('./storage-access-io.js')
- const modbusIo = require('./modbus-io.js')
- const settingsService = require('../../store/settings-store.js')
- const store = require('./store.js')
- const stateMappers = require('./state-mappers.js')
- const {
- loadSelectedFile
- } = require('../../repositories/file.js')
- const transport = require('../../transport/ble-core.js')
- const {
- DATA_TYPE_OPTIONS,
- REGISTER_TYPE_OPTIONS,
- cloneImportedGroup,
- isAddressRangeOverflow,
- normalizeGroup,
- normalizeGroupConfig,
- validateRegisterValue
- } = require('../../domain/parameter-groups/model.js')
- const {
- findGroup,
- getGroups,
- getState,
- init,
- setGroups,
- setStorageCodeInfo,
- switchProtocolMode,
- subscribe,
- updateGroups
- } = store
- let initialized = false
- function getActiveProtocolMode() {
- return settingsService.getState().protocolMode
- }
- function isStorageAccessProtocolMode(protocolMode) {
- return settingsService.isStorageAccessProtocol(protocolMode)
- }
- function initParameterGroups() {
- settingsService.init()
- init(getActiveProtocolMode())
- switchProtocolMode(getActiveProtocolMode(), {
- notify: false
- })
- if (initialized) return
- settingsService.subscribe((settingsState) => {
- switchProtocolMode(settingsState.protocolMode)
- })
- initialized = true
- }
- async function importJsonFromMessageFile() {
- try {
- const importedGroups = (await loadGroupsFromMessageFile()).map(normalizeGroup)
- if (!importedGroups.length) throw new Error('JSON 中没有可导入的寄存器组')
- const merged = mergeImportedGroups(getGroups(), importedGroups)
- setGroups(merged.groups)
- return merged.changedCount || 0
- } catch (error) {
- const message = error && error.message ? error.message : '导入参数组配置失败'
- transport.showCommandAlert('参数组导入', message)
- return 0
- }
- }
- function completeStructInstanceGroupsWithStructSource(sourceText, options = {}) {
- const completed = completeStructInstanceGroups(getGroups(), sourceText, options)
- if (!completed.completedCount) {
- throw new Error('没有找到可匹配的结构体实例')
- }
- setGroups(completed.groups)
- return completed
- }
- function mergeImportedGroupsIntoState(importedGroups = [], options = {}) {
- const normalizedGroups = importedGroups.map(cloneImportedGroup).map(normalizeGroup)
- const merged = mergeImportedGroups(getGroups(), normalizedGroups, options)
- if (normalizedGroups.length) {
- setGroups(merged.groups)
- }
- return merged
- }
- async function completeStructInstanceGroupsWithStructFile(options = {}) {
- try {
- const file = await loadSelectedFile('message', {
- encoding: 'utf8',
- extensionMessage: '请选择 .h 或 .c 结构体定义文件',
- extensions: ['h', 'c', 'txt'],
- fallbackName: 'structs.h'
- })
- return completeStructInstanceGroupsWithStructSource(file.text, options)
- } catch (error) {
- const message = error && error.message ? error.message : '结构体补全失败'
- transport.showCommandAlert('结构体补全', message)
- return {
- completedCount: 0,
- skippedCount: 0,
- structCount: 0,
- variableCount: 0
- }
- }
- }
- async function saveJsonToChat() {
- try {
- return saveGroupsToChat(getGroups())
- } catch (error) {
- const message = error && error.message ? error.message : '保存参数组配置失败'
- if (!isCancelError(error)) {
- transport.showCommandAlert('参数组保存', message)
- }
- return 0
- }
- }
- async function syncFromStorageAccessCodeInfo(options = {}) {
- const result = await storageAccessService.syncCodeInfo(options)
- if (!result || !result.ok) return result
- setStorageCodeInfo(result.codeInfo)
- const merged = mergeImportedGroupsIntoState(result.importedGroups || [], {
- preserveExistingRemarks: true,
- preserveExistingStructLayout: true
- })
- return {
- ...result,
- addedGroups: merged.addedGroupCount,
- addedRegisters: merged.addedRegisterCount,
- updatedGroups: merged.updatedGroupCount,
- updatedRegisters: merged.updatedRegisterCount
- }
- }
- function addGroupFromConfig(config = {}) {
- let groupConfig
- try {
- groupConfig = normalizeGroupConfig(config)
- } catch (error) {
- transport.showCommandAlert('参数组添加', error.message || '寄存器组配置无效')
- return null
- }
- if (isAddressRangeOverflow(groupConfig.startAddress, groupConfig.quantity)) {
- transport.showCommandAlert('参数组添加', '地址范围超出 0xFFFF')
- return null
- }
- const registers = Array.isArray(config.registers) ? config.registers : []
- const group = normalizeGroup({
- ...groupConfig,
- layout: config.layout,
- ...(registers.length ? { registers } : {}),
- expanded: false
- })
- if (group.addressOverflow) {
- transport.showCommandAlert('参数组添加', '地址范围超出 0xFFFF')
- return null
- }
- setGroups(getGroups().concat(group))
- return group
- }
- function updateGroupConfig(groupId, config = {}) {
- const group = findGroup(groupId)
- if (!group) return null
- let nextConfig
- try {
- nextConfig = normalizeGroupConfig({
- ...group,
- ...config
- })
- } catch (error) {
- transport.showCommandAlert('参数组更新', error.message || '寄存器组配置无效')
- return null
- }
- if (isAddressRangeOverflow(nextConfig.startAddress, nextConfig.quantity)) {
- transport.showCommandAlert('参数组更新', '地址范围超出 0xFFFF')
- return null
- }
- const registers = Array.isArray(config.registers) ? config.registers : group.registers
- const updatedGroup = normalizeGroup({
- ...group,
- ...nextConfig,
- registers
- })
- if (updatedGroup.addressOverflow) {
- transport.showCommandAlert('参数组更新', '地址范围超出 0xFFFF')
- return null
- }
- setGroups(getGroups().map((item) => (
- item.id === groupId ? updatedGroup : item
- )))
- return updatedGroup
- }
- function setGroupExpanded(groupId, expanded) {
- updateGroups((group) => group.id === groupId
- ? {
- ...group,
- deleteVisible: false,
- expanded
- }
- : group)
- }
- function setGroupDeleteVisible(groupId, deleteVisible) {
- updateGroups((group) => group.id === groupId
- ? {
- ...group,
- deleteVisible
- }
- : group)
- }
- function removeGroup(groupId) {
- setGroups(getGroups().filter((group) => group.id !== groupId))
- }
- function reorderRegister(groupId, fromIndex, toIndex) {
- const group = findGroup(groupId)
- if (!group) return null
- if (group.isStructLayout) return group
- const registers = group.registers.slice()
- const sourceIndex = Number(fromIndex)
- const targetIndex = Number(toIndex)
- if (!Number.isInteger(sourceIndex) || !Number.isInteger(targetIndex)) return null
- if (sourceIndex < 0 || sourceIndex >= registers.length) return null
- const safeTargetIndex = Math.min(Math.max(targetIndex, 0), registers.length - 1)
- if (safeTargetIndex === sourceIndex) return group
- const moved = registers.splice(sourceIndex, 1)[0]
- registers.splice(safeTargetIndex, 0, moved)
- const updatedGroup = normalizeGroup({
- ...group,
- quantity: registers.length,
- registers
- })
- setGroups(getGroups().map((item) => (
- item.id === groupId ? updatedGroup : item
- )))
- return updatedGroup
- }
- function updateRegister(groupId, registerIndex, changedData) {
- updateGroups((group) => {
- if (group.id !== groupId) return group
- const shouldResetReadState = Object.prototype.hasOwnProperty.call(changedData, 'dataType')
- || Object.prototype.hasOwnProperty.call(changedData, 'textByteLength')
- return {
- ...group,
- registers: group.registers.map((register, currentIndex) => (
- currentIndex === registerIndex
- ? {
- ...register,
- ...(shouldResetReadState ? { rawValue: null, rawWords: [] } : {}),
- ...changedData
- }
- : register
- ))
- }
- })
- }
- function updateRegisterValue(groupId, registerIndex, value) {
- updateRegister(groupId, registerIndex, {
- inputValue: value,
- isDirty: true
- })
- }
- function validateRegisterInputValue(groupId, registerIndex, value) {
- const group = findGroup(groupId)
- if (!group) return false
- const register = group.registers[registerIndex]
- if (!register) return false
- return validateRegisterValue(register, value)
- }
- async function readGroup(groupId, options = {}) {
- const protocolMode = options.protocolMode || getActiveProtocolMode()
- const group = findGroup(groupId, protocolMode)
- if (!group) return false
- if (group.addressOverflow) {
- transport.showCommandAlert('参数组读取', '地址范围超出 0xFFFF')
- return false
- }
- let wordCache = {}
- if (isStorageAccessProtocolMode(protocolMode) && storageAccessIo.isMemoryGroup(group)) {
- const memoryWordCache = await storageAccessIo.readMemoryGroup(group, options)
- if (!memoryWordCache) return false
- wordCache = stateMappers.normalizeNumericCache(memoryWordCache)
- } else {
- const modbusWordCache = await modbusIo.readGroup(group, options)
- if (!modbusWordCache) return false
- wordCache = stateMappers.normalizeNumericCache(modbusWordCache)
- }
- updateGroups((item) => {
- if (item.id !== groupId) return item
- return stateMappers.applyReadCacheToGroup(item, wordCache)
- }, {
- protocolMode
- })
- return true
- }
- async function writeRegister(groupId, registerIndex) {
- const protocolMode = getActiveProtocolMode()
- const group = findGroup(groupId, protocolMode)
- const register = group && group.registers ? group.registers[registerIndex] : null
- if (!group || !register) return false
- if (!group.writable) {
- transport.showCommandAlert('参数组写入', '当前变量为只读')
- return false
- }
- if (group.addressOverflow) {
- transport.showCommandAlert('参数组写入', '地址范围超出 0xFFFF')
- return false
- }
- const written = isStorageAccessProtocolMode(protocolMode) && storageAccessIo.isMemoryGroup(group)
- ? await storageAccessIo.writeMemoryRegister(group, register)
- : null
- if (!written) {
- if (!isStorageAccessProtocolMode(protocolMode) || !storageAccessIo.isMemoryGroup(group)) {
- return writeGroup(groupId, {
- protocolMode
- })
- }
- return false
- }
- updateGroups((item) => {
- if (item.id !== groupId) return item
- return stateMappers.applyWrittenSnapshotToGroupRegister(item, registerIndex, written)
- }, {
- protocolMode
- })
- return true
- }
- async function writeGroup(groupId, options = {}) {
- const protocolMode = options.protocolMode || getActiveProtocolMode()
- const group = findGroup(groupId, protocolMode)
- if (!group) return false
- if (!group.writable) {
- transport.showCommandAlert('参数组写入', '当前寄存器组为只读')
- return false
- }
- if (group.addressOverflow) {
- transport.showCommandAlert('参数组写入', '地址范围超出 0xFFFF')
- return false
- }
- const writtenRegisters = []
- if (isStorageAccessProtocolMode(protocolMode) && storageAccessIo.isMemoryGroup(group)) {
- const snapshots = await storageAccessIo.writeMemoryGroup(group)
- if (!snapshots) return false
- snapshots.forEach((snapshot) => {
- writtenRegisters.push(snapshot)
- })
- } else {
- const snapshots = await modbusIo.writeGroup(group, options)
- if (!snapshots) return false
- snapshots.forEach((snapshot) => {
- writtenRegisters.push(snapshot)
- })
- }
- updateGroups((item) => {
- if (item.id !== groupId) return item
- return stateMappers.applyWrittenSnapshotsToGroup(item, writtenRegisters)
- }, {
- protocolMode
- })
- return true
- }
- module.exports = {
- DATA_TYPE_OPTIONS,
- REGISTER_TYPE_OPTIONS,
- addGroupFromConfig,
- completeStructInstanceGroups,
- completeStructInstanceGroupsWithStructFile,
- completeStructInstanceGroupsWithStructSource,
- getState,
- importJsonFromMessageFile,
- init: initParameterGroups,
- mergeImportedGroups: mergeImportedGroupsIntoState,
- parseStructDefinition,
- readGroup,
- removeGroup,
- reorderRegister,
- saveJsonToChat,
- setGroupDeleteVisible,
- setGroupExpanded,
- subscribe,
- syncFromStorageAccessCodeInfo,
- updateGroupConfig,
- updateRegister,
- updateRegisterValue,
- validateRegisterInputValue,
- writeRegister,
- writeGroup
- }
|