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.Register
|
||||||
import com.ghgande.j2mod.modbus.procimg.SimpleRegister
|
import com.ghgande.j2mod.modbus.procimg.SimpleRegister
|
||||||
import com.ghgande.j2mod.modbus.util.BitVector
|
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 space.kscience.controls.api.Device
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import kotlin.properties.ReadWriteProperty
|
import kotlin.properties.ReadWriteProperty
|
||||||
@ -22,9 +26,56 @@ public interface ModbusDevice : Device {
|
|||||||
public val clientId: Int
|
public val clientId: Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The OPC-UA client initialized on first use
|
* The modubus master connector
|
||||||
*/
|
*/
|
||||||
public val master: AbstractModbusMaster
|
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)
|
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 =
|
public fun ModbusDevice.readInputDiscretes(ref: Int, count: Int): BitVector =
|
||||||
master.readInputDiscretes(clientId, ref, count)
|
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> =
|
public fun ModbusDevice.readInputRegisters(ref: Int, count: Int): List<InputRegister> =
|
||||||
master.readInputRegisters(clientId, ref, count).toList()
|
master.readInputRegisters(clientId, ref, count).toList()
|
||||||
|
|
||||||
@ -64,25 +122,42 @@ private fun Array<out InputRegister>.toBuffer(): ByteBuffer {
|
|||||||
return buffer
|
return buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Array<out InputRegister>.toPacket(): ByteReadPacket = buildPacket {
|
||||||
|
forEach { value ->
|
||||||
|
writeShort(value.toShort())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public fun ModbusDevice.readInputRegistersToBuffer(ref: Int, count: Int): ByteBuffer =
|
public fun ModbusDevice.readInputRegistersToBuffer(ref: Int, count: Int): ByteBuffer =
|
||||||
master.readInputRegisters(clientId, ref, count).toBuffer()
|
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 =
|
public fun ModbusDevice.readDoubleInput(ref: Int): Double =
|
||||||
readInputRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble()
|
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()
|
readInputRegisters(ref, 1).first().toShort()
|
||||||
|
|
||||||
public fun ModbusDevice.readHoldingRegisters(ref: Int, count: Int): List<Register> =
|
public fun ModbusDevice.readHoldingRegisters(ref: Int, count: Int): List<Register> =
|
||||||
master.readMultipleRegisters(clientId, ref, count).toList()
|
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 =
|
public fun ModbusDevice.readHoldingRegistersToBuffer(ref: Int, count: Int): ByteBuffer =
|
||||||
master.readMultipleRegisters(clientId, ref, count).toBuffer()
|
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 =
|
public fun ModbusDevice.readDoubleRegister(ref: Int): Double =
|
||||||
readHoldingRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble()
|
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()
|
readHoldingRegisters(ref, 1).first().toShort()
|
||||||
|
|
||||||
public fun ModbusDevice.writeHoldingRegisters(ref: Int, values: ShortArray): Int =
|
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 {
|
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)
|
return writeHoldingRegisters(ref, array)
|
||||||
}
|
}
|
||||||
@ -109,17 +184,17 @@ public fun ModbusDevice.writeShortRegister(ref: Int, value: Short) {
|
|||||||
master.writeSingleRegister(ref, SimpleRegister().apply { setValue(value) })
|
master.writeSingleRegister(ref, SimpleRegister().apply { setValue(value) })
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun ModbusDevice.modBusRegister(
|
public fun ModbusDevice.modbusRegister(
|
||||||
ref: Int,
|
ref: Int,
|
||||||
): ReadWriteProperty<ModbusDevice, Short> = object : ReadWriteProperty<ModbusDevice, Short> {
|
): 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) {
|
override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Short) {
|
||||||
writeHoldingRegister(ref, value)
|
writeHoldingRegister(ref, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun ModbusDevice.modBusDoubleRegister(
|
public fun ModbusDevice.modbusDoubleRegister(
|
||||||
ref: Int,
|
ref: Int,
|
||||||
): ReadWriteProperty<ModbusDevice, Double> = object : ReadWriteProperty<ModbusDevice, Double> {
|
): ReadWriteProperty<ModbusDevice, Double> = object : ReadWriteProperty<ModbusDevice, Double> {
|
||||||
override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Double = readDoubleRegister(ref)
|
override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Double = readDoubleRegister(ref)
|
||||||
@ -129,18 +204,3 @@ public fun ModbusDevice.modBusDoubleRegister(
|
|||||||
writeHoldingRegisters(ref, buffer)
|
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
|
package space.kscience.controls.modbus
|
||||||
|
|
||||||
|
import space.kscience.dataforge.io.IOFormat
|
||||||
|
import space.kscience.dataforge.io.IOReader
|
||||||
|
|
||||||
|
|
||||||
public sealed class ModbusRegistryKey {
|
public sealed class ModbusRegistryKey {
|
||||||
/**
|
/**
|
||||||
@ -20,25 +23,70 @@ public sealed class ModbusRegistryKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read-only binary value
|
||||||
|
*/
|
||||||
public class InputRegister(public val address: Int) : ModbusRegistryKey() {
|
public class InputRegister(public val address: Int) : ModbusRegistryKey() {
|
||||||
init {
|
init {
|
||||||
require(address in 20001..29999) { "InputRegister address must be in 20001..29999 range" }
|
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() {
|
public class HoldingRegister(public val address: Int) : ModbusRegistryKey() {
|
||||||
init {
|
init {
|
||||||
require(address in 30001..39999) { "HoldingRegister address must be in 30001..39999 range" }
|
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 {
|
public abstract class ModbusRegistryMap {
|
||||||
protected fun coil(address: Int): ModbusRegistryKey.Coil = ModbusRegistryKey.Coil(address)
|
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 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 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 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