Compare commits

...

2 Commits

7 changed files with 29 additions and 26 deletions

View File

@ -124,5 +124,5 @@ public fun Device.getAllProperties(): Meta = Meta {
/** /**
* Subscribe on property changes for the whole device * Subscribe on property changes for the whole device
*/ */
public fun Device.onPropertyChange(callback: suspend PropertyChangedMessage.() -> Unit): Job = public fun Device.onPropertyChange(scope: CoroutineScope = this, callback: suspend PropertyChangedMessage.() -> Unit): Job =
messageFlow.filterIsInstance<PropertyChangedMessage>().onEach(callback).launchIn(this) messageFlow.filterIsInstance<PropertyChangedMessage>().onEach(callback).launchIn(scope)

View File

@ -87,7 +87,7 @@ public abstract class DeviceBase<D : Device>(
/** /**
* Update logical property state and notify listeners * Update logical property state and notify listeners
*/ */
protected suspend fun updateLogical(propertyName: String, value: Meta?) { protected suspend fun propertyChanged(propertyName: String, value: Meta?) {
if (value != logicalState[propertyName]) { if (value != logicalState[propertyName]) {
stateLock.withLock { stateLock.withLock {
logicalState[propertyName] = value logicalState[propertyName] = value
@ -99,10 +99,10 @@ public abstract class DeviceBase<D : Device>(
} }
/** /**
* Update logical state using given [spec] and its convertor * Notify the device that a property with [spec] value is changed
*/ */
public suspend fun <T> updateLogical(spec: DevicePropertySpec<D, T>, value: T) { protected suspend fun <T> propertyChanged(spec: DevicePropertySpec<D, T>, value: T) {
updateLogical(spec.name, spec.converter.objectToMeta(value)) propertyChanged(spec.name, spec.converter.objectToMeta(value))
} }
/** /**
@ -112,7 +112,7 @@ public abstract class DeviceBase<D : Device>(
override suspend fun readProperty(propertyName: String): Meta { override suspend fun readProperty(propertyName: String): Meta {
val spec = properties[propertyName] ?: error("Property with name $propertyName not found") val spec = properties[propertyName] ?: error("Property with name $propertyName not found")
val meta = spec.readMeta(self) ?: error("Failed to read property $propertyName") val meta = spec.readMeta(self) ?: error("Failed to read property $propertyName")
updateLogical(propertyName, meta) propertyChanged(propertyName, meta)
return meta return meta
} }
@ -122,7 +122,7 @@ public abstract class DeviceBase<D : Device>(
public suspend fun readPropertyOrNull(propertyName: String): Meta? { public suspend fun readPropertyOrNull(propertyName: String): Meta? {
val spec = properties[propertyName] ?: return null val spec = properties[propertyName] ?: return null
val meta = spec.readMeta(self) ?: return null val meta = spec.readMeta(self) ?: return null
updateLogical(propertyName, meta) propertyChanged(propertyName, meta)
return meta return meta
} }
@ -137,13 +137,18 @@ public abstract class DeviceBase<D : Device>(
override suspend fun writeProperty(propertyName: String, value: Meta): Unit { override suspend fun writeProperty(propertyName: String, value: Meta): Unit {
when (val property = properties[propertyName]) { when (val property = properties[propertyName]) {
null -> { null -> {
//If there is a physical property with a given name, invalidate logical property and write physical one //If there are no physical properties with given name, write a logical one.
updateLogical(propertyName, value) propertyChanged(propertyName, value)
} }
is WritableDevicePropertySpec -> { is WritableDevicePropertySpec -> {
//if there is a writeable property with a given name, invalidate logical and write physical
invalidate(propertyName) invalidate(propertyName)
property.writeMeta(self, value) property.writeMeta(self, value)
// perform read after writing if the writer did not set the value
if (logicalState[propertyName] == null) {
readPropertyOrNull(propertyName)
}
} }
else -> { else -> {

View File

@ -115,6 +115,7 @@ public fun <D : Device, T> D.propertyFlow(spec: DevicePropertySpec<D, T>): Flow<
*/ */
public fun <D : Device, T> D.onPropertyChange( public fun <D : Device, T> D.onPropertyChange(
spec: DevicePropertySpec<D, T>, spec: DevicePropertySpec<D, T>,
scope: CoroutineScope = this,
callback: suspend PropertyChangedMessage.(T) -> Unit, callback: suspend PropertyChangedMessage.(T) -> Unit,
): Job = messageFlow ): Job = messageFlow
.filterIsInstance<PropertyChangedMessage>() .filterIsInstance<PropertyChangedMessage>()
@ -124,15 +125,16 @@ public fun <D : Device, T> D.onPropertyChange(
if (newValue != null) { if (newValue != null) {
change.callback(newValue) change.callback(newValue)
} }
}.launchIn(this) }.launchIn(scope)
/** /**
* Call [callback] on initial property value and each value change * Call [callback] on initial property value and each value change
*/ */
public fun <D : Device, T> D.useProperty( public fun <D : Device, T> D.useProperty(
spec: DevicePropertySpec<D, T>, spec: DevicePropertySpec<D, T>,
scope: CoroutineScope = this,
callback: suspend (T) -> Unit, callback: suspend (T) -> Unit,
): Job = launch { ): Job = scope.launch {
callback(read(spec)) callback(read(spec))
messageFlow messageFlow
.filterIsInstance<PropertyChangedMessage>() .filterIsInstance<PropertyChangedMessage>()

View File

@ -22,7 +22,7 @@ public val MetaConverter.Companion.unit: MetaConverter<Unit> get() = UnitMetaCon
@OptIn(InternalDeviceAPI::class) @OptIn(InternalDeviceAPI::class)
public abstract class DeviceSpec<D : Device> { public abstract class DeviceSpec<D : Device> {
//initializing meta property for everyone //initializing the metadata property for everyone
private val _properties = hashMapOf<String, DevicePropertySpec<D, *>>( private val _properties = hashMapOf<String, DevicePropertySpec<D, *>>(
DeviceMetaPropertySpec.name to DeviceMetaPropertySpec DeviceMetaPropertySpec.name to DeviceMetaPropertySpec
) )

View File

@ -183,10 +183,6 @@ public fun ModbusDevice.writeHoldingRegisters(address: Int, buffer: ByteBuffer):
return writeHoldingRegisters(address, array) return writeHoldingRegisters(address, array)
} }
public fun ModbusDevice.writeShortRegister(address: Int, value: Short) {
master.writeSingleRegister(address, SimpleInputRegister(value.toInt()))
}
public fun ModbusDevice.modbusRegister( public fun ModbusDevice.modbusRegister(
address: Int, address: Int,
): ReadWriteProperty<ModbusDevice, Short> = object : ReadWriteProperty<ModbusDevice, Short> { ): ReadWriteProperty<ModbusDevice, Short> = object : ReadWriteProperty<ModbusDevice, Short> {

View File

@ -49,16 +49,16 @@ class MksPdr900Device(context: Context, meta: Meta) : DeviceBySpec<MksPdr900Devi
if (powerOnValue) { if (powerOnValue) {
val ans = talk("FP!ON") val ans = talk("FP!ON")
if (ans == "ON") { if (ans == "ON") {
updateLogical(powerOn, true) propertyChanged(powerOn, true)
} else { } else {
updateLogical(error, "Failed to set power state") propertyChanged(error, "Failed to set power state")
} }
} else { } else {
val ans = talk("FP!OFF") val ans = talk("FP!OFF")
if (ans == "OFF") { if (ans == "OFF") {
updateLogical(powerOn, false) propertyChanged(powerOn, false)
} else { } else {
updateLogical(error, "Failed to set power state") propertyChanged(error, "Failed to set power state")
} }
} }
} }
@ -68,13 +68,13 @@ class MksPdr900Device(context: Context, meta: Meta) : DeviceBySpec<MksPdr900Devi
invalidate(error) invalidate(error)
return if (answer.isNullOrEmpty()) { return if (answer.isNullOrEmpty()) {
// updateState(PortSensor.CONNECTED_STATE, false) // updateState(PortSensor.CONNECTED_STATE, false)
updateLogical(error, "No connection") propertyChanged(error, "No connection")
null null
} else { } else {
val res = answer.toDouble() val res = answer.toDouble()
if (res <= 0) { if (res <= 0) {
updateLogical(powerOn, false) propertyChanged(powerOn, false)
updateLogical(error, "No power") propertyChanged(error, "No power")
null null
} else { } else {
res res

View File

@ -172,7 +172,7 @@ class PiMotionMasterDevice(
//Update port //Update port
//address = portSpec.node //address = portSpec.node
port = portFactory(portSpec, context) port = portFactory(portSpec, context)
updateLogical(connected, true) propertyChanged(connected, true)
// connector.open() // connector.open()
//Initialize axes //Initialize axes
val idn = read(identity) val idn = read(identity)
@ -196,7 +196,7 @@ class PiMotionMasterDevice(
it.close() it.close()
} }
port = null port = null
updateLogical(connected, false) propertyChanged(connected, false)
} }