| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- const storageAccessProtocol = require('../../protocols/storage-access/index.js')
- const settingsService = require('../../store/settings-store.js')
- const {
- AREA
- } = storageAccessProtocol
- const {
- bytesToHex,
- bytesToUtf8Text,
- stringToUtf8Bytes
- } = require('../../utils/binary-utils.js')
- const LOG_MODE_OPTIONS = [
- { key: 'hex', label: 'HEX' },
- { key: 'text', label: '文本' }
- ]
- const STORAGE_ACCESS_COMMAND_OPTIONS = [
- { key: 'sync', label: '同步', description: '读取 info 并同步 code' },
- { key: 'read', label: '读取', description: '按字节读取内存' },
- { key: 'write', label: '写入', description: '按字节写入内存' }
- ]
- const STORAGE_ACCESS_AREA_OPTIONS = [
- { key: AREA.DATA, label: 'data' },
- { key: AREA.IDATA, label: 'idata' },
- { key: AREA.XDATA, label: 'xdata' },
- { key: AREA.CODE, label: 'code' }
- ]
- function normalizeHexText(value) {
- return String(value === undefined || value === null ? '' : value)
- .replace(/0x/gi, '')
- .replace(/[\s,;:_-]/g, ' ')
- .replace(/\s+/g, ' ')
- .trim()
- .toUpperCase()
- }
- function validateHexText(value) {
- const text = String(value === undefined || value === null ? '' : value).trim()
- const withoutPrefix = text.replace(/0x/gi, '')
- const compact = withoutPrefix.replace(/[\s,;:_-]/g, '')
- if (!compact) return '请输入十六进制数据'
- if (/[^0-9a-fA-F\s,;:_-]/.test(withoutPrefix)) return '只支持十六进制字符'
- if (compact.length % 2 !== 0) return '十六进制长度必须为偶数'
- return ''
- }
- function parseHexBytes(value) {
- const compact = String(value === undefined || value === null ? '' : value)
- .trim()
- .replace(/0x/gi, '')
- .replace(/[\s,;:_-]/g, '')
- const bytes = []
- for (let index = 0; index < compact.length; index += 2) {
- bytes.push(parseInt(compact.slice(index, index + 2), 16) & 0xFF)
- }
- return bytes
- }
- function getSerialModeLabel(mode) {
- return mode === 'text' ? '文本' : 'HEX'
- }
- function getNextSerialMode(mode) {
- return mode === 'hex' ? 'text' : 'hex'
- }
- function getSerialModeToggleText(mode) {
- return getSerialModeLabel(getNextSerialMode(mode))
- }
- function getLogModeLabel(mode) {
- return mode === 'text' ? '文本' : 'HEX'
- }
- function getNextLogMode(mode) {
- return mode === 'hex' ? 'text' : 'hex'
- }
- function getLogModeToggleText(mode) {
- return getLogModeLabel(getNextLogMode(mode))
- }
- function normalizeSerialState(current = {}, changed = {}) {
- const next = {
- serialInputText: current.serialInputText || '',
- serialMode: current.serialMode || 'hex',
- ...changed
- }
- const mode = next.serialMode === 'text' ? 'text' : 'hex'
- const inputText = String(next.serialInputText || '')
- let errorText = ''
- let bytes = []
- let previewHex = ''
- if (inputText.trim()) {
- if (mode === 'hex') {
- errorText = validateHexText(inputText)
- if (!errorText) {
- bytes = parseHexBytes(inputText)
- previewHex = bytesToHex(bytes, ' ')
- }
- } else {
- bytes = stringToUtf8Bytes(inputText)
- previewHex = bytesToHex(bytes, ' ')
- }
- }
- return {
- serialErrorText: errorText,
- serialInputText: inputText,
- serialMode: mode,
- serialModeLabel: getSerialModeLabel(mode),
- serialModeToggleText: getSerialModeToggleText(mode),
- serialPreviewHex: previewHex,
- serialPreviewLengthText: bytes.length ? `${bytes.length} bytes` : '--'
- }
- }
- function formatAreaLabel(area) {
- const matched = STORAGE_ACCESS_AREA_OPTIONS.find((item) => item.key === area)
- return matched ? matched.label : 'data'
- }
- function buildStorageAccessPreview(commandMode, area, address, length, dataBytes) {
- try {
- if (commandMode === 'sync') {
- return bytesToHex(storageAccessProtocol.buildInfoFrame(), ' ')
- }
- if (commandMode === 'write') {
- return bytesToHex(storageAccessProtocol.buildWriteFrame(area, address, dataBytes), ' ')
- }
- return bytesToHex(storageAccessProtocol.buildReadFrame(area, address, length), ' ')
- } catch (error) {
- return ''
- }
- }
- function normalizeStorageAccessWordText(value, fallback) {
- const text = String(value === undefined || value === null ? '' : value).trim().replace(/^0x/i, '').toUpperCase()
- return text || fallback
- }
- function validateStorageAccessWordText(value, label) {
- const text = String(value || '').trim()
- if (!text) return `${label}请输入十六进制`
- if (!/^[0-9A-F]+$/i.test(text)) return `${label}只支持十六进制`
- if (text.length > 4) return `${label}最多 4 位十六进制`
- return ''
- }
- function formatStorageAccessWordText(value) {
- return Number(value || 0).toString(16).toUpperCase().padStart(4, '0')
- }
- function normalizeStorageAccessState(current = {}, changed = {}) {
- const next = {
- storageAccessAreaIndex: current.storageAccessAreaIndex || 0,
- storageAccessAddress: current.storageAccessAddress || '0000',
- storageAccessCommandIndex: current.storageAccessCommandIndex || 0,
- storageAccessDataText: current.storageAccessDataText || '',
- storageAccessLength: current.storageAccessLength || '0004',
- ...changed
- }
- const command = STORAGE_ACCESS_COMMAND_OPTIONS[Number(next.storageAccessCommandIndex) || 0] || STORAGE_ACCESS_COMMAND_OPTIONS[0]
- const area = STORAGE_ACCESS_AREA_OPTIONS[Number(next.storageAccessAreaIndex) || 0] || STORAGE_ACCESS_AREA_OPTIONS[0]
- const address = normalizeStorageAccessWordText(next.storageAccessAddress, '0000')
- const length = normalizeStorageAccessWordText(next.storageAccessLength, '0004')
- const dataText = normalizeHexText(next.storageAccessDataText)
- const dataBytes = dataText ? parseHexBytes(dataText) : []
- const previewArea = area.key
- const previewAddress = parseInt(address || '0', 16)
- const previewLength = parseInt(length || '0', 16)
- let errorText = ''
- if (command.key === 'read' || command.key === 'write') {
- const addressError = validateStorageAccessWordText(address, '地址')
- const lengthError = validateStorageAccessWordText(length, '长度')
- if (addressError) {
- errorText = addressError
- } else if (lengthError) {
- errorText = lengthError
- } else if (previewLength <= 0) {
- errorText = '长度必须大于 0'
- } else if (command.key === 'write') {
- const hexError = validateHexText(next.storageAccessDataText)
- if (hexError) {
- errorText = hexError
- } else if (dataBytes.length !== previewLength) {
- errorText = `写入长度为 ${previewLength} 字节,当前数据为 ${dataBytes.length} 字节`
- }
- }
- }
- if (command.key === 'sync') {
- const previewHex = buildStorageAccessPreview('sync', AREA.INFO, 0, 4, [])
- return {
- storageAccessAddress: '0000',
- storageAccessAreaIndex: 0,
- storageAccessAreaLabel: 'info',
- storageAccessCommandIndex: next.storageAccessCommandIndex,
- storageAccessCommandLabel: command.label,
- storageAccessDataText: '',
- storageAccessErrorText: '',
- storageAccessGeneratedHex: previewHex,
- storageAccessLength: '',
- storageAccessPreviewAreaText: 'info',
- storageAccessPreviewHexText: previewHex,
- storageAccessPreviewText: 'info 0x0000 / 4 bytes',
- storageAccessSendLabel: '同步',
- storageAccessSyncInfoText: '0x0F 读取 info[0:4]'
- }
- }
- const previewHex = command.key === 'write'
- ? buildStorageAccessPreview('write', previewArea, previewAddress, previewLength, dataBytes)
- : buildStorageAccessPreview('read', previewArea, previewAddress, previewLength, dataBytes)
- return {
- storageAccessAddress: address,
- storageAccessAreaIndex: next.storageAccessAreaIndex,
- storageAccessAreaLabel: area.label,
- storageAccessCommandIndex: next.storageAccessCommandIndex,
- storageAccessCommandLabel: command.label,
- storageAccessDataText: dataText,
- storageAccessErrorText: errorText,
- storageAccessGeneratedHex: errorText ? '' : previewHex,
- storageAccessLength: length,
- storageAccessPreviewAreaText: formatAreaLabel(previewArea),
- storageAccessPreviewHexText: errorText ? '' : previewHex,
- storageAccessPreviewText: errorText
- ? '--'
- : `${formatAreaLabel(previewArea)} 0x${formatStorageAccessWordText(previewAddress)} / ${previewLength} bytes`,
- storageAccessSendLabel: command.label,
- storageAccessSyncInfoText: ''
- }
- }
- function getLogPayloadText(log, mode) {
- if (!log) return '--'
- if (mode === 'text') {
- const payloadText = String(log.payloadText || '').trim()
- if (payloadText) return payloadText
- const bytes = Array.isArray(log.payloadBytes) ? log.payloadBytes : []
- return bytesToUtf8Text(bytes) || String(log.payload || '').trim() || '--'
- }
- if (typeof log.payload === 'string' && log.payload) {
- return log.payload
- }
- const bytes = Array.isArray(log.payloadBytes) ? log.payloadBytes : []
- return bytes.length ? bytesToHex(bytes, ' ') : '--'
- }
- function decorateLogs(logs, mode) {
- return (logs || []).map((item) => ({
- ...item,
- displayText: getLogPayloadText(item, mode)
- }))
- }
- function getProtocolModeState(settingsState = {}) {
- const isModbusProtocol = settingsService.isModbusProtocol(settingsState.protocolMode)
- const isStorageAccessProtocol = settingsService.isStorageAccessProtocol(settingsState.protocolMode)
- return {
- isModbusProtocol,
- isStorageAccessProtocol
- }
- }
- function getManualStatePayload(manualState = {}) {
- const commands = Array.isArray(manualState.protocolCommands) ? manualState.protocolCommands : []
- const command = commands[Number(manualState.commandIndex) || 0] || commands[0] || {}
- return {
- ...manualState,
- protocolCommandLabel: command.label || '指令',
- protocolResponseText: manualState.protocolResponseText || '',
- protocolSendLabel: '发送',
- showProtocolMultipleButton: command.inputMode === 'multiple',
- protocolTitleText: '标准 Modbus'
- }
- }
- module.exports = {
- LOG_MODE_OPTIONS,
- STORAGE_ACCESS_AREA_OPTIONS,
- STORAGE_ACCESS_COMMAND_OPTIONS,
- decorateLogs,
- getLogModeToggleText,
- getManualStatePayload,
- getNextLogMode,
- getNextSerialMode,
- getProtocolModeState,
- normalizeSerialState,
- normalizeHexText,
- normalizeStorageAccessState,
- parseHexBytes,
- validateHexText
- }
|