Add modbus client
This commit is contained in:
parent
2aa26ea802
commit
166cc03fe2
@ -44,25 +44,6 @@ public abstract class DeviceSpec<D : Device> {
|
|||||||
return deviceProperty
|
return deviceProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
// public fun <T> registerProperty(
|
|
||||||
// converter: MetaConverter<T>,
|
|
||||||
// readOnlyProperty: KProperty1<D, T>,
|
|
||||||
// descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
|
||||||
// ): DevicePropertySpec<D, T> {
|
|
||||||
// val deviceProperty = object : DevicePropertySpec<D, T> {
|
|
||||||
//
|
|
||||||
// override val descriptor: PropertyDescriptor = PropertyDescriptor(readOnlyProperty.name)
|
|
||||||
// .apply(descriptorBuilder)
|
|
||||||
//
|
|
||||||
// override val converter: MetaConverter<T> = converter
|
|
||||||
//
|
|
||||||
// override suspend fun read(device: D): T = withContext(device.coroutineContext) {
|
|
||||||
// readOnlyProperty.get(device)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return registerProperty(deviceProperty)
|
|
||||||
// }
|
|
||||||
|
|
||||||
public fun <T> property(
|
public fun <T> property(
|
||||||
converter: MetaConverter<T>,
|
converter: MetaConverter<T>,
|
||||||
readOnlyProperty: KProperty1<D, T>,
|
readOnlyProperty: KProperty1<D, T>,
|
||||||
@ -96,7 +77,7 @@ public abstract class DeviceSpec<D : Device> {
|
|||||||
val deviceProperty = object : WritableDevicePropertySpec<D, T> {
|
val deviceProperty = object : WritableDevicePropertySpec<D, T> {
|
||||||
|
|
||||||
override val descriptor: PropertyDescriptor = PropertyDescriptor(property.name).apply {
|
override val descriptor: PropertyDescriptor = PropertyDescriptor(property.name).apply {
|
||||||
//TODO add type from converter
|
//TODO add the type from converter
|
||||||
writable = true
|
writable = true
|
||||||
}.apply(descriptorBuilder)
|
}.apply(descriptorBuilder)
|
||||||
|
|
||||||
|
@ -0,0 +1,145 @@
|
|||||||
|
package space.kscience.controls.modbus
|
||||||
|
|
||||||
|
import com.ghgande.j2mod.modbus.procimg.*
|
||||||
|
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.spec.DevicePropertySpec
|
||||||
|
import space.kscience.controls.spec.WritableDevicePropertySpec
|
||||||
|
import space.kscience.controls.spec.set
|
||||||
|
import space.kscience.controls.spec.useProperty
|
||||||
|
|
||||||
|
|
||||||
|
public class DeviceToModbusMapping<D : Device> private constructor(
|
||||||
|
private val mapping: Map<DevicePropertySpec<D, *>, ModbusRegistryKey>,
|
||||||
|
) : Map<DevicePropertySpec<D, *>, ModbusRegistryKey> by mapping {
|
||||||
|
public class Builder<D : Device> {
|
||||||
|
private val mapping = HashMap<DevicePropertySpec<D, *>, ModbusRegistryKey>()
|
||||||
|
|
||||||
|
public fun bind(propertySpec: DevicePropertySpec<D, Boolean>, key: ModbusRegistryKey.DiscreteInput) {
|
||||||
|
mapping[propertySpec] = key
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun bind(propertySpec: WritableDevicePropertySpec<D, Boolean>, key: ModbusRegistryKey.Coil) {
|
||||||
|
mapping[propertySpec] = key
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun bind(propertySpec: DevicePropertySpec<D, Short>, key: ModbusRegistryKey.InputRegister) {
|
||||||
|
mapping[propertySpec] = key
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun bind(propertySpec: WritableDevicePropertySpec<D, Short>, key: ModbusRegistryKey.HoldingRegister) {
|
||||||
|
mapping[propertySpec] = key
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T> bind(propertySpec: DevicePropertySpec<D, T>, key: ModbusRegistryKey.InputRange<T>) {
|
||||||
|
mapping[propertySpec] = key
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T> bind(propertySpec: WritableDevicePropertySpec<D, T>, key: ModbusRegistryKey.HoldingRange<T>) {
|
||||||
|
mapping[propertySpec] = key
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun build(): DeviceToModbusMapping<D> = DeviceToModbusMapping(mapping)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <D: Device> DeviceToModbusMapping(block: DeviceToModbusMapping.Builder<D>.()->Unit): DeviceToModbusMapping<D> =
|
||||||
|
DeviceToModbusMapping.Builder<D>().apply(block).build()
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
public fun <D : Device> D.toProcessImage(mapping: DeviceToModbusMapping<D>): ProcessImage {
|
||||||
|
val image = SimpleProcessImage()
|
||||||
|
mapping.forEach { (spec, key) ->
|
||||||
|
when (key) {
|
||||||
|
is ModbusRegistryKey.Coil -> {
|
||||||
|
spec as WritableDevicePropertySpec<D, Boolean>
|
||||||
|
val coil = ObservableDigitalOut()
|
||||||
|
coil.addObserver { _, _ ->
|
||||||
|
set(spec, coil.isSet)
|
||||||
|
}
|
||||||
|
image.setDigitalOut(key.address, coil)
|
||||||
|
useProperty(spec) { value ->
|
||||||
|
coil.set(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is ModbusRegistryKey.DiscreteInput -> {
|
||||||
|
spec as DevicePropertySpec<D, Boolean>
|
||||||
|
val input = SimpleDigitalIn()
|
||||||
|
image.setDigitalIn(key.address, input)
|
||||||
|
useProperty(spec) { value ->
|
||||||
|
input.set(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is ModbusRegistryKey.HoldingRegister -> {
|
||||||
|
spec as WritableDevicePropertySpec<D, Short>
|
||||||
|
val register = ObservableRegister()
|
||||||
|
register.addObserver { _, _ ->
|
||||||
|
set(spec, register.toShort())
|
||||||
|
}
|
||||||
|
image.setRegister(key.address, register)
|
||||||
|
useProperty(spec) { value ->
|
||||||
|
register.setValue(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is ModbusRegistryKey.InputRegister -> {
|
||||||
|
spec as DevicePropertySpec<D, Short>
|
||||||
|
val input = SimpleInputRegister()
|
||||||
|
image.setRegister(key.address, input)
|
||||||
|
useProperty(spec) { value ->
|
||||||
|
input.setValue(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is ModbusRegistryKey.HoldingRange<*> -> {
|
||||||
|
spec as WritableDevicePropertySpec<D, Any?>
|
||||||
|
key as ModbusRegistryKey.HoldingRange<Any?>
|
||||||
|
val registers = List(key.count) {
|
||||||
|
ObservableRegister()
|
||||||
|
}
|
||||||
|
registers.forEachIndexed { index, register ->
|
||||||
|
register.addObserver { _, _ ->
|
||||||
|
val packet = buildPacket {
|
||||||
|
registers.forEach { value ->
|
||||||
|
writeShort(value.toShort())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set(spec, key.format.readObject(packet))
|
||||||
|
}
|
||||||
|
image.setRegister(key.address + index, register)
|
||||||
|
}
|
||||||
|
|
||||||
|
useProperty(spec) { value ->
|
||||||
|
val packet = buildPacket {
|
||||||
|
key.format.writeObject(this, value)
|
||||||
|
}.readByteBuffer()
|
||||||
|
registers.forEachIndexed { index, observableRegister ->
|
||||||
|
observableRegister.setValue(packet.getShort(index * 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is ModbusRegistryKey.InputRange<*> -> {
|
||||||
|
spec as DevicePropertySpec<D, Any?>
|
||||||
|
key as ModbusRegistryKey.InputRange<Any?>
|
||||||
|
val registers = List(key.count) {
|
||||||
|
SimpleInputRegister()
|
||||||
|
}
|
||||||
|
|
||||||
|
useProperty(spec) { value ->
|
||||||
|
val packet = buildPacket {
|
||||||
|
key.format.writeObject(this, value)
|
||||||
|
}.readByteBuffer()
|
||||||
|
registers.forEachIndexed { index, register ->
|
||||||
|
register.setValue(packet.getShort(index * 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return image
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package space.kscience.controls.modbus
|
package space.kscience.controls.modbus
|
||||||
|
|
||||||
import space.kscience.dataforge.io.IOFormat
|
import space.kscience.dataforge.io.IOFormat
|
||||||
import space.kscience.dataforge.io.IOReader
|
|
||||||
|
|
||||||
|
|
||||||
public sealed class ModbusRegistryKey {
|
public sealed class ModbusRegistryKey {
|
||||||
@ -38,7 +37,7 @@ public sealed class ModbusRegistryKey {
|
|||||||
public data class InputRange<T>(
|
public data class InputRange<T>(
|
||||||
override val address: Int,
|
override val address: Int,
|
||||||
override val count: Int,
|
override val count: Int,
|
||||||
public val format: IOReader<T>,
|
public val format: IOFormat<T>,
|
||||||
) : ModbusRegistryKey() {
|
) : ModbusRegistryKey() {
|
||||||
public val endAddress: Int get() = address + count
|
public val endAddress: Int get() = address + count
|
||||||
|
|
||||||
@ -97,7 +96,7 @@ public abstract class ModbusRegistryMap {
|
|||||||
protected fun <T> input(
|
protected fun <T> input(
|
||||||
address: Int,
|
address: Int,
|
||||||
count: Int,
|
count: Int,
|
||||||
reader: IOReader<T>,
|
reader: IOFormat<T>,
|
||||||
description: String = "",
|
description: String = "",
|
||||||
): ModbusRegistryKey.InputRange<T> =
|
): ModbusRegistryKey.InputRange<T> =
|
||||||
register(ModbusRegistryKey.InputRange(address, count, reader), description)
|
register(ModbusRegistryKey.InputRange(address, count, reader), description)
|
||||||
@ -108,7 +107,7 @@ public abstract class ModbusRegistryMap {
|
|||||||
protected fun <T> inputByOffset(
|
protected fun <T> inputByOffset(
|
||||||
offset: Int,
|
offset: Int,
|
||||||
count: Int,
|
count: Int,
|
||||||
reader: IOReader<T>,
|
reader: IOFormat<T>,
|
||||||
description: String = "",
|
description: String = "",
|
||||||
): ModbusRegistryKey.InputRange<T> =
|
): ModbusRegistryKey.InputRange<T> =
|
||||||
register(ModbusRegistryKey.InputRange(20000 + offset, count, reader), description)
|
register(ModbusRegistryKey.InputRange(20000 + offset, count, reader), description)
|
||||||
|
Loading…
Reference in New Issue
Block a user