PLC4x bindings
This commit is contained in:
parent
70ab60f98c
commit
78dade4b49
24
controls-plc4x/build.gradle.kts
Normal file
24
controls-plc4x/build.gradle.kts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import space.kscience.gradle.Maturity
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("space.kscience.gradle.mpp")
|
||||||
|
`maven-publish`
|
||||||
|
}
|
||||||
|
|
||||||
|
val plc4xVersion = "0.12.0"
|
||||||
|
|
||||||
|
description = """
|
||||||
|
A plugin for Controls-kt device server on top of plc4x library
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
kscience{
|
||||||
|
jvm()
|
||||||
|
jvmMain{
|
||||||
|
api(projects.controlsCore)
|
||||||
|
api("org.apache.plc4x:plc4j-spi:$plc4xVersion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readme{
|
||||||
|
maturity = Maturity.EXPERIMENTAL
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package space.kscience.controls.plc4x
|
||||||
|
|
||||||
|
import kotlinx.coroutines.future.await
|
||||||
|
import org.apache.plc4x.java.api.PlcConnection
|
||||||
|
import org.apache.plc4x.java.api.messages.PlcWriteRequest
|
||||||
|
import space.kscience.controls.api.Device
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
|
||||||
|
public interface Plc4XDevice: Device {
|
||||||
|
public val connection: PlcConnection
|
||||||
|
|
||||||
|
public suspend fun read(plc4xProperty: Plc4xProperty): Meta = with(plc4xProperty){
|
||||||
|
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){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
|||||||
|
package space.kscience.controls.plc4x
|
||||||
|
|
||||||
|
import org.apache.plc4x.java.api.messages.PlcReadRequest
|
||||||
|
import org.apache.plc4x.java.api.messages.PlcReadResponse
|
||||||
|
import org.apache.plc4x.java.api.messages.PlcWriteRequest
|
||||||
|
import org.apache.plc4x.java.api.types.PlcValueType
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
|
||||||
|
public interface Plc4xProperty {
|
||||||
|
|
||||||
|
public fun PlcReadRequest.Builder.request(): PlcReadRequest.Builder
|
||||||
|
|
||||||
|
public fun PlcReadResponse.readProperty(): Meta
|
||||||
|
|
||||||
|
public fun PlcWriteRequest.Builder.writeProperty(meta: Meta): PlcWriteRequest.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DefaultPlc4xProperty(
|
||||||
|
private val address: String,
|
||||||
|
private val plcValueType: PlcValueType,
|
||||||
|
private val name: String = "@default",
|
||||||
|
) : Plc4xProperty {
|
||||||
|
|
||||||
|
override fun PlcReadRequest.Builder.request(): PlcReadRequest.Builder =
|
||||||
|
addTagAddress(name, address)
|
||||||
|
override fun PlcReadResponse.readProperty(): Meta =
|
||||||
|
asPlcValue.toMeta()
|
||||||
|
|
||||||
|
override fun PlcWriteRequest.Builder.writeProperty(meta: Meta): PlcWriteRequest.Builder =
|
||||||
|
addTagAddress(name, address, meta.toPlcValue(plcValueType))
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
package space.kscience.controls.plc4x
|
||||||
|
|
||||||
|
import org.apache.plc4x.java.api.types.PlcValueType
|
||||||
|
import org.apache.plc4x.java.api.value.PlcValue
|
||||||
|
import org.apache.plc4x.java.spi.values.*
|
||||||
|
import space.kscience.dataforge.meta.*
|
||||||
|
import space.kscience.dataforge.names.asName
|
||||||
|
import java.math.BigInteger
|
||||||
|
|
||||||
|
internal fun PlcValue.toMeta(): Meta = Meta {
|
||||||
|
when (plcValueType) {
|
||||||
|
null, PlcValueType.NULL -> value = Null
|
||||||
|
PlcValueType.BOOL -> value = this@toMeta.boolean.asValue()
|
||||||
|
PlcValueType.BYTE -> this@toMeta.byte.asValue()
|
||||||
|
PlcValueType.WORD -> this@toMeta.short.asValue()
|
||||||
|
PlcValueType.DWORD -> this@toMeta.int.asValue()
|
||||||
|
PlcValueType.LWORD -> this@toMeta.long.asValue()
|
||||||
|
PlcValueType.USINT -> this@toMeta.short.asValue()
|
||||||
|
PlcValueType.UINT -> this@toMeta.int.asValue()
|
||||||
|
PlcValueType.UDINT -> this@toMeta.long.asValue()
|
||||||
|
PlcValueType.ULINT -> this@toMeta.bigInteger.asValue()
|
||||||
|
PlcValueType.SINT -> this@toMeta.byte.asValue()
|
||||||
|
PlcValueType.INT -> this@toMeta.short.asValue()
|
||||||
|
PlcValueType.DINT -> this@toMeta.int.asValue()
|
||||||
|
PlcValueType.LINT -> this@toMeta.long.asValue()
|
||||||
|
PlcValueType.REAL -> this@toMeta.float.asValue()
|
||||||
|
PlcValueType.LREAL -> this@toMeta.double.asValue()
|
||||||
|
PlcValueType.CHAR -> this@toMeta.int.asValue()
|
||||||
|
PlcValueType.WCHAR -> this@toMeta.short.asValue()
|
||||||
|
PlcValueType.STRING -> this@toMeta.string.asValue()
|
||||||
|
PlcValueType.WSTRING -> this@toMeta.string.asValue()
|
||||||
|
PlcValueType.TIME -> this@toMeta.duration.toString().asValue()
|
||||||
|
PlcValueType.LTIME -> this@toMeta.duration.toString().asValue()
|
||||||
|
PlcValueType.DATE -> this@toMeta.date.toString().asValue()
|
||||||
|
PlcValueType.LDATE -> this@toMeta.date.toString().asValue()
|
||||||
|
PlcValueType.TIME_OF_DAY -> this@toMeta.time.toString().asValue()
|
||||||
|
PlcValueType.LTIME_OF_DAY -> this@toMeta.time.toString().asValue()
|
||||||
|
PlcValueType.DATE_AND_TIME -> this@toMeta.dateTime.toString().asValue()
|
||||||
|
PlcValueType.DATE_AND_LTIME -> this@toMeta.dateTime.toString().asValue()
|
||||||
|
PlcValueType.LDATE_AND_TIME -> this@toMeta.dateTime.toString().asValue()
|
||||||
|
PlcValueType.Struct -> this@toMeta.struct.forEach { (name, item) ->
|
||||||
|
set(name, item.toMeta())
|
||||||
|
}
|
||||||
|
|
||||||
|
PlcValueType.List -> {
|
||||||
|
val listOfMeta = this@toMeta.list.map { it.toMeta() }
|
||||||
|
if (listOfMeta.all { it.items.isEmpty() }) {
|
||||||
|
value = listOfMeta.map { it.value ?: Null }.asValue()
|
||||||
|
} else {
|
||||||
|
setIndexed("@list".asName(), list.map { it.toMeta() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlcValueType.RAW_BYTE_ARRAY -> this@toMeta.raw.asValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Value.toPlcValue(): PlcValue = when (type) {
|
||||||
|
ValueType.NUMBER -> when (val number = number) {
|
||||||
|
is Short -> PlcINT(number.toShort())
|
||||||
|
is Int -> PlcDINT(number.toInt())
|
||||||
|
is Long -> PlcLINT(number.toLong())
|
||||||
|
is Float -> PlcREAL(number.toFloat())
|
||||||
|
else -> PlcLREAL(number.toDouble())
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueType.STRING -> PlcSTRING(string)
|
||||||
|
ValueType.BOOLEAN -> PlcBOOL(boolean)
|
||||||
|
ValueType.NULL -> PlcNull()
|
||||||
|
ValueType.LIST -> TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Meta.toPlcValue(hint: PlcValueType): PlcValue = when (hint) {
|
||||||
|
PlcValueType.Struct -> PlcStruct(
|
||||||
|
items.entries.associate { (token, item) ->
|
||||||
|
token.toString() to item.toPlcValue(PlcValueType.Struct)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
PlcValueType.NULL -> PlcNull()
|
||||||
|
PlcValueType.BOOL -> PlcBOOL(boolean)
|
||||||
|
PlcValueType.BYTE -> PlcBYTE(int)
|
||||||
|
PlcValueType.WORD -> PlcWORD(int)
|
||||||
|
PlcValueType.DWORD -> PlcDWORD(int)
|
||||||
|
PlcValueType.LWORD -> PlcLWORD(long)
|
||||||
|
PlcValueType.USINT -> PlcLWORD(short)
|
||||||
|
PlcValueType.UINT -> PlcUINT(int)
|
||||||
|
PlcValueType.UDINT -> PlcDINT(long)
|
||||||
|
PlcValueType.ULINT -> (number as? BigInteger)?.let { PlcULINT(it) } ?: PlcULINT(long)
|
||||||
|
PlcValueType.SINT -> PlcSINT(int)
|
||||||
|
PlcValueType.INT -> PlcINT(int)
|
||||||
|
PlcValueType.DINT -> PlcDINT(int)
|
||||||
|
PlcValueType.LINT -> PlcLINT(long)
|
||||||
|
PlcValueType.REAL -> PlcREAL(float)
|
||||||
|
PlcValueType.LREAL -> PlcLREAL(double)
|
||||||
|
PlcValueType.CHAR -> PlcCHAR(int)
|
||||||
|
PlcValueType.WCHAR -> PlcWCHAR(short)
|
||||||
|
PlcValueType.STRING -> PlcSTRING(string)
|
||||||
|
PlcValueType.WSTRING -> PlcWSTRING(string)
|
||||||
|
PlcValueType.TIME -> PlcTIME(string?.let { java.time.Duration.parse(it) })
|
||||||
|
PlcValueType.LTIME -> PlcLTIME(string?.let { java.time.Duration.parse(it) })
|
||||||
|
PlcValueType.DATE -> PlcDATE(string?.let { java.time.LocalDate.parse(it) })
|
||||||
|
PlcValueType.LDATE -> PlcLDATE(string?.let { java.time.LocalDate.parse(it) })
|
||||||
|
PlcValueType.TIME_OF_DAY -> PlcTIME_OF_DAY(string?.let { java.time.LocalTime.parse(it) })
|
||||||
|
PlcValueType.LTIME_OF_DAY -> PlcLTIME_OF_DAY(string?.let { java.time.LocalTime.parse(it) })
|
||||||
|
PlcValueType.DATE_AND_TIME -> PlcDATE_AND_TIME(string?.let { java.time.LocalDateTime.parse(it) })
|
||||||
|
PlcValueType.DATE_AND_LTIME -> PlcDATE_AND_LTIME(string?.let { java.time.LocalDateTime.parse(it) })
|
||||||
|
PlcValueType.LDATE_AND_TIME -> PlcLDATE_AND_TIME(string?.let { java.time.LocalDateTime.parse(it) })
|
||||||
|
PlcValueType.List -> PlcList().apply {
|
||||||
|
value?.list?.forEach { add(it.toPlcValue()) }
|
||||||
|
getIndexed("@list").forEach { (_, meta) ->
|
||||||
|
if (meta.items.isEmpty()) {
|
||||||
|
meta.value?.let { add(it.toPlcValue()) }
|
||||||
|
} else {
|
||||||
|
add(meta.toPlcValue(PlcValueType.Struct))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlcValueType.RAW_BYTE_ARRAY -> PlcRawByteArray(
|
||||||
|
value?.list?.map { it.number.toByte() }?.toByteArray() ?: error("The meta content is not byte array")
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user