| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- const {
- buildReadFrame,
- buildWriteMultipleRegistersFrame,
- buildWriteSingleCoilFrame,
- buildWriteSingleRegisterFrame,
- MAX_READ_REGISTER_QUANTITY
- } = require('./modbus-rtu')
- const controlState = require('./control-page-state')
- const transport = require('./ble-transport')
- const {
- addCoilReadValues
- } = require('./register-value-utils')
- let state = controlState.createInitialState()
- let autoReadTimer = 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 splitReadChunks(startAddress, quantity) {
- const chunks = []
- let offset = 0
- while (offset < quantity) {
- const chunkQuantity = Math.min(quantity - offset, MAX_READ_REGISTER_QUANTITY)
- chunks.push({
- address: startAddress + offset,
- quantity: chunkQuantity
- })
- offset += chunkQuantity
- }
- return chunks
- }
- async function readRegisterChunks(slaveAddress, functionCode, startAddress, quantity, label, expectedKind, options = {}) {
- const chunks = splitReadChunks(startAddress, quantity)
- const words = []
- for (const chunk of chunks) {
- const response = await transport.sendManagedFrame(
- buildReadFrame(slaveAddress, functionCode, chunk.address, chunk.quantity),
- chunks.length > 1 ? `${label} ${chunk.address.toString(16).toUpperCase()}-${(chunk.address + chunk.quantity - 1).toString(16).toUpperCase()}` : label,
- {
- address: chunk.address,
- functionCode,
- kind: expectedKind,
- quantity: chunk.quantity,
- slaveAddress
- },
- {
- showModal: options.showModal
- }
- )
- if (!response) return null
- const chunkWords = response.words || []
- chunkWords.forEach((word, index) => {
- words[chunk.address - startAddress + index] = Number(word) & 0xFFFF
- })
- if (typeof options.onChunk === 'function') {
- options.onChunk(response, chunk)
- }
- }
- return words
- }
- 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()
- if (unsubscribeTransport) return
- unsubscribeTransport = transport.subscribe((transportState) => {
- const nextState = controlState.applyTransportState(state, transportState)
- if (nextState.autoReadStatus === false) {
- stopAutoReadStatus()
- }
- setState(nextState)
- })
- }
- 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) {
- setState(controlState.applyDriverParameterReadValues(state, words))
- }
- function applyStatusReadWords(words, startAddress = controlState.STATUS_START_ADDRESS) {
- setState(controlState.applyStatusReadValues(words, startAddress))
- }
- function getSharedSlaveAddress() {
- try {
- return transport.getSlaveAddress()
- } catch (error) {
- transport.showCommandAlert('从机地址错误', error.message)
- return null
- }
- }
- 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 = getSharedSlaveAddress()
- if (slaveAddress === null) return false
- const writeWord = getSpeedCommandWriteWord()
- if (writeWord === null) {
- transport.showCommandAlert('转速命令错误', '请检查转速命令输入值')
- return false
- }
- const address = parseInt(state.speedCommand.address, 16)
- const response = await transport.sendManagedFrame(
- buildWriteSingleRegisterFrame(slaveAddress, address, writeWord),
- '转速命令',
- {
- address,
- functionCode: 0x06,
- kind: 'speed-command-write',
- quantity: 1,
- value: writeWord,
- slaveAddress
- }
- )
- 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 slaveAddress = getSharedSlaveAddress()
- if (slaveAddress === null) return
- const writeValue = controlState.getControlButtonWriteValue(button)
- const address = parseInt(button.address, 16)
- const coilEnabled = Number(writeValue) !== 0
- const response = await transport.sendManagedFrame(
- buildWriteSingleCoilFrame(slaveAddress, address, coilEnabled),
- button.name,
- {
- address,
- functionCode: 0x05,
- kind: 'control-write',
- quantity: 1,
- value: coilEnabled ? 0xFF00 : 0x0000,
- slaveAddress
- }
- )
- if (response) {
- setState(controlState.applyControlSuccess(state, button))
- }
- }
- async function readControlStatus() {
- const slaveAddress = getSharedSlaveAddress()
- if (slaveAddress === null) return false
- const startAddress = 0x00
- const quantity = 3
- const response = await transport.sendManagedFrame(
- buildReadFrame(slaveAddress, 0x01, startAddress, quantity),
- '控制状态读取',
- {
- address: startAddress,
- functionCode: 0x01,
- kind: 'control-status-read',
- quantity,
- slaveAddress
- }
- )
- if (!response) return false
- const readValues = {
- coils: {}
- }
- addCoilReadValues(readValues, startAddress, quantity, response)
- setState({
- ...controlState.applyControlReadValues(state, readValues.coils),
- systemTip: '控制状态读取完成'
- })
- return true
- }
- async function readMotorParameters() {
- if (state.isReadingMotor) return
- const slaveAddress = getSharedSlaveAddress()
- if (slaveAddress === null) return
- setState({
- errorText: '',
- isReadingMotor: true,
- systemTip: ''
- })
- try {
- const words = await readRegisterChunks(
- 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 = 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 transport.sendManagedFrame(
- buildWriteMultipleRegistersFrame(
- slaveAddress,
- controlState.MOTOR_PARAM_START_ADDRESS,
- mainWrite.values
- ),
- '电机参数写入',
- {
- address: controlState.MOTOR_PARAM_START_ADDRESS,
- functionCode: 0x10,
- kind: 'motor-main-write',
- quantity: mainWrite.values.length,
- slaveAddress
- }
- )
- if (!mainResponse) return
- setState({
- ...controlState.clearMotorParameterDirty(state),
- systemTip: '电机参数写入完成'
- })
- } finally {
- setState({
- isWritingMotor: false
- })
- }
- }
- async function readDriverParameters() {
- if (state.isReadingDriver) return
- const slaveAddress = getSharedSlaveAddress()
- if (slaveAddress === null) return
- setState({
- errorText: '',
- isReadingDriver: true,
- systemTip: ''
- })
- try {
- const words = await readRegisterChunks(
- slaveAddress,
- 0x04,
- controlState.DRIVER_PARAM_START_ADDRESS,
- controlState.DRIVER_PARAM_WORD_COUNT,
- '驱动器硬件参数读取',
- 'driver-read',
- { showModal: true }
- )
- if (words) {
- setState({
- ...controlState.applyDriverParameterReadValues(state, words),
- systemTip: '驱动器硬件参数读取完成'
- })
- }
- } finally {
- setState({
- isReadingDriver: false
- })
- }
- }
- function setAutoReadStatus(autoReadStatus) {
- setState({
- autoReadStatus
- })
- if (autoReadStatus) {
- scheduleAutoReadStatus(0)
- return
- }
- stopAutoReadStatus()
- }
- function setAutoReadInterval(value) {
- const autoReadInterval = controlState.clampNumber(
- value,
- controlState.AUTO_READ_MIN_INTERVAL,
- controlState.AUTO_READ_MAX_INTERVAL,
- state.autoReadInterval
- )
- setState({
- autoReadInterval
- })
- if (state.autoReadStatus) {
- scheduleAutoReadStatus(autoReadInterval)
- }
- }
- async function readStatus(options = {}) {
- if (options.auto && !state.connectedDevice) return false
- const slaveAddress = getSharedSlaveAddress()
- if (slaveAddress === null) return false
- const words = await readRegisterChunks(
- slaveAddress,
- 0x04,
- controlState.STATUS_START_ADDRESS,
- controlState.STATUS_WORD_COUNT,
- '状态读取',
- '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,
- readControlStatus,
- readDriverParameters,
- readMotorParameters,
- readStatus,
- sendControlCommand,
- sendSpeedCommand,
- setAutoReadInterval,
- setAutoReadStatus,
- stopAutoReadStatus,
- subscribe,
- syncSharedInputs,
- updateMotorParameterBlur,
- updateMotorParameterInput,
- updateSpeedCommandBlur,
- updateSpeedCommandInput,
- writeMotorParameters
- }
|