| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- const {
- buildCodeInfoQueryFrame,
- buildDebugReadMemoryFrame,
- buildDebugWriteMemoryFrame,
- buildReadFrame,
- buildWriteMultipleRegistersFrame,
- buildWriteSingleCoilFrame,
- buildWriteSingleRegisterFrame,
- getMaxDebugReadByteLength,
- getMaxDebugWriteByteLength,
- getMaxReadQuantity,
- getMaxWriteMultipleRegisterQuantity
- } = require('./frame.js')
- const settingsService = require('../../store/settings-store.js')
- const transport = require('../../transport/ble-core.js')
- const {
- addCoilReadValues,
- addWordReadValues
- } = require('../../utils/register-value-utils.js')
- function getSharedSlaveAddress(title = '从机地址错误') {
- try {
- return settingsService.getModbusSlaveAddress()
- } catch (error) {
- transport.showCommandAlert(title, error.message)
- return null
- }
- }
- function formatAddress(value) {
- return Number(value || 0).toString(16).toUpperCase()
- }
- function getChunkLabel(label, chunks, chunk) {
- if (!label || chunks.length <= 1) return label
- return `${label} ${formatAddress(chunk.address)}-${formatAddress(chunk.address + chunk.quantity - 1)}`
- }
- function isBroadcastAddress(slaveAddress) {
- return Number(slaveAddress) === 0
- }
- function showBroadcastReadAlert(label) {
- transport.showCommandAlert(label || '读取命令错误', '广播地址 0x00 不支持读取')
- }
- function splitQuantity(startAddress, quantity, maxQuantity) {
- const chunks = []
- let address = Number(startAddress) || 0
- let remaining = Math.max(0, Math.floor(Number(quantity) || 0))
- const chunkLimit = Math.max(1, Math.floor(Number(maxQuantity) || remaining || 1))
- while (remaining > 0) {
- const chunkQuantity = Math.min(remaining, chunkLimit)
- chunks.push({
- address,
- quantity: chunkQuantity
- })
- address += chunkQuantity
- remaining -= chunkQuantity
- }
- return chunks
- }
- function getReadChunks(functionCode, startAddress, quantity, options = {}) {
- const maxQuantity = getMaxReadQuantity(functionCode, options.maxFrameBytes)
- return splitQuantity(startAddress, quantity, maxQuantity || quantity)
- }
- function getDebugReadChunks(startAddress, byteLength, options = {}) {
- const maxByteLength = getMaxDebugReadByteLength(options.maxFrameBytes)
- return splitQuantity(startAddress, byteLength, maxByteLength || byteLength)
- }
- function getDebugWriteChunks(startAddress, bytes, options = {}) {
- const sourceBytes = Array.prototype.slice.call(bytes || [])
- const maxByteLength = getMaxDebugWriteByteLength(options.maxFrameBytes)
- const chunks = splitQuantity(startAddress, sourceBytes.length, maxByteLength || sourceBytes.length)
- let offset = 0
- return chunks.map((chunk) => {
- const dataBytes = sourceBytes.slice(offset, offset + chunk.quantity)
- offset += chunk.quantity
- return {
- ...chunk,
- dataBytes
- }
- })
- }
- async function sendReadChunk(slaveAddress, functionCode, chunk, label, kind, options = {}) {
- if (isBroadcastAddress(slaveAddress)) {
- showBroadcastReadAlert(label)
- return false
- }
- return transport.sendManagedFrame(
- buildReadFrame(slaveAddress, functionCode, chunk.address, chunk.quantity, {
- maxFrameBytes: options.maxFrameBytes
- }),
- label,
- {
- address: chunk.address,
- functionCode,
- kind,
- quantity: chunk.quantity,
- slaveAddress
- },
- {
- maxFrameBytes: options.maxFrameBytes,
- showModal: options.showModal
- }
- )
- }
- async function sendDebugReadChunk(slaveAddress, memoryType, chunk, label, kind, options = {}) {
- if (isBroadcastAddress(slaveAddress)) {
- showBroadcastReadAlert(label)
- return false
- }
- return transport.sendManagedFrame(
- buildDebugReadMemoryFrame(slaveAddress, memoryType, chunk.address, chunk.quantity, {
- maxFrameBytes: options.maxFrameBytes
- }),
- label,
- {
- address: chunk.address,
- functionCode: 0x41,
- kind,
- memoryType,
- quantity: chunk.quantity,
- slaveAddress
- },
- {
- maxFrameBytes: options.maxFrameBytes,
- showModal: options.showModal
- }
- )
- }
- async function readSpans(slaveAddress, functionCode, spans, label, kind, options = {}) {
- const readValues = {
- coils: {},
- words: {}
- }
- const normalizedSpans = (spans || []).filter((span) => span && span.quantity > 0)
- for (const span of normalizedSpans) {
- const chunks = getReadChunks(functionCode, span.address, span.quantity, options)
- for (const chunk of chunks) {
- const response = await sendReadChunk(
- slaveAddress,
- functionCode,
- chunk,
- getChunkLabel(label, chunks, chunk),
- kind,
- options
- )
- if (!response) return null
- if (functionCode === 0x01 || functionCode === 0x02) {
- addCoilReadValues(readValues, chunk.address, chunk.quantity, response)
- } else {
- addWordReadValues(readValues, chunk.address, response)
- }
- if (typeof options.onChunk === 'function') {
- options.onChunk(response, chunk)
- }
- }
- }
- return readValues
- }
- async function readRegisterWords(slaveAddress, functionCode, startAddress, quantity, label, kind, options = {}) {
- const words = []
- const chunks = getReadChunks(functionCode, startAddress, quantity, options)
- for (const chunk of chunks) {
- const response = await sendReadChunk(
- slaveAddress,
- functionCode,
- chunk,
- getChunkLabel(label, chunks, chunk),
- kind,
- options
- )
- 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
- }
- async function readDebugMemory(slaveAddress, memoryType, startAddress, byteLength, label, kind = 'debug-memory-read', options = {}) {
- const bytes = []
- const chunks = getDebugReadChunks(startAddress, byteLength, options)
- for (const chunk of chunks) {
- const response = await sendDebugReadChunk(
- slaveAddress,
- memoryType,
- chunk,
- getChunkLabel(label, chunks, chunk),
- kind,
- options
- )
- if (!response) return null
- const dataBytes = Array.isArray(response.dataBytes) ? response.dataBytes : []
- dataBytes.forEach((byte, index) => {
- bytes[chunk.address - startAddress + index] = Number(byte) & 0xFF
- })
- if (typeof options.onChunk === 'function') {
- options.onChunk(response, chunk)
- }
- }
- return bytes
- }
- async function queryCodeInfoBlock(slaveAddress, label = '查询Code信息块', kind = 'code-info-query', options = {}) {
- if (isBroadcastAddress(slaveAddress)) {
- showBroadcastReadAlert(label)
- return null
- }
- const response = await transport.sendManagedFrame(
- buildCodeInfoQueryFrame(slaveAddress),
- label,
- {
- functionCode: 0x40,
- kind,
- quantity: 1,
- slaveAddress
- },
- {
- maxFrameBytes: options.maxFrameBytes,
- showModal: options.showModal
- }
- )
- if (!response) return null
- return {
- address: response.codeInfoAddress,
- byteLength: response.codeInfoLength,
- memoryType: response.memoryType
- }
- }
- async function readCodeInfoBlock(slaveAddress, label = '读取Code信息块', kind = 'code-info-read', options = {}) {
- const info = await queryCodeInfoBlock(slaveAddress, label, 'code-info-query', options)
- if (!info) return null
- if (info.memoryType !== 0x03) {
- transport.showCommandAlert(label, '设备返回的信息块不在 code 区')
- return null
- }
- const bytes = await readDebugMemory(
- slaveAddress,
- 0x03,
- info.address,
- info.byteLength,
- label,
- kind,
- options
- )
- if (!bytes) return null
- return {
- ...info,
- bytes
- }
- }
- async function readBitValues(slaveAddress, functionCode, startAddress, quantity, label, kind, options = {}) {
- const result = await readSpans(
- slaveAddress,
- functionCode,
- [{ address: startAddress, quantity }],
- label,
- kind,
- options
- )
- return result ? result.coils : null
- }
- async function readSingleHoldingWord(slaveAddress, address, label = '读取配对寄存器', kind = 'holding-word-read') {
- const words = await readRegisterWords(slaveAddress, 0x03, address, 1, label, kind)
- return words && Number.isInteger(words[0]) ? words[0] & 0xFFFF : null
- }
- function writeSingleCoil(slaveAddress, address, checked, label, kind = 'coil-write', options = {}) {
- const coilValue = checked ? 0xFF00 : 0x0000
- return transport.sendManagedFrame(
- buildWriteSingleCoilFrame(slaveAddress, address, checked),
- label,
- isBroadcastAddress(slaveAddress) ? null : {
- address,
- functionCode: 0x05,
- kind,
- quantity: 1,
- slaveAddress,
- value: coilValue
- },
- {
- maxFrameBytes: options.maxFrameBytes,
- showModal: options.showModal
- }
- )
- }
- function writeSingleRegister(slaveAddress, address, value, label, kind = 'register-write', options = {}) {
- return transport.sendManagedFrame(
- buildWriteSingleRegisterFrame(slaveAddress, address, value),
- label,
- isBroadcastAddress(slaveAddress) ? null : {
- address,
- functionCode: 0x06,
- kind,
- quantity: 1,
- slaveAddress,
- value
- },
- {
- maxFrameBytes: options.maxFrameBytes,
- showModal: options.showModal
- }
- )
- }
- function writeMultipleRegisters(slaveAddress, address, values, label, kind = 'registers-write', options = {}) {
- return transport.sendManagedFrame(
- buildWriteMultipleRegistersFrame(slaveAddress, address, values, {
- maxFrameBytes: options.maxFrameBytes
- }),
- label,
- isBroadcastAddress(slaveAddress) ? null : {
- address,
- functionCode: 0x10,
- kind,
- quantity: values.length,
- slaveAddress
- },
- {
- maxFrameBytes: options.maxFrameBytes,
- showModal: options.showModal
- }
- )
- }
- async function writeDebugMemory(slaveAddress, memoryType, startAddress, bytes, label, kind = 'debug-memory-write', options = {}) {
- const dataBytes = Array.prototype.slice.call(bytes || []).map((byte) => Number(byte) & 0xFF)
- const chunks = getDebugWriteChunks(startAddress, dataBytes, options)
- for (const chunk of chunks) {
- const response = await transport.sendManagedFrame(
- buildDebugWriteMemoryFrame(slaveAddress, memoryType, chunk.address, chunk.dataBytes, {
- maxFrameBytes: options.maxFrameBytes
- }),
- getChunkLabel(label, chunks, chunk),
- isBroadcastAddress(slaveAddress) ? null : {
- address: chunk.address,
- functionCode: 0x42,
- kind,
- memoryType,
- quantity: chunk.quantity,
- slaveAddress
- },
- {
- maxFrameBytes: options.maxFrameBytes,
- showModal: options.showModal
- }
- )
- if (!response) return false
- if (typeof options.onChunk === 'function') {
- options.onChunk(response, chunk)
- }
- }
- return true
- }
- module.exports = {
- getDebugReadChunks,
- getDebugWriteChunks,
- getReadChunks,
- getSharedSlaveAddress,
- getMaxReadQuantity,
- queryCodeInfoBlock,
- readBitValues,
- readCodeInfoBlock,
- readDebugMemory,
- readRegisterWords,
- readSingleHoldingWord,
- readSpans,
- splitQuantity,
- getMaxWriteMultipleRegisterQuantity,
- writeDebugMemory,
- writeMultipleRegisters,
- writeSingleCoil,
- writeSingleRegister
- }
|