Fixex in modbus and write-protection for same meta

This commit is contained in:
Alexander Nozik 2023-10-02 21:24:01 +03:00
parent 34e7dd2c6d
commit efe9a2e842
5 changed files with 65 additions and 27 deletions

View File

@ -11,6 +11,8 @@
### Removed
### Fixed
- Property writing does not trigger change if logical state already is the same as value to be set.
- Modbus-slave triggers only once for multi-register write.
### Security

View File

@ -13,7 +13,7 @@ val xodusVersion by extra("2.0.1")
allprojects {
group = "space.kscience"
version = "0.2.2-dev-1"
version = "0.2.2-dev-2"
repositories{
maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
}

View File

@ -7,18 +7,24 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import space.kscience.controls.api.*
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.debug
import space.kscience.dataforge.context.error
import space.kscience.dataforge.context.logger
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.misc.DFExperimental
import kotlin.coroutines.CoroutineContext
/**
* Write a meta [item] to [device]
*/
@OptIn(InternalDeviceAPI::class)
private suspend fun <D : Device, T> WritableDevicePropertySpec<D, T>.writeMeta(device: D, item: Meta) {
write(device, converter.metaToObject(item) ?: error("Meta $item could not be read with $converter"))
}
/**
* Read Meta item from the [device]
*/
@OptIn(InternalDeviceAPI::class)
private suspend fun <D : Device, T> DevicePropertySpec<D, T>.readMeta(device: D): Meta? =
read(device)?.let(converter::objectToMeta)
@ -135,9 +141,14 @@ public abstract class DeviceBase<D : Device>(
}
override suspend fun writeProperty(propertyName: String, value: Meta): Unit {
//bypass property setting if it already has that value
if (logicalState[propertyName] == value) {
logger.debug { "Skipping setting $propertyName to $value because value is already set" }
return
}
when (val property = properties[propertyName]) {
null -> {
//If there are no physical properties with given name, write a logical one.
//If there are no registered physical properties with given name, write a logical one.
propertyChanged(propertyName, value)
}
@ -145,7 +156,7 @@ public abstract class DeviceBase<D : Device>(
//if there is a writeable property with a given name, invalidate logical and write physical
invalidate(propertyName)
property.writeMeta(self, value)
// perform read after writing if the writer did not set the value
// perform read after writing if the writer did not set the value and the value is still in invalid state
if (logicalState[propertyName] == null) {
val meta = property.readMeta(self)
propertyChanged(propertyName, meta)

View File

@ -1,9 +1,9 @@
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 io.ktor.utils.io.core.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import space.kscience.controls.api.Device
import space.kscience.controls.spec.DevicePropertySpec
@ -118,22 +118,48 @@ public class DeviceProcessImageBuilder<D : Device> internal constructor(
}
}
/**
* Trigger [block] if one of register changes.
*/
private fun List<ObservableRegister>.onChange(block: (ByteReadPacket) -> Unit) {
var ready = false
forEach { register ->
register.addObserver { _, _ ->
ready = true
}
}
device.launch {
val builder = BytePacketBuilder()
while (isActive) {
delay(1)
if (ready) {
val packet = builder.apply {
forEach { value ->
writeShort(value.toShort())
}
}.build()
block(packet)
ready = false
}
}
}
}
public fun <T> bind(key: ModbusRegistryKey.HoldingRange<T>, propertySpec: WritableDevicePropertySpec<D, T>) {
val registers = List(key.count) {
ObservableRegister()
}
registers.forEachIndexed { index, register ->
register.addObserver { _, _ ->
val packet = buildPacket {
registers.forEach { value ->
writeShort(value.toShort())
}
}
device[propertySpec] = key.format.readObject(packet)
}
image.addRegister(key.address + index, register)
}
registers.onChange { packet ->
device[propertySpec] = key.format.readObject(packet)
}
device.useProperty(propertySpec) { value ->
val packet = buildPacket {
key.format.writeObject(this, value)
@ -182,20 +208,17 @@ public class DeviceProcessImageBuilder<D : Device> internal constructor(
val registers = List(key.count) {
ObservableRegister()
}
registers.forEachIndexed { index, register ->
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)
}
registers.onChange { packet ->
device.launch {
device.action(key.format.readObject(packet))
}
}
return registers
}
@ -205,11 +228,13 @@ public class DeviceProcessImageBuilder<D : Device> internal constructor(
* Bind the device to Modbus slave (server) image.
*/
public fun <D : Device> D.bindProcessImage(
unitId: Int = 0,
openOnBind: Boolean = true,
binding: DeviceProcessImageBuilder<D>.() -> Unit,
): ProcessImage {
val image = SimpleProcessImage()
val image = SimpleProcessImage(unitId)
DeviceProcessImageBuilder(this, image).apply(binding)
image.setLocked(true)
if (openOnBind) {
launch {
open()

View File

@ -61,7 +61,7 @@ public interface ModbusDevice : Device {
}
public operator fun <T> ModbusRegistryKey.HoldingRange<T>.getValue(thisRef: Any?, property: KProperty<*>): T {
val packet = readInputRegistersToPacket(address, count)
val packet = readHoldingRegistersToPacket(address, count)
return format.readObject(packet)
}