Modbus revision acording to modern standards
This commit is contained in:
parent
33e1aafa01
commit
20951a0b0f
@ -9,10 +9,21 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import space.kscience.controls.api.Device.Companion.DEVICE_TARGET
|
import space.kscience.controls.api.Device.Companion.DEVICE_TARGET
|
||||||
import space.kscience.dataforge.context.ContextAware
|
import space.kscience.dataforge.context.ContextAware
|
||||||
|
import space.kscience.dataforge.context.info
|
||||||
|
import space.kscience.dataforge.context.logger
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.misc.Type
|
import space.kscience.dataforge.misc.Type
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lifecycle state of a device
|
||||||
|
*/
|
||||||
|
public enum class DeviceLifecycleState{
|
||||||
|
INIT,
|
||||||
|
OPEN,
|
||||||
|
CLOSED
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General interface describing a managed Device.
|
* General interface describing a managed Device.
|
||||||
@ -79,12 +90,16 @@ public interface Device : AutoCloseable, ContextAware, CoroutineScope {
|
|||||||
public suspend fun open(): Unit = Unit
|
public suspend fun open(): Unit = Unit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close and terminate the device. This function does not wait for device to be closed.
|
* Close and terminate the device. This function does not wait for the device to be closed.
|
||||||
*/
|
*/
|
||||||
override fun close() {
|
override fun close() {
|
||||||
|
logger.info { "Device $this is closed" }
|
||||||
cancel("The device is closed")
|
cancel("The device is closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
public val lifecycleState: DeviceLifecycleState
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public const val DEVICE_TARGET: String = "device"
|
public const val DEVICE_TARGET: String = "device"
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
package space.kscience.controls.spec
|
package space.kscience.controls.spec
|
||||||
|
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.SupervisorJob
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import space.kscience.controls.api.*
|
import space.kscience.controls.api.*
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.Global
|
import space.kscience.dataforge.context.error
|
||||||
|
import space.kscience.dataforge.context.logger
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ private suspend fun <D : Device, I, O> DeviceActionSpec<D, I, O>.executeWithMeta
|
|||||||
* A base abstractions for [Device], introducing specifications for properties
|
* A base abstractions for [Device], introducing specifications for properties
|
||||||
*/
|
*/
|
||||||
public abstract class DeviceBase<D : Device>(
|
public abstract class DeviceBase<D : Device>(
|
||||||
override val context: Context = Global,
|
final override val context: Context,
|
||||||
override val meta: Meta = Meta.EMPTY,
|
override val meta: Meta = Meta.EMPTY,
|
||||||
) : Device {
|
) : Device {
|
||||||
|
|
||||||
@ -58,8 +59,15 @@ public abstract class DeviceBase<D : Device>(
|
|||||||
get() = actions.values.map { it.descriptor }
|
get() = actions.values.map { it.descriptor }
|
||||||
|
|
||||||
override val coroutineContext: CoroutineContext by lazy {
|
override val coroutineContext: CoroutineContext by lazy {
|
||||||
context.coroutineContext + SupervisorJob(context.coroutineContext[Job])
|
context.newCoroutineContext(
|
||||||
|
SupervisorJob(context.coroutineContext[Job]) +
|
||||||
|
CoroutineName("Device $this") +
|
||||||
|
CoroutineExceptionHandler { _, throwable ->
|
||||||
|
logger.error(throwable) { "Exception in device $this job" }
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logical state store
|
* Logical state store
|
||||||
@ -149,5 +157,23 @@ public abstract class DeviceBase<D : Device>(
|
|||||||
return spec.executeWithMeta(self, argument ?: Meta.EMPTY)
|
return spec.executeWithMeta(self, argument ?: Meta.EMPTY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
override var lifecycleState: DeviceLifecycleState = DeviceLifecycleState.INIT
|
||||||
|
protected set
|
||||||
|
|
||||||
|
@OptIn(DFExperimental::class)
|
||||||
|
override suspend fun open() {
|
||||||
|
super.open()
|
||||||
|
lifecycleState = DeviceLifecycleState.OPEN
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(DFExperimental::class)
|
||||||
|
override fun close() {
|
||||||
|
lifecycleState = DeviceLifecycleState.CLOSED
|
||||||
|
super.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract override fun toString(): String
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ package space.kscience.controls.spec
|
|||||||
|
|
||||||
import space.kscience.controls.api.Device
|
import space.kscience.controls.api.Device
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.Global
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -11,7 +10,7 @@ import space.kscience.dataforge.meta.Meta
|
|||||||
*/
|
*/
|
||||||
public open class DeviceBySpec<D : Device>(
|
public open class DeviceBySpec<D : Device>(
|
||||||
public val spec: DeviceSpec<in D>,
|
public val spec: DeviceSpec<in D>,
|
||||||
context: Context = Global,
|
context: Context,
|
||||||
meta: Meta = Meta.EMPTY,
|
meta: Meta = Meta.EMPTY,
|
||||||
) : DeviceBase<D>(context, meta) {
|
) : DeviceBase<D>(context, meta) {
|
||||||
override val properties: Map<String, DevicePropertySpec<D, *>> get() = spec.properties
|
override val properties: Map<String, DevicePropertySpec<D, *>> get() = spec.properties
|
||||||
@ -26,4 +25,6 @@ public open class DeviceBySpec<D : Device>(
|
|||||||
self.onClose()
|
self.onClose()
|
||||||
super.close()
|
super.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = "Device(spec=$spec)"
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ import com.ghgande.j2mod.modbus.procimg.*
|
|||||||
import io.ktor.utils.io.core.buildPacket
|
import io.ktor.utils.io.core.buildPacket
|
||||||
import io.ktor.utils.io.core.readByteBuffer
|
import io.ktor.utils.io.core.readByteBuffer
|
||||||
import io.ktor.utils.io.core.writeShort
|
import io.ktor.utils.io.core.writeShort
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import space.kscience.controls.api.Device
|
import space.kscience.controls.api.Device
|
||||||
import space.kscience.controls.spec.DevicePropertySpec
|
import space.kscience.controls.spec.DevicePropertySpec
|
||||||
import space.kscience.controls.spec.WritableDevicePropertySpec
|
import space.kscience.controls.spec.WritableDevicePropertySpec
|
||||||
@ -11,93 +12,113 @@ import space.kscience.controls.spec.set
|
|||||||
import space.kscience.controls.spec.useProperty
|
import space.kscience.controls.spec.useProperty
|
||||||
|
|
||||||
|
|
||||||
public class DeviceToModbusMapping<D : Device> private constructor(
|
public class DeviceProcessImageBuilder<D : Device>(
|
||||||
private val mapping: Map<DevicePropertySpec<D, *>, ModbusRegistryKey>,
|
private val device: D,
|
||||||
) : Map<DevicePropertySpec<D, *>, ModbusRegistryKey> by mapping {
|
public val image: ProcessImageImplementation,
|
||||||
public class Builder<D : Device> {
|
) {
|
||||||
private val mapping = HashMap<DevicePropertySpec<D, *>, ModbusRegistryKey>()
|
|
||||||
|
|
||||||
public fun bind(propertySpec: DevicePropertySpec<D, Boolean>, key: ModbusRegistryKey.DiscreteInput) {
|
public fun bind(
|
||||||
mapping[propertySpec] = key
|
key: ModbusRegistryKey.Coil,
|
||||||
}
|
block: D.(ObservableDigitalOut) -> Unit = {},
|
||||||
|
): ObservableDigitalOut {
|
||||||
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 inline 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()
|
val coil = ObservableDigitalOut()
|
||||||
coil.addObserver { _, _ ->
|
device.block(coil)
|
||||||
set(spec, coil.isSet)
|
image.addDigitalOut(key.address, coil)
|
||||||
|
return coil
|
||||||
}
|
}
|
||||||
image.setDigitalOut(key.address, coil)
|
|
||||||
useProperty(spec) { value ->
|
public fun bind(
|
||||||
|
key: ModbusRegistryKey.Coil,
|
||||||
|
propertySpec: WritableDevicePropertySpec<D, Boolean>,
|
||||||
|
): ObservableDigitalOut = bind(key) { coil ->
|
||||||
|
coil.addObserver { _, _ ->
|
||||||
|
device[propertySpec] = coil.isSet
|
||||||
|
}
|
||||||
|
device.useProperty(propertySpec) { value ->
|
||||||
coil.set(value)
|
coil.set(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ModbusRegistryKey.DiscreteInput -> {
|
public fun bind(
|
||||||
spec as DevicePropertySpec<D, Boolean>
|
key: ModbusRegistryKey.DiscreteInput,
|
||||||
|
block: D.(SimpleDigitalIn) -> Unit = {},
|
||||||
|
): DigitalIn {
|
||||||
val input = SimpleDigitalIn()
|
val input = SimpleDigitalIn()
|
||||||
image.setDigitalIn(key.address, input)
|
device.block(input)
|
||||||
useProperty(spec) { value ->
|
image.addDigitalIn(key.address, input)
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun bind(
|
||||||
|
key: ModbusRegistryKey.DiscreteInput,
|
||||||
|
propertySpec: DevicePropertySpec<D, Boolean>,
|
||||||
|
): DigitalIn = bind(key) { input ->
|
||||||
|
device.useProperty(propertySpec) { value ->
|
||||||
input.set(value)
|
input.set(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ModbusRegistryKey.HoldingRegister -> {
|
public fun bind(
|
||||||
spec as WritableDevicePropertySpec<D, Short>
|
key: ModbusRegistryKey.InputRegister,
|
||||||
val register = ObservableRegister()
|
block: D.(SimpleInputRegister) -> Unit = {},
|
||||||
register.addObserver { _, _ ->
|
): SimpleInputRegister {
|
||||||
set(spec, register.toShort())
|
val input = SimpleInputRegister()
|
||||||
}
|
device.block(input)
|
||||||
image.setRegister(key.address, register)
|
image.addInputRegister(key.address, input)
|
||||||
useProperty(spec) { value ->
|
return input
|
||||||
register.setValue(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is ModbusRegistryKey.InputRegister -> {
|
public fun bind(
|
||||||
spec as DevicePropertySpec<D, Short>
|
key: ModbusRegistryKey.InputRegister,
|
||||||
val input = SimpleInputRegister()
|
propertySpec: DevicePropertySpec<D, Short>,
|
||||||
image.setRegister(key.address, input)
|
): SimpleInputRegister = bind(key) { input ->
|
||||||
useProperty(spec) { value ->
|
device.useProperty(propertySpec) { value ->
|
||||||
input.setValue(value)
|
input.setValue(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ModbusRegistryKey.HoldingRange<*> -> {
|
public fun bind(
|
||||||
spec as WritableDevicePropertySpec<D, Any?>
|
key: ModbusRegistryKey.HoldingRegister,
|
||||||
key as ModbusRegistryKey.HoldingRange<Any?>
|
block: D.(ObservableRegister) -> Unit = {},
|
||||||
|
): ObservableRegister {
|
||||||
|
val register = ObservableRegister()
|
||||||
|
device.block(register)
|
||||||
|
image.addRegister(key.address, register)
|
||||||
|
return register
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun bind(
|
||||||
|
key: ModbusRegistryKey.HoldingRegister,
|
||||||
|
propertySpec: WritableDevicePropertySpec<D, Short>,
|
||||||
|
): ObservableRegister = bind(key) { register ->
|
||||||
|
register.addObserver { _, _ ->
|
||||||
|
device[propertySpec] = register.toShort()
|
||||||
|
}
|
||||||
|
device.useProperty(propertySpec) { value ->
|
||||||
|
register.setValue(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T> bind(key: ModbusRegistryKey.InputRange<T>, propertySpec: DevicePropertySpec<D, T>) {
|
||||||
|
val registers = List(key.count) {
|
||||||
|
SimpleInputRegister()
|
||||||
|
}
|
||||||
|
|
||||||
|
registers.forEachIndexed { index, register ->
|
||||||
|
image.addInputRegister(key.address + index, register)
|
||||||
|
}
|
||||||
|
|
||||||
|
device.useProperty(propertySpec) { value ->
|
||||||
|
val packet = buildPacket {
|
||||||
|
key.format.writeObject(this, value)
|
||||||
|
}.readByteBuffer()
|
||||||
|
registers.forEachIndexed { index, register ->
|
||||||
|
register.setValue(packet.getShort(index * 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T> bind(key: ModbusRegistryKey.HoldingRange<T>, propertySpec: WritableDevicePropertySpec<D, T>) {
|
||||||
val registers = List(key.count) {
|
val registers = List(key.count) {
|
||||||
ObservableRegister()
|
ObservableRegister()
|
||||||
}
|
}
|
||||||
@ -108,12 +129,12 @@ public fun <D : Device> D.toProcessImage(mapping: DeviceToModbusMapping<D>): Pro
|
|||||||
writeShort(value.toShort())
|
writeShort(value.toShort())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set(spec, key.format.readObject(packet))
|
device[propertySpec] = key.format.readObject(packet)
|
||||||
}
|
}
|
||||||
image.setRegister(key.address + index, register)
|
image.addRegister(key.address + index, register)
|
||||||
}
|
}
|
||||||
|
|
||||||
useProperty(spec) { value ->
|
device.useProperty(propertySpec) { value ->
|
||||||
val packet = buildPacket {
|
val packet = buildPacket {
|
||||||
key.format.writeObject(this, value)
|
key.format.writeObject(this, value)
|
||||||
}.readByteBuffer()
|
}.readByteBuffer()
|
||||||
@ -123,26 +144,74 @@ public fun <D : Device> D.toProcessImage(mapping: DeviceToModbusMapping<D>): Pro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ModbusRegistryKey.InputRange<*> -> {
|
public fun bindAction(
|
||||||
spec as DevicePropertySpec<D, Any?>
|
key: ModbusRegistryKey.Coil,
|
||||||
key as ModbusRegistryKey.InputRange<Any?>
|
action: suspend D.(Boolean) -> Unit,
|
||||||
val registers = List(key.count) {
|
): ObservableDigitalOut {
|
||||||
SimpleInputRegister()
|
val coil = ObservableDigitalOut()
|
||||||
|
coil.addObserver { _, _ ->
|
||||||
|
device.launch {
|
||||||
|
device.action(coil.isSet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
image.addDigitalOut(key.address, coil)
|
||||||
|
return coil
|
||||||
}
|
}
|
||||||
|
|
||||||
useProperty(spec) { value ->
|
public fun bindAction(
|
||||||
val packet = buildPacket {
|
key: ModbusRegistryKey.HoldingRegister,
|
||||||
key.format.writeObject(this, value)
|
action: suspend D.(Short) -> Unit,
|
||||||
}.readByteBuffer()
|
): ObservableRegister {
|
||||||
|
val register = ObservableRegister()
|
||||||
|
register.addObserver { _, _ ->
|
||||||
|
|
||||||
|
with(device) {
|
||||||
|
launch {
|
||||||
|
action(register.toShort())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
image.addRegister(key.address, register)
|
||||||
|
return register
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T> bindAction(
|
||||||
|
key: ModbusRegistryKey.HoldingRange<T>,
|
||||||
|
action: suspend D.(T) -> Unit,
|
||||||
|
): List<ObservableRegister> {
|
||||||
|
val registers = List(key.count) {
|
||||||
|
ObservableRegister()
|
||||||
|
}
|
||||||
registers.forEachIndexed { index, register ->
|
registers.forEachIndexed { index, register ->
|
||||||
register.setValue(packet.getShort(index * 2))
|
register.addObserver { _, _ ->
|
||||||
|
val packet = buildPacket {
|
||||||
|
registers.forEach { value ->
|
||||||
|
writeShort(value.toShort())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
device.launch {
|
||||||
|
device.action(key.format.readObject(packet))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
image.addRegister(key.address + index, register)
|
||||||
|
}
|
||||||
|
|
||||||
|
return registers
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public fun <D : Device> D.bindProcessImage(
|
||||||
|
openOnBind: Boolean = true,
|
||||||
|
binding: DeviceProcessImageBuilder<D>.() -> Unit,
|
||||||
|
): ProcessImage {
|
||||||
|
val image = SimpleProcessImage()
|
||||||
|
DeviceProcessImageBuilder(this, image).apply(binding)
|
||||||
|
if (openOnBind) {
|
||||||
|
launch {
|
||||||
|
open()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline fun <D : Device> D.toProcessImage(block: DeviceToModbusMapping.Builder<D>.()->Unit): ProcessImage =
|
|
||||||
toProcessImage(DeviceToModbusMapping(block))
|
|
@ -3,7 +3,7 @@ package space.kscience.controls.modbus
|
|||||||
import com.ghgande.j2mod.modbus.facade.AbstractModbusMaster
|
import com.ghgande.j2mod.modbus.facade.AbstractModbusMaster
|
||||||
import com.ghgande.j2mod.modbus.procimg.InputRegister
|
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.SimpleInputRegister
|
||||||
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.ByteReadPacket
|
||||||
import io.ktor.utils.io.core.buildPacket
|
import io.ktor.utils.io.core.buildPacket
|
||||||
@ -73,7 +73,7 @@ public interface ModbusDevice : Device {
|
|||||||
val buffer = buildPacket {
|
val buffer = buildPacket {
|
||||||
format.writeObject(this, value)
|
format.writeObject(this, value)
|
||||||
}.readByteBuffer()
|
}.readByteBuffer()
|
||||||
writeHoldingRegisters(address,buffer)
|
writeHoldingRegisters(address, buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -81,36 +81,36 @@ public interface ModbusDevice : Device {
|
|||||||
/**
|
/**
|
||||||
* Read multiple sequential modbus coils (bit-values)
|
* Read multiple sequential modbus coils (bit-values)
|
||||||
*/
|
*/
|
||||||
public fun ModbusDevice.readCoils(ref: Int, count: Int): BitVector =
|
public fun ModbusDevice.readCoils(address: Int, count: Int): BitVector =
|
||||||
master.readCoils(clientId, ref, count)
|
master.readCoils(clientId, address, count)
|
||||||
|
|
||||||
public fun ModbusDevice.readCoil(ref: Int): Boolean =
|
public fun ModbusDevice.readCoil(address: Int): Boolean =
|
||||||
master.readCoils(clientId, ref, 1).getBit(0)
|
master.readCoils(clientId, address, 1).getBit(0)
|
||||||
|
|
||||||
public fun ModbusDevice.writeCoils(ref: Int, values: BooleanArray) {
|
public fun ModbusDevice.writeCoils(address: Int, values: BooleanArray) {
|
||||||
val bitVector = BitVector(values.size)
|
val bitVector = BitVector(values.size)
|
||||||
values.forEachIndexed { index, value ->
|
values.forEachIndexed { index, value ->
|
||||||
bitVector.setBit(index, value)
|
bitVector.setBit(index, value)
|
||||||
}
|
}
|
||||||
master.writeMultipleCoils(clientId, ref, bitVector)
|
master.writeMultipleCoils(clientId, address, bitVector)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun ModbusDevice.writeCoil(ref: Int, value: Boolean) {
|
public fun ModbusDevice.writeCoil(address: Int, value: Boolean) {
|
||||||
master.writeCoil(clientId, ref, value)
|
master.writeCoil(clientId, address, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun ModbusDevice.writeCoil(key: ModbusRegistryKey.Coil, value: Boolean) {
|
public fun ModbusDevice.writeCoil(key: ModbusRegistryKey.Coil, value: Boolean) {
|
||||||
master.writeCoil(clientId, key.address, value)
|
master.writeCoil(clientId, key.address, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun ModbusDevice.readInputDiscretes(ref: Int, count: Int): BitVector =
|
public fun ModbusDevice.readInputDiscretes(address: Int, count: Int): BitVector =
|
||||||
master.readInputDiscretes(clientId, ref, count)
|
master.readInputDiscretes(clientId, address, count)
|
||||||
|
|
||||||
public fun ModbusDevice.readInputDiscrete(ref: Int): Boolean =
|
public fun ModbusDevice.readInputDiscrete(address: Int): Boolean =
|
||||||
master.readInputDiscretes(clientId, ref, 1).getBit(0)
|
master.readInputDiscretes(clientId, address, 1).getBit(0)
|
||||||
|
|
||||||
public fun ModbusDevice.readInputRegisters(ref: Int, count: Int): List<InputRegister> =
|
public fun ModbusDevice.readInputRegisters(address: Int, count: Int): List<InputRegister> =
|
||||||
master.readInputRegisters(clientId, ref, count).toList()
|
master.readInputRegisters(clientId, address, count).toList()
|
||||||
|
|
||||||
private fun Array<out InputRegister>.toBuffer(): ByteBuffer {
|
private fun Array<out InputRegister>.toBuffer(): ByteBuffer {
|
||||||
val buffer: ByteBuffer = ByteBuffer.allocate(size * 2)
|
val buffer: ByteBuffer = ByteBuffer.allocate(size * 2)
|
||||||
@ -128,79 +128,82 @@ private fun Array<out InputRegister>.toPacket(): ByteReadPacket = buildPacket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun ModbusDevice.readInputRegistersToBuffer(ref: Int, count: Int): ByteBuffer =
|
public fun ModbusDevice.readInputRegistersToBuffer(address: Int, count: Int): ByteBuffer =
|
||||||
master.readInputRegisters(clientId, ref, count).toBuffer()
|
master.readInputRegisters(clientId, address, count).toBuffer()
|
||||||
|
|
||||||
public fun ModbusDevice.readInputRegistersToPacket(ref: Int, count: Int): ByteReadPacket =
|
public fun ModbusDevice.readInputRegistersToPacket(address: Int, count: Int): ByteReadPacket =
|
||||||
master.readInputRegisters(clientId, ref, count).toPacket()
|
master.readInputRegisters(clientId, address, count).toPacket()
|
||||||
|
|
||||||
public fun ModbusDevice.readDoubleInput(ref: Int): Double =
|
public fun ModbusDevice.readDoubleInput(address: Int): Double =
|
||||||
readInputRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble()
|
readInputRegistersToBuffer(address, Double.SIZE_BYTES).getDouble()
|
||||||
|
|
||||||
public fun ModbusDevice.readInputRegister(ref: Int): Short =
|
public fun ModbusDevice.readInputRegister(address: Int): Short =
|
||||||
readInputRegisters(ref, 1).first().toShort()
|
readInputRegisters(address, 1).first().toShort()
|
||||||
|
|
||||||
public fun ModbusDevice.readHoldingRegisters(ref: Int, count: Int): List<Register> =
|
public fun ModbusDevice.readHoldingRegisters(address: Int, count: Int): List<Register> =
|
||||||
master.readMultipleRegisters(clientId, ref, count).toList()
|
master.readMultipleRegisters(clientId, address, count).toList()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a number of registers to a [ByteBuffer]
|
* Read a number of registers to a [ByteBuffer]
|
||||||
* @param ref address of a register
|
* @param address of a register
|
||||||
* @param count number of 2-bytes registers to read. Buffer size is 2*[count]
|
* @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(address: Int, count: Int): ByteBuffer =
|
||||||
master.readMultipleRegisters(clientId, ref, count).toBuffer()
|
master.readMultipleRegisters(clientId, address, count).toBuffer()
|
||||||
|
|
||||||
public fun ModbusDevice.readHoldingRegistersToPacket(ref: Int, count: Int): ByteReadPacket =
|
public fun ModbusDevice.readHoldingRegistersToPacket(address: Int, count: Int): ByteReadPacket =
|
||||||
master.readMultipleRegisters(clientId, ref, count).toPacket()
|
master.readMultipleRegisters(clientId, address, count).toPacket()
|
||||||
|
|
||||||
public fun ModbusDevice.readDoubleRegister(ref: Int): Double =
|
public fun ModbusDevice.readDoubleRegister(address: Int): Double =
|
||||||
readHoldingRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble()
|
readHoldingRegistersToBuffer(address, Double.SIZE_BYTES).getDouble()
|
||||||
|
|
||||||
public fun ModbusDevice.readHoldingRegister(ref: Int): Short =
|
public fun ModbusDevice.readHoldingRegister(address: Int): Short =
|
||||||
readHoldingRegisters(ref, 1).first().toShort()
|
readHoldingRegisters(address, 1).first().toShort()
|
||||||
|
|
||||||
public fun ModbusDevice.writeHoldingRegisters(ref: Int, values: ShortArray): Int =
|
public fun ModbusDevice.writeHoldingRegisters(address: Int, values: ShortArray): Int =
|
||||||
master.writeMultipleRegisters(
|
master.writeMultipleRegisters(
|
||||||
clientId,
|
clientId,
|
||||||
ref,
|
address,
|
||||||
Array<Register>(values.size) { SimpleRegister().apply { setValue(values[it]) } }
|
Array<Register>(values.size) { SimpleInputRegister(values[it].toInt()) }
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun ModbusDevice.writeHoldingRegister(ref: Int, value: Short): Int =
|
public fun ModbusDevice.writeHoldingRegister(address: Int, value: Short): Int =
|
||||||
master.writeSingleRegister(
|
master.writeSingleRegister(
|
||||||
clientId,
|
clientId,
|
||||||
ref,
|
address,
|
||||||
SimpleRegister().apply { setValue(value) }
|
SimpleInputRegister(value.toInt())
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun ModbusDevice.writeHoldingRegisters(ref: Int, buffer: ByteBuffer): Int {
|
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) }
|
val array: ShortArray = ShortArray(buffer.limit().floorDiv(2)) { buffer.getShort(it * 2) }
|
||||||
|
|
||||||
return writeHoldingRegisters(ref, array)
|
return writeHoldingRegisters(address, array)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun ModbusDevice.writeShortRegister(ref: Int, value: Short) {
|
public fun ModbusDevice.writeShortRegister(address: Int, value: Short) {
|
||||||
master.writeSingleRegister(ref, SimpleRegister().apply { setValue(value) })
|
master.writeSingleRegister(address, SimpleInputRegister(value.toInt()))
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun ModbusDevice.modbusRegister(
|
public fun ModbusDevice.modbusRegister(
|
||||||
ref: Int,
|
address: Int,
|
||||||
): ReadWriteProperty<ModbusDevice, Short> = object : ReadWriteProperty<ModbusDevice, Short> {
|
): ReadWriteProperty<ModbusDevice, Short> = object : ReadWriteProperty<ModbusDevice, Short> {
|
||||||
override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Short = readHoldingRegister(ref)
|
override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Short = readHoldingRegister(address)
|
||||||
|
|
||||||
override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Short) {
|
override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Short) {
|
||||||
writeHoldingRegister(ref, value)
|
writeHoldingRegister(address, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun ModbusDevice.modbusDoubleRegister(
|
public fun ModbusDevice.modbusDoubleRegister(
|
||||||
ref: Int,
|
address: 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(address)
|
||||||
|
|
||||||
override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Double) {
|
override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Double) {
|
||||||
val buffer = ByteBuffer.allocate(Double.SIZE_BYTES).apply { putDouble(value) }
|
val buffer = ByteBuffer.allocate(Double.SIZE_BYTES).apply { putDouble(value) }
|
||||||
writeHoldingRegisters(ref, buffer)
|
writeHoldingRegisters(address, buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,21 @@ public open class ModbusDeviceBySpec<D: Device>(
|
|||||||
spec: DeviceSpec<D>,
|
spec: DeviceSpec<D>,
|
||||||
override val clientId: Int,
|
override val clientId: Int,
|
||||||
override val master: AbstractModbusMaster,
|
override val master: AbstractModbusMaster,
|
||||||
|
private val disposeMasterOnClose: Boolean = true,
|
||||||
meta: Meta = Meta.EMPTY,
|
meta: Meta = Meta.EMPTY,
|
||||||
) : ModbusDevice, DeviceBySpec<D>(spec, context, meta)
|
) : ModbusDevice, DeviceBySpec<D>(spec, context, meta){
|
||||||
|
override suspend fun open() {
|
||||||
|
master.connect()
|
||||||
|
super<ModbusDevice>.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
if(disposeMasterOnClose){
|
||||||
|
master.disconnect()
|
||||||
|
}
|
||||||
|
super<ModbusDevice>.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public class ModbusHub(
|
public class ModbusHub(
|
||||||
|
@ -7,63 +7,48 @@ public sealed class ModbusRegistryKey {
|
|||||||
public abstract val address: Int
|
public abstract val address: Int
|
||||||
public open val count: Int = 1
|
public open val count: Int = 1
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read-only boolean value
|
* Read-only boolean value
|
||||||
*/
|
*/
|
||||||
public data class Coil(override val address: Int) : ModbusRegistryKey() {
|
public data class Coil(override val address: Int) : ModbusRegistryKey()
|
||||||
init {
|
|
||||||
require(address in 1..9999) { "Coil address must be in 1..9999 range" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read-write boolean value
|
* Read-write boolean value
|
||||||
*/
|
*/
|
||||||
public data class DiscreteInput(override val address: Int) : ModbusRegistryKey() {
|
public data class DiscreteInput(override val address: Int) : ModbusRegistryKey()
|
||||||
init {
|
|
||||||
require(address in 10001..19999) { "DiscreteInput address must be in 10001..19999 range" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read-only binary value
|
* Read-only binary value
|
||||||
*/
|
*/
|
||||||
public data class InputRegister(override val address: Int) : ModbusRegistryKey() {
|
public open class InputRegister(override val address: Int) : ModbusRegistryKey() {
|
||||||
init {
|
override fun toString(): String = "InputRegister(address=$address)"
|
||||||
require(address in 20001..29999) { "InputRegister address must be in 20001..29999 range" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public data class InputRange<T>(
|
public class InputRange<T>(
|
||||||
override val address: Int,
|
address: Int,
|
||||||
override val count: Int,
|
override val count: Int,
|
||||||
public val format: IOFormat<T>,
|
public val format: IOFormat<T>,
|
||||||
) : ModbusRegistryKey() {
|
) : InputRegister(address) {
|
||||||
public val endAddress: Int get() = address + count
|
public val endAddress: Int get() = address + count
|
||||||
|
override fun toString(): String = "InputRange(count=$count, format=$format)"
|
||||||
|
|
||||||
|
|
||||||
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 data class HoldingRegister(override val address: Int) : ModbusRegistryKey() {
|
public open class HoldingRegister(override val address: Int) : ModbusRegistryKey() {
|
||||||
init {
|
override fun toString(): String = "HoldingRegister(address=$address)"
|
||||||
require(address in 30001..39999) { "HoldingRegister address must be in 30001..39999 range" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public data class HoldingRange<T>(
|
public class HoldingRange<T>(
|
||||||
override val address: Int,
|
address: Int,
|
||||||
override val count: Int,
|
override val count: Int,
|
||||||
public val format: IOFormat<T>,
|
public val format: IOFormat<T>,
|
||||||
) : ModbusRegistryKey() {
|
) : HoldingRegister(address) {
|
||||||
public val endAddress: Int get() = address + count
|
public val endAddress: Int get() = address + count
|
||||||
|
override fun toString(): String = "HoldingRange(count=$count, format=$format)"
|
||||||
|
|
||||||
|
|
||||||
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" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,15 +66,10 @@ public abstract class ModbusRegistryMap {
|
|||||||
protected fun coil(address: Int, description: String = ""): ModbusRegistryKey.Coil =
|
protected fun coil(address: Int, description: String = ""): ModbusRegistryKey.Coil =
|
||||||
register(ModbusRegistryKey.Coil(address), description)
|
register(ModbusRegistryKey.Coil(address), description)
|
||||||
|
|
||||||
protected fun coilByOffset(offset: Int, description: String = ""): ModbusRegistryKey.Coil =
|
|
||||||
register(ModbusRegistryKey.Coil(offset), description)
|
|
||||||
|
|
||||||
protected fun discrete(address: Int, description: String = ""): ModbusRegistryKey.DiscreteInput =
|
protected fun discrete(address: Int, description: String = ""): ModbusRegistryKey.DiscreteInput =
|
||||||
register(ModbusRegistryKey.DiscreteInput(address), description)
|
register(ModbusRegistryKey.DiscreteInput(address), description)
|
||||||
|
|
||||||
protected fun discreteByOffset(offset: Int, description: String = ""): ModbusRegistryKey.DiscreteInput =
|
|
||||||
register(ModbusRegistryKey.DiscreteInput(10000 + offset), description)
|
|
||||||
|
|
||||||
protected fun input(address: Int, description: String = ""): ModbusRegistryKey.InputRegister =
|
protected fun input(address: Int, description: String = ""): ModbusRegistryKey.InputRegister =
|
||||||
register(ModbusRegistryKey.InputRegister(address), description)
|
register(ModbusRegistryKey.InputRegister(address), description)
|
||||||
|
|
||||||
@ -101,17 +81,6 @@ public abstract class ModbusRegistryMap {
|
|||||||
): ModbusRegistryKey.InputRange<T> =
|
): ModbusRegistryKey.InputRange<T> =
|
||||||
register(ModbusRegistryKey.InputRange(address, count, reader), description)
|
register(ModbusRegistryKey.InputRange(address, count, reader), description)
|
||||||
|
|
||||||
protected fun inputByOffset(offset: Int, description: String = ""): ModbusRegistryKey.InputRegister =
|
|
||||||
register(ModbusRegistryKey.InputRegister(20000 + offset), description)
|
|
||||||
|
|
||||||
protected fun <T> inputByOffset(
|
|
||||||
offset: Int,
|
|
||||||
count: Int,
|
|
||||||
reader: IOFormat<T>,
|
|
||||||
description: String = "",
|
|
||||||
): ModbusRegistryKey.InputRange<T> =
|
|
||||||
register(ModbusRegistryKey.InputRange(20000 + offset, count, reader), description)
|
|
||||||
|
|
||||||
protected fun register(address: Int, description: String = ""): ModbusRegistryKey.HoldingRegister =
|
protected fun register(address: Int, description: String = ""): ModbusRegistryKey.HoldingRegister =
|
||||||
register(ModbusRegistryKey.HoldingRegister(address), description)
|
register(ModbusRegistryKey.HoldingRegister(address), description)
|
||||||
|
|
||||||
@ -123,32 +92,62 @@ public abstract class ModbusRegistryMap {
|
|||||||
): ModbusRegistryKey.HoldingRange<T> =
|
): ModbusRegistryKey.HoldingRange<T> =
|
||||||
register(ModbusRegistryKey.HoldingRange(address, count, format), description)
|
register(ModbusRegistryKey.HoldingRange(address, count, format), description)
|
||||||
|
|
||||||
protected fun registerByOffset(offset: Int, description: String = ""): ModbusRegistryKey.HoldingRegister =
|
|
||||||
register(ModbusRegistryKey.HoldingRegister(30000 + offset), description)
|
|
||||||
|
|
||||||
protected fun <T> registerByOffset(
|
|
||||||
offset: Int,
|
|
||||||
count: Int,
|
|
||||||
format: IOFormat<T>,
|
|
||||||
description: String = "",
|
|
||||||
): ModbusRegistryKey.HoldingRange<T> =
|
|
||||||
register(ModbusRegistryKey.HoldingRange(offset + 30000, count, format), description)
|
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public fun validate(map: ModbusRegistryMap) {
|
public fun validate(map: ModbusRegistryMap) {
|
||||||
map.entries.keys.sortedBy { it.address }.zipWithNext().forEach { (l, r) ->
|
var lastCoil: ModbusRegistryKey.Coil? = null
|
||||||
if (l.address + l.count > r.address) error("Key $l overlaps with key $r")
|
var lastDiscreteInput: ModbusRegistryKey.DiscreteInput? = null
|
||||||
|
var lastInput: ModbusRegistryKey.InputRegister? = null
|
||||||
|
var lastRegister: ModbusRegistryKey.HoldingRegister? = null
|
||||||
|
map.entries.keys.sortedBy { it.address }.forEach { key ->
|
||||||
|
when (key) {
|
||||||
|
is ModbusRegistryKey.Coil -> if (lastCoil?.let { key.address >= it.address + it.count } != false) {
|
||||||
|
lastCoil = key
|
||||||
|
} else {
|
||||||
|
error("Key $lastCoil overlaps with key $key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ModbusRegistryKey.DiscreteInput -> if (lastDiscreteInput?.let { key.address >= it.address + it.count } != false) {
|
||||||
|
lastDiscreteInput = key
|
||||||
|
} else {
|
||||||
|
error("Key $lastDiscreteInput overlaps with key $key")
|
||||||
|
}
|
||||||
|
|
||||||
|
is ModbusRegistryKey.InputRegister -> if (lastInput?.let { key.address >= it.address + it.count } != false) {
|
||||||
|
lastInput = key
|
||||||
|
} else {
|
||||||
|
error("Key $lastInput overlaps with key $key")
|
||||||
|
}
|
||||||
|
|
||||||
|
is ModbusRegistryKey.HoldingRegister -> if (lastRegister?.let { key.address >= it.address + it.count } != false) {
|
||||||
|
lastRegister = key
|
||||||
|
} else {
|
||||||
|
error("Key $lastRegister overlaps with key $key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ModbusRegistryKey.sectionNumber
|
||||||
|
get() = when (this) {
|
||||||
|
is ModbusRegistryKey.Coil -> 1
|
||||||
|
is ModbusRegistryKey.DiscreteInput -> 2
|
||||||
|
is ModbusRegistryKey.HoldingRegister -> 4
|
||||||
|
is ModbusRegistryKey.InputRegister -> 3
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun print(map: ModbusRegistryMap, to: Appendable = System.out) {
|
public fun print(map: ModbusRegistryMap, to: Appendable = System.out) {
|
||||||
validate(map)
|
validate(map)
|
||||||
map.entries.entries.sortedBy { it.key.address }.forEach { (key, description) ->
|
map.entries.entries
|
||||||
|
.sortedWith(
|
||||||
|
Comparator.comparingInt<Map.Entry<ModbusRegistryKey, String>?> { it.key.sectionNumber }
|
||||||
|
.thenComparingInt { it.key.address }
|
||||||
|
)
|
||||||
|
.forEach { (key, description) ->
|
||||||
val typeString = when (key) {
|
val typeString = when (key) {
|
||||||
is ModbusRegistryKey.Coil -> "Coil"
|
is ModbusRegistryKey.Coil -> "Coil"
|
||||||
is ModbusRegistryKey.DiscreteInput -> "Discrete"
|
is ModbusRegistryKey.DiscreteInput -> "Discrete"
|
||||||
is ModbusRegistryKey.HoldingRange<*>, is ModbusRegistryKey.HoldingRegister -> "Register"
|
is ModbusRegistryKey.HoldingRegister -> "Register"
|
||||||
is ModbusRegistryKey.InputRange<*>, is ModbusRegistryKey.InputRegister -> "Input"
|
is ModbusRegistryKey.InputRegister -> "Input"
|
||||||
}
|
}
|
||||||
val rangeString = if (key.count == 1) {
|
val rangeString = if (key.count == 1) {
|
||||||
key.address.toString()
|
key.address.toString()
|
||||||
|
Loading…
Reference in New Issue
Block a user