Modbus-based implementation of controller
This commit is contained in:
parent
e7d02be849
commit
b8a82feed0
@ -5,6 +5,10 @@ import com.ghgande.j2mod.modbus.procimg.InputRegister
|
||||
import com.ghgande.j2mod.modbus.procimg.Register
|
||||
import com.ghgande.j2mod.modbus.procimg.SimpleRegister
|
||||
import com.ghgande.j2mod.modbus.util.BitVector
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.buildPacket
|
||||
import io.ktor.utils.io.core.readByteBuffer
|
||||
import io.ktor.utils.io.core.writeShort
|
||||
import space.kscience.controls.api.Device
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
@ -22,9 +26,56 @@ public interface ModbusDevice : Device {
|
||||
public val clientId: Int
|
||||
|
||||
/**
|
||||
* The OPC-UA client initialized on first use
|
||||
* 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.readObject(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 = readInputRegistersToPacket(address, count)
|
||||
return format.readObject(packet)
|
||||
}
|
||||
|
||||
public operator fun <T> ModbusRegistryKey.HoldingRange<T>.setValue(
|
||||
thisRef: Any?,
|
||||
property: KProperty<*>,
|
||||
value: T,
|
||||
) {
|
||||
val buffer = buildPacket {
|
||||
format.writeObject(this, value)
|
||||
}.readByteBuffer()
|
||||
writeHoldingRegisters(address,buffer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,9 +99,16 @@ public fun ModbusDevice.writeCoil(ref: Int, value: Boolean) {
|
||||
master.writeCoil(clientId, ref, value)
|
||||
}
|
||||
|
||||
public fun ModbusDevice.writeCoil(key: ModbusRegistryKey.Coil, value: Boolean) {
|
||||
master.writeCoil(clientId, key.address, value)
|
||||
}
|
||||
|
||||
public fun ModbusDevice.readInputDiscretes(ref: Int, count: Int): BitVector =
|
||||
master.readInputDiscretes(clientId, ref, count)
|
||||
|
||||
public fun ModbusDevice.readInputDiscrete(ref: Int): Boolean =
|
||||
master.readInputDiscretes(clientId, ref, 1).getBit(0)
|
||||
|
||||
public fun ModbusDevice.readInputRegisters(ref: Int, count: Int): List<InputRegister> =
|
||||
master.readInputRegisters(clientId, ref, count).toList()
|
||||
|
||||
@ -64,25 +122,42 @@ private fun Array<out InputRegister>.toBuffer(): ByteBuffer {
|
||||
return buffer
|
||||
}
|
||||
|
||||
private fun Array<out InputRegister>.toPacket(): ByteReadPacket = buildPacket {
|
||||
forEach { value ->
|
||||
writeShort(value.toShort())
|
||||
}
|
||||
}
|
||||
|
||||
public fun ModbusDevice.readInputRegistersToBuffer(ref: Int, count: Int): ByteBuffer =
|
||||
master.readInputRegisters(clientId, ref, count).toBuffer()
|
||||
|
||||
public fun ModbusDevice.readInputRegistersToPacket(ref: Int, count: Int): ByteReadPacket =
|
||||
master.readInputRegisters(clientId, ref, count).toPacket()
|
||||
|
||||
public fun ModbusDevice.readDoubleInput(ref: Int): Double =
|
||||
readInputRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble()
|
||||
|
||||
public fun ModbusDevice.readShortInput(ref: Int): Short =
|
||||
public fun ModbusDevice.readInputRegister(ref: Int): Short =
|
||||
readInputRegisters(ref, 1).first().toShort()
|
||||
|
||||
public fun ModbusDevice.readHoldingRegisters(ref: Int, count: Int): List<Register> =
|
||||
master.readMultipleRegisters(clientId, ref, count).toList()
|
||||
|
||||
/**
|
||||
* Read a number of registers to a [ByteBuffer]
|
||||
* @param ref address of a register
|
||||
* @param count number of 2-bytes registers to read. Buffer size is 2*[count]
|
||||
*/
|
||||
public fun ModbusDevice.readHoldingRegistersToBuffer(ref: Int, count: Int): ByteBuffer =
|
||||
master.readMultipleRegisters(clientId, ref, count).toBuffer()
|
||||
|
||||
public fun ModbusDevice.readHoldingRegistersToPacket(ref: Int, count: Int): ByteReadPacket =
|
||||
master.readMultipleRegisters(clientId, ref, count).toPacket()
|
||||
|
||||
public fun ModbusDevice.readDoubleRegister(ref: Int): Double =
|
||||
readHoldingRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble()
|
||||
|
||||
public fun ModbusDevice.readShortRegister(ref: Int): Short =
|
||||
public fun ModbusDevice.readHoldingRegister(ref: Int): Short =
|
||||
readHoldingRegisters(ref, 1).first().toShort()
|
||||
|
||||
public fun ModbusDevice.writeHoldingRegisters(ref: Int, values: ShortArray): Int =
|
||||
@ -100,7 +175,7 @@ public fun ModbusDevice.writeHoldingRegister(ref: Int, value: Short): Int =
|
||||
)
|
||||
|
||||
public fun ModbusDevice.writeHoldingRegisters(ref: Int, buffer: ByteBuffer): Int {
|
||||
val array = ShortArray(buffer.limit().floorDiv(2)) { buffer.getShort(it * 2) }
|
||||
val array: ShortArray = ShortArray(buffer.limit().floorDiv(2)) { buffer.getShort(it * 2) }
|
||||
|
||||
return writeHoldingRegisters(ref, array)
|
||||
}
|
||||
@ -109,17 +184,17 @@ public fun ModbusDevice.writeShortRegister(ref: Int, value: Short) {
|
||||
master.writeSingleRegister(ref, SimpleRegister().apply { setValue(value) })
|
||||
}
|
||||
|
||||
public fun ModbusDevice.modBusRegister(
|
||||
public fun ModbusDevice.modbusRegister(
|
||||
ref: Int,
|
||||
): ReadWriteProperty<ModbusDevice, Short> = object : ReadWriteProperty<ModbusDevice, Short> {
|
||||
override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Short = readShortRegister(ref)
|
||||
override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Short = readHoldingRegister(ref)
|
||||
|
||||
override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Short) {
|
||||
writeHoldingRegister(ref, value)
|
||||
}
|
||||
}
|
||||
|
||||
public fun ModbusDevice.modBusDoubleRegister(
|
||||
public fun ModbusDevice.modbusDoubleRegister(
|
||||
ref: Int,
|
||||
): ReadWriteProperty<ModbusDevice, Double> = object : ReadWriteProperty<ModbusDevice, Double> {
|
||||
override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Double = readDoubleRegister(ref)
|
||||
@ -129,18 +204,3 @@ public fun ModbusDevice.modBusDoubleRegister(
|
||||
writeHoldingRegisters(ref, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//public inline fun <reified T> ModbusDevice.opcDouble(
|
||||
//): ReadWriteProperty<Any?, Double> = ma
|
||||
//
|
||||
//public inline fun <reified T> ModbusDeviceBySpec<*>.opcInt(
|
||||
// nodeId: NodeId,
|
||||
// magAge: Double = 1.0,
|
||||
//): ReadWriteProperty<Any?, Int> = opc(nodeId, MetaConverter.int, magAge)
|
||||
//
|
||||
//public inline fun <reified T> ModbusDeviceBySpec<*>.opcString(
|
||||
// nodeId: NodeId,
|
||||
// magAge: Double = 1.0,
|
||||
//): ReadWriteProperty<Any?, String> = opc(nodeId, MetaConverter.string, magAge)
|
||||
|
@ -1,5 +1,8 @@
|
||||
package space.kscience.controls.modbus
|
||||
|
||||
import space.kscience.dataforge.io.IOFormat
|
||||
import space.kscience.dataforge.io.IOReader
|
||||
|
||||
|
||||
public sealed class ModbusRegistryKey {
|
||||
/**
|
||||
@ -20,25 +23,70 @@ public sealed class ModbusRegistryKey {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read-only binary value
|
||||
*/
|
||||
public class InputRegister(public val address: Int) : ModbusRegistryKey() {
|
||||
init {
|
||||
require(address in 20001..29999) { "InputRegister address must be in 20001..29999 range" }
|
||||
}
|
||||
}
|
||||
|
||||
public class InputRange<T>(public val address: Int, public val count: Int, public val format: IOReader<T>) {
|
||||
public val endAddress: Int get() = address + count
|
||||
|
||||
init {
|
||||
require(address in 20001..29999) { "InputRange begin address is $address, but must be in 20001..29999 range" }
|
||||
require(endAddress in 20001..29999) { "InputRange end address is ${endAddress}, but must be in 20001..29999 range" }
|
||||
}
|
||||
}
|
||||
|
||||
public class HoldingRegister(public val address: Int) : ModbusRegistryKey() {
|
||||
init {
|
||||
require(address in 30001..39999) { "HoldingRegister address must be in 30001..39999 range" }
|
||||
}
|
||||
}
|
||||
|
||||
public class HoldingRange<T>(public val address: Int, public val count: Int, public val format: IOFormat<T>) {
|
||||
public val endAddress: Int get() = address + count
|
||||
|
||||
init {
|
||||
require(address in 30001..39999) { "HoldingRange begin address is $address, but must be in 30001..39999 range" }
|
||||
require(endAddress in 30001..39999) { "HoldingRange end address is ${endAddress}, but must be in 30001..39999 range" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ModbusRegistryMap {
|
||||
protected fun coil(address: Int): ModbusRegistryKey.Coil = ModbusRegistryKey.Coil(address)
|
||||
|
||||
protected fun coilByOffset(offset: Int): ModbusRegistryKey.Coil = ModbusRegistryKey.Coil(offset)
|
||||
|
||||
protected fun discrete(address: Int): ModbusRegistryKey.DiscreteInput = ModbusRegistryKey.DiscreteInput(address)
|
||||
|
||||
protected fun discreteByOffset(offset: Int): ModbusRegistryKey.DiscreteInput =
|
||||
ModbusRegistryKey.DiscreteInput(10000 + offset)
|
||||
|
||||
protected fun input(address: Int): ModbusRegistryKey.InputRegister = ModbusRegistryKey.InputRegister(address)
|
||||
|
||||
protected fun <T> input(address: Int, count: Int, reader: IOReader<T>): ModbusRegistryKey.InputRange<T> =
|
||||
ModbusRegistryKey.InputRange(address, count, reader)
|
||||
|
||||
|
||||
protected fun inputByOffset(offset: Int): ModbusRegistryKey.InputRegister =
|
||||
ModbusRegistryKey.InputRegister(20000 + offset)
|
||||
|
||||
protected fun <T> inputByOffset(offset: Int, count: Int, reader: IOReader<T>): ModbusRegistryKey.InputRange<T> =
|
||||
ModbusRegistryKey.InputRange(20000 + offset, count, reader)
|
||||
|
||||
protected fun register(address: Int): ModbusRegistryKey.HoldingRegister = ModbusRegistryKey.HoldingRegister(address)
|
||||
|
||||
protected fun <T> register(address: Int, count: Int, format: IOFormat<T>): ModbusRegistryKey.HoldingRange<T> =
|
||||
ModbusRegistryKey.HoldingRange(address, count, format)
|
||||
|
||||
protected fun registerByOffset(offset: Int): ModbusRegistryKey.HoldingRegister =
|
||||
ModbusRegistryKey.HoldingRegister(30000 + offset)
|
||||
|
||||
protected fun <T> registerByOffset(offset: Int, count: Int, format: IOFormat<T>): ModbusRegistryKey.HoldingRange<T> =
|
||||
ModbusRegistryKey.HoldingRange(offset + 30000, count, format)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user