Add opcua test
This commit is contained in:
parent
197675fc15
commit
e7cfb1d2ba
@ -4,14 +4,16 @@ plugins {
|
||||
|
||||
val ktorVersion: String by rootProject.extra
|
||||
|
||||
val miloVersion: String = "0.6.7"
|
||||
val miloVersion: String = "0.6.9"
|
||||
|
||||
dependencies {
|
||||
api(project(":controls-core"))
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:${space.kscience.gradle.KScienceVersions.coroutinesVersion}")
|
||||
api(projects.controlsCore)
|
||||
api(spclibs.kotlinx.coroutines.jdk8)
|
||||
|
||||
api("org.eclipse.milo:sdk-client:$miloVersion")
|
||||
api("org.eclipse.milo:bsd-parser:$miloVersion")
|
||||
|
||||
api("org.eclipse.milo:sdk-server:$miloVersion")
|
||||
|
||||
testImplementation(spclibs.kotlinx.coroutines.test)
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package space.kscience.controls.opcua.client
|
||||
|
||||
import kotlinx.coroutines.future.await
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.eclipse.milo.opcua.sdk.client.OpcUaClient
|
||||
import org.eclipse.milo.opcua.stack.core.types.builtin.*
|
||||
@ -9,6 +11,8 @@ import space.kscience.controls.api.Device
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
|
||||
/**
|
||||
@ -19,11 +23,6 @@ public interface MiloDevice : Device {
|
||||
* The OPC-UA client initialized on first use
|
||||
*/
|
||||
public val client: OpcUaClient
|
||||
|
||||
override fun close() {
|
||||
client.disconnect()
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,3 +80,47 @@ public suspend inline fun <reified T> MiloDevice.writeOpc(
|
||||
val meta = converter.objectToMeta(value)
|
||||
return client.writeValue(nodeId, DataValue(Variant(meta))).await()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A device-bound OPC-UA property. Does not trigger device properties change.
|
||||
*/
|
||||
public inline fun <reified T> MiloDevice.opc(
|
||||
nodeId: NodeId,
|
||||
converter: MetaConverter<T>,
|
||||
magAge: Double = 500.0
|
||||
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T = runBlocking {
|
||||
readOpc(nodeId, converter, magAge)
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
launch {
|
||||
writeOpc(nodeId, converter, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a mutable OPC-UA based [Double] property in a device spec
|
||||
*/
|
||||
public fun MiloDevice.opcDouble(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, Double> = opc<Double>(nodeId, MetaConverter.double, magAge)
|
||||
|
||||
/**
|
||||
* Register a mutable OPC-UA based [Int] property in a device spec
|
||||
*/
|
||||
public fun MiloDevice.opcInt(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, Int> = opc(nodeId, MetaConverter.int, magAge)
|
||||
|
||||
/**
|
||||
* Register a mutable OPC-UA based [String] property in a device spec
|
||||
*/
|
||||
public fun MiloDevice.opcString(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, String> = opc(nodeId, MetaConverter.string, magAge)
|
@ -1,69 +1,66 @@
|
||||
package space.kscience.controls.opcua.client
|
||||
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.eclipse.milo.opcua.sdk.client.OpcUaClient
|
||||
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId
|
||||
import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider
|
||||
import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider
|
||||
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy
|
||||
import space.kscience.controls.spec.DeviceBySpec
|
||||
import space.kscience.controls.spec.DeviceSpec
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.Scheme
|
||||
import space.kscience.dataforge.meta.SchemeSpec
|
||||
import space.kscience.dataforge.meta.specOrNull
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
|
||||
public sealed class MiloIdentity: Scheme()
|
||||
|
||||
public class MiloUsername : MiloIdentity() {
|
||||
|
||||
public var username: String by string{ error("Username not defined") }
|
||||
public var password: String by string{ error("Password not defined") }
|
||||
|
||||
public companion object : SchemeSpec<MiloUsername>(::MiloUsername)
|
||||
}
|
||||
|
||||
//public class MiloKeyPair : MiloIdentity() {
|
||||
//
|
||||
// public companion object : SchemeSpec<MiloUsername>(::MiloUsername)
|
||||
//}
|
||||
|
||||
public class MiloConfiguration : Scheme() {
|
||||
|
||||
public var endpointUrl: String by string { error("Endpoint url is not defined") }
|
||||
|
||||
public var username: MiloUsername? by specOrNull(MiloUsername)
|
||||
|
||||
public companion object : SchemeSpec<MiloConfiguration>(::MiloConfiguration)
|
||||
}
|
||||
|
||||
/**
|
||||
* A variant of [DeviceBySpec] that includes OPC-UA client
|
||||
*/
|
||||
public open class MiloDeviceBySpec<D : MiloDeviceBySpec<D>>(
|
||||
spec: DeviceSpec<D>,
|
||||
config: MiloConfiguration,
|
||||
context: Context = Global,
|
||||
meta: Meta = Meta.EMPTY
|
||||
) : MiloDevice, DeviceBySpec<D>(spec, context, meta) {
|
||||
) : MiloDevice, DeviceBySpec<D>(spec, context, config.meta) {
|
||||
|
||||
override val client: OpcUaClient by lazy {
|
||||
val endpointUrl = meta["endpointUrl"].string ?: error("Endpoint url is not defined")
|
||||
context.createMiloClient(endpointUrl).apply {
|
||||
context.createMiloClient(
|
||||
config.endpointUrl,
|
||||
securityPolicy = SecurityPolicy.None,
|
||||
identityProvider = config.username?.let {
|
||||
UsernameProvider(it.username,it.password)
|
||||
} ?: AnonymousProvider()
|
||||
).apply {
|
||||
connect().get()
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
super<MiloDevice>.close()
|
||||
client.disconnect()
|
||||
super<DeviceBySpec>.close()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A device-bound OPC-UA property. Does not trigger device properties change.
|
||||
*/
|
||||
public inline fun <reified T> MiloDeviceBySpec<*>.opc(
|
||||
nodeId: NodeId,
|
||||
converter: MetaConverter<T>,
|
||||
magAge: Double = 500.0
|
||||
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T = runBlocking {
|
||||
readOpc(nodeId, converter, magAge)
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
launch {
|
||||
writeOpc(nodeId, converter, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun <reified T> MiloDeviceBySpec<*>.opcDouble(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, Double> = opc(nodeId, MetaConverter.double, magAge)
|
||||
|
||||
public inline fun <reified T> MiloDeviceBySpec<*>.opcInt(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, Int> = opc(nodeId, MetaConverter.int, magAge)
|
||||
|
||||
public inline fun <reified T> MiloDeviceBySpec<*>.opcString(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, String> = opc(nodeId, MetaConverter.string, magAge)
|
@ -0,0 +1,47 @@
|
||||
package space.kscience.controls.opcua.client
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId
|
||||
import org.junit.jupiter.api.Test
|
||||
import space.kscience.controls.opcua.client.OpcUaClientTest.DemoMiloDevice.Companion.randomDouble
|
||||
import space.kscience.controls.spec.DeviceSpec
|
||||
import space.kscience.controls.spec.doubleProperty
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
|
||||
class OpcUaClientTest {
|
||||
class DemoMiloDevice(config: MiloConfiguration) : MiloDeviceBySpec<DemoMiloDevice>(DemoMiloDevice, config) {
|
||||
|
||||
//val randomDouble by opcDouble(NodeId(2, "Dynamic/RandomDouble"))
|
||||
|
||||
suspend fun readRandomDouble() = readOpc(NodeId(2, "Dynamic/RandomDouble"), MetaConverter.double)
|
||||
|
||||
|
||||
companion object : DeviceSpec<DemoMiloDevice>() {
|
||||
fun build(): DemoMiloDevice {
|
||||
val config = MiloConfiguration {
|
||||
endpointUrl = "opc.tcp://milo.digitalpetri.com:62541/milo"
|
||||
// username = MiloUsername{
|
||||
// username = "user1"
|
||||
// password = "password"
|
||||
// }
|
||||
}
|
||||
return DemoMiloDevice(config)
|
||||
}
|
||||
|
||||
inline fun <R> use(block: DemoMiloDevice.() -> R): R = build().use(block)
|
||||
|
||||
val randomDouble by doubleProperty(read = DemoMiloDevice::readRandomDouble)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun testReadDouble() = runTest {
|
||||
println(DemoMiloDevice.use { randomDouble.read() })
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user