Quick fix for OPC us server

This commit is contained in:
Alexander Nozik 2023-12-28 22:40:58 +03:00
parent aa52b4b927
commit 7579ddfad4
2 changed files with 53 additions and 44 deletions

View File

@ -56,6 +56,7 @@ internal class MetaEnumCodec : OpcUaBinaryDataTypeCodec<Number> {
internal fun opcToMeta(value: Any?): Meta = when (value) {
null -> Meta(Null)
is Variant -> opcToMeta(value.value)
is Meta -> value
is Value -> Meta(value)
is Number -> when (value) {
@ -79,12 +80,17 @@ internal fun opcToMeta(value: Any?): Meta = when (value) {
"text" put value.text?.asValue()
}
is DataValue -> Meta {
"value" put opcToMeta(value.value) // need SerializationContext to do that properly
value.statusCode?.value?.let { "status" put Meta(it.asValue()) }
value.sourceTime?.javaInstant?.let { "sourceTime" put it.toKotlinInstant().toMeta() }
value.sourcePicoseconds?.let { "sourcePicoseconds" put Meta(it.asValue()) }
value.serverTime?.javaInstant?.let { "serverTime" put it.toKotlinInstant().toMeta() }
value.serverPicoseconds?.let { "serverPicoseconds" put Meta(it.asValue()) }
val variant= opcToMeta(value.value)
update(variant)// need SerializationContext to do that properly
//TODO remove after DF 0.7.2
this.value = variant.value
"@opc" put {
value.statusCode?.value?.let { "status" put Meta(it.asValue()) }
value.sourceTime?.javaInstant?.let { "sourceTime" put it.toKotlinInstant().toMeta() }
value.sourcePicoseconds?.let { "sourcePicoseconds" put Meta(it.asValue()) }
value.serverTime?.javaInstant?.let { "serverTime" put it.toKotlinInstant().toMeta() }
value.serverPicoseconds?.let { "serverPicoseconds" put Meta(it.asValue()) }
}
}
is ByteString -> Meta(value.bytesOrEmpty().asValue())
is XmlElement -> Meta(value.fragment?.asValue() ?: Null)

View File

@ -2,7 +2,6 @@ package space.kscience.controls.opcua.server
import kotlinx.coroutines.launch
import kotlinx.datetime.toJavaInstant
import kotlinx.serialization.json.Json
import org.eclipse.milo.opcua.sdk.core.AccessLevel
import org.eclipse.milo.opcua.sdk.core.Reference
import org.eclipse.milo.opcua.sdk.server.Lifecycle
@ -21,14 +20,15 @@ import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText
import space.kscience.controls.api.*
import space.kscience.controls.manager.DeviceManager
import space.kscience.controls.opcua.client.opcToMeta
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaSerializer
import space.kscience.dataforge.meta.ValueType
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.plus
public operator fun CachingDevice.get(propertyDescriptor: PropertyDescriptor): Meta? = getProperty(propertyDescriptor.name)
public operator fun CachingDevice.get(propertyDescriptor: PropertyDescriptor): Meta? =
getProperty(propertyDescriptor.name)
public suspend fun Device.read(propertyDescriptor: PropertyDescriptor): Meta = readProperty(propertyDescriptor.name)
@ -38,29 +38,11 @@ https://github.com/eclipse/milo/blob/master/milo-examples/server-examples/src/ma
public class DeviceNameSpace(
server: OpcUaServer,
public val deviceManager: DeviceManager
public val deviceManager: DeviceManager,
) : ManagedNamespaceWithLifecycle(server, NAMESPACE_URI) {
private val subscription = SubscriptionModel(server, this)
init {
lifecycleManager.addLifecycle(subscription)
lifecycleManager.addStartupTask {
nodeContext.registerHub(deviceManager, Name.EMPTY)
}
lifecycleManager.addLifecycle(object : Lifecycle {
override fun startup() {
server.addressSpaceManager.register(this@DeviceNameSpace)
}
override fun shutdown() {
server.addressSpaceManager.unregister(this@DeviceNameSpace)
}
})
}
private fun UaFolderNode.registerDeviceNodes(deviceName: Name, device: Device) {
val nodes = device.propertyDescriptors.associate { descriptor ->
val propertyName = descriptor.name
@ -74,14 +56,17 @@ public class DeviceNameSpace(
setAccessLevel(AccessLevel.READ_WRITE)
setUserAccessLevel(AccessLevel.READ_WRITE)
}
descriptor.mutable -> {
setAccessLevel(AccessLevel.WRITE_ONLY)
setUserAccessLevel(AccessLevel.WRITE_ONLY)
}
descriptor.readable -> {
setAccessLevel(AccessLevel.READ_ONLY)
setUserAccessLevel(AccessLevel.READ_ONLY)
}
else -> {
setAccessLevel(AccessLevel.NONE)
setUserAccessLevel(AccessLevel.NONE)
@ -104,26 +89,23 @@ public class DeviceNameSpace(
}.build()
// Update initial value, but only if it is cached
if(device is CachingDevice) {
if (device is CachingDevice) {
device[descriptor]?.toOpc(sourceTime = null, serverTime = null)?.let {
node.value = it
}
}
/**
* Subscribe to node value changes
*/
node.addAttributeObserver { _: UaNode, attributeId: AttributeId, value: Any ->
if (attributeId == AttributeId.Value) {
val meta: Meta = when (value) {
is Meta -> value
is Boolean -> Meta(value)
is Number -> Meta(value)
is String -> Json.decodeFromString(MetaSerializer, value)
else -> return@addAttributeObserver //TODO("other types not implemented")
}
deviceManager.context.launch {
device.writeProperty(propertyName, meta)
if (descriptor.mutable) {
/**
* Subscribe to node value changes
*/
node.addAttributeObserver { _: UaNode, attributeId: AttributeId, value: Any? ->
if (attributeId == AttributeId.Value) {
val meta: Meta = opcToMeta(value)
deviceManager.context.launch {
device.writeProperty(propertyName, meta)
}
}
}
}
@ -137,7 +119,10 @@ public class DeviceNameSpace(
device.onPropertyChange {
nodes[property]?.let { node ->
val sourceTime = time?.let { DateTime(it.toJavaInstant()) }
node.value = value.toOpc(sourceTime = sourceTime)
val newValue = value.toOpc(sourceTime = sourceTime)
if (node.value.value != newValue.value) {
node.value = newValue
}
}
}
//recursively add sub-devices
@ -168,6 +153,24 @@ public class DeviceNameSpace(
}
}
init {
lifecycleManager.addLifecycle(subscription)
lifecycleManager.addStartupTask {
nodeContext.registerHub(deviceManager, Name.EMPTY)
}
lifecycleManager.addLifecycle(object : Lifecycle {
override fun startup() {
server.addressSpaceManager.register(this@DeviceNameSpace)
}
override fun shutdown() {
server.addressSpaceManager.unregister(this@DeviceNameSpace)
}
})
}
override fun onDataItemsCreated(dataItems: List<DataItem?>?) {
subscription.onDataItemsCreated(dataItems)
}