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
5 changes: 5 additions & 0 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ jobs:
cache: 'gradle'
- name: Build
run: ./gradlew build
- name: Upload Unsigned Module
uses: actions/upload-artifact@v3
with:
name: ignition-extensions-unsigned
path: build/Ignition-Extensions.unsigned.modl
15 changes: 0 additions & 15 deletions LICENSE.html

This file was deleted.

13 changes: 13 additions & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
MIT License
Copyright (c) 2023 Ignition Module Development Community
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ ignitionModule {
id.set("org.imdc.extensions.IgnitionExtensions")
moduleVersion.set("${project.version}")
moduleDescription.set("Useful but niche extensions to Ignition for power users")
license.set("LICENSE.html")
license.set("LICENSE.txt")
requiredIgnitionVersion.set(libs.versions.ignition)

projectScopes.putAll(
Expand Down
63 changes: 63 additions & 0 deletions common/src/main/kotlin/org/imdc/extensions/common/TagExtensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.imdc.extensions.common

import com.inductiveautomation.ignition.common.config.PyTagDictionary
import com.inductiveautomation.ignition.common.config.PyTagList
import com.inductiveautomation.ignition.common.script.PyArgParser
import com.inductiveautomation.ignition.common.script.ScriptContext
import com.inductiveautomation.ignition.common.script.builtin.KeywordArgs
import com.inductiveautomation.ignition.common.script.hints.ScriptFunction
import com.inductiveautomation.ignition.common.tags.config.TagConfigurationModel
import com.inductiveautomation.ignition.common.tags.model.TagPath
import com.inductiveautomation.ignition.common.tags.paths.parser.TagPathParser
import org.python.core.PyDictionary
import org.python.core.PyObject

abstract class TagExtensions {
@UnsafeExtension
@ScriptFunction(docBundlePrefix = "TagExtensions")
@KeywordArgs(
names = ["basePath", "recursive"],
types = [String::class, Boolean::class],
)
fun getLocalConfiguration(args: Array<PyObject>, keywords: Array<String>): PyTagList {
val parsedArgs = PyArgParser.parseArgs(
args,
keywords,
arrayOf("basePath", "recursive"),
arrayOf(Any::class.java, Any::class.java),
"getLocalConfiguration",
)
val configurationModels = getConfigurationImpl(
parseTagPath(parsedArgs.requireString("basePath")),
parsedArgs.getBoolean("recursive").orElse(false),
)

return configurationModels.toPyTagList()
}

protected open fun parseTagPath(path: String): TagPath {
val parsed = TagPathParser.parse(ScriptContext.defaultTagProvider(), path)
if (TagPathParser.isRelativePath(parsed) && ScriptContext.relativeTagPathRoot() != null) {
return TagPathParser.derelativize(parsed, ScriptContext.relativeTagPathRoot())
}
return parsed
}

private fun TagConfigurationModel.toPyDictionary(): PyDictionary {
return PyTagDictionary.Builder()
.setTagPath(path)
.setTagType(type)
.build(this).apply {
if (children.isNotEmpty()) {
put("tags", children.toPyTagList())
}
}
}

private fun List<TagConfigurationModel>.toPyTagList() = fold(PyTagList()) { acc, childModel ->
acc.add(childModel.toPyDictionary())
acc
}

protected abstract fun getConfigurationImpl(basePath: TagPath, recursive: Boolean): List<TagConfigurationModel>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
getLocalConfiguration.desc=WIP
getLocalConfiguration.param.basePath=WIP
getLocalConfiguration.param.recursive=WIP
getLocalConfiguration.returns=WIP
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class GatewayHook : AbstractGatewayModuleHook() {
addPropertyBundle<DatasetExtensions>()
addPropertyBundle<UtilitiesExtensions>()
addPropertyBundle<GatewayProjectExtensions>()
addPropertyBundle<GatewayTagExtensions>()
}

PyDatasetBuilder.register()
Expand All @@ -44,6 +45,7 @@ class GatewayHook : AbstractGatewayModuleHook() {
addScriptModule("system.dataset", DatasetExtensions, ExtensionDocProvider)
addScriptModule("system.util", UtilitiesExtensions(context), ExtensionDocProvider)
addScriptModule("system.project", GatewayProjectExtensions(context), ExtensionDocProvider)
addScriptModule("system.tag", GatewayTagExtensions(context), ExtensionDocProvider)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.imdc.extensions.gateway

import com.inductiveautomation.ignition.common.tags.config.TagConfigurationModel
import com.inductiveautomation.ignition.common.tags.model.TagPath
import com.inductiveautomation.ignition.gateway.model.GatewayContext
import org.imdc.extensions.common.TagExtensions
import org.python.core.Py

class GatewayTagExtensions(private val context: GatewayContext) : TagExtensions() {
override fun getConfigurationImpl(basePath: TagPath, recursive: Boolean): List<TagConfigurationModel> {
val provider = (
context.tagManager.getTagProvider(basePath.source)
?: throw Py.ValueError("No such tag provider ${basePath.source}")
)

return provider.getTagConfigsAsync(listOf(basePath), recursive, true).get()
}
}