Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
plugins {
alias(libs.plugins.kotlin)
alias(libs.plugins.modl)
// alias(libs.plugins.dokka) TODO: Investigate Dokka for automatic generation of module docs
}

subprojects {
Expand All @@ -21,10 +20,10 @@ ignitionModule {

projectScopes.putAll(
mapOf(
":client" to "C",
":common" to "GDC",
":designer" to "D",
":gateway" to "G",
projects.common.dependencyProject.path to "GDC",
projects.gateway.dependencyProject.path to "G",
projects.designer.dependencyProject.path to "D",
projects.client.dependencyProject.path to "C",
),
)

Expand Down
10 changes: 3 additions & 7 deletions client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@ plugins {
kotlin("jvm")
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(libs.versions.java.get()))
}
}

kotlin {
jvmToolchain(libs.versions.java.map(String::toInt).get())
jvmToolchain {
languageVersion.set(libs.versions.java.map(JavaLanguageVersion::of))
}
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.inductiveautomation.ignition.common.script.ScriptManager
import com.inductiveautomation.vision.api.client.AbstractClientModuleHook
import org.imdc.extensions.common.DatasetExtensions
import org.imdc.extensions.common.ExtensionDocProvider
import org.imdc.extensions.common.PyDatasetBuilder
import org.imdc.extensions.common.UtilitiesExtensions
import org.imdc.extensions.common.addPropertyBundle
import org.imdc.extensions.common.expressions.IsAvailableFunction
Expand All @@ -24,6 +25,12 @@ class ClientHook : AbstractClientModuleHook() {
addPropertyBundle<UtilitiesExtensions>()
addPropertyBundle<ClientProjectExtensions>()
}

PyDatasetBuilder.register()
}

override fun shutdown() {
PyDatasetBuilder.unregister()
}

override fun initializeScriptManager(manager: ScriptManager) {
Expand Down
10 changes: 3 additions & 7 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@ plugins {
kotlin("jvm")
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(libs.versions.java.get()))
}
}

kotlin {
jvmToolchain(libs.versions.java.map(String::toInt).get())
jvmToolchain {
languageVersion.set(libs.versions.java.map(JavaLanguageVersion::of))
}
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,24 @@ import com.inductiveautomation.ignition.common.script.builtin.KeywordArgs
import com.inductiveautomation.ignition.common.script.hints.ScriptArg
import com.inductiveautomation.ignition.common.script.hints.ScriptFunction
import com.inductiveautomation.ignition.common.util.DatasetBuilder
import com.inductiveautomation.ignition.common.xmlserialization.ClassNameResolver
import org.apache.poi.ss.usermodel.CellType.BOOLEAN
import org.apache.poi.ss.usermodel.CellType.FORMULA
import org.apache.poi.ss.usermodel.CellType.NUMERIC
import org.apache.poi.ss.usermodel.CellType.STRING
import org.apache.poi.ss.usermodel.DateUtil
import org.apache.poi.ss.usermodel.WorkbookFactory
import org.python.core.Py
import org.python.core.PyBaseString
import org.python.core.PyBoolean
import org.python.core.PyFloat
import org.python.core.PyFunction
import org.python.core.PyInteger
import org.python.core.PyLong
import org.python.core.PyObject
import org.python.core.PyString
import org.python.core.PyType
import org.python.core.PyUnicode
import java.io.File
import java.math.BigDecimal
import java.util.Date
Expand Down Expand Up @@ -405,4 +414,35 @@ object DatasetExtensions {
left == right
}
}

private val classNameResolver = ClassNameResolver.createBasic()

@ScriptFunction(docBundlePrefix = "DatasetExtensions")
@KeywordArgs(
names = ["**columns"],
types = [KeywordArgs::class],
)
fun builder(args: Array<PyObject>, keywords: Array<String>): DatasetBuilder {
if (args.size != keywords.size) throw Py.ValueError("builder must be called with only keyword arguments")
val colNames = keywords.toList()
val colTypes = args.mapIndexed { i, type ->
try {
type.asJavaClass()
} catch (e: ClassCastException) {
throw Py.TypeError("${keywords[i]} was a ${type::class.simpleName}, but should be a type or valid string typecode")
}
}
return DatasetBuilder.newBuilder().colNames(colNames).colTypes(colTypes)
}

fun PyObject.asJavaClass(): Class<*>? = when (this) {
is PyBaseString -> classNameResolver.classForName(asString())
!is PyType -> throw ClassCastException()
PyString.TYPE, PyUnicode.TYPE -> String::class.java
PyBoolean.TYPE -> Boolean::class.java
PyInteger.TYPE -> Int::class.java
PyLong.TYPE -> Long::class.java
PyFloat.TYPE -> Double::class.java
else -> toJava<Class<*>>()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.imdc.extensions.common

import com.inductiveautomation.ignition.common.Dataset
import com.inductiveautomation.ignition.common.script.DisposablePyObjectAdapter
import com.inductiveautomation.ignition.common.util.DatasetBuilder
import com.inductiveautomation.ignition.common.xmlserialization.ClassNameResolver
import org.imdc.extensions.common.DatasetExtensions.asJavaClass
import org.python.core.Py
import org.python.core.PyObject
import org.python.core.adapter.PyObjectAdapter

@Suppress("unused")
class PyDatasetBuilder(private val builder: DatasetBuilder) : PyObject() {
private val resolver = ClassNameResolver.createBasic()

fun colNames(vararg names: String) = apply {
builder.colNames(names.toList())
}

fun colNames(names: List<String>) = apply {
builder.colNames(names)
}

fun colTypes(vararg types: PyObject) = apply {
builder.colTypes(types.map { it.asJavaClass() })
}

fun colTypes(types: List<Class<*>>) = apply {
builder.colTypes(types)
}

fun addRow(vararg values: Any?) = apply {
builder.addRow(*values)
}

fun build(): Dataset = builder.build()

companion object {
private val adapter = DisposablePyObjectAdapter(DatasetBuilderAdapter())

fun register() {
Py.getAdapter().addPostClass(adapter)
}

fun unregister() {
adapter.dispose()
}
}
}

class DatasetBuilderAdapter : PyObjectAdapter {
override fun adapt(o: Any?): PyObject = PyDatasetBuilder(o as DatasetBuilder)
override fun canAdapt(o: Any?): Boolean = o is DatasetBuilder
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import java.lang.reflect.Method
class PyObjectAppendable(target: PyObject) : Appendable {
private val writeMethod = target.__getattr__("write")

override fun append(csq: CharSequence): Appendable = this.apply {
override fun append(csq: CharSequence?): Appendable = apply {
writeMethod.__call__(Py.newStringOrUnicode(csq.toString()))
}

override fun append(csq: CharSequence, start: Int, end: Int): Appendable = this.apply {
append(csq.subSequence(start, end))
override fun append(csq: CharSequence?, start: Int, end: Int): Appendable = apply {
append(csq.toString().subSequence(start, end))
}

override fun append(c: Char): Appendable = this.apply {
override fun append(c: Char): Appendable = apply {
append(c.toString())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@ columnsEqual.param.dataset2=The second dataset. Must not be null.
columnsEqual.param.ignoreCase=Pass True if the column names should be compared case-insensitive. Defaults to False.
columnsEqual.param.includeTypes=Pass True if the column types must match as well. Defaults to True.
columnsEqual.returns=True if the two datasets have the same columns, per additional parameters.

builder.desc=Creates a new dataset using supplied column names and types.
builder.param.**columns=Optional. Keyword arguments can be supplied to predefine column names and types. The value of the argument should be string "typecode" (see system.dataset.fromCSV) or a Java or Python class instance.
builder.returns=A DatasetBuilder object. Use <code>addRow(value, ...)</code> to add new values, and <code>build()</code> to construct the final dataset. \
If keyword arguments were not supplied, column names and types can be manually declared using <code>colNames()</code> and <code>colTypes()</code>.
Loading