Modbus registry validation and print
This commit is contained in:
parent
b8a82feed0
commit
0612c6e3a2
@ -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 <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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun coil(address: Int, description: String = ""): ModbusRegistryKey.Coil =
|
||||||
|
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 =
|
||||||
|
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 =
|
||||||
|
register(ModbusRegistryKey.InputRegister(address), description)
|
||||||
|
|
||||||
|
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 inputByOffset(offset: Int, description: String = ""): ModbusRegistryKey.InputRegister =
|
||||||
|
register(ModbusRegistryKey.InputRegister(20000 + offset), description)
|
||||||
|
|
||||||
|
protected fun <T> inputByOffset(
|
||||||
|
offset: Int,
|
||||||
|
count: Int,
|
||||||
|
reader: IOReader<T>,
|
||||||
|
description: String = "",
|
||||||
|
): ModbusRegistryKey.InputRange<T> =
|
||||||
|
register(ModbusRegistryKey.InputRange(20000 + offset, count, reader), description)
|
||||||
|
|
||||||
|
protected fun register(address: Int, description: String = ""): ModbusRegistryKey.HoldingRegister =
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
Loading…
Reference in New Issue
Block a user