Plc4X refactor

This commit is contained in:
Alexander Nozik 2024-04-07 10:07:23 +03:00
parent 58675f72f5
commit 977500223d
5 changed files with 105 additions and 27 deletions

View File

@ -11,14 +11,14 @@ description = """
A plugin for Controls-kt device server on top of plc4x library A plugin for Controls-kt device server on top of plc4x library
""".trimIndent() """.trimIndent()
kscience{ kscience {
jvm() jvm()
jvmMain{ jvmMain {
api(projects.controlsCore) api(projects.controlsCore)
api("org.apache.plc4x:plc4j-spi:$plc4xVersion") api("org.apache.plc4x:plc4j-spi:$plc4xVersion")
} }
} }
readme{ readme {
maturity = Maturity.EXPERIMENTAL maturity = Maturity.EXPERIMENTAL
} }

View File

@ -2,27 +2,75 @@ package space.kscience.controls.plc4x
import kotlinx.coroutines.future.await import kotlinx.coroutines.future.await
import org.apache.plc4x.java.api.PlcConnection import org.apache.plc4x.java.api.PlcConnection
import org.apache.plc4x.java.api.messages.PlcBrowseItem
import org.apache.plc4x.java.api.messages.PlcTagResponse
import org.apache.plc4x.java.api.messages.PlcWriteRequest import org.apache.plc4x.java.api.messages.PlcWriteRequest
import org.apache.plc4x.java.api.messages.PlcWriteResponse
import org.apache.plc4x.java.api.types.PlcResponseCode
import space.kscience.controls.api.Device import space.kscience.controls.api.Device
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
public interface Plc4XDevice: Device { private val PlcTagResponse.responseCodes: Map<String, PlcResponseCode>
public val connection: PlcConnection get() = tagNames.associateWith { getResponseCode(it) }
public suspend fun read(plc4xProperty: Plc4xProperty): Meta = with(plc4xProperty){ private val Map<String, PlcResponseCode>.isOK get() = values.all { it == PlcResponseCode.OK }
val request = connection.readRequestBuilder().request().build()
val response = request.execute().await()
response.readProperty()
}
public suspend fun write(plc4xProperty: Plc4xProperty, value: Meta): Unit = with(plc4xProperty){
val request: PlcWriteRequest = connection.writeRequestBuilder().writeProperty(value).build()
request.execute().await()
}
public suspend fun subscribe(propertyName: String, plc4xProperty: Plc4xProperty): Unit = with(plc4xProperty){
}
public class PlcException(public val codes: Map<String, PlcResponseCode>) : Exception() {
override val message: String
get() = "Plc request unsuccessful:" + codes.entries.joinToString(prefix = "\n\t", separator = "\n\t") {
"${it.key}: ${it.value.name}"
}
} }
private fun PlcTagResponse.throwOnFail() {
val codes = responseCodes
if (!codes.isOK) throw PlcException(codes)
}
public interface Plc4XDevice : Device {
public val connection: PlcConnection
}
/**
* Send ping request and suspend until it comes back
*/
public suspend fun Plc4XDevice.ping(): PlcResponseCode = connection.ping().await().responseCode
/**
* Send browse request to list available tags
*/
public suspend fun Plc4XDevice.browse(): Map<String, MutableList<PlcBrowseItem>> {
require(connection.metadata.isBrowseSupported){"Browse actions are not supported on connection"}
val request = connection.browseRequestBuilder().build()
val response = request.execute().await()
return response.queryNames.associateWith { response.getValues(it) }
}
/**
* Send read request and suspend until it returns. Throw a [PlcException] if at least one tag read fails.
*
* @throws PlcException
*/
public suspend fun Plc4XDevice.read(plc4xProperty: Plc4xProperty): Meta = with(plc4xProperty) {
require(connection.metadata.isReadSupported) {"Read actions are not supported on connections"}
val request = connection.readRequestBuilder().request().build()
val response = request.execute().await()
response.throwOnFail()
response.readProperty()
}
/**
* Send write request and suspend until it finishes. Throw a [PlcException] if at least one tag write fails.
*
* @throws PlcException
*/
public suspend fun Plc4XDevice.write(plc4xProperty: Plc4xProperty, value: Meta): Unit = with(plc4xProperty) {
require(connection.metadata.isWriteSupported){"Write actions are not supported on connection"}
val request: PlcWriteRequest = connection.writeRequestBuilder().writeProperty(value).build()
val response: PlcWriteResponse = request.execute().await()
response.throwOnFail()
}

View File

@ -0,0 +1,22 @@
package space.kscience.controls.plc4x
import org.apache.plc4x.java.api.PlcConnection
import space.kscience.controls.spec.DeviceActionSpec
import space.kscience.controls.spec.DeviceBase
import space.kscience.controls.spec.DevicePropertySpec
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.meta.Meta
public class Plc4XDeviceBase(
context: Context,
meta: Meta,
override val connection: PlcConnection,
) : Plc4XDevice, DeviceBase<Plc4XDevice>(context, meta) {
override val properties: Map<String, DevicePropertySpec<Plc4XDevice, *>>
get() = TODO("Not yet implemented")
override val actions: Map<String, DeviceActionSpec<Plc4XDevice, *, *>> = emptyMap()
override fun toString(): String {
TODO("Not yet implemented")
}
}

View File

@ -8,6 +8,8 @@ import space.kscience.dataforge.meta.Meta
public interface Plc4xProperty { public interface Plc4xProperty {
public val keys: Set<String>
public fun PlcReadRequest.Builder.request(): PlcReadRequest.Builder public fun PlcReadRequest.Builder.request(): PlcReadRequest.Builder
public fun PlcReadResponse.readProperty(): Meta public fun PlcReadResponse.readProperty(): Meta
@ -15,17 +17,23 @@ public interface Plc4xProperty {
public fun PlcWriteRequest.Builder.writeProperty(meta: Meta): PlcWriteRequest.Builder public fun PlcWriteRequest.Builder.writeProperty(meta: Meta): PlcWriteRequest.Builder
} }
public class DefaultPlc4xProperty( private class DefaultPlc4xProperty(
private val address: String, private val address: String,
private val plcValueType: PlcValueType, private val plcValueType: PlcValueType,
private val name: String = "@default", private val name: String = "@default",
) : Plc4xProperty { ) : Plc4xProperty {
override val keys: Set<String> = setOf(name)
override fun PlcReadRequest.Builder.request(): PlcReadRequest.Builder = override fun PlcReadRequest.Builder.request(): PlcReadRequest.Builder =
addTagAddress(name, address) addTagAddress(name, address)
override fun PlcReadResponse.readProperty(): Meta = override fun PlcReadResponse.readProperty(): Meta =
asPlcValue.toMeta() getPlcValue(name).toMeta()
override fun PlcWriteRequest.Builder.writeProperty(meta: Meta): PlcWriteRequest.Builder = override fun PlcWriteRequest.Builder.writeProperty(meta: Meta): PlcWriteRequest.Builder =
addTagAddress(name, address, meta.toPlcValue(plcValueType)) addTagAddress(name, address, meta.toPlcValue(plcValueType))
} }
public fun Plc4xProperty(address: String, plcValueType: PlcValueType, name: String = "@default"): Plc4xProperty =
DefaultPlc4xProperty(address, plcValueType, name)

View File

@ -84,24 +84,24 @@ private fun Context.launchPidDevice(
showDashboard { showDashboard {
plot { plot {
plotNumberState(context, state, maxAge = maxAge) { plotNumberState(context, state, maxAge = maxAge, sampling = 50.milliseconds) {
name = "real position" name = "real position"
} }
plotDeviceProperty(device.pid, Regulator.position.name, maxAge = maxAge) { plotDeviceProperty(device.pid, Regulator.position.name, maxAge = maxAge, sampling = 50.milliseconds) {
name = "read position" name = "read position"
} }
plotDeviceProperty(device.pid, Regulator.target.name, maxAge = maxAge) { plotDeviceProperty(device.pid, Regulator.target.name, maxAge = maxAge, sampling = 50.milliseconds) {
name = "target" name = "target"
} }
} }
plot { plot {
plotDeviceProperty(device.start, LimitSwitch.locked.name, maxAge = maxAge) { plotDeviceProperty(device.start, LimitSwitch.locked.name, maxAge = maxAge, sampling = 50.milliseconds) {
name = "start measured" name = "start measured"
mode = ScatterMode.markers mode = ScatterMode.markers
} }
plotDeviceProperty(device.end, LimitSwitch.locked.name, maxAge = maxAge) { plotDeviceProperty(device.end, LimitSwitch.locked.name, maxAge = maxAge, sampling = 50.milliseconds) {
name = "end measured" name = "end measured"
mode = ScatterMode.markers mode = ScatterMode.markers
} }