Fixex in modbus and write-protection for same meta
This commit is contained in:
parent
34e7dd2c6d
commit
efe9a2e842
@ -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
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user