const { parseHexInteger } = require('../../utils/base-utils.js') const MODBUS_AREAS = { coil: { key: 'coil', label: '线圈', readFunction: '01', writeFunction: '05 / 0F', access: '读写' }, input: { key: 'input', label: '输入寄存器', readFunction: '04', writeFunction: '--', access: '只读' }, holding: { key: 'holding', label: '保持寄存器', readFunction: '03', writeFunction: '06 / 10', access: '读写' } } const BYTE_POSITIONS = { high: { label: '高8位', shift: 8 }, low: { label: '低8位', shift: 0 } } function hex(address) { return `0x${address}` } function getRegisterCount(item) { return item.registerCount || (item.type === 'float' ? 2 : 1) } function getAddressDisplay(address, registerCount) { if (registerCount <= 1) return hex(address) const start = parseHexInteger(address) const end = start + registerCount - 1 return `${hex(address)}-${hex(end.toString(16).toUpperCase())}` } function withArea(items, areaKey, category) { return items.map((item) => ({ ...item, ...(() => { const byteMeta = item.bytePosition ? BYTE_POSITIONS[item.bytePosition] : null const byteLabel = item.byteLabel || (byteMeta ? byteMeta.label : '') const registerCount = getRegisterCount(item) const addressDisplay = byteLabel ? `${getAddressDisplay(item.address, registerCount)} ${byteLabel}` : getAddressDisplay(item.address, registerCount) return { byteLabel, byteShift: byteMeta ? byteMeta.shift : null, category, addressText: hex(item.address), addressDisplay, registerCount, area: MODBUS_AREAS[areaKey] } })() })) } const OBS_ADDRESSES = { EK1: '30', EK2: '31', EK3: '32', EK4: '33', FOC_KFG: '34', SPEED_KLPF: '35', OBS_FBASE: '36', OBS_EA_KS: '37', OBS_KP_START: '38', OBS_KI_START: '39', OBS_KP_RUN1: '3A', OBS_KI_RUN1: '3B', OBS_KP_RUN2: '3C', OBS_KI_RUN2: '3D', OBS_KP_RUN3: '3E', OBS_KI_RUN3: '3F', OBS_KP_RUN4: '40', OBS_KI_RUN4: '41', DQ_KP_START: '42', DQ_KI_START: '43', DQ_KP_RUN: '44', DQ_KI_RUN: '45', TAILWIND_SPEED_KLPF: '46', TAILWIND_OBS_EA_KS: '47', TAILWIND_OBS_KP: '48', TAILWIND_OBS_KI: '49', ALIGN_ANGLE: '4A' } const CONFIG_ADDRESSES = { LD: '60', LQ: '62', RS: '64', POLE_PAIRS: '66', SPEED_BASE: '67', SPEED_CTRL: '68', CURVE_MAX_SPEED: '69', CURVE_MIN_SPEED: '6A', SOUT_MAX: '6B', START_RAMP_INC: '6C', START_RAMP_DEC: '6E', RUN_RAMP_INC: '70', RUN_RAMP_DEC: '72', START_VOLT: '74', STOP_VOLT: '75', CURVE_MAX_VOLT: '76', CURVE_MIN_VOLT: '77', SPEED_CURVE_K: '78', OIL_SPEED: '7A', OIL_TIME: '7B' } const controlButtonRegisters = withArea([ { key: 'protocol', address: '00', type: 'uint8_t', name: 'Modbus控制', writeValue: 1, nextName: 'VSP控制', nextWriteValue: 0 }, { key: 'direction', address: '02', type: 'uint8_t', name: '正转', writeValue: 0, nextName: '反转', nextWriteValue: 1 }, { key: 'power', address: '01', type: 'uint8_t', name: '开机', writeValue: 1, nextName: '关机', nextWriteValue: 0 }, { key: 'save', address: '03', type: 'uint8_t', name: '固化', writeValue: 1, momentary: true }, { key: 'reset', address: '04', type: 'uint8_t', name: '复位', writeValue: 1, momentary: true } ], 'coil', '控制类寄存器') const speedCommandRegister = withArea([ { address: CONFIG_ADDRESSES.SPEED_CTRL, type: 'uint16_t', name: '转速命令', unit: 'RPM', inputValue: '', writeValue: '--' } ], 'holding', '参数配置')[0] const tailwindSwitchRegisters = withArea([ { address: '05', type: 'uint8_t', name: '顺逆风启用', value: false, writeValue: 0 }, { address: '06', type: 'uint8_t', name: '预定位启用', value: false, writeValue: 0 } ], 'coil', '顺逆风控制') const protectionSwitchRegisters = withArea([ { address: '07', type: 'uint8_t', name: '保护使能', value: false, writeValue: 0 }, { address: '08', type: 'uint8_t', name: '恢复使能', value: false, writeValue: 0 }, { address: '09', type: 'uint8_t', name: '电压保护使能', value: false, writeValue: 0 }, { address: '0A', type: 'uint8_t', name: '电流保护使能', value: false, writeValue: 0 }, { address: '0B', type: 'uint8_t', name: '堵转保护使能', value: false, writeValue: 0 }, { address: '0C', type: 'uint8_t', name: '功率保护使能', value: false, writeValue: 0 }, { address: '0D', type: 'uint8_t', name: '温度保护使能', value: false, writeValue: 0 }, { address: '0E', type: 'uint8_t', name: '缺相保护使能', value: false, writeValue: 0 }, { address: '0F', type: 'uint8_t', name: 'PWM丢失保护使能', value: false, writeValue: 0 }, { address: '10', type: 'uint8_t', name: '串口保护使能', value: false, writeValue: 0 } ], 'coil', '保护') const estimatorRegisters = withArea([ { address: OBS_ADDRESSES.EK1, type: 'uint16_t', name: 'OBS_E1K', conversion: '2047 * 3.0 / 125.0 * LQ / TPWM_VALUE * 电流基准 / 电压基准' }, { address: OBS_ADDRESSES.EK2, type: 'uint16_t', name: 'OBS_E2K', conversion: '2047 * 0.8 * RS * 电流基准 / 电压基准' }, { address: OBS_ADDRESSES.EK3, type: 'uint16_t', name: 'OBS_E3K', conversion: '255 * 2.5' }, { address: OBS_ADDRESSES.EK4, type: 'uint16_t', name: 'OBS_E4K', conversion: '((LD - LQ) * TPWM_VALUE * MAX_OMEGA_RAD_SEC) / (LD + RS * TPWM_VALUE)' }, { address: OBS_ADDRESSES.FOC_KFG, type: 'uint16_t', name: 'FOC_KFG', conversion: 'TIM4频率 / BASE_FREQ / FG_K * 极对数' }, { address: OBS_ADDRESSES.SPEED_KLPF, type: 'uint16_t', name: 'SPEED_KLPF', conversion: '32767 * 2PI * SPD_BW * TPWM_VALUE' }, { address: OBS_ADDRESSES.OBS_FBASE, type: 'uint16_t', name: 'OBS_FBASE', conversion: '32767 * TPWM_VALUE' }, { address: OBS_ADDRESSES.OBS_EA_KS, type: 'uint16_t', name: 'OBS_EA_KS', conversion: '32767 * 2 * SMOMIN * 2PI * BASE_FREQ * TPWM_VALUE / 速度基准' }, { address: OBS_ADDRESSES.OBS_KP_START, type: 'uint16_t', name: 'OBS_KP_START', conversion: '4095 * 2PI * ATT_COEF * ATO_BW / BASE_FREQ' }, { address: OBS_ADDRESSES.OBS_KI_START, type: 'uint16_t', name: 'OBS_KI_START', conversion: '32767 * 2PI * ATO_BW * ATO_BW * TPWM_VALUE / BASE_FREQ' }, { address: OBS_ADDRESSES.OBS_KP_RUN1, type: 'uint16_t', name: 'OBS_KP_RUN1' }, { address: OBS_ADDRESSES.OBS_KI_RUN1, type: 'uint16_t', name: 'OBS_KI_RUN1' }, { address: OBS_ADDRESSES.OBS_KP_RUN2, type: 'uint16_t', name: 'OBS_KP_RUN2' }, { address: OBS_ADDRESSES.OBS_KI_RUN2, type: 'uint16_t', name: 'OBS_KI_RUN2' }, { address: OBS_ADDRESSES.OBS_KP_RUN3, type: 'uint16_t', name: 'OBS_KP_RUN3' }, { address: OBS_ADDRESSES.OBS_KI_RUN3, type: 'uint16_t', name: 'OBS_KI_RUN3' }, { address: OBS_ADDRESSES.OBS_KP_RUN4, type: 'uint16_t', name: 'OBS_KP_RUN4' }, { address: OBS_ADDRESSES.OBS_KI_RUN4, type: 'uint16_t', name: 'OBS_KI_RUN4' }, { address: OBS_ADDRESSES.DQ_KP_START, type: 'uint16_t', name: 'DQ_KP_START' }, { address: OBS_ADDRESSES.DQ_KI_START, type: 'uint16_t', name: 'DQ_KI_START' }, { address: OBS_ADDRESSES.DQ_KP_RUN, type: 'uint16_t', name: 'DQ_KP_RUN' }, { address: OBS_ADDRESSES.DQ_KI_RUN, type: 'uint16_t', name: 'DQ_KI_RUN' }, { address: OBS_ADDRESSES.TAILWIND_SPEED_KLPF, type: 'uint16_t', name: 'SPEED_KLPF_TAILWIND' }, { address: OBS_ADDRESSES.TAILWIND_OBS_EA_KS, type: 'uint16_t', name: 'OBS_EA_KS_TAILWIND' }, { address: OBS_ADDRESSES.TAILWIND_OBS_KP, type: 'uint16_t', name: 'OBS_KP_TAILWIND' }, { address: OBS_ADDRESSES.TAILWIND_OBS_KI, type: 'uint16_t', name: 'OBS_KI_TAILWIND' }, { address: OBS_ADDRESSES.ALIGN_ANGLE, type: 'uint16_t', name: '预定位角度' } ], 'holding', '估算器配置参数') const atoBandwidthInputRegisters = [ { suffix: 'START', name: 'ATO_BW_START', kpAddress: OBS_ADDRESSES.OBS_KP_START, kiAddress: OBS_ADDRESSES.OBS_KI_START, kpName: 'OBS_KP_START', kiName: 'OBS_KI_START' }, { suffix: 'RUN1', name: 'ATO_BW_RUN1', kpAddress: OBS_ADDRESSES.OBS_KP_RUN1, kiAddress: OBS_ADDRESSES.OBS_KI_RUN1, kpName: 'OBS_KP_RUN1', kiName: 'OBS_KI_RUN1' }, { suffix: 'RUN2', name: 'ATO_BW_RUN2', kpAddress: OBS_ADDRESSES.OBS_KP_RUN2, kiAddress: OBS_ADDRESSES.OBS_KI_RUN2, kpName: 'OBS_KP_RUN2', kiName: 'OBS_KI_RUN2' }, { suffix: 'RUN3', name: 'ATO_BW_RUN3', kpAddress: OBS_ADDRESSES.OBS_KP_RUN3, kiAddress: OBS_ADDRESSES.OBS_KI_RUN3, kpName: 'OBS_KP_RUN3', kiName: 'OBS_KI_RUN3' }, { suffix: 'RUN4', name: 'ATO_BW_RUN4', kpAddress: OBS_ADDRESSES.OBS_KP_RUN4, kiAddress: OBS_ADDRESSES.OBS_KI_RUN4, kpName: 'OBS_KP_RUN4', kiName: 'OBS_KI_RUN4' }, { suffix: 'TAILWIND', name: 'ATO_BW_TAILWIND', kpAddress: OBS_ADDRESSES.TAILWIND_OBS_KP, kiAddress: OBS_ADDRESSES.TAILWIND_OBS_KI, kpName: 'OBS_KP_TAILWIND', kiName: 'OBS_KI_TAILWIND' } ].map((item) => ({ ...item, type: 'float', inputValue: '', writeValue: '--', kpWriteValue: '--', kiWriteValue: '--', category: '估算器配置参数', addressDisplay: `${hex(item.kpAddress)}/${hex(item.kiAddress)}`, area: MODBUS_AREAS.holding })) const dqGainInputRegisters = withArea([ { address: OBS_ADDRESSES.DQ_KP_START, type: 'uint16_t', name: 'DQ_KP_START', protocolName: 'DQ_KP_START', gainType: 'kp' }, { address: OBS_ADDRESSES.DQ_KI_START, type: 'uint16_t', name: 'DQ_KI_START', protocolName: 'DQ_KI_START', gainType: 'ki' }, { address: OBS_ADDRESSES.DQ_KP_RUN, type: 'uint16_t', name: 'DQ_KP_RUN', protocolName: 'DQ_KP_RUN', gainType: 'kp' }, { address: OBS_ADDRESSES.DQ_KI_RUN, type: 'uint16_t', name: 'DQ_KI_RUN', protocolName: 'DQ_KI_RUN', gainType: 'ki' } ], 'holding', '估算器配置参数').map((item) => ({ ...item, inputValue: '', writeValue: '--' })) const parameterRegisters = withArea([ { address: CONFIG_ADDRESSES.LD, type: 'float', name: 'LD', unit: 'H', step: '0.000001' }, { address: CONFIG_ADDRESSES.LQ, type: 'float', name: 'LQ', unit: 'H', step: '0.000001' }, { address: CONFIG_ADDRESSES.RS, type: 'float', name: 'RS', unit: 'Ω', step: '0.0001' }, { address: CONFIG_ADDRESSES.POLE_PAIRS, type: 'uint16_t', name: '极对数' }, { address: CONFIG_ADDRESSES.SPEED_BASE, type: 'uint16_t', name: '速度基准', unit: 'RPM' }, { address: CONFIG_ADDRESSES.CURVE_MAX_SPEED, type: 'uint16_t', name: '速度最大值', unit: 'RPM' }, { address: CONFIG_ADDRESSES.CURVE_MIN_SPEED, type: 'uint16_t', name: '速度最小值', unit: 'RPM' }, { address: CONFIG_ADDRESSES.SOUT_MAX, type: 'uint16_t', name: 'SOUT_MAX', unit: 'A' }, { address: CONFIG_ADDRESSES.START_RAMP_INC, type: 'float', name: '启动加速加速度', unit: 'RPM/S' }, { address: CONFIG_ADDRESSES.START_RAMP_DEC, type: 'float', name: '启动减速加速度', unit: 'RPM/S' }, { address: CONFIG_ADDRESSES.RUN_RAMP_INC, type: 'float', name: '运行加速加速度', unit: 'RPM/S' }, { address: CONFIG_ADDRESSES.RUN_RAMP_DEC, type: 'float', name: '运行减速加速度', unit: 'RPM/S' }, { address: CONFIG_ADDRESSES.START_VOLT, type: 'uint16_t', name: '开机电压', unit: 'V' }, { address: CONFIG_ADDRESSES.STOP_VOLT, type: 'uint16_t', name: '关机电压', unit: 'V' }, { address: CONFIG_ADDRESSES.CURVE_MAX_VOLT, type: 'uint16_t', name: '调速最高电压', unit: 'V' }, { address: CONFIG_ADDRESSES.CURVE_MIN_VOLT, type: 'uint16_t', name: '调速最低电压', unit: 'V' }, { address: CONFIG_ADDRESSES.SPEED_CURVE_K, type: 'float', name: '调速曲线斜率' }, { address: CONFIG_ADDRESSES.OIL_SPEED, type: 'uint16_t', name: '上油转速', unit: 'RPM' }, { address: CONFIG_ADDRESSES.OIL_TIME, type: 'uint16_t', name: '上油时间', unit: 's' } ], 'holding', '参数配置') const parameterInputNames = [ 'LD', 'LQ', 'RS', '极对数', '速度基准' ] const configParameterInputNames = [ '开机电压', '关机电压', '速度最小值', '速度最大值', 'SOUT_MAX', '调速最低电压', '调速最高电压' ] const speedLoopExtraInputNames = [ '启动加速加速度', '启动减速加速度', '运行加速加速度', '运行减速加速度' ] const oilParameterInputNames = [ '上油转速', '上油时间' ] const prepositionParameterInputNames = [ '预定位角度' ] const parameterByName = parameterRegisters.reduce((result, item) => { result[item.name] = item return result }, {}) const motorParameterInputRegisters = parameterInputNames .map((name) => parameterByName[name]) .filter(Boolean) .map((item) => ({ ...item, inputValue: item.inputValue || '', writeValue: item.writeValue || '--' })) const parameterInputRegisters = configParameterInputNames .map((name) => parameterByName[name]) .filter(Boolean) .map((item) => ({ ...item, inputValue: item.inputValue || '', writeValue: item.writeValue || '--' })) const speedSlopeRegister = { ...parameterByName['调速曲线斜率'], inputValue: '', writeValue: '--' } const speedLoopExtraRegisters = speedLoopExtraInputNames .map((name) => parameterByName[name]) .filter(Boolean) .map((item) => ({ ...item, inputValue: '', writeValue: '--' })) const oilParameterInputRegisters = oilParameterInputNames .map((name) => parameterByName[name]) .filter(Boolean) .map((item) => ({ ...item, inputValue: '', writeValue: '--' })) const prepositionParameterInputRegisters = prepositionParameterInputNames .map((name) => estimatorRegisters.find((item) => item.name === name)) .filter(Boolean) .map((item) => ({ ...item, inputValue: '', writeValue: '--' })) function isAtoGainRegister(item) { return item.name.startsWith('OBS_KP_') || item.name.startsWith('OBS_KI_') } function isDqGainRegister(item) { return item.name.startsWith('DQ_KP_') || item.name.startsWith('DQ_KI_') } const calculatedParameterRegisters = [ ...estimatorRegisters.filter((item) => !isAtoGainRegister(item) && !isDqGainRegister(item) && !prepositionParameterInputNames.includes(item.name)), ...parameterRegisters.filter((item) => !parameterInputNames.includes(item.name) && !configParameterInputNames.includes(item.name) && !speedLoopExtraInputNames.includes(item.name) && !oilParameterInputNames.includes(item.name) && !prepositionParameterInputNames.includes(item.name) && item.name !== '调速曲线斜率') ].map((item) => ({ ...item, inputValue: '', writeValue: '--' })) const protectionRegisters = withArea([ { address: '7C', type: 'uint16_t', name: '硬件过流值', unit: 'A' }, { address: '7D', type: 'uint16_t', name: '软件过流值', unit: 'A', conversion: '32767 * 限制值 / 电流基准' }, { address: '7E', type: 'uint16_t', name: '过压保护值', unit: 'V', conversion: '32767 * 限制值 / 电压采样最大值' }, { address: '7F', type: 'uint16_t', name: '欠压保护值', unit: 'V' }, { address: '80', type: 'uint16_t', name: '过压恢复值', unit: 'V' }, { address: '81', type: 'uint16_t', name: '欠压恢复值', unit: 'V' }, { address: '82', type: 'uint16_t', name: '速度限制最大值', unit: 'RPM', conversion: '32767 * 限制值 / 速度基准' }, { address: '83', type: 'uint16_t', name: '速度限制最小值', unit: 'RPM' }, { address: '84', type: 'uint16_t', name: '反电动势低阈值' }, { address: '85', type: 'uint16_t', name: '反电动势高阈值' }, { address: '86', type: 'uint16_t', name: '速度中间值', unit: 'RPM' }, { address: '87', type: 'uint16_t', name: '功率保护值', unit: 'W', conversion: '32767 * 保护值 / 电流采样最大值 / 电压采样最大值' }, { address: '88', type: 'uint16_t', name: '功率保护时间', unit: 'ms' }, { address: '89', type: 'uint16_t', name: '温度保护值', unit: '℃' }, { address: '8A', type: 'uint16_t', name: '温度恢复值', unit: '℃' }, { address: '8B', type: 'uint16_t', name: '温度保护时间', unit: 'ms' }, { address: '8C', type: 'uint16_t', name: '故障恢复时间', unit: 's' }, { address: '8D', type: 'uint16_t', name: '串口丢失检测时间', unit: 'ms' } ], 'holding', '保护配置') const readonlyParamRegisters = withArea([ { address: 'A8', type: 'ascii', name: '芯片型号', displayValue: '--', registerCount: 4, hideMeta: true }, { address: 'AC', type: 'ascii', name: '型号', displayValue: '--', registerCount: 8, hideMeta: true }, { address: 'A0', type: 'uint8_t', name: '载波频率', unit: 'KHz', displayValue: '--', bytePosition: 'high' }, { address: 'A0', type: 'uint8_t', name: '基准电压', unit: 'V', displayValue: '--', bytePosition: 'low' }, { address: 'A1', type: 'uint16_t', name: '运放倍数', displayValue: '--' }, { address: 'A2', type: 'uint16_t', name: '采样电阻', unit: 'mΩ', displayValue: '--' }, { address: 'A3', type: 'uint16_t', name: '全区 Flash 校验码', displayValue: '--' }, { address: 'A4', type: 'float', name: '母线电压分压比', displayValue: '--' }, { address: 'A6', type: 'float', name: '模拟输入电压分压比', displayValue: '--' } ], 'input', '只读参数寄存器') const userStatusRegisters = Array.from({ length: 10 }, (_, index) => ({ address: (0xD3 + index).toString(16).toUpperCase(), type: 'uint16_t', name: `用户状态字 ${index + 1}` })) const statusRegisters = withArea([ { address: 'C0', type: 'uint8_t', name: '状态机', bytePosition: 'high' }, { address: 'C0', type: 'uint8_t', name: '故障码', bytePosition: 'low' }, { address: 'C1', type: 'int16_t', name: 'UQ' }, { address: 'C2', type: 'int16_t', name: 'UD' }, { address: 'C3', type: 'int16_t', name: 'IQ' }, { address: 'C4', type: 'int16_t', name: 'ID' }, { address: 'C5', type: 'int16_t', name: 'A 相电流' }, { address: 'C6', type: 'int16_t', name: 'B 相电流' }, { address: 'C7', type: 'int16_t', name: 'C 相电流' }, { address: 'C8', type: 'uint16_t', name: '相电流最大值' }, { address: 'C9', type: 'uint16_t', name: '相电流最小值' }, { address: 'CA', type: 'int16_t', name: '估算速度', unit: 'RPM' }, { address: 'CB', type: 'uint16_t', name: '估算反电动势' }, { address: 'CC', type: 'uint16_t', name: '母线电压', unit: '0.1V', displayUnit: 'V' }, { address: 'CD', type: 'uint16_t', name: '母线电流', unit: '0.01A', displayUnit: 'A' }, { address: 'CE', type: 'uint16_t', name: '估算功率', unit: 'W' }, { address: 'CF', type: 'uint16_t', name: 'NTC 温度', unit: '℃', displayUnit: '℃' }, { address: 'D0', type: 'uint16_t', name: '模拟输入电压', unit: 'V', displayUnit: 'V' }, { address: 'D1', type: 'uint16_t', name: '频率', unit: 'Hz', displayUnit: 'Hz' }, { address: 'D2', type: 'uint16_t', name: '占空比', unit: '%', displayUnit: '%' }, ...userStatusRegisters ], 'input', '状态类寄存器') function getByteRegisterValue(item, wordValue) { if (!item || item.type !== 'uint8_t' || !item.bytePosition) return wordValue return (Number(wordValue) >> item.byteShift) & 0xFF } module.exports = { controlButtonRegisters, speedCommandRegister, atoBandwidthInputRegisters, dqGainInputRegisters, oilParameterInputRegisters, prepositionParameterInputRegisters, motorParameterInputRegisters, parameterInputRegisters, calculatedParameterRegisters, protectionSwitchRegisters, protectionRegisters, readonlyParamRegisters, speedLoopExtraRegisters, speedSlopeRegister, statusRegisters, tailwindSwitchRegisters, getByteRegisterValue, getRegisterCount }