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) { internal fun opcToMeta(value: Any?): Meta = when (value) {
null -> Meta(Null) null -> Meta(Null)
is Variant -> opcToMeta(value.value)
is Meta -> value is Meta -> value
is Value -> Meta(value) is Value -> Meta(value)
is Number -> when (value) { is Number -> when (value) {
@ -79,13 +80,18 @@ internal fun opcToMeta(value: Any?): Meta = when (value) {
"text" put value.text?.asValue() "text" put value.text?.asValue()
} }
is DataValue -> Meta { is DataValue -> Meta {
"value" put opcToMeta(value.value) // need SerializationContext to do that properly 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.statusCode?.value?.let { "status" put Meta(it.asValue()) }
value.sourceTime?.javaInstant?.let { "sourceTime" put it.toKotlinInstant().toMeta() } value.sourceTime?.javaInstant?.let { "sourceTime" put it.toKotlinInstant().toMeta() }
value.sourcePicoseconds?.let { "sourcePicoseconds" put Meta(it.asValue()) } value.sourcePicoseconds?.let { "sourcePicoseconds" put Meta(it.asValue()) }
value.serverTime?.javaInstant?.let { "serverTime" put it.toKotlinInstant().toMeta() } value.serverTime?.javaInstant?.let { "serverTime" put it.toKotlinInstant().toMeta() }
value.serverPicoseconds?.let { "serverPicoseconds" put Meta(it.asValue()) } value.serverPicoseconds?.let { "serverPicoseconds" put Meta(it.asValue()) }
} }
}
is ByteString -> Meta(value.bytesOrEmpty().asValue()) is ByteString -> Meta(value.bytesOrEmpty().asValue())
is XmlElement -> Meta(value.fragment?.asValue() ?: Null) is XmlElement -> Meta(value.fragment?.asValue() ?: Null)
is NodeId -> Meta(value.toParseableString().asValue()) is NodeId -> Meta(value.toParseableString().asValue())

View File

@ -2,7 +2,6 @@ package space.kscience.controls.opcua.server
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.datetime.toJavaInstant import kotlinx.datetime.toJavaInstant
import kotlinx.serialization.json.Json
import org.eclipse.milo.opcua.sdk.core.AccessLevel import org.eclipse.milo.opcua.sdk.core.AccessLevel
import org.eclipse.milo.opcua.sdk.core.Reference import org.eclipse.milo.opcua.sdk.core.Reference
import org.eclipse.milo.opcua.sdk.server.Lifecycle 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 org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText
import space.kscience.controls.api.* import space.kscience.controls.api.*
import space.kscience.controls.manager.DeviceManager import space.kscience.controls.manager.DeviceManager
import space.kscience.controls.opcua.client.opcToMeta
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaSerializer
import space.kscience.dataforge.meta.ValueType import space.kscience.dataforge.meta.ValueType
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.plus 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) 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( public class DeviceNameSpace(
server: OpcUaServer, server: OpcUaServer,
public val deviceManager: DeviceManager public val deviceManager: DeviceManager,
) : ManagedNamespaceWithLifecycle(server, NAMESPACE_URI) { ) : ManagedNamespaceWithLifecycle(server, NAMESPACE_URI) {
private val subscription = SubscriptionModel(server, this) 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) { private fun UaFolderNode.registerDeviceNodes(deviceName: Name, device: Device) {
val nodes = device.propertyDescriptors.associate { descriptor -> val nodes = device.propertyDescriptors.associate { descriptor ->
val propertyName = descriptor.name val propertyName = descriptor.name
@ -74,14 +56,17 @@ public class DeviceNameSpace(
setAccessLevel(AccessLevel.READ_WRITE) setAccessLevel(AccessLevel.READ_WRITE)
setUserAccessLevel(AccessLevel.READ_WRITE) setUserAccessLevel(AccessLevel.READ_WRITE)
} }
descriptor.mutable -> { descriptor.mutable -> {
setAccessLevel(AccessLevel.WRITE_ONLY) setAccessLevel(AccessLevel.WRITE_ONLY)
setUserAccessLevel(AccessLevel.WRITE_ONLY) setUserAccessLevel(AccessLevel.WRITE_ONLY)
} }
descriptor.readable -> { descriptor.readable -> {
setAccessLevel(AccessLevel.READ_ONLY) setAccessLevel(AccessLevel.READ_ONLY)
setUserAccessLevel(AccessLevel.READ_ONLY) setUserAccessLevel(AccessLevel.READ_ONLY)
} }
else -> { else -> {
setAccessLevel(AccessLevel.NONE) setAccessLevel(AccessLevel.NONE)
setUserAccessLevel(AccessLevel.NONE) setUserAccessLevel(AccessLevel.NONE)
@ -104,29 +89,26 @@ public class DeviceNameSpace(
}.build() }.build()
// Update initial value, but only if it is cached // 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 { device[descriptor]?.toOpc(sourceTime = null, serverTime = null)?.let {
node.value = it node.value = it
} }
} }
if (descriptor.mutable) {
/** /**
* Subscribe to node value changes * Subscribe to node value changes
*/ */
node.addAttributeObserver { _: UaNode, attributeId: AttributeId, value: Any -> node.addAttributeObserver { _: UaNode, attributeId: AttributeId, value: Any? ->
if (attributeId == AttributeId.Value) { if (attributeId == AttributeId.Value) {
val meta: Meta = when (value) { val meta: Meta = opcToMeta(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 { deviceManager.context.launch {
device.writeProperty(propertyName, meta) device.writeProperty(propertyName, meta)
} }
} }
} }
}
nodeManager.addNode(node) nodeManager.addNode(node)
addOrganizes(node) addOrganizes(node)
@ -137,7 +119,10 @@ public class DeviceNameSpace(
device.onPropertyChange { device.onPropertyChange {
nodes[property]?.let { node -> nodes[property]?.let { node ->
val sourceTime = time?.let { DateTime(it.toJavaInstant()) } 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 //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?>?) { override fun onDataItemsCreated(dataItems: List<DataItem?>?) {
subscription.onDataItemsCreated(dataItems) subscription.onDataItemsCreated(dataItems)
} }