const { controlState } = require('./motor-control-data') const { parseHexInteger } = require('./base-utils') const transport = require('./ble-transport') const bootloaderService = require('./bootloader-service') const settingsService = require('./settings-service') const modbusAccess = require('./modbus-access') const motorControlProtocol = require('./motor-control-protocol') let state = { ...controlState.createInitialState(), ...bootloaderService.getState() } let autoReadTimer = null let unsubscribeSettings = null let unsubscribeTransport = null const subscribers = [] function getState() { return { ...state } } function notify() { const nextState = getState() subscribers.slice().forEach((subscriber) => { subscriber(nextState) }) } function setState(changedData) { state = { ...state, ...changedData } notify() } function applySettingsState(settings) { const autoReadInterval = controlState.clampNumber( settings.statusPollInterval, controlState.AUTO_READ_MIN_INTERVAL, controlState.AUTO_READ_MAX_INTERVAL, state.autoReadInterval ) const userStatusCount = controlState.getUserStatusCount(settings.userStatusCount) setState({ autoReadInterval, userStatusCount }) if (state.autoReadStatus) { scheduleAutoReadStatus(autoReadInterval) } } function subscribe(subscriber) { if (typeof subscriber !== 'function') return () => {} subscribers.push(subscriber) subscriber(getState()) return () => { const index = subscribers.indexOf(subscriber) if (index >= 0) subscribers.splice(index, 1) } } function init() { transport.init() bootloaderService.init() settingsService.init() if (!unsubscribeSettings) { unsubscribeSettings = settingsService.subscribe(applySettingsState) } if (unsubscribeTransport) return unsubscribeTransport = transport.subscribe((transportState) => { const nextState = controlState.applyTransportState(state, transportState) if (nextState.autoReadStatus === false) { stopAutoReadStatus() } setState(nextState) }) bootloaderService.subscribe((bootloaderState) => { setState(bootloaderState) }) } function syncSharedInputs() { controlState.setSharedInputValues(state.motorParameterInputRegisters) } function applyControlReadValues(coilValues) { setState(controlState.applyControlReadValues(state, coilValues)) } function applyMotorReadWords(words, startAddress = controlState.MOTOR_PARAM_START_ADDRESS) { const registerWordCache = controlState.getRegisterWordCache(startAddress, words) const motorState = controlState.applyMotorParameterReadValues(state, registerWordCache) const nextState = { ...state, ...motorState } setState({ ...motorState, ...controlState.applySpeedCommandReadValue(nextState, registerWordCache[0x68]) }) } function applyDriverReadWords(words) { const changedState = controlState.applyDriverParameterReadValues(state, words) if (changedState.chipModel) { bootloaderService.setChipModel(changedState.chipModel) } setState(changedState) } function applyStatusReadWords(words, startAddress = controlState.STATUS_START_ADDRESS) { setState(controlState.applyStatusReadValues(words, startAddress)) } function updateMotorParameterInput(index, value) { setState(controlState.applyMotorParameterInput(state, index, value)) } function updateMotorParameterBlur(index, value) { setState(controlState.applyMotorParameterBlur(state, index, value)) } function updateSpeedCommandInput(value) { setState(controlState.applySpeedCommandInput(state, value)) } function updateSpeedCommandBlur(value) { setState(controlState.applySpeedCommandBlur(state, value)) sendSpeedCommand() } function getSpeedCommandWriteWord() { const writeValue = Number(state.speedCommand.writeValue) if (!Number.isFinite(writeValue)) return null const word = Math.round(writeValue) return word >= 0 && word <= 0xFFFF ? word : null } async function sendSpeedCommand() { const slaveAddress = modbusAccess.getSharedSlaveAddress() if (slaveAddress === null) return false const writeWord = getSpeedCommandWriteWord() if (writeWord === null) { transport.showCommandAlert('转速命令错误', '请检查转速命令输入值') return false } const address = parseHexInteger(state.speedCommand.address) const response = await modbusAccess.writeSingleRegister( slaveAddress, address, writeWord, '转速命令', 'speed-command-write' ) if (response) { setState({ ...controlState.clearSpeedCommandDirty(state), systemTip: '转速命令已下发' }) return true } return false } async function sendControlCommand(key) { const button = state.controlButtons .concat(state.controlActionButtons || []) .find((item) => item.key === key) if (!button) return const response = await motorControlProtocol.writeControlButton( button, { kind: 'control-write' } ) if (response) { setState(controlState.applyControlSuccess(state, button)) } } async function readControlStatus() { const slaveAddress = modbusAccess.getSharedSlaveAddress() if (slaveAddress === null) return false const startAddress = 0x00 const quantity = 3 const coilValues = await modbusAccess.readBitValues( slaveAddress, 0x01, startAddress, quantity, '控制状态读取', 'control-status-read' ) if (!coilValues) return false setState({ ...controlState.applyControlReadValues(state, coilValues), systemTip: '控制状态读取完成' }) return true } async function readMotorParameters() { if (state.isReadingMotor) return const slaveAddress = modbusAccess.getSharedSlaveAddress() if (slaveAddress === null) return setState({ errorText: '', isReadingMotor: true, systemTip: '' }) try { const words = await modbusAccess.readRegisterWords( slaveAddress, 0x03, controlState.MOTOR_PARAM_START_ADDRESS, controlState.MOTOR_PARAM_WORD_COUNT, '电机参数读取', 'motor-main-read', { showModal: true } ) if (!words) return const registerWordCache = controlState.getRegisterWordCache(controlState.MOTOR_PARAM_START_ADDRESS, words) setState({ ...controlState.applyMotorParameterReadValues(state, registerWordCache), systemTip: '电机参数读取完成' }) } finally { setState({ isReadingMotor: false }) } } async function writeMotorParameters() { if (state.isWritingMotor) return const slaveAddress = modbusAccess.getSharedSlaveAddress() if (slaveAddress === null) return const mainWrite = controlState.buildMotorMainWriteValues(state) if (!mainWrite.values) { transport.showCommandAlert('参数错误', mainWrite.errorText) return } setState({ errorText: '', isWritingMotor: true, systemTip: '' }) try { const mainResponse = await modbusAccess.writeMultipleRegisters( slaveAddress, controlState.MOTOR_PARAM_START_ADDRESS, mainWrite.values, '电机参数写入', 'motor-main-write' ) if (!mainResponse) return setState({ ...controlState.clearMotorParameterDirty(state), systemTip: '电机参数写入完成' }) } finally { setState({ isWritingMotor: false }) } } async function readDriverParameters() { if (state.isReadingDriver) return const slaveAddress = modbusAccess.getSharedSlaveAddress() if (slaveAddress === null) return setState({ errorText: '', isReadingDriver: true, systemTip: '' }) try { const words = await modbusAccess.readRegisterWords( slaveAddress, 0x04, controlState.DRIVER_PARAM_START_ADDRESS, controlState.DRIVER_PARAM_WORD_COUNT, '驱动器参数读取', 'driver-read', { showModal: true } ) if (words) { const changedState = controlState.applyDriverParameterReadValues(state, words) if (changedState.chipModel) { bootloaderService.setChipModel(changedState.chipModel) } setState({ ...changedState, systemTip: '驱动器参数读取完成' }) } } finally { setState({ isReadingDriver: false }) } } function chooseFirmwareFile(source) { return bootloaderService.chooseFirmwareFile(source) } function startFirmwareUpgrade() { stopAutoReadStatus() setState({ autoReadStatus: false }) return bootloaderService.startUpgrade() } function readProgramChecksum() { return bootloaderService.readProgramChecksum() } function handshakeBootloader() { return bootloaderService.sendHandshakeKeepAlive() } function exitBootloader() { return bootloaderService.exitBootloader() } function setAutoReadStatus(autoReadStatus) { setState({ autoReadStatus }) if (autoReadStatus) { scheduleAutoReadStatus(0) return } stopAutoReadStatus() } async function readStatus(options = {}) { if (options.auto && !state.connectedDevice) return false const slaveAddress = modbusAccess.getSharedSlaveAddress() if (slaveAddress === null) return false const words = await modbusAccess.readRegisterWords( slaveAddress, 0x04, controlState.STATUS_START_ADDRESS, controlState.getStatusWordCount(state.userStatusCount), '状态读取', 'status-read', { showModal: !options.auto } ) if (words) { setState({ ...controlState.applyStatusReadValues(words, controlState.STATUS_START_ADDRESS), systemTip: options.auto ? '' : '状态读取完成' }) } return words } function scheduleAutoReadStatus(delay) { stopAutoReadStatus() autoReadTimer = setTimeout(async () => { if (!state.autoReadStatus) return await readStatus({ auto: true }) scheduleAutoReadStatus(state.autoReadInterval) }, delay) } function stopAutoReadStatus() { if (!autoReadTimer) return clearTimeout(autoReadTimer) autoReadTimer = null } module.exports = { getState, init, applyControlReadValues, applyDriverReadWords, applyMotorReadWords, applyStatusReadWords, exitBootloader, handshakeBootloader, readControlStatus, readDriverParameters, readMotorParameters, readProgramChecksum, readStatus, sendControlCommand, setAutoReadStatus, chooseFirmwareFile, startFirmwareUpgrade, subscribe, syncSharedInputs, updateMotorParameterBlur, updateMotorParameterInput, updateSpeedCommandBlur, updateSpeedCommandInput, writeMotorParameters }