Quick fix for OPC us server
This commit is contained in:
parent
aa52b4b927
commit
7579ddfad4
@ -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,12 +80,17 @@ 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)
|
||||||
value.statusCode?.value?.let { "status" put Meta(it.asValue()) }
|
update(variant)// need SerializationContext to do that properly
|
||||||
value.sourceTime?.javaInstant?.let { "sourceTime" put it.toKotlinInstant().toMeta() }
|
//TODO remove after DF 0.7.2
|
||||||
value.sourcePicoseconds?.let { "sourcePicoseconds" put Meta(it.asValue()) }
|
this.value = variant.value
|
||||||
value.serverTime?.javaInstant?.let { "serverTime" put it.toKotlinInstant().toMeta() }
|
"@opc" put {
|
||||||
value.serverPicoseconds?.let { "serverPicoseconds" put Meta(it.asValue()) }
|
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 ByteString -> Meta(value.bytesOrEmpty().asValue())
|
||||||
is XmlElement -> Meta(value.fragment?.asValue() ?: Null)
|
is XmlElement -> Meta(value.fragment?.asValue() ?: Null)
|
||||||
|
@ -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,26 +89,23 @@ 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
|
|
||||||
*/
|
/**
|
||||||
node.addAttributeObserver { _: UaNode, attributeId: AttributeId, value: Any ->
|
* Subscribe to node value changes
|
||||||
if (attributeId == AttributeId.Value) {
|
*/
|
||||||
val meta: Meta = when (value) {
|
node.addAttributeObserver { _: UaNode, attributeId: AttributeId, value: Any? ->
|
||||||
is Meta -> value
|
if (attributeId == AttributeId.Value) {
|
||||||
is Boolean -> Meta(value)
|
val meta: Meta = opcToMeta(value)
|
||||||
is Number -> Meta(value)
|
deviceManager.context.launch {
|
||||||
is String -> Json.decodeFromString(MetaSerializer, value)
|
device.writeProperty(propertyName, meta)
|
||||||
else -> return@addAttributeObserver //TODO("other types not implemented")
|
}
|
||||||
}
|
|
||||||
deviceManager.context.launch {
|
|
||||||
device.writeProperty(propertyName, meta)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user