Modbus registry validation and print

This commit is contained in:
Alexander Nozik 2023-05-21 17:36:26 +03:00
parent b8a82feed0
commit 0612c6e3a2
2 changed files with 99 additions and 25 deletions

View File

@ -5,10 +5,13 @@ import space.kscience.dataforge.io.IOReader
public sealed class ModbusRegistryKey { public sealed class ModbusRegistryKey {
public abstract val address: Int
public open val count: Int = 1
/** /**
* Read-only boolean value * Read-only boolean value
*/ */
public class Coil(public val address: Int) : ModbusRegistryKey() { public data class Coil(override val address: Int) : ModbusRegistryKey() {
init { init {
require(address in 1..9999) { "Coil address must be in 1..9999 range" } require(address in 1..9999) { "Coil address must be in 1..9999 range" }
} }
@ -17,7 +20,7 @@ public sealed class ModbusRegistryKey {
/** /**
* Read-write boolean value * Read-write boolean value
*/ */
public class DiscreteInput(public val address: Int) : ModbusRegistryKey() { public data class DiscreteInput(override val address: Int) : ModbusRegistryKey() {
init { init {
require(address in 10001..19999) { "DiscreteInput address must be in 10001..19999 range" } require(address in 10001..19999) { "DiscreteInput address must be in 10001..19999 range" }
} }
@ -26,13 +29,17 @@ public sealed class ModbusRegistryKey {
/** /**
* Read-only binary value * Read-only binary value
*/ */
public class InputRegister(public val address: Int) : ModbusRegistryKey() { public data class InputRegister(override 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 data class InputRange<T>(
override val address: Int,
override val count: Int,
public val format: IOReader<T>,
) : ModbusRegistryKey() {
public val endAddress: Int get() = address + count public val endAddress: Int get() = address + count
init { init {
@ -41,13 +48,17 @@ public sealed class ModbusRegistryKey {
} }
} }
public class HoldingRegister(public val address: Int) : ModbusRegistryKey() { public data class HoldingRegister(override 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 data class HoldingRange<T>(
override val address: Int,
override val count: Int,
public val format: IOFormat<T>,
) : ModbusRegistryKey() {
public val endAddress: Int get() = address + count public val endAddress: Int get() = address + count
init { init {
@ -58,35 +69,96 @@ public sealed class ModbusRegistryKey {
} }
public abstract class ModbusRegistryMap { public abstract class ModbusRegistryMap {
protected fun coil(address: Int): ModbusRegistryKey.Coil = ModbusRegistryKey.Coil(address)
protected fun coilByOffset(offset: Int): ModbusRegistryKey.Coil = ModbusRegistryKey.Coil(offset) private val _entries: MutableMap<ModbusRegistryKey, String> = mutableMapOf<ModbusRegistryKey, String>()
protected fun discrete(address: Int): ModbusRegistryKey.DiscreteInput = ModbusRegistryKey.DiscreteInput(address) public val entries: Map<ModbusRegistryKey, String> get() = _entries
protected fun discreteByOffset(offset: Int): ModbusRegistryKey.DiscreteInput = protected fun <T : ModbusRegistryKey> register(key: T, description: String): T {
ModbusRegistryKey.DiscreteInput(10000 + offset) _entries[key] = description
return key
}
protected fun input(address: Int): ModbusRegistryKey.InputRegister = ModbusRegistryKey.InputRegister(address) protected fun coil(address: Int, description: String = ""): ModbusRegistryKey.Coil =
register(ModbusRegistryKey.Coil(address), description)
protected fun <T> input(address: Int, count: Int, reader: IOReader<T>): ModbusRegistryKey.InputRange<T> = protected fun coilByOffset(offset: Int, description: String = ""): ModbusRegistryKey.Coil =
ModbusRegistryKey.InputRange(address, count, reader) register(ModbusRegistryKey.Coil(offset), description)
protected fun discrete(address: Int, description: String = ""): ModbusRegistryKey.DiscreteInput =
register(ModbusRegistryKey.DiscreteInput(address), description)
protected fun inputByOffset(offset: Int): ModbusRegistryKey.InputRegister = protected fun discreteByOffset(offset: Int, description: String = ""): ModbusRegistryKey.DiscreteInput =
ModbusRegistryKey.InputRegister(20000 + offset) register(ModbusRegistryKey.DiscreteInput(10000 + offset), description)
protected fun <T> inputByOffset(offset: Int, count: Int, reader: IOReader<T>): ModbusRegistryKey.InputRange<T> = protected fun input(address: Int, description: String = ""): ModbusRegistryKey.InputRegister =
ModbusRegistryKey.InputRange(20000 + offset, count, reader) register(ModbusRegistryKey.InputRegister(address), description)
protected fun register(address: Int): ModbusRegistryKey.HoldingRegister = ModbusRegistryKey.HoldingRegister(address) protected fun <T> input(
address: Int,
count: Int,
reader: IOReader<T>,
description: String = "",
): ModbusRegistryKey.InputRange<T> =
register(ModbusRegistryKey.InputRange(address, count, reader), description)
protected fun <T> register(address: Int, count: Int, format: IOFormat<T>): ModbusRegistryKey.HoldingRange<T> = protected fun inputByOffset(offset: Int, description: String = ""): ModbusRegistryKey.InputRegister =
ModbusRegistryKey.HoldingRange(address, count, format) register(ModbusRegistryKey.InputRegister(20000 + offset), description)
protected fun registerByOffset(offset: Int): ModbusRegistryKey.HoldingRegister = protected fun <T> inputByOffset(
ModbusRegistryKey.HoldingRegister(30000 + offset) offset: Int,
count: Int,
reader: IOReader<T>,
description: String = "",
): ModbusRegistryKey.InputRange<T> =
register(ModbusRegistryKey.InputRange(20000 + offset, count, reader), description)
protected fun <T> registerByOffset(offset: Int, count: Int, format: IOFormat<T>): ModbusRegistryKey.HoldingRange<T> = protected fun register(address: Int, description: String = ""): ModbusRegistryKey.HoldingRegister =
ModbusRegistryKey.HoldingRange(offset + 30000, count, format) register(ModbusRegistryKey.HoldingRegister(address), description)
protected fun <T> register(
address: Int,
count: Int,
format: IOFormat<T>,
description: String = "",
): ModbusRegistryKey.HoldingRange<T> =
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 fun validate(map: ModbusRegistryMap) {
map.entries.keys.sortedBy { it.address }.zipWithNext().forEach { (l, r) ->
if (l.address + l.count > r.address) error("Key $l overlaps with key $r")
}
}
public fun print(map: ModbusRegistryMap, to: Appendable = System.out) {
map.entries.entries.sortedBy { it.key.address }.forEach { (key, description) ->
val typeString = when (key) {
is ModbusRegistryKey.Coil -> "Coil"
is ModbusRegistryKey.DiscreteInput -> "Discrete"
is ModbusRegistryKey.HoldingRange<*>, is ModbusRegistryKey.HoldingRegister -> "Register"
is ModbusRegistryKey.InputRange<*>, is ModbusRegistryKey.InputRegister -> "Input"
}
val rangeString = if (key.count == 1) {
key.address.toString()
} else {
"${key.address} - ${key.address + key.count}"
}
to.appendLine("${typeString}\t$rangeString\t$description")
}
}
}
} }

View File

@ -8,6 +8,7 @@ import space.kscience.controls.spec.DeviceSpec
import space.kscience.controls.spec.doubleProperty import space.kscience.controls.spec.doubleProperty
import space.kscience.controls.spec.read import space.kscience.controls.spec.read
import space.kscience.dataforge.meta.transformations.MetaConverter import space.kscience.dataforge.meta.transformations.MetaConverter
import kotlin.test.Ignore
class OpcUaClientTest { class OpcUaClientTest {
class DemoOpcUaDevice(config: MiloConfiguration) : OpcUaDeviceBySpec<DemoOpcUaDevice>(DemoOpcUaDevice, config) { class DemoOpcUaDevice(config: MiloConfiguration) : OpcUaDeviceBySpec<DemoOpcUaDevice>(DemoOpcUaDevice, config) {
@ -37,6 +38,7 @@ class OpcUaClientTest {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
@Ignore
fun testReadDouble() = runTest { fun testReadDouble() = runTest {
DemoOpcUaDevice.build().use{ DemoOpcUaDevice.build().use{
println(it.read(DemoOpcUaDevice.randomDouble)) println(it.read(DemoOpcUaDevice.randomDouble))