212 lines
7.5 KiB
Kotlin
212 lines
7.5 KiB
Kotlin
package space.kscience.controls.modbus
|
|
|
|
import com.ghgande.j2mod.modbus.facade.AbstractModbusMaster
|
|
import com.ghgande.j2mod.modbus.procimg.InputRegister
|
|
import com.ghgande.j2mod.modbus.procimg.Register
|
|
import com.ghgande.j2mod.modbus.procimg.SimpleInputRegister
|
|
import com.ghgande.j2mod.modbus.util.BitVector
|
|
import kotlinx.io.Buffer
|
|
import space.kscience.controls.api.Device
|
|
import space.kscience.dataforge.io.Buffer
|
|
import space.kscience.dataforge.io.ByteArray
|
|
import java.nio.ByteBuffer
|
|
import kotlin.properties.ReadWriteProperty
|
|
import kotlin.reflect.KProperty
|
|
|
|
|
|
/**
|
|
* A Modbus device backed by j2mod client
|
|
*/
|
|
public interface ModbusDevice : Device {
|
|
|
|
/**
|
|
* Unit id for this specific device
|
|
*/
|
|
public val unitId: Int
|
|
|
|
/**
|
|
* The modubus master connector
|
|
*/
|
|
public val master: AbstractModbusMaster
|
|
|
|
public operator fun ModbusRegistryKey.Coil.getValue(thisRef: Any?, property: KProperty<*>): Boolean =
|
|
readCoil(address)
|
|
|
|
public operator fun ModbusRegistryKey.Coil.setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
|
|
writeCoil(address, value)
|
|
}
|
|
|
|
public operator fun ModbusRegistryKey.DiscreteInput.getValue(thisRef: Any?, property: KProperty<*>): Boolean =
|
|
readInputDiscrete(address)
|
|
|
|
public operator fun ModbusRegistryKey.InputRegister.getValue(thisRef: Any?, property: KProperty<*>): Short =
|
|
readInputRegister(address)
|
|
|
|
public operator fun <T> ModbusRegistryKey.InputRange<T>.getValue(thisRef: Any?, property: KProperty<*>): T {
|
|
val packet = readInputRegistersToPacket(address, count)
|
|
return format.readFrom(packet)
|
|
}
|
|
|
|
|
|
public operator fun ModbusRegistryKey.HoldingRegister.getValue(thisRef: Any?, property: KProperty<*>): Short =
|
|
readHoldingRegister(address)
|
|
|
|
public operator fun ModbusRegistryKey.HoldingRegister.setValue(
|
|
thisRef: Any?,
|
|
property: KProperty<*>,
|
|
value: Short,
|
|
) {
|
|
writeHoldingRegister(address, value)
|
|
}
|
|
|
|
public operator fun <T> ModbusRegistryKey.HoldingRange<T>.getValue(thisRef: Any?, property: KProperty<*>): T {
|
|
val packet = readHoldingRegistersToPacket(address, count)
|
|
return format.readFrom(packet)
|
|
}
|
|
|
|
public operator fun <T> ModbusRegistryKey.HoldingRange<T>.setValue(
|
|
thisRef: Any?,
|
|
property: KProperty<*>,
|
|
value: T,
|
|
) {
|
|
val buffer = ByteArray {
|
|
format.writeTo(this, value)
|
|
}
|
|
writeHoldingRegisters(address, buffer)
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Read multiple sequential modbus coils (bit-values)
|
|
*/
|
|
public fun ModbusDevice.readCoils(address: Int, count: Int): BitVector =
|
|
master.readCoils(unitId, address, count)
|
|
|
|
public fun ModbusDevice.readCoil(address: Int): Boolean =
|
|
master.readCoils(unitId, address, 1).getBit(0)
|
|
|
|
public fun ModbusDevice.writeCoils(address: Int, values: BooleanArray) {
|
|
val bitVector = BitVector(values.size)
|
|
values.forEachIndexed { index, value ->
|
|
bitVector.setBit(index, value)
|
|
}
|
|
master.writeMultipleCoils(unitId, address, bitVector)
|
|
}
|
|
|
|
public fun ModbusDevice.writeCoil(address: Int, value: Boolean) {
|
|
master.writeCoil(unitId, address, value)
|
|
}
|
|
|
|
public fun ModbusDevice.writeCoil(key: ModbusRegistryKey.Coil, value: Boolean) {
|
|
master.writeCoil(unitId, key.address, value)
|
|
}
|
|
|
|
public fun ModbusDevice.readInputDiscretes(address: Int, count: Int): BitVector =
|
|
master.readInputDiscretes(unitId, address, count)
|
|
|
|
public fun ModbusDevice.readInputDiscrete(address: Int): Boolean =
|
|
master.readInputDiscretes(unitId, address, 1).getBit(0)
|
|
|
|
public fun ModbusDevice.readInputRegisters(address: Int, count: Int): List<InputRegister> =
|
|
master.readInputRegisters(unitId, address, count).toList()
|
|
|
|
private fun Array<out InputRegister>.toBuffer(): ByteBuffer {
|
|
val buffer: ByteBuffer = ByteBuffer.allocate(size * 2)
|
|
forEachIndexed { index, value ->
|
|
buffer.position(index * 2)
|
|
buffer.put(value.toBytes())
|
|
}
|
|
buffer.flip()
|
|
return buffer
|
|
}
|
|
|
|
private fun Array<out InputRegister>.toPacket(): Buffer = Buffer {
|
|
forEach { value ->
|
|
writeShort(value.toShort())
|
|
}
|
|
}
|
|
|
|
public fun ModbusDevice.readInputRegistersToBuffer(address: Int, count: Int): ByteBuffer =
|
|
master.readInputRegisters(unitId, address, count).toBuffer()
|
|
|
|
public fun ModbusDevice.readInputRegistersToPacket(address: Int, count: Int): Buffer =
|
|
master.readInputRegisters(unitId, address, count).toPacket()
|
|
|
|
public fun ModbusDevice.readDoubleInput(address: Int): Double =
|
|
readInputRegistersToBuffer(address, Double.SIZE_BYTES).getDouble()
|
|
|
|
public fun ModbusDevice.readInputRegister(address: Int): Short =
|
|
readInputRegisters(address, 1).first().toShort()
|
|
|
|
public fun ModbusDevice.readHoldingRegisters(address: Int, count: Int): List<Register> =
|
|
master.readMultipleRegisters(unitId, address, count).toList()
|
|
|
|
/**
|
|
* Read a number of registers to a [ByteBuffer]
|
|
* @param address of a register
|
|
* @param count number of 2-bytes registers to read. Buffer size is 2*[count]
|
|
*/
|
|
public fun ModbusDevice.readHoldingRegistersToBuffer(address: Int, count: Int): ByteBuffer =
|
|
master.readMultipleRegisters(unitId, address, count).toBuffer()
|
|
|
|
public fun ModbusDevice.readHoldingRegistersToPacket(address: Int, count: Int): Buffer =
|
|
master.readMultipleRegisters(unitId, address, count).toPacket()
|
|
|
|
public fun ModbusDevice.readDoubleRegister(address: Int): Double =
|
|
readHoldingRegistersToBuffer(address, Double.SIZE_BYTES).getDouble()
|
|
|
|
public fun ModbusDevice.readHoldingRegister(address: Int): Short =
|
|
readHoldingRegisters(address, 1).first().toShort()
|
|
|
|
public fun ModbusDevice.writeHoldingRegisters(address: Int, values: ShortArray): Int =
|
|
master.writeMultipleRegisters(
|
|
unitId,
|
|
address,
|
|
Array<Register>(values.size) { SimpleInputRegister(values[it].toInt()) }
|
|
)
|
|
|
|
public fun ModbusDevice.writeHoldingRegister(address: Int, value: Short): Int =
|
|
master.writeSingleRegister(
|
|
unitId,
|
|
address,
|
|
SimpleInputRegister(value.toInt())
|
|
)
|
|
|
|
public fun ModbusDevice.writeHoldingRegister(key: ModbusRegistryKey.HoldingRegister, value: Short): Int =
|
|
writeHoldingRegister(key.address, value)
|
|
|
|
public fun ModbusDevice.writeHoldingRegisters(address: Int, buffer: ByteBuffer): Int {
|
|
val array: ShortArray = ShortArray(buffer.limit().floorDiv(2)) { buffer.getShort(it * 2) }
|
|
|
|
return writeHoldingRegisters(address, array)
|
|
}
|
|
|
|
public fun ModbusDevice.writeHoldingRegisters(address: Int, byteArray: ByteArray): Int {
|
|
val buffer = ByteBuffer.wrap(byteArray)
|
|
val array: ShortArray = ShortArray(buffer.limit().floorDiv(2)) { buffer.getShort(it * 2) }
|
|
|
|
return writeHoldingRegisters(address, array)
|
|
}
|
|
|
|
public fun ModbusDevice.modbusRegister(
|
|
address: Int,
|
|
): ReadWriteProperty<ModbusDevice, Short> = object : ReadWriteProperty<ModbusDevice, Short> {
|
|
override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Short = readHoldingRegister(address)
|
|
|
|
override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Short) {
|
|
writeHoldingRegister(address, value)
|
|
}
|
|
}
|
|
|
|
public fun ModbusDevice.modbusDoubleRegister(
|
|
address: Int,
|
|
): ReadWriteProperty<ModbusDevice, Double> = object : ReadWriteProperty<ModbusDevice, Double> {
|
|
override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Double = readDoubleRegister(address)
|
|
|
|
override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Double) {
|
|
val buffer = ByteBuffer.allocate(Double.SIZE_BYTES).apply { putDouble(value) }
|
|
writeHoldingRegisters(address, buffer)
|
|
}
|
|
}
|