PLC4x bindings

This commit is contained in:
Alexander Nozik 2024-03-18 17:18:31 +03:00
parent 70ab60f98c
commit 78dade4b49
4 changed files with 206 additions and 0 deletions

View 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
}

View File

@ -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){
}
}

View File

@ -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))
}

View File

@ -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")
)
}