///|
/// Pack boolean values into Modbus LSB-first coil bytes.
pub fn pack_bits(values : Array[Bool]) -> Array[Byte] {
let out : Array[Byte] = []
let byte_count = (values.length() + 7) / 8
for _ in 0.. Result[Array[Bool], ModbusError] {
if quantity < 0 {
return Err(InvalidQuantity)
}
let expected = (quantity + 7) / 8
if bytes.length() != expected {
return Err(InvalidByteCount)
}
let out : Array[Bool] = []
for index in 0.. Result[Bool, ModbusError] {
if index < 0 || index / 8 >= bytes.length() {
Err(InvalidAddress)
} else {
Ok((bytes[index / 8] & (1 << (index % 8)).to_byte()) != 0)
}
}
///|
/// Set a single bit in a packed byte array.
pub fn set_bit(
bytes : Array[Byte],
index : Int,
value : Bool,
) -> Result[Unit, ModbusError] {
if index < 0 || index / 8 >= bytes.length() {
return Err(InvalidAddress)
}
let mask = (1 << (index % 8)).to_byte()
if value {
bytes[index / 8] = bytes[index / 8] | mask
} else {
bytes[index / 8] = bytes[index / 8] & (0xFF ^ mask)
}
Ok(())
}
///|
/// Count the set bits in a packed byte array.
pub fn count_bits(bytes : Array[Byte]) -> Int {
let mut total = 0
for byte in bytes {
let mut value = byte
for _ in 0..<8 {
if (value & 1) != 0 {
total += 1
}
value = value >> 1
}
}
total
}
///|
/// A mutable bounded bit vector for coils and discrete inputs.
pub(all) struct BitVector {
length : Int
storage : Array[Byte]
}
///|
/// Create a zero-filled bit vector.
pub fn BitVector::new(length : Int) -> Result[BitVector, ModbusError] {
if length < 0 || length > 65536 {
return Err(CapacityExceeded)
}
let storage : Array[Byte] = []
for _ in 0..<((length + 7) / 8) {
storage.push(0)
}
Ok({ length, storage })
}
///|
pub fn BitVector::from_bits(values : Array[Bool]) -> BitVector {
{ length: values.length(), storage: pack_bits(values) }
}
///|
pub fn BitVector::length(self : BitVector) -> Int {
self.length
}
///|
pub fn BitVector::byte_length(self : BitVector) -> Int {
self.storage.length()
}
///|
pub fn BitVector::get(
self : BitVector,
index : Int,
) -> Result[Bool, ModbusError] {
if index < 0 || index >= self.length {
Err(InvalidAddress)
} else {
get_bit(self.storage, index)
}
}
///|
pub fn BitVector::set(
self : BitVector,
index : Int,
value : Bool,
) -> Result[Unit, ModbusError] {
if index < 0 || index >= self.length {
Err(InvalidAddress)
} else {
set_bit(self.storage, index, value)
}
}
///|
pub fn BitVector::fill(self : BitVector, value : Bool) -> Unit {
let fill : Byte = if value { 0xFF } else { 0 }
for index in 0.. 0 {
self.storage[self.storage.length() - 1] = (1 << (self.length % 8)).to_byte() -
1
}
}
///|
pub fn BitVector::to_bits(self : BitVector) -> Array[Bool] {
let out : Array[Bool] = []
for index in 0.. Array[Byte] {
copy_bytes(self.storage)
}
///|
pub fn BitVector::count(self : BitVector) -> Int {
let mut count = 0
for index in 0.. BitVector {
{ length: self.length, storage: copy_bytes(self.storage) }
}
///|
/// Compare two bit vectors including their logical lengths.
pub fn BitVector::equal(self : BitVector, other : BitVector) -> Bool {
self.length == other.length && self.storage == other.storage
}
///|
/// Apply a bitwise OR to two equally sized vectors.
pub fn BitVector::or(
self : BitVector,
other : BitVector,
) -> Result[BitVector, ModbusError] {
if self.length != other.length {
return Err(InvalidLength)
}
let out = self.copy()
for index in 0.. Result[BitVector, ModbusError] {
if self.length != other.length {
return Err(InvalidLength)
}
let out = self.copy()
for index in 0.. BitVector {
let out = self.copy()
for index in 0.. 0 {
out.storage[out.storage.length() - 1] = out.storage[out.storage.length() - 1] &
((1 << (out.length % 8)).to_byte() - 1)
}
out
}
///|
/// Shift logical bits left, dropping values that fall outside the vector.
pub fn BitVector::shift_left(
self : BitVector,
distance : Int,
) -> Result[BitVector, ModbusError] {
if distance < 0 {
return Err(InvalidQuantity)
}
let out = match BitVector::new(self.length) {
Ok(value) => value
Err(error) => return Err(error)
}
for index in distance.. ignore(out.set(index, value))
Err(_) => ()
}
}
Ok(out)
}
///|
/// Shift logical bits right, dropping values that fall outside the vector.
pub fn BitVector::shift_right(
self : BitVector,
distance : Int,
) -> Result[BitVector, ModbusError] {
if distance < 0 {
return Err(InvalidQuantity)
}
let out = match BitVector::new(self.length) {
Ok(value) => value
Err(error) => return Err(error)
}
if distance < self.length {
for index in 0..<(self.length - distance) {
match self.get(index + distance) {
Ok(value) => ignore(out.set(index, value))
Err(_) => ()
}
}
}
Ok(out)
}
///|
/// Pack a range of bits from a vector for a Modbus response.
pub fn BitVector::pack_range(
self : BitVector,
start : Int,
quantity : Int,
) -> Result[Array[Byte], ModbusError] {
if start < 0 || quantity < 0 || start + quantity > self.length {
return Err(InvalidAddress)
}
let values : Array[Bool] = []
for index in start..<(start + quantity) {
values.push(self.get(index).unwrap())
}
Ok(pack_bits(values))
}
///|
/// Replace a range of bits from packed input bytes.
pub fn BitVector::unpack_into(
self : BitVector,
start : Int,
quantity : Int,
bytes : Array[Byte],
) -> Result[Unit, ModbusError] {
if start < 0 || quantity < 0 || start + quantity > self.length {
return Err(InvalidAddress)
}
let values = match unpack_bits(bytes, quantity) {
Ok(value) => value
Err(error) => return Err(error)
}
for index, value in values {
let _ = self.set(start + index, value)
}
Ok(())
}