jsmodbus
Version:
Implementation for the Serial/TCP Modbus protocol.
128 lines (105 loc) • 4.06 kB
JavaScript
'use strict'
const debug = require('debug')('buffer-utils')
// Buffer utilities to make simplify writing multiple coils
/*
* Outputs to set might be a long buffer starting mid way through a byte.
* For example, outputs [0b11111111, 0b11111111, 0b11111111] starting at coil 5
* Original Coils: [0b00000010, 0b00000000, 0b00000000, 0b11111111]
* Outputs shifted: [0b11110000, 0b11111111, 0b11111111, 0b00001111]
* Resultant Coils: [0b11110010, 0b11111111, 0b11111111, 0b11111111]
* The resultant coils are set to shifted outputs, but special attention needs to
* be paid to the first and last bytes.
*
* This code is broken up into 3 funtions:
* bufferShift
* firstByte
* lastByte
*/
/** bufferShift shift a buffer of ouputs so they can be used to overwrite existing coils
* @param {start_address} first coil to write
* @param {end_address} last coil to write
* @param {outputs} buffer of outputs to write
* @returns shifted output buffer
*/
class BufferUtils {
static bufferShift (startAddress, endAddress, outputs) {
startAddress = startAddress - 1
const startShift = startAddress % 8
const startByte = Math.floor(startAddress / 8)
const endByte = Math.floor(endAddress / 8)
const size = endByte - startByte + 1
// Define a new buffer
const buffer = Buffer.allocUnsafe(size)
buffer[0] = outputs[0] << startShift
debug('buffer[0] = %s ( %s << %d )', buffer[0].toString(2), outputs[0].toString(2), startShift)
const paddedBuffer = Buffer.concat([outputs, Buffer.alloc(1)], outputs.length + 1)
for (let i = 1; i < size; i++) {
buffer[i] = (paddedBuffer[i] << startShift) + (paddedBuffer[i - 1] >> (8 - startShift))
debug('buffer[%d] = %s ( %s << %d + %s >> %d)',
i,
buffer[i].toString(2),
paddedBuffer[i].toString(2),
startShift,
paddedBuffer[i - 1].toString(2),
8 - startAddress
)
}
return buffer
}
/** firstByte ensure first byte is set correctly
* @param {start_address} first coil to write
* @param {origianl_byte} byte from the original coils buffer
* @param {output_byte} first byte from the shifted outputs buffer
* @returns correct first byte to be written to coils buffer
*/
static firstByte (startAddress, originalByte, outputByte) {
startAddress = startAddress - 1
const startShift = startAddress % 8
const mask = 0xff >> (8 - startShift)
const maskedOriginalByte = originalByte & mask
return outputByte + maskedOriginalByte
}
/** lastByte ensure last byte is set correctly
* @param {end_address} last coil to write
* @param {origianl_byte} byte from the original coils buffer
* @param {output_byte} last byte from the shifted outputs buffer
* @returns correct last byte to be written to coils buffer
*/
static lastByte (endAddress, originalByte, outputByte) {
const endShift = endAddress % 8
const mask = 0xff << endShift
const maskedOriginalByte = originalByte & mask
return outputByte + maskedOriginalByte
}
static bufferToArrayStatus (buffer) {
const statusArray = []
let pos, curByteIdx, curByte
if (!(buffer instanceof Buffer)) {
return statusArray
}
for (let i = 0; i < buffer.length * 8; i += 1) {
pos = i % 8
curByteIdx = Math.floor(i / 8)
curByte = buffer.readUInt8(curByteIdx)
statusArray.push(((curByte & Math.pow(2, pos)) > 0) + 0)
}
return statusArray
}
static arrayStatusToBuffer (array) {
const byteCount = array instanceof Array ? Math.ceil(array.length / 8) : 0
const buffer = Buffer.alloc(byteCount)
if (!(array instanceof Array)) {
return buffer
}
let byteOffset, bitOffset, byte
for (let i = 0; i < array.length; i += 1) {
byteOffset = Math.floor(i / 8)
bitOffset = i % 8
byte = buffer.readUInt8(byteOffset)
byte += array[i] ? Math.pow(2, bitOffset) : 0
buffer.writeUInt8(byte, byteOffset)
}
return buffer
}
}
module.exports = BufferUtils