From bb569f0ab4e25c4dfa9cf4d5a079a59ca09e0373 Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 31 Aug 2025 20:32:13 +0200 Subject: [PATCH 01/19] new classes added for csparql streaming-monitoring --- build.gradle | 1 + .../uio/microobject/ast/stmt/MonitorStmt.kt | 96 +++++++++++ .../no/uio/microobject/ast/stmt/WindowStmt.kt | 27 +++ .../no/uio/microobject/data/StreamManager.kt | 158 ++++++++++++++++++ 4 files changed, 282 insertions(+) create mode 100644 src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt create mode 100644 src/main/kotlin/no/uio/microobject/ast/stmt/WindowStmt.kt create mode 100644 src/main/kotlin/no/uio/microobject/data/StreamManager.kt diff --git a/build.gradle b/build.gradle index 83cb4bd3..da465d81 100644 --- a/build.gradle +++ b/build.gradle @@ -49,6 +49,7 @@ dependencies { implementation 'com.sksamuel.hoplite:hoplite-yaml:1.4.1' implementation 'com.github.owlcs:ontapi:2.1.0' implementation 'org.jline:jline:3.21.0' + implementation files('libs/csparql-ui-0.9.7-jar-with-dependencies.jar') } diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt new file mode 100644 index 00000000..325d0b37 --- /dev/null +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt @@ -0,0 +1,96 @@ +package no.uio.microobject.ast.stmt + +import com.sksamuel.hoplite.ConfigLoader +import no.uio.microobject.ast.* +import no.uio.microobject.ast.expr.FALSEEXPR +import no.uio.microobject.ast.expr.LiteralExpr +import no.uio.microobject.ast.expr.TRUEEXPR +import no.uio.microobject.runtime.* +import no.uio.microobject.type.* +import org.apache.jena.datatypes.xsd.XSDDatatype +import java.io.File + +import eu.larkc.csparql.core.engine.CsparqlQueryResultProxy +import eu.larkc.csparql.common.RDFTuple + +data class MonitorStmt(val target : Location, val query: Expression, val params : List, val pos : Int = -1, val declares: Type?) : + Statement { + override fun toString(): String = "$target := monitor($query, ${params.joinToString(",")})" + override fun getRDF(): String { + var s = """ + prog:stmt${this.hashCode()} rdf:type smol:MonitorStatement. + prog:stmt${this.hashCode()} smol:hasTarget prog:loc${target.hashCode()}. + prog:stmt${this.hashCode()} smol:hasQuery prog:expr${query.hashCode()}. + prog:stmt${this.hashCode()} smol:Line '$pos'^^xsd:integer. + + """.trimIndent() + for (i in params.indices){ + s += "prog:stmt${this.hashCode()} smol:hasParameter [smol:hasParameterIndex $i ; smol:hasParameterValue prog:expr${params[i].hashCode()}; ].\n" + s += params[i].getRDF() + } + // return s + target.getRDF() + return s + target.getRDF() + query.getRDF() + // '${literal.removePrefix("\"").removeSuffix("\"")}' + } + + + override fun eval(heapObj: Memory, stackFrame: StackEntry, interpreter: Interpreter): EvalResult { + val name = Names.getObjName("Monitor") + interpreter.streamManager.registerQuery(name, query, params, stackFrame.store, interpreter.heap, stackFrame.obj) + if (declares is ComposedType && declares.getPrimary().getNameString().equals("Monitor")) { + // only consider the first type for now. e.g., Monitor + interpreter.streamManager.addMonitor(name, MonitorObject(name, declares.params[0])) + } else { + throw Exception("Monitor statement can only be assigned to type Monitor") + } + return replaceStmt(AssignStmt(target, name, declares = declares), stackFrame) + } + +} + +class MonitorObject(private val name: LiteralExpr, private val declaredType: Type) { + + private fun iriToLiteral(iri: String, interpreter: Interpreter): LiteralExpr { + + if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#integer")) return LiteralExpr(iri.split("^^")[0], INTTYPE) + if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#boolean")) return LiteralExpr(iri.split("^^")[0], BOOLEANTYPE) + if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#double")) return LiteralExpr(iri.split("^^")[0], DOUBLETYPE) + if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#string")) return LiteralExpr(iri.split("^^")[0], STRINGTYPE) + val literal = iri.removePrefix(interpreter.settings.runPrefix) + for (obj in interpreter.heap.keys + interpreter.simMemory.keys) + if (obj.literal.equals(literal)) return obj + return LiteralExpr("ERROR") + } + + fun getWindowResults(interpreter: Interpreter): LiteralExpr { + val rdfTable = interpreter.streamManager.getQueryResults(name) + var list = LiteralExpr("null") + + if (rdfTable != null) { + + val resIt = rdfTable.iterator() + while (resIt.hasNext()) { + val rdfTuple = resIt.next() + try { + // only consider first result for now + var literal = iriToLiteral(rdfTuple.get(0), interpreter) + if (literal.tag != declaredType) + throw Exception("Monitor parameter has incorrect type") + + val name = Names.getObjName("List") + val newMemory: Memory = mutableMapOf() + newMemory["content"] = literal + newMemory["next"] = list + interpreter.heap[name] = newMemory + list = name + + } catch (e: Exception) { + throw Exception("Error while processing query result: ${e.message}") + } + + } + } + return list + } +} + diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/WindowStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/WindowStmt.kt new file mode 100644 index 00000000..f9a382b5 --- /dev/null +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/WindowStmt.kt @@ -0,0 +1,27 @@ +package no.uio.microobject.ast.stmt + +import no.uio.microobject.ast.Expression +import no.uio.microobject.ast.Statement +import no.uio.microobject.ast.Location +import no.uio.microobject.runtime.EvalResult +import no.uio.microobject.runtime.Interpreter +import no.uio.microobject.runtime.Memory +import no.uio.microobject.runtime.StackEntry +import no.uio.microobject.type.Type + +data class WindowStmt(val target: Location, val monitor: Expression, val pos : Int = -1, val declares: Type?) : Statement { + override fun toString(): String = "$target := window($monitor)" + override fun getRDF(): String { + //TODO: extend ontology + return "" + } + + override fun eval(heapObj: Memory, stackFrame: StackEntry, interpreter: Interpreter): EvalResult { + val lit = interpreter.eval(monitor, stackFrame) + val monitorObj = interpreter.streamManager.getMonitor(lit) + if(monitorObj == null) + throw Exception("Object $monitorObj is not a monitor object") + val list = monitorObj.getWindowResults(interpreter) + return replaceStmt(AssignStmt(target, list, pos=pos, declares=declares), stackFrame) + } +} \ No newline at end of file diff --git a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt new file mode 100644 index 00000000..c1ffcfb4 --- /dev/null +++ b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt @@ -0,0 +1,158 @@ +package no.uio.microobject.data + +import no.uio.microobject.runtime.* +import no.uio.microobject.ast.expr.* +import no.uio.microobject.ast.Expression +import no.uio.microobject.ast.stmt.MonitorObject +import no.uio.microobject.main.Settings +import no.uio.microobject.type.* + + +import eu.larkc.csparql.core.engine.* +import eu.larkc.csparql.cep.api.* +import eu.larkc.csparql.core.* +import eu.larkc.csparql.common.* +import java.util.Observer; +import java.util.Observable +import java.util.concurrent.ConcurrentHashMap +import java.io.File +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.apache.log4j.BasicConfigurator +import org.apache.log4j.Level +import org.apache.log4j.Logger as Log4jLogger +import org.apache.log4j.PropertyConfigurator +import kotlin.text.toLong + +class ResultPusher(private val name: LiteralExpr, private val resultTable: MutableMap) : Observer { + public override fun update(o: Observable, arg: Any) { + val results: RDFTable = arg as RDFTable + resultTable[name] = results + } +} + +// Class managing streams +class StreamManager(private val settings: Settings, val staticTable: StaticTable, private val interpreter: Interpreter?) { + + private val engine: CsparqlEngineImpl = CsparqlEngineImpl() + private var engineInitialized = false + val LOG : Logger? = LoggerFactory.getLogger(StreamManager::class.java) + + private var streams: MutableMap> = mutableMapOf() + private var monitors: MutableMap = mutableMapOf() + + private var queryResults: MutableMap = ConcurrentHashMap() + + var clockVar : String? = null + var clockTimestampSec : String? = null + + init { + + // configure c-sparql logging + val propFile = File("src/main/resources/log4j_configuration/csparql_log4j.properties") + if (propFile.exists()) { + PropertyConfigurator.configure(propFile.absolutePath) + } else { + // only log errors if the logger config file is not present + BasicConfigurator.configure() + Log4jLogger.getRootLogger().level = Level.ERROR + System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "ERROR") + } + } + + private fun initEngine() { + + engine.initialize(true) // timestamp enabled + engineInitialized = true + } + + public fun getQueryResults(name: LiteralExpr): RDFTable? { + return queryResults[name] + } + + public fun registerStream(className: String, obj: LiteralExpr) { + if (!engineInitialized) initEngine() + val streamIri = "${settings.runPrefix}${obj.toString()}" + val stream = RdfStream(streamIri) + engine.registerStream(stream) + if (!streams.containsKey(className)) streams[className] = mutableMapOf() + streams[className]!![obj] = stream + } + + private fun getTimestamp(): Long { + if (clockTimestampSec != null) return secToMs(clockTimestampSec!!.toLong()) + return System.currentTimeMillis() + } + + private fun secToMs(s: Long): Long { + return s * 1000 + } + + public fun triggerStream(className: String, obj: LiteralExpr, methodName: String, stackEntry: StackEntry) { + val stream = streams[className]!![obj]!! + val expressions = interpreter!!.staticInfo.streamersTable[className]!![methodName]!! + + val subjIri = "${settings.runPrefix}${obj.literal}" + val timestamp = getTimestamp() + + for (expr in expressions) { + val res = interpreter.eval(expr, stackEntry) + + val predIri = "${settings.progPrefix}${className}_${expr.toString().removePrefix("this.").replace('.', '_')}" + val objIri = literalToIri(res) + + val quad = RdfQuadruple(subjIri, predIri, objIri, timestamp) + // println(quad.toString()) + stream.put(quad) + } + } + + private fun literalToIri(lit: LiteralExpr): String { + if (lit.tag == INTTYPE) return "\"${lit.literal}\"^^http://www.w3.org/2001/XMLSchema#integer" + if (lit.tag == BOOLEANTYPE) return "\"${lit.literal}\"^^http://www.w3.org/2001/XMLSchema#boolean" + if (lit.tag == DOUBLETYPE) return "\"${lit.literal}\"^^http://www.w3.org/2001/XMLSchema#double" + if (lit.tag == STRINGTYPE) return "\"${lit.literal}\"^^http://www.w3.org/2001/XMLSchema#string" + return "${settings.runPrefix}${lit.literal}" + } + + public fun addMonitor(name: LiteralExpr, monitor: MonitorObject) { + monitors[name] = monitor + } + + public fun getMonitor(name: LiteralExpr): MonitorObject? { + return monitors[name] + } + + public fun registerQuery(name: LiteralExpr, queryExpr : Expression, params: List, stackMemory: Memory, heap: GlobalMemory, obj: LiteralExpr, SPARQL : Boolean = true): CsparqlQueryResultProxy { + if (!engineInitialized) initEngine() + val queryStr = prepareQuery(name, queryExpr, params, stackMemory, heap, obj, SPARQL) + var resultProxy = engine.registerQuery(queryStr, true) // reasoning enabled + resultProxy.addObserver(ResultPusher(name, queryResults)) // each key is only used by one observer + return resultProxy + } + + private fun prepareQuery(name: LiteralExpr, queryExpr : Expression, params : List, stackMemory: Memory, heap: GlobalMemory, obj: LiteralExpr, SPARQL : Boolean = true) : String{ + val queryHeader = "REGISTER QUERY ${name.literal} AS " + + val queryBody = interpreter!!.prepareQuery(queryExpr, params, stackMemory, heap, obj, SPARQL) + .removePrefix("\"").removeSuffix("\"") + + var queryWithPrefixes = queryHeader + // for ((key, value) in settings.prefixMap()) queryWithPrefixes += "PREFIX $key: <$value>\n" + queryWithPrefixes += queryBody + queryWithPrefixes = queryWithPrefixes.replace("\\\"", "\"") + + // Replace occurrences of value:x with for each prefix in prefixMap + for ((key, value) in settings.prefixMap()) { + // Regex to match key: followed by a valid identifier (e.g., obj4) + val regex = Regex("""${Regex.escape(key)}:([A-Za-z0-9_]+)""") + queryWithPrefixes = queryWithPrefixes.replace(regex) { matchResult -> + "<$value${matchResult.groupValues[1]}>" + } + } + + return queryWithPrefixes + } + +} + \ No newline at end of file From af8246e24d0f53c13f9a1e223e368d0779f1346c Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 31 Aug 2025 20:34:09 +0200 Subject: [PATCH 02/19] grammar file updated for stream-monitor statements --- src/main/antlr/While.g4 | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/antlr/While.g4 b/src/main/antlr/While.g4 index 463c8741..d38bedba 100644 --- a/src/main/antlr/While.g4 +++ b/src/main/antlr/While.g4 @@ -31,7 +31,9 @@ SIMULATE : 'simulate'; VALIDATE : 'validate'; CLASSIFY : 'classify'; ADAPT : 'adapt'; +MONITOR : 'monitor'; TICK : 'tick'; +WINDOW : 'window'; BREAKPOINT : 'breakpoint'; SUPER : 'super'; DESTROY : 'destroy'; @@ -49,6 +51,8 @@ CLASSIFIES: 'classifies'; RETRIEVES: 'retrieves'; DOMAIN : 'domain'; CONTEXT : 'context'; +STREAMER : 'streamer'; +EMITS : 'emits'; //Keywords: constants TRUE : 'True'; @@ -94,6 +98,9 @@ FMU : 'FMO'; PORT : 'port'; SPARQLMODE : 'SPARQL'; INFLUXMODE : 'INFLUXDB'; +// MONITORTYPE : 'Monitor'; +// QUERYRESULTTYPE : 'QueryResult'; +CLOCK : 'clock'; // Note that the IN, OUT constants are also used in // TypeChecker.kt:translateType in the FMU branch; adapt strings there if // changing the syntax here @@ -117,13 +124,13 @@ namelist : NAME (COMMA NAME)*; program : (class_def)* MAIN statement END (class_def)*; //classes -class_def : (abs=ABSTRACT)? (hidden=HIDE)? CLASS className = NAME (LT namelist GT)? (EXTENDS superType = type)? OPARAN (external=fieldDeclList)? CPARAN +class_def : (abs=ABSTRACT)? (hidden=HIDE)? (streamer=STREAMER)? CLASS className = NAME (LT namelist GT)? (EXTENDS superType = type)? OPARAN (external=fieldDeclList)? CPARAN (internal = fieldDeclInitList)? (models_block)? (classifies_block (retrieves_block)?)? method_def* END; -method_def : (abs=ABSTRACT)? (builtinrule=RULE)? (domainrule=DOMAIN)? (overriding=OVERRIDE)? type NAME OPARAN paramList? CPARAN (statement END)?; +method_def : (abs=ABSTRACT)? (builtinrule=RULE)? (domainrule=DOMAIN)? (overriding=OVERRIDE)? type NAME OPARAN paramList? CPARAN emits_block? (statement END)?; models_block : MODELS owldescription=STRING SEMI #simple_models_block | MODELS OPARAN guard=expression CPARAN owldescription=STRING SEMI models_block #complex_models_block @@ -132,14 +139,18 @@ classifies_block : CLASSIFIES owldescription=STRING SEMI # adapt ; retrieves_block : RETRIEVES selectquery=STRING SEMI # adaptation_retrieves_block ; +emits_block : EMITS OPARAN expression (COMMA expression)* CPARAN; + //Statements statement : SKIP_S SEMI # skip_statment | ((declType = type)? target=expression ASS)? CLASSIFY OPARAN context=expression CPARAN SEMI # classify_statement | ADAPT OPARAN adapter=expression CPARAN SEMI # adapt_statement - | (declType = type)? expression ASS expression SEMI # assign_statement + | (declType = type)? target=expression ASS MONITOR OPARAN registeredQuery=expression (COMMA expression)* CPARAN SEMI # monitor_statement + | (clock = CLOCK)? (declType = type)? expression ASS expression SEMI # assign_statement | ((declType = type)? target=expression ASS)? SUPER OPARAN (expression (COMMA expression)*)? CPARAN SEMI # super_statement | RETURN expression SEMI # return_statement | fmu=expression DOT TICK OPARAN time=expression CPARAN SEMI # tick_statement + | (declType = type)? target=expression ASS WINDOW OPARAN monitor=expression CPARAN SEMI # window_statement | ((declType = type)? target=expression ASS)? expression DOT NAME OPARAN (expression (COMMA expression)*)? CPARAN SEMI # call_statement // TODO: allow new statements without assignment | (declType = type)? target=expression ASS NEW newType = type OPARAN (expression (COMMA expression)*)? CPARAN (MODELS owldescription = expression)? SEMI # create_statement @@ -194,6 +205,7 @@ expression : THIS # this_expression type : NAME #simple_type | NAME LT typelist GT #nested_type | FMU OBRACK fmuParamList? CBRACK #fmu_type + // | MONITORTYPE LT typelist GT #monitor_type ; typelist : type (COMMA type)*; param : type NAME; From 986a7909561975b1f9d4c4b00b34ed4ba8daf295 Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 31 Aug 2025 20:34:52 +0200 Subject: [PATCH 03/19] monitor object added to stdlib, test updated --- src/main/resources/StdLib.smol | 4 +++- .../kotlin/no/uio/microobject/test/triples/OWLQueryTest.kt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/resources/StdLib.smol b/src/main/resources/StdLib.smol index 39037aba..7720c9fb 100644 --- a/src/main/resources/StdLib.smol +++ b/src/main/resources/StdLib.smol @@ -127,4 +127,6 @@ class ExplList(LISTTT content, ExplList next) end end -class ExplPair(PAIRTONET first, PAIRTTWOT second) end \ No newline at end of file +class ExplPair(PAIRTONET first, PAIRTTWOT second) end + +hidden class Monitor() end diff --git a/src/test/kotlin/no/uio/microobject/test/triples/OWLQueryTest.kt b/src/test/kotlin/no/uio/microobject/test/triples/OWLQueryTest.kt index 526bf80d..95169fb1 100644 --- a/src/test/kotlin/no/uio/microobject/test/triples/OWLQueryTest.kt +++ b/src/test/kotlin/no/uio/microobject/test/triples/OWLQueryTest.kt @@ -17,7 +17,7 @@ class OWLQueryTest: MicroObjectTest() { "OWL query 1" { val s = interpreter.owlQuery(q1) - assertEquals(s.count(), 8) + assertEquals(s.count(), 9) } "OWL query 2" { val s = interpreter.owlQuery(q2) From 0e7c3082f7314062459fc954a4737294e82bd995 Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 31 Aug 2025 20:43:51 +0200 Subject: [PATCH 04/19] translate, interpreter and statements updated for csparql --- .../no/uio/microobject/ast/Translate.kt | 48 ++++++++++++++++++- .../no/uio/microobject/ast/stmt/AssignStmt.kt | 12 ++++- .../no/uio/microobject/ast/stmt/CallStmt.kt | 8 +++- .../uio/microobject/ast/stmt/ConstructStmt.kt | 3 ++ .../no/uio/microobject/ast/stmt/CreateStmt.kt | 3 ++ .../no/uio/microobject/ast/stmt/ReturnStmt.kt | 11 +++++ .../microobject/ast/stmt/StoreReturnStmt.kt | 2 +- .../no/uio/microobject/runtime/Interpreter.kt | 3 ++ .../no/uio/microobject/runtime/State.kt | 5 +- 9 files changed, 87 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/no/uio/microobject/ast/Translate.kt b/src/main/kotlin/no/uio/microobject/ast/Translate.kt index 16fafa6c..78f4b56e 100644 --- a/src/main/kotlin/no/uio/microobject/ast/Translate.kt +++ b/src/main/kotlin/no/uio/microobject/ast/Translate.kt @@ -22,6 +22,7 @@ class Translate : WhileBaseVisitor() { private val classifiesTable: MutableMap> = mutableMapOf() private val checkClassifiesTable: MutableMap>> = mutableMapOf() private val contextTable : MutableMap = mutableMapOf() + private val streamersTable : MutableMap>> = mutableMapOf() private fun translateModels(ctx : Models_blockContext) : Pair>, String>{ if(ctx is Simple_models_blockContext) @@ -75,6 +76,11 @@ class Translate : WhileBaseVisitor() { addClassifyQuery(cl.className.text, cl.classifies_block(), null) } } + + // create streamer configuration + if (cl.streamer != null) { + streamersTable[cl.className.text] = mutableMapOf() + } val inFields = if(cl.external != null) { var res = listOf() if(cl.external.fieldDecl() != null) { @@ -141,6 +147,14 @@ class Translate : WhileBaseVisitor() { val params = if (nm.paramList() != null) paramListTranslate(nm.paramList()) else listOf() res[nm.NAME().text] = MethodInfo(SkipStmt(ctx!!.start.line), params, nm.builtinrule != null, nm.domainrule != null, cl.className.text, metType) } + if (nm.emits_block() != null) { + if (!streamersTable.containsKey(cl.className.text)) + throw Exception("Class with methods containing emits clause must be a streamer: ${nm.NAME().text}") + + streamersTable[cl.className.text]!![nm.NAME().text] = mutableSetOf() + for(i in 0 until nm.emits_block().expression().size) + streamersTable[cl.className.text]!![nm.NAME().text]!! += visit(nm.emits_block().expression(i)) as Expression + } } table[cl.className.text] = Pair(fields, res) } @@ -179,7 +193,7 @@ class Translate : WhileBaseVisitor() { return Pair( StackEntry(visit(ctx.statement()) as Statement, mutableMapOf(), Names.getObjName("_Entry_"), Names.getStackId()), - StaticTable(fieldTable, methodTable, hierarchy, modelsTable, hidden, owldescr, checkClassifiesTable, contextTable) + StaticTable(fieldTable, methodTable, hierarchy, modelsTable, hidden, owldescr, checkClassifiesTable, contextTable, streamersTable) ) } @@ -402,6 +416,35 @@ class Translate : WhileBaseVisitor() { return SimulationStmt(target, path, res, ctx!!.start.line, declares) } + override fun visitMonitor_statement(ctx: Monitor_statementContext?): ProgramElement { + val target = visit(ctx!!.target) as Location + if(ctx.declType != null) { + val decl = getClassDecl(ctx) + val className = if(decl == null) ERRORTYPE.name else decl!!.className.text + val targetType = TypeChecker.translateType(ctx.declType, className, mutableMapOf()) + target.setType(targetType) + } + + val query = visit(ctx!!.registeredQuery) as Expression + val ll = emptyList().toMutableList() + for(i in 2 until ctx!!.expression().size) + ll += visit(ctx.expression(i)) as Expression + + return MonitorStmt(target, query, ll, ctx!!.start.line, target.getType()) + } + + override fun visitWindow_statement(ctx: Window_statementContext?): ProgramElement { + val target = visit(ctx!!.target) as Location + if(ctx.declType != null) { + val decl = getClassDecl(ctx) + val className = if(decl == null) ERRORTYPE.name else decl!!.className.text + val targetType = TypeChecker.translateType(ctx.declType, className, mutableMapOf()) + target.setType(targetType) + } + val monitor = visit(ctx!!.monitor) as Expression + return WindowStmt(target, monitor, ctx!!.start.line, declares=target.getType()) + } + override fun visitTick_statement(ctx: Tick_statementContext?): ProgramElement { return TickStmt(visit(ctx!!.fmu) as Expression, visit(ctx!!.time) as Expression ) } @@ -418,7 +461,8 @@ class Translate : WhileBaseVisitor() { val def = getClassDecl(ctx as RuleContext) val declares = if(ctx!!.declType == null) null else TypeChecker.translateType(ctx.declType, if(def != null) def!!.className.text else ERRORTYPE.name, mutableMapOf()) - return AssignStmt(visit(ctx!!.expression(0)) as Location, visit(ctx.expression(1)) as Expression, ctx!!.start.line, declares) + val isClock = if(ctx.clock == null) false else true + return AssignStmt(visit(ctx!!.expression(0)) as Location, visit(ctx.expression(1)) as Expression, isClock, ctx!!.start.line, declares) } override fun visitSkip_statment(ctx: Skip_statmentContext?): ProgramElement { diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/AssignStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/AssignStmt.kt index fb2cef62..3a6ee500 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/AssignStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/AssignStmt.kt @@ -10,9 +10,10 @@ import no.uio.microobject.runtime.Memory import no.uio.microobject.runtime.StackEntry import no.uio.microobject.type.BaseType import no.uio.microobject.type.Type +import no.uio.microobject.type.INTTYPE // Assignment, where value cannot refer to calls or object creations. -data class AssignStmt(val target : Location, val value : Expression, val pos : Int = -1, val declares: Type?) : +data class AssignStmt(val target : Location, val value : Expression, val isClock: Boolean = false, val pos : Int = -1, val declares: Type?) : Statement { override fun toString(): String = "$target := $value" override fun getRDF(): String { @@ -28,7 +29,14 @@ data class AssignStmt(val target : Location, val value : Expression, val pos : I override fun eval(heapObj: Memory, stackFrame : StackEntry, interpreter: Interpreter) : EvalResult { val res = interpreter.eval(value, stackFrame) when (target) { - is LocalVar -> stackFrame.store[target.name] = res + is LocalVar -> { + if (isClock && interpreter.streamManager.clockVar == null) { + interpreter.streamManager.clockVar = target.name + } + if (interpreter.streamManager.clockVar != null && target.name.equals(interpreter.streamManager.clockVar) + && res.tag == INTTYPE) interpreter.streamManager.clockTimestampSec = res.literal + stackFrame.store[target.name] = res + } is OwnVar -> { val got = interpreter.staticInfo.fieldTable[(stackFrame.obj.tag as BaseType).name] ?: throw Exception("Cannot find class ${stackFrame.obj.tag.name}") if (!got.map {it.name} .contains(target.name)) diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/CallStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/CallStmt.kt index 8e27012b..efd55c04 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/CallStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/CallStmt.kt @@ -39,8 +39,14 @@ data class CallStmt(val target : Location, val callee : Location, val method : S for (i in m.params.indices) { newMemory[m.params[i]] = interpreter.eval(params[i], stackFrame) } + + var emitFromMethod: String? = null + if (interpreter.staticInfo.streamersTable.containsKey(newObj.tag.name) && + interpreter.staticInfo.streamersTable[newObj.tag.name]!!.containsKey(method)) + emitFromMethod = method + return EvalResult( - StackEntry(StoreReturnStmt(target), stackFrame.store, stackFrame.obj, stackFrame.id), + StackEntry(StoreReturnStmt(target, emitFromMethod=emitFromMethod), stackFrame.store, stackFrame.obj, stackFrame.id), listOf(StackEntry(m.stmt, newMemory, newObj, Names.getStackId())) ) } diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/ConstructStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/ConstructStmt.kt index 1cc8aba7..f569b081 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/ConstructStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/ConstructStmt.kt @@ -109,6 +109,9 @@ data class ConstructStmt(val target : Location, val query: Expression, val param newListMemory["next"] = list interpreter.heap[newListName] = newListMemory list = newListName + if (interpreter.staticInfo.streamersTable.containsKey(className)) { + interpreter.streamManager.registerStream(className, newObjName) + } } } return replaceStmt(AssignStmt(target, list, declares = declares), stackFrame) diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/CreateStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/CreateStmt.kt index 35f90cc7..c80d5e51 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/CreateStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/CreateStmt.kt @@ -50,6 +50,9 @@ data class CreateStmt(val target : Location, val className: String, val params : interpreter.heap[name] = newMemory val localFrame = StackEntry(SkipStmt(), mutableMapOf(Pair("this", name)),name,0) n.filter { it.internalInit != null }.forEach { newMemory[it.name] = interpreter.eval(it.internalInit!!, localFrame) } + if (interpreter.staticInfo.streamersTable.containsKey(className)) { + interpreter.streamManager.registerStream(className, name) + } return replaceStmt(AssignStmt(target, name, declares = declares), stackFrame) } } \ No newline at end of file diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/ReturnStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/ReturnStmt.kt index 883a8831..84ab6321 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/ReturnStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/ReturnStmt.kt @@ -5,6 +5,7 @@ import no.uio.microobject.runtime.EvalResult import no.uio.microobject.runtime.Interpreter import no.uio.microobject.runtime.Memory import no.uio.microobject.runtime.StackEntry +import no.uio.microobject.type.BaseType // Return statement data class ReturnStmt(var value : Expression, val pos : Int = -1) : Statement { @@ -22,6 +23,7 @@ data class ReturnStmt(var value : Expression, val pos : Int = -1) : Statement { val over = interpreter.stack.pop() if (over.active is StoreReturnStmt) { val res = interpreter.eval(value, stackFrame) + checkTriggerStream(over.active, stackFrame, interpreter) return EvalResult( StackEntry( AssignStmt(over.active.target, res, declares = null), @@ -35,8 +37,17 @@ data class ReturnStmt(var value : Expression, val pos : Int = -1) : Statement { val active = over.active.first val next = over.active.second val res = interpreter.eval(value, stackFrame) + checkTriggerStream(over.active.first, stackFrame, interpreter) return replaceStmt(appendStmt(AssignStmt(active.target, res, declares = null), next), over) } throw Exception("Malformed heap") } + + // trigger stream at the end of emission method, right before returning the result + private fun checkTriggerStream(storeReturnStmt: StoreReturnStmt, stackFrame: StackEntry, interpreter: Interpreter) { + if (storeReturnStmt.emitFromMethod != null) { + val className = (stackFrame.obj.tag as BaseType).name + interpreter.streamManager.triggerStream(className, stackFrame.obj, storeReturnStmt.emitFromMethod, stackFrame) + } + } } \ No newline at end of file diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/StoreReturnStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/StoreReturnStmt.kt index f55840b5..8612b4ba 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/StoreReturnStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/StoreReturnStmt.kt @@ -8,7 +8,7 @@ import no.uio.microobject.runtime.Memory import no.uio.microobject.runtime.StackEntry // This is a runtime-syntax only statement which models that we will write the return value of the next method in the stack into target -data class StoreReturnStmt(val target : Location, val pos : Int = -1) : Statement { +data class StoreReturnStmt(val target : Location, val pos : Int = -1, val emitFromMethod: String? = null) : Statement { override fun toString(): String = "$target <- stack" override fun getRDF(): String { return """ diff --git a/src/main/kotlin/no/uio/microobject/runtime/Interpreter.kt b/src/main/kotlin/no/uio/microobject/runtime/Interpreter.kt index ae1fb91b..4297b78a 100644 --- a/src/main/kotlin/no/uio/microobject/runtime/Interpreter.kt +++ b/src/main/kotlin/no/uio/microobject/runtime/Interpreter.kt @@ -14,7 +14,9 @@ import kotlinx.coroutines.runBlocking import no.uio.microobject.ast.* import no.uio.microobject.ast.expr.LiteralExpr import no.uio.microobject.ast.stmt.ReturnStmt +import no.uio.microobject.ast.stmt.MonitorObject import no.uio.microobject.data.TripleManager +import no.uio.microobject.data.StreamManager import no.uio.microobject.main.Settings import no.uio.microobject.type.* import org.apache.jena.query.QueryExecution @@ -68,6 +70,7 @@ class Interpreter( // TripleManager used to provide virtual triples etc. val tripleManager : TripleManager = TripleManager(settings, staticInfo, this) + val streamManager : StreamManager = StreamManager(settings, staticInfo, this) /** * Evaluates a call on a method of a class diff --git a/src/main/kotlin/no/uio/microobject/runtime/State.kt b/src/main/kotlin/no/uio/microobject/runtime/State.kt index d7958935..cd510cd5 100644 --- a/src/main/kotlin/no/uio/microobject/runtime/State.kt +++ b/src/main/kotlin/no/uio/microobject/runtime/State.kt @@ -3,6 +3,7 @@ package no.uio.microobject.runtime import no.uio.microobject.ast.Expression import no.uio.microobject.ast.expr.LiteralExpr import no.uio.microobject.ast.Statement +import no.uio.microobject.ast.stmt.MonitorObject import no.uio.microobject.type.Type import org.apache.jena.query.ResultSet @@ -29,8 +30,8 @@ data class StaticTable( val hiddenSet: Set,//This set of classes is skipped by the lifting val owldescr: MutableMap, // This maps class names to the default models block val checkClassifiesTable: MutableMap>> = mutableMapOf(), // Queries for classification - val contextTable: MutableMap - + val contextTable: MutableMap, + val streamersTable: MutableMap>> ) { override fun toString(): String = """ From a998c240ef707c8b80973622b4e9f33f2d712e26 Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 31 Aug 2025 20:44:50 +0200 Subject: [PATCH 05/19] type checker updated for window and monitor statements --- .../no/uio/microobject/type/TypeChecker.kt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/main/kotlin/no/uio/microobject/type/TypeChecker.kt b/src/main/kotlin/no/uio/microobject/type/TypeChecker.kt index 598a8b66..81a2dcf5 100644 --- a/src/main/kotlin/no/uio/microobject/type/TypeChecker.kt +++ b/src/main/kotlin/no/uio/microobject/type/TypeChecker.kt @@ -1071,6 +1071,41 @@ class TypeChecker(private val ctx: WhileParser.ProgramContext, private val setti if(tickType != DOUBLETYPE) log("Tick statement expects a Double as second parameter, but got $tickType }.",ctx) } + is WhileParser.Monitor_statementContext -> { + // todo check monitor type + var expType: Type? = null + if (ctx.declType != null) { + val lhs = ctx.expression(0) + if (lhs !is WhileParser.Var_expressionContext) { + log("Variable declaration must declare a variable.", ctx) + } else { + val name = lhs.NAME().text + if (vars.keys.contains(name)) log("Variable $name declared twice.", ctx) + else { + expType = translateType(ctx.type(), className, generics) + vars[name] = expType + } + } + } + } + is WhileParser.Window_statementContext -> { + var expType: Type? = null + if (ctx.declType != null) { + val lhs = ctx.expression(0) + if (lhs !is WhileParser.Var_expressionContext) { + log("Variable declaration must declare a variable.", ctx) + } else { + val name = lhs.NAME().text + if (vars.keys.contains(name)) log("Variable $name declared twice.", ctx) + else { + expType = translateType(ctx.type(), className, generics) + vars[name] = expType + } + } + } + if(ctx.target != null && ctx.target !is WhileParser.Var_expressionContext && inRule) + log("Non-local access in rule method.", ctx) + } else -> { log("Statements with class ${ctx.javaClass} cannot be type checked",ctx) } From f35c4147e1730e88840774dc9a7ecc0a03dd152a Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 31 Aug 2025 21:33:26 +0200 Subject: [PATCH 06/19] static join enabled with pushStatic statement --- src/main/antlr/While.g4 | 2 + .../no/uio/microobject/ast/Translate.kt | 5 +++ .../microobject/ast/stmt/PushStaticStmt.kt | 43 +++++++++++++++++++ .../no/uio/microobject/data/StreamManager.kt | 13 ++++++ 4 files changed, 63 insertions(+) create mode 100644 src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt diff --git a/src/main/antlr/While.g4 b/src/main/antlr/While.g4 index d38bedba..c0858fd4 100644 --- a/src/main/antlr/While.g4 +++ b/src/main/antlr/While.g4 @@ -34,6 +34,7 @@ ADAPT : 'adapt'; MONITOR : 'monitor'; TICK : 'tick'; WINDOW : 'window'; +PUSH_STATIC: 'pushStatic'; BREAKPOINT : 'breakpoint'; SUPER : 'super'; DESTROY : 'destroy'; @@ -151,6 +152,7 @@ statement : SKIP_S SEMI | RETURN expression SEMI # return_statement | fmu=expression DOT TICK OPARAN time=expression CPARAN SEMI # tick_statement | (declType = type)? target=expression ASS WINDOW OPARAN monitor=expression CPARAN SEMI # window_statement + | PUSH_STATIC SEMI # pushStatic_statement | ((declType = type)? target=expression ASS)? expression DOT NAME OPARAN (expression (COMMA expression)*)? CPARAN SEMI # call_statement // TODO: allow new statements without assignment | (declType = type)? target=expression ASS NEW newType = type OPARAN (expression (COMMA expression)*)? CPARAN (MODELS owldescription = expression)? SEMI # create_statement diff --git a/src/main/kotlin/no/uio/microobject/ast/Translate.kt b/src/main/kotlin/no/uio/microobject/ast/Translate.kt index 78f4b56e..36d3308c 100644 --- a/src/main/kotlin/no/uio/microobject/ast/Translate.kt +++ b/src/main/kotlin/no/uio/microobject/ast/Translate.kt @@ -433,6 +433,11 @@ class Translate : WhileBaseVisitor() { return MonitorStmt(target, query, ll, ctx!!.start.line, target.getType()) } + override fun visitPushStatic_statement(ctx: PushStatic_statementContext?): ProgramElement { + return PushStaticStatement(ctx!!.start.line) + } + + override fun visitWindow_statement(ctx: Window_statementContext?): ProgramElement { val target = visit(ctx!!.target) as Location if(ctx.declType != null) { diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt new file mode 100644 index 00000000..3131f177 --- /dev/null +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt @@ -0,0 +1,43 @@ +package no.uio.microobject.ast.stmt + +import com.sksamuel.hoplite.ConfigLoader +import no.uio.microobject.ast.* +import no.uio.microobject.ast.expr.FALSEEXPR +import no.uio.microobject.ast.expr.LiteralExpr +import no.uio.microobject.ast.expr.TRUEEXPR +import no.uio.microobject.runtime.* +import no.uio.microobject.type.* +import no.uio.microobject.data.TripleSettings +import org.apache.jena.datatypes.xsd.XSDDatatype +import java.io.File + +import eu.larkc.csparql.core.engine.CsparqlQueryResultProxy +import eu.larkc.csparql.common.RDFTuple + +data class PushStaticStmt(val pos : Int = -1) : Statement { + override fun toString(): String = "pushStatic()" + override fun getRDF(): String { + var s = "prog:stmt${this.hashCode()} rdf:type smol:PushStaticStmt.".trimIndent() + return s + } + + + override fun eval(heapObj: Memory, stackFrame: StackEntry, interpreter: Interpreter): EvalResult { + // fixed iri: prog:staticTable + val namedIri = "${interpreter.settings.progPrefix}staticTable" + + // add only staticTable + val ts = TripleSettings( + sources = hashMapOf("heap" to false, "staticTable" to true, "vocabularyFile" to false, "fmos" to false, "externalOntology" to false, "urlOntology" to false), + guards = hashMapOf("heap" to true, "staticTable" to true), + virtualization = hashMapOf("heap" to true, "staticTable" to true, "fmos" to true), + jenaReasoner = interpreter.settings.reasoner, + cachedModel = null + ) + val sTableModel = interpreter.tripleManager.getModel(ts) + interpreter.streamManager.putStaticNamedGraph(namedIri, sTableModel) + + return EvalResult(null, emptyList()) + } + +} diff --git a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt index c1ffcfb4..445efe7a 100644 --- a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt +++ b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt @@ -16,12 +16,14 @@ import java.util.Observer; import java.util.Observable import java.util.concurrent.ConcurrentHashMap import java.io.File +import java.io.StringWriter import org.slf4j.Logger import org.slf4j.LoggerFactory import org.apache.log4j.BasicConfigurator import org.apache.log4j.Level import org.apache.log4j.Logger as Log4jLogger import org.apache.log4j.PropertyConfigurator +import org.apache.jena.rdf.model.Model import kotlin.text.toLong class ResultPusher(private val name: LiteralExpr, private val resultTable: MutableMap) : Observer { @@ -154,5 +156,16 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable return queryWithPrefixes } + public fun putStaticNamedGraph(iri: String, model: Model) { + if (!engineInitialized) initEngine() + + // serialize the model (RDF/XML matches the engine's first attempt) + val sw = StringWriter() + model.write(sw, "RDF/XML") + + // hand it to the C-SPARQL engine + engine.putStaticNamedModel(iri, sw.toString()) + } + } \ No newline at end of file From 8a25b34a827f32437c1df2ff7fca597dbb5504e9 Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 14 Sep 2025 14:12:41 +0200 Subject: [PATCH 07/19] minor fix - average returns double successfully --- .../uio/microobject/ast/stmt/MonitorStmt.kt | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt index 325d0b37..d7d86bf1 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt @@ -52,9 +52,23 @@ class MonitorObject(private val name: LiteralExpr, private val declaredType: Typ private fun iriToLiteral(iri: String, interpreter: Interpreter): LiteralExpr { - if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#integer")) return LiteralExpr(iri.split("^^")[0], INTTYPE) + if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#integer")) + return LiteralExpr(iri.split("^^")[0].removeSurrounding("\""), INTTYPE) if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#boolean")) return LiteralExpr(iri.split("^^")[0], BOOLEANTYPE) - if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#double")) return LiteralExpr(iri.split("^^")[0], DOUBLETYPE) + if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#double") || + iri.endsWith("^^http://www.w3.org/2001/XMLSchema#float") || + iri.endsWith("^^http://www.w3.org/2001/XMLSchema#decimal")) { + val raw = iri.split("^^")[0] + val inner = raw.removeSurrounding("\"") + // try to remove scientific notation + try { + val normalized = inner.toDouble() + val normalizedStr = "${normalized}" + return LiteralExpr(normalizedStr, DOUBLETYPE) + } catch (e: NumberFormatException) { + throw Exception("Invalid xsd:double literal: $raw") + } + } if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#string")) return LiteralExpr(iri.split("^^")[0], STRINGTYPE) val literal = iri.removePrefix(interpreter.settings.runPrefix) for (obj in interpreter.heap.keys + interpreter.simMemory.keys) @@ -75,7 +89,7 @@ class MonitorObject(private val name: LiteralExpr, private val declaredType: Typ // only consider first result for now var literal = iriToLiteral(rdfTuple.get(0), interpreter) if (literal.tag != declaredType) - throw Exception("Monitor parameter has incorrect type") + throw Exception("Monitor parameter has incorrect type (expected ${declaredType}, got ${literal.tag})") val name = Names.getObjName("List") val newMemory: Memory = mutableMapOf() From 9601d0a7273822ede6cc80efe46d338a15779643 Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 14 Sep 2025 14:14:33 +0200 Subject: [PATCH 08/19] push static with comma-separated sources enabled --- src/main/antlr/While.g4 | 2 +- .../no/uio/microobject/ast/Translate.kt | 12 ++++- .../microobject/ast/stmt/PushStaticStmt.kt | 44 +++++++++++++++---- .../no/uio/microobject/data/StreamManager.kt | 29 ++++++++++-- 4 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/main/antlr/While.g4 b/src/main/antlr/While.g4 index c0858fd4..9c23d988 100644 --- a/src/main/antlr/While.g4 +++ b/src/main/antlr/While.g4 @@ -152,7 +152,7 @@ statement : SKIP_S SEMI | RETURN expression SEMI # return_statement | fmu=expression DOT TICK OPARAN time=expression CPARAN SEMI # tick_statement | (declType = type)? target=expression ASS WINDOW OPARAN monitor=expression CPARAN SEMI # window_statement - | PUSH_STATIC SEMI # pushStatic_statement + | (declType = type)? target=expression ASS PUSH_STATIC OPARAN sources=expression CPARAN SEMI # pushStatic_statement | ((declType = type)? target=expression ASS)? expression DOT NAME OPARAN (expression (COMMA expression)*)? CPARAN SEMI # call_statement // TODO: allow new statements without assignment | (declType = type)? target=expression ASS NEW newType = type OPARAN (expression (COMMA expression)*)? CPARAN (MODELS owldescription = expression)? SEMI # create_statement diff --git a/src/main/kotlin/no/uio/microobject/ast/Translate.kt b/src/main/kotlin/no/uio/microobject/ast/Translate.kt index 36d3308c..398c5780 100644 --- a/src/main/kotlin/no/uio/microobject/ast/Translate.kt +++ b/src/main/kotlin/no/uio/microobject/ast/Translate.kt @@ -434,7 +434,17 @@ class Translate : WhileBaseVisitor() { } override fun visitPushStatic_statement(ctx: PushStatic_statementContext?): ProgramElement { - return PushStaticStatement(ctx!!.start.line) + val target = visit(ctx!!.target) as Location + if(ctx.declType != null) { + val decl = getClassDecl(ctx) + val className = if(decl == null) ERRORTYPE.name else decl!!.className.text + val targetType = TypeChecker.translateType(ctx.declType, className, mutableMapOf()) + target.setType(targetType) + } + + val sources = visit(ctx!!.sources) as Expression + + return PushStaticStmt(target, sources, ctx!!.start.line, target.getType()) } diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt index 3131f177..5cf76496 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt @@ -8,14 +8,16 @@ import no.uio.microobject.ast.expr.TRUEEXPR import no.uio.microobject.runtime.* import no.uio.microobject.type.* import no.uio.microobject.data.TripleSettings +import no.uio.microobject.main.ReasonerMode import org.apache.jena.datatypes.xsd.XSDDatatype +import org.apache.jena.rdf.model.Model import java.io.File import eu.larkc.csparql.core.engine.CsparqlQueryResultProxy import eu.larkc.csparql.common.RDFTuple -data class PushStaticStmt(val pos : Int = -1) : Statement { - override fun toString(): String = "pushStatic()" +data class PushStaticStmt(val target : Location, val sources: Expression, val pos : Int = -1, val declares: Type?) : Statement { + override fun toString(): String = "$target:=pushStatic(sources=$sources)" override fun getRDF(): String { var s = "prog:stmt${this.hashCode()} rdf:type smol:PushStaticStmt.".trimIndent() return s @@ -23,21 +25,45 @@ data class PushStaticStmt(val pos : Int = -1) : Statement { override fun eval(heapObj: Memory, stackFrame: StackEntry, interpreter: Interpreter): EvalResult { - // fixed iri: prog:staticTable - val namedIri = "${interpreter.settings.progPrefix}staticTable" + val namedIri = interpreter.streamManager.getStaticNamedIri() + + // parse sources from comma-separated string sources + val sourcesLit = interpreter.eval(sources, stackFrame) + if (sourcesLit.tag != STRINGTYPE) { + throw Exception("The sources parameter in pushStatic statement must be a string literal") + } + val sourcesStr = sourcesLit.literal.removeSurrounding("\"") + val sourcesList = sourcesStr.split(",") + val sourcesMap = hashMapOf( + "heap" to false, + "staticTable" to false, + "vocabularyFile" to false, + "externalOntology" to false, + "urlOntology" to false, + "fmos" to false + ) + for (s in sourcesList) { + val trimmed = s.trim() + if (sourcesMap.containsKey(trimmed)) { + sourcesMap[trimmed] = true + } else { + throw Exception("Unknown source '$trimmed' in pushStatic statement, only comma-separated [heap, staticTable, vocabularyFile, externalOntology, urlOntology, fmos] are allowed") + } + } - // add only staticTable val ts = TripleSettings( - sources = hashMapOf("heap" to false, "staticTable" to true, "vocabularyFile" to false, "fmos" to false, "externalOntology" to false, "urlOntology" to false), + sources = sourcesMap, guards = hashMapOf("heap" to true, "staticTable" to true), virtualization = hashMapOf("heap" to true, "staticTable" to true, "fmos" to true), jenaReasoner = interpreter.settings.reasoner, cachedModel = null ) - val sTableModel = interpreter.tripleManager.getModel(ts) - interpreter.streamManager.putStaticNamedGraph(namedIri, sTableModel) - return EvalResult(null, emptyList()) + val model = interpreter.tripleManager.getModel(ts) + + interpreter.streamManager.putStaticNamedGraph(namedIri, model) + val resultLit = LiteralExpr(namedIri, STRINGTYPE) + return replaceStmt(AssignStmt(target, resultLit, declares = declares), stackFrame) } } diff --git a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt index 445efe7a..cd3b83fb 100644 --- a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt +++ b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt @@ -47,6 +47,10 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable var clockVar : String? = null var clockTimestampSec : String? = null + var lastTimestamp: Long = 0 + var firstTsPassed: Boolean = false + + var nStaticGraphsPushed = 0 init { @@ -82,8 +86,18 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable } private fun getTimestamp(): Long { - if (clockTimestampSec != null) return secToMs(clockTimestampSec!!.toLong()) - return System.currentTimeMillis() + var ms: Long + if (clockTimestampSec != null) { + ms = secToMs(clockTimestampSec!!.toLong()) + if (!firstTsPassed) ms -= 1 // workaround: first timestamp is decreased by 1 ms + } else { + ms = System.currentTimeMillis() + } + + if (ms > lastTimestamp && !firstTsPassed) firstTsPassed = true + + lastTimestamp = ms + return ms } private fun secToMs(s: Long): Long { @@ -128,13 +142,14 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable public fun registerQuery(name: LiteralExpr, queryExpr : Expression, params: List, stackMemory: Memory, heap: GlobalMemory, obj: LiteralExpr, SPARQL : Boolean = true): CsparqlQueryResultProxy { if (!engineInitialized) initEngine() val queryStr = prepareQuery(name, queryExpr, params, stackMemory, heap, obj, SPARQL) - var resultProxy = engine.registerQuery(queryStr, true) // reasoning enabled + if (settings.verbose) println("Registering query:\n$queryStr") + var resultProxy = engine.registerQuery(queryStr, false) // reasoning disabled resultProxy.addObserver(ResultPusher(name, queryResults)) // each key is only used by one observer return resultProxy } private fun prepareQuery(name: LiteralExpr, queryExpr : Expression, params : List, stackMemory: Memory, heap: GlobalMemory, obj: LiteralExpr, SPARQL : Boolean = true) : String{ - val queryHeader = "REGISTER QUERY ${name.literal} AS " + val queryHeader = "REGISTER QUERY Query${name.literal} AS " val queryBody = interpreter!!.prepareQuery(queryExpr, params, stackMemory, heap, obj, SPARQL) .removePrefix("\"").removeSuffix("\"") @@ -167,5 +182,11 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable engine.putStaticNamedModel(iri, sw.toString()) } + public fun getStaticNamedIri(): String { + val s = "${settings.runPrefix}loadStatic${nStaticGraphsPushed}" + nStaticGraphsPushed += 1 + return s + } + } \ No newline at end of file From a09940f735d8b51a4ccfdc83af3474968569efb1 Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 14 Sep 2025 14:15:37 +0200 Subject: [PATCH 09/19] smol scripts for csparql unit tests added --- .../resources/csparql/01_physical_window.smol | 59 +++++++++++++ .../csparql/02_logical_window_tumbling.smol | 59 +++++++++++++ .../csparql/03_logical_window_step.smol | 59 +++++++++++++ .../resources/csparql/04_no_aggregation.smol | 55 ++++++++++++ src/test/resources/csparql/05_count.smol | 52 +++++++++++ src/test/resources/csparql/06_average.smol | 57 ++++++++++++ .../resources/csparql/07_stream_object.smol | 53 ++++++++++++ .../csparql/08_use_window_results.smol | 61 +++++++++++++ .../csparql/09_timestamp_function.smol | 59 +++++++++++++ .../csparql/10_multiple_streams.smol | 82 ++++++++++++++++++ src/test/resources/csparql/11_use_domain.smol | 86 +++++++++++++++++++ .../csparql/12_different_ranges.smol | 82 ++++++++++++++++++ .../resources/csparql/13_static_join.smol | 56 ++++++++++++ src/test/resources/csparql/example.owl | 27 ++++++ 14 files changed, 847 insertions(+) create mode 100644 src/test/resources/csparql/01_physical_window.smol create mode 100644 src/test/resources/csparql/02_logical_window_tumbling.smol create mode 100644 src/test/resources/csparql/03_logical_window_step.smol create mode 100644 src/test/resources/csparql/04_no_aggregation.smol create mode 100644 src/test/resources/csparql/05_count.smol create mode 100644 src/test/resources/csparql/06_average.smol create mode 100644 src/test/resources/csparql/07_stream_object.smol create mode 100644 src/test/resources/csparql/08_use_window_results.smol create mode 100644 src/test/resources/csparql/09_timestamp_function.smol create mode 100644 src/test/resources/csparql/10_multiple_streams.smol create mode 100644 src/test/resources/csparql/11_use_domain.smol create mode 100644 src/test/resources/csparql/12_different_ranges.smol create mode 100644 src/test/resources/csparql/13_static_join.smol create mode 100644 src/test/resources/csparql/example.owl diff --git a/src/test/resources/csparql/01_physical_window.smol b/src/test/resources/csparql/01_physical_window.smol new file mode 100644 index 00000000..37557c31 --- /dev/null +++ b/src/test/resources/csparql/01_physical_window.smol @@ -0,0 +1,59 @@ +// Commands: +// java -jar build/libs/smol.jar +// reada stream/01_physical_window.smol + +streamer class C(Int x, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT (SUM(?x) as ?sumX) + FROM STREAM %1 [RANGE TRIPLES 3] + WHERE { ?s prog:C_x ?x } + ", this); + this.m = m; + end + + Unit doStream() emits(this.x) + this.x = this.x + 1; + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end +end + +main + C o = new C(0, null); + o.register(); + + Int i = 100; + Int endAt = 200; + while i < endAt do + o.doStream(); + String res = o.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: 6 = 1+2+3 +// >>103: 6 +// >>104: 6 +// >>105: 15 = 4+5+6 +// >>106: 15 +// >>107: 15 +// >>108: 24 = 7+8+9 +// .. + +// Notes: +// - First 2 windows are empty because the window size is 3 triples +// - RANGE TRIPLES 3 = sliding window of 3 triples diff --git a/src/test/resources/csparql/02_logical_window_tumbling.smol b/src/test/resources/csparql/02_logical_window_tumbling.smol new file mode 100644 index 00000000..5054c0f9 --- /dev/null +++ b/src/test/resources/csparql/02_logical_window_tumbling.smol @@ -0,0 +1,59 @@ +streamer class C(Int x, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT (SUM(?x) as ?sumX) + FROM STREAM %1 [RANGE 3s TUMBLING] + WHERE { ?s prog:C_x ?x } + ", this); + this.m = m; + end + + Unit doStream() emits(this.x) + this.x = this.x + 1; + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end +end + +main + C o = new C(0, null); + o.register(); + + clock Int i = 100; + Int endAt = 200; + + while i < endAt do + o.doStream(); + String res = o.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: +// >>103: 6 = 1+2+3 +// >>104: 6 +// >>105: 6 +// >>106: 15 = 4+5+6 +// >>107: 15 +// >>108: 15 +// >>109: 24 = 7+8+9 +// .. + +// Notes: +// - External timestamp: clock variable increased by 1 second each iteration = timestamp increased by 1000 ms +// - RANGE 3s TUMBLING = tumbling window of 3 seconds +// - Similar output as in 01_physical_window.smol (3 seconds * 1 triple per second = 3 triples) +// - First 3 windows are empty because the window size is 3 seconds diff --git a/src/test/resources/csparql/03_logical_window_step.smol b/src/test/resources/csparql/03_logical_window_step.smol new file mode 100644 index 00000000..7caaeb68 --- /dev/null +++ b/src/test/resources/csparql/03_logical_window_step.smol @@ -0,0 +1,59 @@ +streamer class C(Int x, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT (SUM(?x) as ?sumX) + FROM STREAM %1 [RANGE 5s STEP 2s] + WHERE { ?s prog:C_x ?x } + ", this); + this.m = m; + end + + Unit doStream() emits(this.x) + this.x = this.x + 1; + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end +end + +main + C o = new C(0, null); + o.register(); + + clock Int i = 100; + Int endAt = 200; + + while i < endAt do + o.doStream(); + String res = o.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: 3 = 1+2 +// >>103: 3 +// >>104: 10 = 1+2+3+4 +// >>105: 10 +// >>106: 20 = 2+3+4+5+6 +// >>107: 20 +// >>108: 30 = 4+5+6+7+8 +// >>109: 30 +// >>110: 40 = 6+7+8+9+10 +// .. + +// Notes: +// - Step 2 seconds = new window every 2 seconds +// - Windows have {2, 4, 5, 5, 5, ...} elements + diff --git a/src/test/resources/csparql/04_no_aggregation.smol b/src/test/resources/csparql/04_no_aggregation.smol new file mode 100644 index 00000000..a1c28917 --- /dev/null +++ b/src/test/resources/csparql/04_no_aggregation.smol @@ -0,0 +1,55 @@ +streamer class C(Int x, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT ?x + FROM STREAM %1 [RANGE 5s STEP 2s] + WHERE { ?s prog:C_x ?x } + ", this); + this.m = m; + end + + Unit doStream() emits(this.x) + this.x = this.x + 1; + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end +end + +main + C o = new C(0, null); + o.register(); + + clock Int i = 100; + Int endAt = 200; + + while i < endAt do + o.doStream(); + String res = o.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: 1 2 +// >>103: 1 2 +// >>104: 1 2 3 4 +// >>105: 1 2 3 4 +// >>106: 2 3 4 5 6 +// >>107: 2 3 4 5 6 +// >>108: 4 5 6 7 8 +// .. + +// Notes: +// - Multiple elements in the window because there is no aggregation function \ No newline at end of file diff --git a/src/test/resources/csparql/05_count.smol b/src/test/resources/csparql/05_count.smol new file mode 100644 index 00000000..76403258 --- /dev/null +++ b/src/test/resources/csparql/05_count.smol @@ -0,0 +1,52 @@ +streamer class C(Int x, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT (COUNT(?x) as ?countX) + FROM STREAM %1 [RANGE 5s STEP 2s] + WHERE { ?s prog:C_x ?x } + ", this); + this.m = m; + end + + Unit doStream() emits(this.x) + this.x = this.x + 1; + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end +end + +main + C o = new C(0, null); + o.register(); + + clock Int i = 100; + Int endAt = 200; + + while i < endAt do + o.doStream(); + String res = o.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: 2 +// >>103: 2 +// >>104: 4 +// >>105: 4 +// >>106: 5 +// >>107: 5 +// >>108: 5 +// .. diff --git a/src/test/resources/csparql/06_average.smol b/src/test/resources/csparql/06_average.smol new file mode 100644 index 00000000..bbc68096 --- /dev/null +++ b/src/test/resources/csparql/06_average.smol @@ -0,0 +1,57 @@ +streamer class C(Int x, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT (AVG(?x) as ?avgX) + FROM STREAM %1 [RANGE 5s STEP 2s] + WHERE { ?s prog:C_x ?x } + ", this); + this.m = m; + end + + Unit doStream() emits(this.x) + this.x = this.x + 1; + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ doubleToString(l.content) ++ " "; + l = l.next; + end + return s; + end +end + +main + C o = new C(0, null); + o.register(); + + clock Int i = 100; + Int endAt = 200; + + while i < endAt do + o.doStream(); + String res = o.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: 1.5 = (1+2) / 2 +// >>103: 1.5 +// >>104: 2.5 = (1+2+3+4) / 4 +// >>105: 2.5 +// >>106: 4.0 = (2+3+4+5+6) / 5 +// >>107: 4.0 +// >>108: 6.0 = (4+5+6+7+8) / 5 +// >>109: 6.0 +// >>110: 8.0 = (6+7+8+9+10) / 5 +// .. + +// Notes: +// - Average returns double values (not integer) diff --git a/src/test/resources/csparql/07_stream_object.smol b/src/test/resources/csparql/07_stream_object.smol new file mode 100644 index 00000000..4a670597 --- /dev/null +++ b/src/test/resources/csparql/07_stream_object.smol @@ -0,0 +1,53 @@ +streamer class C(Int x, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT ?s + FROM STREAM %1 [RANGE 6s STEP 4s] + WHERE { ?s prog:C_x ?x } + ", this); + this.m = m; + end + + Unit doStream() emits(this, this.x) + this.x = this.x + 1; + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ l.content ++ " "; + l = l.next; + end + return s; + end +end + +main + C o = new C(0, null); + o.register(); + + clock Int i = 100; + Int endAt = 200; + + while i < endAt do + o.doStream(); + String res = o.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: +// >>103: +// >>104: obj5 obj5 obj5 obj5 +// >>105: obj5 obj5 obj5 obj5 +// >>106: obj5 obj5 obj5 obj5 +// >>107: obj5 obj5 obj5 obj5 +// >>108: obj5 obj5 obj5 obj5 obj5 obj5 +// >>109: obj5 obj5 obj5 obj5 obj5 obj5 +// .. diff --git a/src/test/resources/csparql/08_use_window_results.smol b/src/test/resources/csparql/08_use_window_results.smol new file mode 100644 index 00000000..5f0cb597 --- /dev/null +++ b/src/test/resources/csparql/08_use_window_results.smol @@ -0,0 +1,61 @@ +streamer class C(Int x, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT (SUM(?x) as ?sumX) + FROM STREAM %1 [RANGE 5s STEP 2s] + WHERE { ?s prog:C_x ?x } + ", this); + this.m = m; + end + + Unit doStream() emits(this.x) + this.x = this.x + 1; + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + if l.content / 20 * 20 == l.content then + s = s ++ "mult20 "; + end + l = l.next; + end + return s; + end +end + +main + C o = new C(0, null); + o.register(); + + clock Int i = 100; + Int endAt = 200; + + while i < endAt do + o.doStream(); + String res = o.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: 3 +// >>103: 3 +// >>104: 10 +// >>105: 10 +// >>106: 20 mult20 +// >>107: 20 mult20 +// >>108: 30 +// >>109: 30 +// >>110: 40 mult20 +// >>111: 40 mult20 +// >>112: 50 + +// Notes: +// - Shows how to use the window results (e.g., check if the sum is multiple of 20) diff --git a/src/test/resources/csparql/09_timestamp_function.smol b/src/test/resources/csparql/09_timestamp_function.smol new file mode 100644 index 00000000..3fa1f027 --- /dev/null +++ b/src/test/resources/csparql/09_timestamp_function.smol @@ -0,0 +1,59 @@ +streamer class C(Int x, Monitor m) + + Unit register() + Monitor m = monitor(" + PREFIX ext: + SELECT (ext:timestamp(?s, prog:C_x, ?x) as ?t) + FROM STREAM %1 [RANGE 5s STEP 2s] + WHERE { ?s prog:C_x ?x } + ", this); + this.m = m; + end + + Unit doStream() emits(this.x) + this.x = this.x + 1; + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end +end + +main + C o = new C(0, null); + o.register(); + + clock Int i = 100; + Int endAt = 200; + + while i < endAt do + o.doStream(); + String res = o.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: 99999 101000 +// >>103: 99999 101000 +// >>104: 99999 101000 102000 103000 +// >>105: 99999 101000 102000 103000 +// >>106: 101000 102000 103000 104000 105000 +// >>107: 101000 102000 103000 104000 105000 +// >>108: 103000 104000 105000 106000 107000 +// >>109: 103000 104000 105000 106000 107000 +// >>110: 105000 106000 107000 108000 109000 +// .. + +// Notes: +// - Timestamps are in milliseconds +// - The first timestamp is decreased by 1 as a workaround to C-SPARQL external timestamp bug diff --git a/src/test/resources/csparql/10_multiple_streams.smol b/src/test/resources/csparql/10_multiple_streams.smol new file mode 100644 index 00000000..0152b747 --- /dev/null +++ b/src/test/resources/csparql/10_multiple_streams.smol @@ -0,0 +1,82 @@ +class Controller(List streamers, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT ?x + FROM STREAM %1 [RANGE TRIPLES 6] + FROM STREAM %2 [RANGE TRIPLES 6] + FROM STREAM %3 [RANGE TRIPLES 6] + WHERE { ?s prog:C_x ?x }", + this.streamers.content, + this.streamers.next.content, + this.streamers.next.next.content + ); + this.m = m; + end + + Unit doStream() + List objs = this.streamers; + while objs != null do + C o = objs.content; + o.doStream(); + objs = objs.next; + end + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end + +end + +streamer class C(Int x) + + Unit doStream() emits(this.x) + this.x = this.x + 1; + end + +end + +main + C o1 = new C(0); + C o2 = new C(10); + C o3 = new C(20); + List l3 = new List(o3, null); + List l2 = new List(o2, l3); + List streamers = new List(o1, l2); + + Controller ctrl = new Controller(streamers, null); + + clock Int i = 100; + Int endAt = 200; + + ctrl.register(); + + while i < endAt do + ctrl.doStream(); + String res = ctrl.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: 1 11 21 2 12 22 +// >>103: 1 11 21 2 12 22 +// >>104: 3 13 23 4 14 24 +// >>105: 3 13 23 4 14 24 +// >>106: 5 15 25 6 16 26 +// >>107: 5 15 25 6 16 26 +// >>108: 7 17 27 8 18 28 +// .. + +// Notes: +// - Three streams with different starting points (0, 10, 20) diff --git a/src/test/resources/csparql/11_use_domain.smol b/src/test/resources/csparql/11_use_domain.smol new file mode 100644 index 00000000..bd0d2e5e --- /dev/null +++ b/src/test/resources/csparql/11_use_domain.smol @@ -0,0 +1,86 @@ +// Commands: +// java -jar build/libs/smol.jar -b reada src/test/resources/csparql/example.owl -p ex=https://example.com/ontology# +// reada src/test/resources/csparql/11_use_domain.smol + +class Controller(List streamers, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT ?x + FROM STREAM %1 [RANGE TRIPLES 6] + FROM STREAM %2 [RANGE TRIPLES 6] + FROM STREAM %3 [RANGE TRIPLES 6] + WHERE { ?s prog:C_x ?x }", + this.streamers.content, + this.streamers.next.content, + this.streamers.next.next.content + ); + this.m = m; + end + + Unit doStream() + List objs = this.streamers; + while objs != null do + C o = objs.content; + o.doStream(); + objs = objs.next; + end + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end + +end + +streamer class C(String id, Int x) models "a ex:Class."; + Unit doStream() emits(this.x) + this.x = this.x + 1; + end +end + +main + List streamers = construct(" + SELECT ?id ?x + WHERE { + ?s a ex:Class ; + ex:id ?id ; + ex:x0 ?x . + } ORDER BY DESC (?id) + "); + Controller ctrl = new Controller(streamers, null); + + clock Int i = 100; + Int endAt = 200; + + ctrl.register(); + + while i < endAt do + ctrl.doStream(); + String res = ctrl.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: 1 11 21 2 12 22 +// >>103: 1 11 21 2 12 22 +// >>104: 3 13 23 4 14 24 +// >>105: 3 13 23 4 14 24 +// >>106: 5 15 25 6 16 26 +// >>107: 5 15 25 6 16 26 +// >>108: 7 17 27 8 18 28 +// .. + +// Notes: +// - Same output as 10_multiple_streams.smol +// - But now the streamers are created based on the domain knowledge diff --git a/src/test/resources/csparql/12_different_ranges.smol b/src/test/resources/csparql/12_different_ranges.smol new file mode 100644 index 00000000..3f1a5759 --- /dev/null +++ b/src/test/resources/csparql/12_different_ranges.smol @@ -0,0 +1,82 @@ +class Controller(List streamers, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT ?x + FROM STREAM %1 [RANGE TRIPLES 2] + FROM STREAM %2 [RANGE TRIPLES 4] + FROM STREAM %3 [RANGE TRIPLES 6] + WHERE { ?s prog:C_x ?x }", + this.streamers.content, + this.streamers.next.content, + this.streamers.next.next.content + ); + this.m = m; + end + + Unit doStream() + List objs = this.streamers; + while objs != null do + C o = objs.content; + o.doStream(); + objs = objs.next; + end + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end + +end + +streamer class C(Int x) + + Unit doStream() emits(this.x) + this.x = this.x + 1; + end + +end + +main + C o1 = new C(0); + C o2 = new C(10); + C o3 = new C(20); + List l3 = new List(o3, null); + List l2 = new List(o2, l3); + List streamers = new List(o1, l2); + + Controller ctrl = new Controller(streamers, null); + + clock Int i = 100; + Int endAt = 200; + + ctrl.register(); + + while i < endAt do + ctrl.doStream(); + String res = ctrl.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: 1 11 21 2 12 22 +// >>103: 1 11 21 2 12 22 +// >>104: 3 13 23 4 14 24 +// >>105: 3 13 23 4 14 24 +// >>106: 5 15 25 6 16 26 +// >>107: 5 15 25 6 16 26 +// >>108: 7 17 27 8 18 28 +// .. + +// Notes: +// - Error: Indeterministic! Windows have 2, 4, or 6 elements diff --git a/src/test/resources/csparql/13_static_join.smol b/src/test/resources/csparql/13_static_join.smol new file mode 100644 index 00000000..c560e79f --- /dev/null +++ b/src/test/resources/csparql/13_static_join.smol @@ -0,0 +1,56 @@ +streamer class C(Int x, Monitor m) + + Unit register() + String static = pushStatic("heap,staticTable"); + Monitor m = monitor(" + SELECT (SUM(?x) as ?sumX) + FROM STREAM %1 [RANGE 3s TUMBLING] + FROM <%2> + WHERE { ?s prog:C_x ?x . ?s a prog:C . } + ", this, static); + this.m = m; + end + + Unit doStream() emits(this.x) + this.x = this.x + 1; + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end + +end + +main + C o = new C(10, null); + o.register(); + + clock Int i = 100; + Int endAt = 200; + + while i < endAt do + o.doStream(); + String res = o.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + i = i + 1; + end +end + +// Output: +// >>100: +// >>101: +// >>102: +// >>103: 0 +// >>104: 0 +// >>105: 0 +// >>106: 0 +// .. + +// Notes: +// Error: Static join does not work! diff --git a/src/test/resources/csparql/example.owl b/src/test/resources/csparql/example.owl new file mode 100644 index 00000000..9ca7f7cc --- /dev/null +++ b/src/test/resources/csparql/example.owl @@ -0,0 +1,27 @@ +@prefix owl: . +@prefix rdf: . +@prefix xsd: . +@prefix rdfs: . +@prefix ex: . + +# class terminology +ex:Class a owl:Class . + +# property terminology +ex:id a owl:DatatypeProperty ; + rdfs:domain ex:Class ; + rdfs:range xsd:string . +ex:x0 a owl:DatatypeProperty ; + rdfs:domain ex:Class ; + rdfs:range xsd:integer . + +# instances +ex:A a ex:Class ; + ex:id "A" ; + ex:x0 0 . +ex:B a ex:Class ; + ex:id "B" ; + ex:x0 10 . +ex:C a ex:Class ; + ex:id "C" ; + ex:x0 20 . From 36d778c46bd135d9aaa75172208ff06c328860c3 Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Thu, 18 Sep 2025 23:52:03 +0200 Subject: [PATCH 10/19] upgrade to csparql2: impl, deps, compatibility + minor fixes --- build.gradle | 18 +- .../uio/microobject/ast/stmt/MonitorStmt.kt | 16 +- .../microobject/ast/stmt/PushStaticStmt.kt | 13 +- .../no/uio/microobject/data/StreamManager.kt | 176 +++++++++++------- .../no/uio/microobject/data/TripleManager.kt | 36 ++-- .../resources/default-csparql2.properties | 22 +++ 6 files changed, 173 insertions(+), 108 deletions(-) create mode 100644 src/main/resources/default-csparql2.properties diff --git a/build.gradle b/build.gradle index da465d81..3256cae5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.jetbrains.kotlin.jvm' version '1.6.21' + id 'org.jetbrains.kotlin.jvm' version '1.8.22' id 'com.github.johnrengelman.shadow' version '5.2.0' id 'antlr' id "kr.motd.sphinx" version "2.10.0" @@ -17,7 +17,7 @@ java { repositories { mavenCentral() - maven { url "https://overture.au.dk/artifactory/libs-release/" } + maven { url "https://jitpack.io" } } test { @@ -35,21 +35,25 @@ dependencies { testImplementation 'io.kotest:kotest-runner-junit5:5.2.3' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit' testImplementation 'org.apache.commons:commons-lang3:3.12.0' - testImplementation 'org.apache.jena:jena-fuseki-main:3.16.0' + testImplementation 'org.apache.jena:jena-fuseki-main:4.10.0' implementation 'com.github.ajalt.clikt:clikt:3.4.2' implementation 'org.antlr:antlr4:4.8' antlr 'org.antlr:antlr4:4.8' implementation 'net.sourceforge.owlapi:org.semanticweb.hermit:1.4.5.519' implementation 'org.slf4j:slf4j-simple:1.7.25' - implementation 'org.apache.jena:apache-jena-libs:3.16.0' - implementation 'org.apache.jena:jena-core:3.16.0' + implementation 'org.apache.jena:apache-jena-libs:4.10.0' + implementation 'org.apache.jena:jena-core:4.10.0' implementation 'org.siani.javafmi:fmu-wrapper:2.26.3' implementation "com.influxdb:influxdb-client-kotlin:2.3.0" implementation 'com.sksamuel.hoplite:hoplite-core:1.4.1' implementation 'com.sksamuel.hoplite:hoplite-yaml:1.4.1' - implementation 'com.github.owlcs:ontapi:2.1.0' + implementation 'com.github.owlcs:ontapi:3.0.5' implementation 'org.jline:jline:3.21.0' - implementation files('libs/csparql-ui-0.9.7-jar-with-dependencies.jar') + implementation 'com.github.streamreasoning.rsp4j:api:1.1.8' + implementation 'com.github.streamreasoning.rsp4j:yasper:1.1.8' + implementation 'com.github.streamreasoning.rsp4j:io:1.1.8' + implementation 'com.github.streamreasoning.rsp4j:dsms:1.1.8' + implementation 'com.github.streamreasoning.rsp4j:csparql2:1.1.8' } diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt index d7d86bf1..32a126de 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt @@ -10,9 +10,6 @@ import no.uio.microobject.type.* import org.apache.jena.datatypes.xsd.XSDDatatype import java.io.File -import eu.larkc.csparql.core.engine.CsparqlQueryResultProxy -import eu.larkc.csparql.common.RDFTuple - data class MonitorStmt(val target : Location, val query: Expression, val params : List, val pos : Int = -1, val declares: Type?) : Statement { override fun toString(): String = "$target := monitor($query, ${params.joinToString(",")})" @@ -36,10 +33,9 @@ data class MonitorStmt(val target : Location, val query: Expression, val params override fun eval(heapObj: Memory, stackFrame: StackEntry, interpreter: Interpreter): EvalResult { val name = Names.getObjName("Monitor") - interpreter.streamManager.registerQuery(name, query, params, stackFrame.store, interpreter.heap, stackFrame.obj) if (declares is ComposedType && declares.getPrimary().getNameString().equals("Monitor")) { // only consider the first type for now. e.g., Monitor - interpreter.streamManager.addMonitor(name, MonitorObject(name, declares.params[0])) + interpreter.streamManager.registerQuery(name, query, params, stackFrame.store, interpreter.heap, stackFrame.obj, declaredType=declares.params[0]) } else { throw Exception("Monitor statement can only be assigned to type Monitor") } @@ -52,7 +48,10 @@ class MonitorObject(private val name: LiteralExpr, private val declaredType: Typ private fun iriToLiteral(iri: String, interpreter: Interpreter): LiteralExpr { - if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#integer")) + if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#integer") || + iri.endsWith("^^http://www.w3.org/2001/XMLSchema#long") || + iri.endsWith("^^http://www.w3.org/2001/XMLSchema#int") + ) return LiteralExpr(iri.split("^^")[0].removeSurrounding("\""), INTTYPE) if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#boolean")) return LiteralExpr(iri.split("^^")[0], BOOLEANTYPE) if (iri.endsWith("^^http://www.w3.org/2001/XMLSchema#double") || @@ -82,12 +81,13 @@ class MonitorObject(private val name: LiteralExpr, private val declaredType: Typ if (rdfTable != null) { - val resIt = rdfTable.iterator() + val resIt = rdfTable.rows().iterator() + val firstVar = rdfTable.varNames.get(0) while (resIt.hasNext()) { val rdfTuple = resIt.next() try { // only consider first result for now - var literal = iriToLiteral(rdfTuple.get(0), interpreter) + var literal = iriToLiteral(rdfTuple.get(firstVar).toString(), interpreter) if (literal.tag != declaredType) throw Exception("Monitor parameter has incorrect type (expected ${declaredType}, got ${literal.tag})") diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt index 5cf76496..0a8084fc 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt @@ -12,9 +12,7 @@ import no.uio.microobject.main.ReasonerMode import org.apache.jena.datatypes.xsd.XSDDatatype import org.apache.jena.rdf.model.Model import java.io.File - -import eu.larkc.csparql.core.engine.CsparqlQueryResultProxy -import eu.larkc.csparql.common.RDFTuple +import java.io.FileWriter data class PushStaticStmt(val target : Location, val sources: Expression, val pos : Int = -1, val declares: Type?) : Statement { override fun toString(): String = "$target:=pushStatic(sources=$sources)" @@ -60,9 +58,14 @@ data class PushStaticStmt(val target : Location, val sources: Expression, val po ) val model = interpreter.tripleManager.getModel(ts) + // todo implement + val file = "output.ttl" + File(interpreter.settings.outdir).mkdirs() + File("${interpreter.settings.outdir}/${file}").createNewFile() + model.write(FileWriter("${interpreter.settings.outdir}/${file}"),"TTL") + val resultPath = "" - interpreter.streamManager.putStaticNamedGraph(namedIri, model) - val resultLit = LiteralExpr(namedIri, STRINGTYPE) + val resultLit = LiteralExpr(resultPath, STRINGTYPE) return replaceStmt(AssignStmt(target, resultLit, declares = declares), stackFrame) } diff --git a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt index cd3b83fb..40efe0ea 100644 --- a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt +++ b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt @@ -7,14 +7,11 @@ import no.uio.microobject.ast.stmt.MonitorObject import no.uio.microobject.main.Settings import no.uio.microobject.type.* - -import eu.larkc.csparql.core.engine.* -import eu.larkc.csparql.cep.api.* -import eu.larkc.csparql.core.* -import eu.larkc.csparql.common.* +import java.nio.file.* import java.util.Observer; import java.util.Observable import java.util.concurrent.ConcurrentHashMap +import java.util.function.Consumer import java.io.File import java.io.StringWriter import org.slf4j.Logger @@ -24,32 +21,41 @@ import org.apache.log4j.Level import org.apache.log4j.Logger as Log4jLogger import org.apache.log4j.PropertyConfigurator import org.apache.jena.rdf.model.Model +import org.apache.jena.sparql.algebra.Table +import org.apache.jena.graph.Graph import kotlin.text.toLong -class ResultPusher(private val name: LiteralExpr, private val resultTable: MutableMap) : Observer { - public override fun update(o: Observable, arg: Any) { - val results: RDFTable = arg as RDFTable - resultTable[name] = results - } -} +import org.streamreasoning.rsp4j.csparql2.engine.CSPARQLEngine +import org.streamreasoning.rsp4j.csparql2.engine.JenaContinuousQueryExecution +import org.streamreasoning.rsp4j.csparql2.stream.GraphStreamSchema +import org.streamreasoning.rsp4j.csparql2.sysout.ResponseFormatterFactory +import org.streamreasoning.rsp4j.csparql2.syntax.QueryFactory +import org.streamreasoning.rsp4j.csparql2.sysout.GenericResponseSysOutFormatter +import org.streamreasoning.rsp4j.api.engine.config.EngineConfiguration; +import org.streamreasoning.rsp4j.io.DataStreamImpl +import org.streamreasoning.rsp4j.api.stream.data.DataStream +import org.streamreasoning.rsp4j.api.sds.SDSConfiguration +import org.apache.jena.rdf.model.*; +import org.apache.jena.atlas.lib.tuple.Tuple // Class managing streams class StreamManager(private val settings: Settings, val staticTable: StaticTable, private val interpreter: Interpreter?) { - private val engine: CsparqlEngineImpl = CsparqlEngineImpl() + private var engine: CSPARQLEngine? = null private var engineInitialized = false + private var sdsConfig: SDSConfiguration? = null + private var ec: EngineConfiguration? = null val LOG : Logger? = LoggerFactory.getLogger(StreamManager::class.java) - private var streams: MutableMap> = mutableMapOf() + private var streams: MutableMap> = mutableMapOf() private var monitors: MutableMap = mutableMapOf() - - private var queryResults: MutableMap = ConcurrentHashMap() + + private var queryResults: MutableMap = ConcurrentHashMap() var clockVar : String? = null var clockTimestampSec : String? = null var lastTimestamp: Long = 0 - var firstTsPassed: Boolean = false - + var nStaticGraphsPushed = 0 init { @@ -65,22 +71,41 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "ERROR") } } + + private fun initEngineIfNeeded() { + if (!engineInitialized) { + val ts = getTimestamp() + + val configPath = "src/main/resources/csparql2.properties" + val defaultPath = "src/main/resources/default-csparql2.properties" + + if (File(configPath).exists()) { + ec = EngineConfiguration(configPath) + sdsConfig = SDSConfiguration(configPath) + println("Loaded csparql2.properties successfully at ts=$ts.") + } else { + println("Failed to load csparql2.properties, falling back to default-csparql2.properties at ts=$ts.") + ec = EngineConfiguration(defaultPath) + sdsConfig = SDSConfiguration(defaultPath) + } - private fun initEngine() { - - engine.initialize(true) // timestamp enabled - engineInitialized = true + engine = CSPARQLEngine(0, ec) + engineInitialized = true + } } - public fun getQueryResults(name: LiteralExpr): RDFTable? { + public fun getQueryResults(name: LiteralExpr): Table? { return queryResults[name] } public fun registerStream(className: String, obj: LiteralExpr) { - if (!engineInitialized) initEngine() + initEngineIfNeeded() val streamIri = "${settings.runPrefix}${obj.toString()}" - val stream = RdfStream(streamIri) - engine.registerStream(stream) + + val stream = StreamObject(streamIri) + val reg = engine!!.register(stream) + stream.setWritable(reg) + if (!streams.containsKey(className)) streams[className] = mutableMapOf() streams[className]!![obj] = stream } @@ -89,13 +114,10 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable var ms: Long if (clockTimestampSec != null) { ms = secToMs(clockTimestampSec!!.toLong()) - if (!firstTsPassed) ms -= 1 // workaround: first timestamp is decreased by 1 ms } else { ms = System.currentTimeMillis() } - if (ms > lastTimestamp && !firstTsPassed) firstTsPassed = true - lastTimestamp = ms return ms } @@ -115,72 +137,72 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable val res = interpreter.eval(expr, stackEntry) val predIri = "${settings.progPrefix}${className}_${expr.toString().removePrefix("this.").replace('.', '_')}" - val objIri = literalToIri(res) - val quad = RdfQuadruple(subjIri, predIri, objIri, timestamp) - // println(quad.toString()) - stream.put(quad) - } - } + val m = ModelFactory.createDefaultModel() + val stmt = m.createStatement(m.createResource(subjIri), m.createProperty(predIri), literalToIri(m, res, settings)) + m.add(stmt) - private fun literalToIri(lit: LiteralExpr): String { - if (lit.tag == INTTYPE) return "\"${lit.literal}\"^^http://www.w3.org/2001/XMLSchema#integer" - if (lit.tag == BOOLEANTYPE) return "\"${lit.literal}\"^^http://www.w3.org/2001/XMLSchema#boolean" - if (lit.tag == DOUBLETYPE) return "\"${lit.literal}\"^^http://www.w3.org/2001/XMLSchema#double" - if (lit.tag == STRINGTYPE) return "\"${lit.literal}\"^^http://www.w3.org/2001/XMLSchema#string" - return "${settings.runPrefix}${lit.literal}" + stream.putGraph(m.getGraph(), timestamp) + } } - public fun addMonitor(name: LiteralExpr, monitor: MonitorObject) { - monitors[name] = monitor + private fun literalToIri(m: Model, lit: LiteralExpr, settings: Settings): RDFNode = + when (lit.tag) { + INTTYPE -> m.createTypedLiteral(lit.literal.toInt()) + DOUBLETYPE -> m.createTypedLiteral(lit.literal.toDouble()) + BOOLEANTYPE-> m.createTypedLiteral(lit.literal.toBoolean()) + STRINGTYPE -> m.createLiteral(lit.literal) + else -> m.createResource("${settings.runPrefix}${lit.literal}") } public fun getMonitor(name: LiteralExpr): MonitorObject? { return monitors[name] } - public fun registerQuery(name: LiteralExpr, queryExpr : Expression, params: List, stackMemory: Memory, heap: GlobalMemory, obj: LiteralExpr, SPARQL : Boolean = true): CsparqlQueryResultProxy { - if (!engineInitialized) initEngine() + public fun registerQuery(name: LiteralExpr, queryExpr : Expression, params: List, stackMemory: Memory, heap: GlobalMemory, obj: LiteralExpr, SPARQL : Boolean = true, declaredType: Type): JenaContinuousQueryExecution { + initEngineIfNeeded() val queryStr = prepareQuery(name, queryExpr, params, stackMemory, heap, obj, SPARQL) - if (settings.verbose) println("Registering query:\n$queryStr") - var resultProxy = engine.registerQuery(queryStr, false) // reasoning disabled - resultProxy.addObserver(ResultPusher(name, queryResults)) // each key is only used by one observer - return resultProxy + + val cqe = engine!!.register(queryStr, sdsConfig) as JenaContinuousQueryExecution + monitors[name] = MonitorObject(name, declaredType) + + val outputStream = cqe.outstream() + outputStream?.addConsumer { arg, ts -> + val results: Table = arg as Table + queryResults[name] = results + } + + return cqe } - private fun prepareQuery(name: LiteralExpr, queryExpr : Expression, params : List, stackMemory: Memory, heap: GlobalMemory, obj: LiteralExpr, SPARQL : Boolean = true) : String{ - val queryHeader = "REGISTER QUERY Query${name.literal} AS " + private fun prepareQuery(name: LiteralExpr, queryExpr : Expression, params : List, + stackMemory: Memory, heap: GlobalMemory, obj: LiteralExpr, SPARQL : Boolean = true) : String{ + + var prefixes = "" + for ((key, value) in settings.prefixMap()) prefixes += "PREFIX $key: <$value>\n" + + val queryHeader = "REGISTER RSTREAM <${settings.runPrefix}${name.literal}> AS " val queryBody = interpreter!!.prepareQuery(queryExpr, params, stackMemory, heap, obj, SPARQL) .removePrefix("\"").removeSuffix("\"") - var queryWithPrefixes = queryHeader - // for ((key, value) in settings.prefixMap()) queryWithPrefixes += "PREFIX $key: <$value>\n" - queryWithPrefixes += queryBody + var queryWithPrefixes = prefixes + queryHeader + queryBody queryWithPrefixes = queryWithPrefixes.replace("\\\"", "\"") - // Replace occurrences of value:x with for each prefix in prefixMap - for ((key, value) in settings.prefixMap()) { - // Regex to match key: followed by a valid identifier (e.g., obj4) - val regex = Regex("""${Regex.escape(key)}:([A-Za-z0-9_]+)""") - queryWithPrefixes = queryWithPrefixes.replace(regex) { matchResult -> - "<$value${matchResult.groupValues[1]}>" - } - } - return queryWithPrefixes } - public fun putStaticNamedGraph(iri: String, model: Model) { - if (!engineInitialized) initEngine() + // todo implement + // public fun putStaticNamedGraph(iri: String, model: Model) { + // if (!engineInitialized) initEngine() - // serialize the model (RDF/XML matches the engine's first attempt) - val sw = StringWriter() - model.write(sw, "RDF/XML") + // // serialize the model (RDF/XML matches the engine's first attempt) + // val sw = StringWriter() + // model.write(sw, "RDF/XML") - // hand it to the C-SPARQL engine - engine.putStaticNamedModel(iri, sw.toString()) - } + // // hand it to the C-SPARQL engine + // engine.putStaticNamedModel(iri, sw.toString()) + // } public fun getStaticNamedIri(): String { val s = "${settings.runPrefix}loadStatic${nStaticGraphsPushed}" @@ -189,4 +211,18 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable } } + +class StreamObject(var iri: String) : DataStreamImpl(iri) { + + private var s: DataStream? = null + + fun setWritable(s: DataStream) { + this.s = s + } + + fun putGraph(m: Graph, t: Long) { + if (s == null) throw Exception("Stream $iri is not writable") + s!!.put(m, t) + } +} \ No newline at end of file diff --git a/src/main/kotlin/no/uio/microobject/data/TripleManager.kt b/src/main/kotlin/no/uio/microobject/data/TripleManager.kt index 20e38a2c..d3c55f51 100644 --- a/src/main/kotlin/no/uio/microobject/data/TripleManager.kt +++ b/src/main/kotlin/no/uio/microobject/data/TripleManager.kt @@ -18,7 +18,7 @@ import org.apache.jena.graph.impl.GraphBase import org.apache.jena.graph.Node import org.apache.jena.graph.Node_URI import org.apache.jena.graph.NodeFactory -import org.apache.jena.graph.Triple +import org.apache.jena.graph.Triple as JenaTriple import org.apache.jena.graph.compose.MultiUnion import org.apache.jena.query.* import org.apache.jena.rdf.model.* @@ -212,30 +212,30 @@ class TripleManager(private val settings: Settings, val staticTable: StaticTable // A custom type of (nice)iterator which takes a list as input and iterates over them. // It iterates through all elements in the list from start to end. - private class TripleListIterator(private val tripleList: List): NiceIterator() { + private class TripleListIterator(private val tripleList: List): NiceIterator() { var listIndex: Int = 0 // index of next element override fun hasNext(): Boolean = listIndex < tripleList.size - override fun next(): Triple = tripleList[(listIndex++)] + override fun next(): JenaTriple = tripleList[(listIndex++)] } // Helper method to crate triple with URIs in all three positions - private fun uriTriple(s: String, p: String, o: String): Triple { - return Triple(NodeFactory.createURI(s), NodeFactory.createURI(p), NodeFactory.createURI(o)) + private fun uriTriple(s: String, p: String, o: String): JenaTriple { + return JenaTriple.create(NodeFactory.createURI(s), NodeFactory.createURI(p), NodeFactory.createURI(o)) } // Helper method to crate triple with URIs in two first positions and a literal in object position - private fun literalTriple(s: String, p: String, o: Any?, type: BaseType): Triple? { + private fun literalTriple(s: String, p: String, o: Any?, type: BaseType): JenaTriple? { if (o == null) return null - return Triple( + return JenaTriple.create( NodeFactory.createURI(s), NodeFactory.createURI(p), getLiteralNode(LiteralExpr(o.toString(), type), settings) ) } // If searchTriple matches candidateTriple, then candidateTriple will be added to matchList - private fun addIfMatch(candidateTriple: Triple?, searchTriple: Triple?, matchList: MutableList, pseudo: Boolean) { + private fun addIfMatch(candidateTriple: JenaTriple?, searchTriple: JenaTriple?, matchList: MutableList, pseudo: Boolean) { if (searchTriple == null) return if (candidateTriple == null) return // This is just a quick fix to resolve the problem with > and < in the uris. They appear for example when the stdlib.smol is used, since it has List. @@ -290,7 +290,7 @@ class TripleManager(private val settings: Settings, val staticTable: StaticTable } private inner class FMOGraph : GraphBase() { - override fun graphBaseFind(searchTriple: Triple): ExtendedIterator { + override fun graphBaseFind(searchTriple: JenaTriple): ExtendedIterator { if(interpreter == null) return TripleListIterator(mutableListOf()) @@ -298,7 +298,7 @@ class TripleManager(private val settings: Settings, val staticTable: StaticTable val smol = prefixMap["smol"] val run = prefixMap["run"] - val matchingTriples: MutableList = mutableListOf() + val matchingTriples: MutableList = mutableListOf() for( fmo in interpreter.simMemory ){ val name = fmo.key.literal @@ -345,7 +345,7 @@ class TripleManager(private val settings: Settings, val staticTable: StaticTable // Returns an iterator of all triples in the static table that matches searchTriple // graphBaseFind only constructs the triples that match searchTriple. - public override fun graphBaseFind(searchTriple: Triple): ExtendedIterator { + public override fun graphBaseFind(searchTriple: JenaTriple): ExtendedIterator { val useGuardClauses = tripleSettings.guards.getOrDefault("staticTable", true) val fieldTable: Map = staticTable.fieldTable val methodTable: Map> = staticTable.methodTable @@ -386,7 +386,7 @@ class TripleManager(private val settings: Settings, val staticTable: StaticTable } - val matchingTriples: MutableList = mutableListOf() + val matchingTriples: MutableList = mutableListOf() // Generate triples for fields (and classes) for(classObj in fieldTable){ @@ -545,7 +545,7 @@ class TripleManager(private val settings: Settings, val staticTable: StaticTable // Returns an iterator of all triples in the heap that matches searchTriple // graphBaseFind only constructs/fetches the triples that match searchTriple. - public override fun graphBaseFind(searchTriple: Triple): ExtendedIterator { + public override fun graphBaseFind(searchTriple: JenaTriple): ExtendedIterator { val useGuardClauses = false //tripleSettings.guards.getOrDefault("heap", true) val settings: Settings = interpreter.settings val heap: GlobalMemory = interpreter.heap @@ -568,7 +568,7 @@ class TripleManager(private val settings: Settings, val staticTable: StaticTable } } - val matchingTriples: MutableList = mutableListOf() + val matchingTriples: MutableList = mutableListOf() for(obj in heap.keys){ if(staticTable.hiddenSet.contains(obj.tag.getPrimary().getNameString())) continue; @@ -605,7 +605,7 @@ class TripleManager(private val settings: Settings, val staticTable: StaticTable retVal = interpreter.evalCall(obj.literal, obj.tag.name, m.key) val resNode = getLiteralNode(retVal.second, settings) val resTriple = - Triple( + JenaTriple.create( NodeFactory.createURI(settings.replaceKnownPrefixesNoColon("run:${obj.literal}")), NodeFactory.createURI(predicateString), resNode @@ -631,7 +631,7 @@ class TripleManager(private val settings: Settings, val staticTable: StaticTable if(retVal == null) retVal = interpreter.evalCall(obj.literal, obj.tag.name, m.key) val resNode = getLiteralNode(retVal.second, settings) val resTriple = - Triple( + JenaTriple.create( NodeFactory.createURI(settings.replaceKnownPrefixesNoColon(models)), NodeFactory.createURI(predicateString), resNode @@ -720,7 +720,7 @@ class TripleManager(private val settings: Settings, val staticTable: StaticTable } } - val candidateTriple = Triple( + val candidateTriple = JenaTriple.create( NodeFactory.createURI(settings.replaceKnownPrefixesNoColon(target)), NodeFactory.createURI(predicateString), getLiteralNode(value, settings) @@ -738,7 +738,7 @@ class TripleManager(private val settings: Settings, val staticTable: StaticTable } val target: LiteralExpr = heap[obj]!!.getOrDefault(store, LiteralExpr("ERROR")) - val candidateTriple = Triple( + val candidateTriple = JenaTriple.create( NodeFactory.createURI(subjectString), NodeFactory.createURI(predicateString), getLiteralNode(target, settings) diff --git a/src/main/resources/default-csparql2.properties b/src/main/resources/default-csparql2.properties new file mode 100644 index 00000000..b7bcef64 --- /dev/null +++ b/src/main/resources/default-csparql2.properties @@ -0,0 +1,22 @@ +rsp_engine.time=EventTime +# rsp_engine.base_uri=http://streamreasoning.org/csparql/ +rsp_engine.stream.item.class=org.streamreasoning.rsp4j.csparql2.stream.GraphStreamSchema + +rsp_engine.response_format=JSON-LD + +rsp_engine.on_window_close=true +rsp_engine.non_empty_content=true +rsp_engine.periodic=false +rsp_engine.on_content_change=false +rsp_engine.tick=TIME_DRIVEN + +rsp_engine.report_grain=SINGLE +rsp_engine.sds.mantainance=NAIVE +rsp_engine.partialwindow=false + +# jasper.entailment=RDFS +# rsp_engine.tbox_location=artist.tbox.owl +# rsp_engine.tbox_location="" + +#CSPARQLEngine.entailment=CUSTOM +#CSPARQLEngine.rules=/Users/riccardo/_Projects/csparql2/src/test/resources/rdfs-fb.rules From a5a3324fce8d8f09ecb7cf13462f5b915988687e Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Thu, 18 Sep 2025 23:53:37 +0200 Subject: [PATCH 11/19] test updated with csparql2 (rsp-ql) syntax + structured --- .../csparql/03_logical_window_step.smol | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/test/resources/csparql/03_logical_window_step.smol b/src/test/resources/csparql/03_logical_window_step.smol index 7caaeb68..c34565ea 100644 --- a/src/test/resources/csparql/03_logical_window_step.smol +++ b/src/test/resources/csparql/03_logical_window_step.smol @@ -1,10 +1,14 @@ +// reada src/test/resources/csparql/03_logical_window_step.smol + streamer class C(Int x, Monitor m) Unit register() Monitor m = monitor(" - SELECT (SUM(?x) as ?sumX) - FROM STREAM %1 [RANGE 5s STEP 2s] - WHERE { ?s prog:C_x ?x } + SELECT (SUM(?x) AS ?sumX) + FROM NAMED WINDOW ON %1 [RANGE PT6s STEP PT2s] + WHERE { + WINDOW ?w { ?s prog:C_x ?x } + } ", this); this.m = m; end @@ -25,16 +29,27 @@ streamer class C(Int x, Monitor m) end main - C o = new C(0, null); - o.register(); - + // configure simulation clock Int i = 100; Int endAt = 200; + // initialize stream + C o = new C(0, null); + + // initialize monitor + o.register(); + + // run simulation while i < endAt do + + // put triples in the stream o.doStream(); + + // read window contents String res = o.windowToString(); print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) i = i + 1; end end @@ -55,5 +70,5 @@ end // Notes: // - Step 2 seconds = new window every 2 seconds -// - Windows have {2, 4, 5, 5, 5, ...} elements +// - Range 6s: Windows have {2, 4, 5, 5, 5, ...} elements From a20f6479c97652cb1a5e975a804746744bf50d72 Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 21 Sep 2025 01:21:30 +0200 Subject: [PATCH 12/19] multiple results in list enabled --- .../uio/microobject/ast/stmt/MonitorStmt.kt | 43 ++++++++-------- .../no/uio/microobject/data/StreamManager.kt | 50 +++++++++++++++---- .../resources/default-csparql2.properties | 5 +- .../resources/csparql/04_no_aggregation.smol | 37 ++++++++++---- 4 files changed, 93 insertions(+), 42 deletions(-) diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt index 32a126de..d82b12a8 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/MonitorStmt.kt @@ -76,32 +76,35 @@ class MonitorObject(private val name: LiteralExpr, private val declaredType: Typ } fun getWindowResults(interpreter: Interpreter): LiteralExpr { - val rdfTable = interpreter.streamManager.getQueryResults(name) + val rdfTables = interpreter.streamManager.getQueryResults(name) + var list = LiteralExpr("null") - if (rdfTable != null) { + if (rdfTables != null) { + for (rdfTable in rdfTables) { - val resIt = rdfTable.rows().iterator() - val firstVar = rdfTable.varNames.get(0) - while (resIt.hasNext()) { - val rdfTuple = resIt.next() - try { - // only consider first result for now - var literal = iriToLiteral(rdfTuple.get(firstVar).toString(), interpreter) - if (literal.tag != declaredType) - throw Exception("Monitor parameter has incorrect type (expected ${declaredType}, got ${literal.tag})") + val resIt = rdfTable.rows().iterator() + val firstVar = rdfTable.varNames.get(0) + + while (resIt.hasNext()) { + val rdfTuple = resIt.next() + try { + // only consider first result for now + var literal = iriToLiteral(rdfTuple.get(firstVar).toString(), interpreter) + if (literal.tag != declaredType) + throw Exception("Monitor parameter has incorrect type (expected ${declaredType}, got ${literal.tag})") - val name = Names.getObjName("List") - val newMemory: Memory = mutableMapOf() - newMemory["content"] = literal - newMemory["next"] = list - interpreter.heap[name] = newMemory - list = name + val name = Names.getObjName("List") + val newMemory: Memory = mutableMapOf() + newMemory["content"] = literal + newMemory["next"] = list + interpreter.heap[name] = newMemory + list = name - } catch (e: Exception) { - throw Exception("Error while processing query result: ${e.message}") + } catch (e: Exception) { + throw Exception("Error while processing query result: ${e.message}") + } } - } } return list diff --git a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt index 40efe0ea..16e3d34f 100644 --- a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt +++ b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt @@ -38,6 +38,9 @@ import org.streamreasoning.rsp4j.api.sds.SDSConfiguration import org.apache.jena.rdf.model.*; import org.apache.jena.atlas.lib.tuple.Tuple +// Per-monitor state +data class WindowBuf(var tick: Long? = null, val current: MutableList = mutableListOf()) + // Class managing streams class StreamManager(private val settings: Settings, val staticTable: StaticTable, private val interpreter: Interpreter?) { @@ -50,12 +53,14 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable private var streams: MutableMap> = mutableMapOf() private var monitors: MutableMap = mutableMapOf() - private var queryResults: MutableMap = ConcurrentHashMap() + private val windowBufs = ConcurrentHashMap() + private val queryResults = ConcurrentHashMap>() // read-only snapshots var clockVar : String? = null var clockTimestampSec : String? = null var lastTimestamp: Long = 0 - + var lastWindowTs: Long? = null + var nStaticGraphsPushed = 0 init { @@ -89,12 +94,12 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable sdsConfig = SDSConfiguration(defaultPath) } - engine = CSPARQLEngine(0, ec) + engine = CSPARQLEngine(ts, ec) engineInitialized = true } } - public fun getQueryResults(name: LiteralExpr): Table? { + public fun getQueryResults(name: LiteralExpr): List
? { return queryResults[name] } @@ -130,13 +135,27 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable val stream = streams[className]!![obj]!! val expressions = interpreter!!.staticInfo.streamersTable[className]!![methodName]!! - val subjIri = "${settings.runPrefix}${obj.literal}" val timestamp = getTimestamp() for (expr in expressions) { val res = interpreter.eval(expr, stackEntry) - val predIri = "${settings.progPrefix}${className}_${expr.toString().removePrefix("this.").replace('.', '_')}" + val exprParts = expr.toString().split('.') + var subjIri: String = "${settings.runPrefix}${obj.literal}" + var predIri = "${settings.progPrefix}${className}_${expr.toString().removePrefix("this.").replace('.', '_')}" + if (exprParts.size < 2) { + throw Exception("Streamer expression must be of the form this.field or this.obj.field") + } + if (exprParts.size > 2) { + // find the one before last part + var currentObj: LiteralExpr = obj + for (i in 1 until exprParts.size - 1) { + val fieldName = exprParts[i] + currentObj = interpreter.heap[currentObj]!![fieldName]!! + } + subjIri = "${settings.runPrefix}${currentObj.literal}" + predIri = "${settings.progPrefix}${(currentObj.tag as BaseType).name}_${exprParts.last()}" + } val m = ModelFactory.createDefaultModel() val stmt = m.createStatement(m.createResource(subjIri), m.createProperty(predIri), literalToIri(m, res, settings)) @@ -162,14 +181,25 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable public fun registerQuery(name: LiteralExpr, queryExpr : Expression, params: List, stackMemory: Memory, heap: GlobalMemory, obj: LiteralExpr, SPARQL : Boolean = true, declaredType: Type): JenaContinuousQueryExecution { initEngineIfNeeded() val queryStr = prepareQuery(name, queryExpr, params, stackMemory, heap, obj, SPARQL) + if (settings.verbose) println("Registering query:\n$queryStr") val cqe = engine!!.register(queryStr, sdsConfig) as JenaContinuousQueryExecution monitors[name] = MonitorObject(name, declaredType) + windowBufs.computeIfAbsent(name) { WindowBuf() } + val outputStream = cqe.outstream() - outputStream?.addConsumer { arg, ts -> - val results: Table = arg as Table - queryResults[name] = results + outputStream?.addConsumer { arg, ts -> + val table = arg as Table + val b = windowBufs.getOrPut(name) { WindowBuf() } + + if (b.tick != ts) { + b.tick = ts + b.current.clear() + } + b.current += table + + queryResults[name] = b.current.toList() } return cqe @@ -181,7 +211,7 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable var prefixes = "" for ((key, value) in settings.prefixMap()) prefixes += "PREFIX $key: <$value>\n" - val queryHeader = "REGISTER RSTREAM <${settings.runPrefix}${name.literal}> AS " + val queryHeader = "REGISTER RSTREAM <${settings.runPrefix}${name.literal}/out> AS " val queryBody = interpreter!!.prepareQuery(queryExpr, params, stackMemory, heap, obj, SPARQL) .removePrefix("\"").removeSuffix("\"") diff --git a/src/main/resources/default-csparql2.properties b/src/main/resources/default-csparql2.properties index b7bcef64..06765796 100644 --- a/src/main/resources/default-csparql2.properties +++ b/src/main/resources/default-csparql2.properties @@ -1,10 +1,9 @@ rsp_engine.time=EventTime -# rsp_engine.base_uri=http://streamreasoning.org/csparql/ rsp_engine.stream.item.class=org.streamreasoning.rsp4j.csparql2.stream.GraphStreamSchema rsp_engine.response_format=JSON-LD -rsp_engine.on_window_close=true +rsp_engine.on_window_close=false rsp_engine.non_empty_content=true rsp_engine.periodic=false rsp_engine.on_content_change=false @@ -12,7 +11,7 @@ rsp_engine.tick=TIME_DRIVEN rsp_engine.report_grain=SINGLE rsp_engine.sds.mantainance=NAIVE -rsp_engine.partialwindow=false +rsp_engine.partialwindow=true # jasper.entailment=RDFS # rsp_engine.tbox_location=artist.tbox.owl diff --git a/src/test/resources/csparql/04_no_aggregation.smol b/src/test/resources/csparql/04_no_aggregation.smol index a1c28917..38355d04 100644 --- a/src/test/resources/csparql/04_no_aggregation.smol +++ b/src/test/resources/csparql/04_no_aggregation.smol @@ -1,16 +1,23 @@ -streamer class C(Int x, Monitor m) +// reada src/test/resources/csparql/04_no_aggregation.smol + +class Observation(Int x, Int ts) end + +streamer class C(Int x, Observation obs, Monitor m) Unit register() Monitor m = monitor(" SELECT ?x - FROM STREAM %1 [RANGE 5s STEP 2s] - WHERE { ?s prog:C_x ?x } + FROM NAMED WINDOW ON %1 [RANGE PT5.001s STEP PT2s] + WHERE { + WINDOW ?w { ?s prog:Observation_x ?x } + } ", this); this.m = m; end - Unit doStream() emits(this.x) + Unit doStream(Int ts) emits(this.obs, this.obs.x, this.obs.ts) this.x = this.x + 1; + this.obs = new Observation(this.x, ts); end String windowToString() @@ -25,16 +32,27 @@ streamer class C(Int x, Monitor m) end main - C o = new C(0, null); - o.register(); - + // configure simulation clock Int i = 100; Int endAt = 200; + // initialize stream + C o = new C(0, null, null); + + // initialize monitor + o.register(); + + // run simulation while i < endAt do - o.doStream(); + + // put triples in the stream + o.doStream(i); + + // read window contents String res = o.windowToString(); print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) i = i + 1; end end @@ -52,4 +70,5 @@ end // .. // Notes: -// - Multiple elements in the window because there is no aggregation function \ No newline at end of file +// - Step 2 seconds = new window every 2 seconds +// - Range 5.001s: Windows have {2, 4, 5, 5, 5, ...} elements From 5c527e7ca73c2de491ba920b5b8870a980eb9aa9 Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 21 Sep 2025 15:05:20 +0200 Subject: [PATCH 13/19] multiple streams enabled (aligned with csparql-int branch) --- .../no/uio/microobject/data/StreamManager.kt | 17 ++++-- ...3_logical_window_step.smol => 03_sum.smol} | 21 +++---- src/test/resources/csparql/05_count.smol | 39 +++++++++--- src/test/resources/csparql/06_average.smol | 37 +++++++++--- .../resources/csparql/07_stream_object.smol | 57 ++++++++++++------ .../csparql/08_use_window_results.smol | 37 +++++++++--- .../csparql/10_multiple_streams.smol | 56 ++++++++++++----- src/test/resources/csparql/11_use_domain.smol | 60 +++++++++++++------ .../csparql/12_different_ranges.smol | 60 +++++++++++++------ 9 files changed, 275 insertions(+), 109 deletions(-) rename src/test/resources/csparql/{03_logical_window_step.smol => 03_sum.smol} (69%) diff --git a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt index 16e3d34f..c18b482f 100644 --- a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt +++ b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt @@ -61,6 +61,9 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable var lastTimestamp: Long = 0 var lastWindowTs: Long? = null + var lastTriggerTs: Long? = null + var lastModel: Model? = null + var nStaticGraphsPushed = 0 init { @@ -136,6 +139,13 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable val expressions = interpreter!!.staticInfo.streamersTable[className]!![methodName]!! val timestamp = getTimestamp() + if (lastTriggerTs == null || timestamp > lastTriggerTs!!) { + // put model only if time has advanced (guarantee one graph per timestamp) + if (lastTriggerTs != null) + stream.putGraph(lastModel!!.getGraph(), lastTriggerTs!!) + lastModel = ModelFactory.createDefaultModel() + lastTriggerTs = timestamp + } for (expr in expressions) { val res = interpreter.eval(expr, stackEntry) @@ -157,11 +167,8 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable predIri = "${settings.progPrefix}${(currentObj.tag as BaseType).name}_${exprParts.last()}" } - val m = ModelFactory.createDefaultModel() - val stmt = m.createStatement(m.createResource(subjIri), m.createProperty(predIri), literalToIri(m, res, settings)) - m.add(stmt) - - stream.putGraph(m.getGraph(), timestamp) + val stmt = lastModel!!.createStatement(lastModel!!.createResource(subjIri), lastModel!!.createProperty(predIri), literalToIri(lastModel!!, res, settings)) + lastModel!!.add(stmt) } } diff --git a/src/test/resources/csparql/03_logical_window_step.smol b/src/test/resources/csparql/03_sum.smol similarity index 69% rename from src/test/resources/csparql/03_logical_window_step.smol rename to src/test/resources/csparql/03_sum.smol index c34565ea..235d1e1b 100644 --- a/src/test/resources/csparql/03_logical_window_step.smol +++ b/src/test/resources/csparql/03_sum.smol @@ -1,20 +1,23 @@ -// reada src/test/resources/csparql/03_logical_window_step.smol +// reada src/test/resources/csparql/03_sum.smol -streamer class C(Int x, Monitor m) +class Observation(Int x, Int ts) end + +streamer class C(Int x, Observation obs, Monitor m) Unit register() Monitor m = monitor(" SELECT (SUM(?x) AS ?sumX) - FROM NAMED WINDOW ON %1 [RANGE PT6s STEP PT2s] + FROM NAMED WINDOW ON %1 [RANGE PT5.001s STEP PT2s] WHERE { - WINDOW ?w { ?s prog:C_x ?x } + WINDOW ?w { ?s prog:Observation_x ?x } } ", this); this.m = m; end - Unit doStream() emits(this.x) + Unit doStream(Int ts) emits(this.obs, this.obs.x, this.obs.ts) this.x = this.x + 1; + this.obs = new Observation(this.x, ts); end String windowToString() @@ -34,7 +37,7 @@ main Int endAt = 200; // initialize stream - C o = new C(0, null); + C o = new C(0, null, null); // initialize monitor o.register(); @@ -43,7 +46,7 @@ main while i < endAt do // put triples in the stream - o.doStream(); + o.doStream(i); // read window contents String res = o.windowToString(); @@ -69,6 +72,4 @@ end // .. // Notes: -// - Step 2 seconds = new window every 2 seconds -// - Range 6s: Windows have {2, 4, 5, 5, 5, ...} elements - +// - SUM aggregation works as expected diff --git a/src/test/resources/csparql/05_count.smol b/src/test/resources/csparql/05_count.smol index 76403258..af389d69 100644 --- a/src/test/resources/csparql/05_count.smol +++ b/src/test/resources/csparql/05_count.smol @@ -1,16 +1,23 @@ -streamer class C(Int x, Monitor m) +// reada src/test/resources/csparql/05_count.smol + +class Observation(Int x, Int ts) end + +streamer class C(Int x, Observation obs, Monitor m) Unit register() Monitor m = monitor(" - SELECT (COUNT(?x) as ?countX) - FROM STREAM %1 [RANGE 5s STEP 2s] - WHERE { ?s prog:C_x ?x } + SELECT (COUNT(?x) AS ?countX) + FROM NAMED WINDOW ON %1 [RANGE PT5.001s STEP PT2s] + WHERE { + WINDOW ?w { ?s prog:Observation_x ?x } + } ", this); this.m = m; end - Unit doStream() emits(this.x) + Unit doStream(Int ts) emits(this.obs, this.obs.x, this.obs.ts) this.x = this.x + 1; + this.obs = new Observation(this.x, ts); end String windowToString() @@ -25,16 +32,27 @@ streamer class C(Int x, Monitor m) end main - C o = new C(0, null); - o.register(); - + // configure simulation clock Int i = 100; Int endAt = 200; + // initialize stream + C o = new C(0, null, null); + + // initialize monitor + o.register(); + + // run simulation while i < endAt do - o.doStream(); + + // put triples in the stream + o.doStream(i); + + // read window contents String res = o.windowToString(); print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) i = i + 1; end end @@ -50,3 +68,6 @@ end // >>107: 5 // >>108: 5 // .. + +// Notes: +// - SUM aggregation works as expected diff --git a/src/test/resources/csparql/06_average.smol b/src/test/resources/csparql/06_average.smol index bbc68096..fa4e12cb 100644 --- a/src/test/resources/csparql/06_average.smol +++ b/src/test/resources/csparql/06_average.smol @@ -1,16 +1,23 @@ -streamer class C(Int x, Monitor m) +// reada src/test/resources/csparql/06_average.smol + +class Observation(Int x, Int ts) end + +streamer class C(Int x, Observation obs, Monitor m) Unit register() Monitor m = monitor(" - SELECT (AVG(?x) as ?avgX) - FROM STREAM %1 [RANGE 5s STEP 2s] - WHERE { ?s prog:C_x ?x } + SELECT (AVG(?x) AS ?avgX) + FROM NAMED WINDOW ON %1 [RANGE PT5.001s STEP PT2s] + WHERE { + WINDOW ?w { ?s prog:Observation_x ?x } + } ", this); this.m = m; end - Unit doStream() emits(this.x) + Unit doStream(Int ts) emits(this.obs, this.obs.x, this.obs.ts) this.x = this.x + 1; + this.obs = new Observation(this.x, ts); end String windowToString() @@ -25,16 +32,27 @@ streamer class C(Int x, Monitor m) end main - C o = new C(0, null); - o.register(); - + // configure simulation clock Int i = 100; Int endAt = 200; + // initialize stream + C o = new C(0, null, null); + + // initialize monitor + o.register(); + + // run simulation while i < endAt do - o.doStream(); + + // put triples in the stream + o.doStream(i); + + // read window contents String res = o.windowToString(); print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) i = i + 1; end end @@ -54,4 +72,5 @@ end // .. // Notes: +// - AVG aggregation works as expected // - Average returns double values (not integer) diff --git a/src/test/resources/csparql/07_stream_object.smol b/src/test/resources/csparql/07_stream_object.smol index 4a670597..8af3893a 100644 --- a/src/test/resources/csparql/07_stream_object.smol +++ b/src/test/resources/csparql/07_stream_object.smol @@ -1,21 +1,28 @@ -streamer class C(Int x, Monitor m) +// reada src/test/resources/csparql/07_stream_object.smol + +class Observation(Int x, Int ts) end + +streamer class C(Int x, Observation obs, Monitor m) Unit register() - Monitor m = monitor(" + Monitor m = monitor(" SELECT ?s - FROM STREAM %1 [RANGE 6s STEP 4s] - WHERE { ?s prog:C_x ?x } + FROM NAMED WINDOW ON %1 [RANGE PT5.001s STEP PT2s] + WHERE { + WINDOW ?w { ?s prog:Observation_x ?x } + } ", this); this.m = m; end - Unit doStream() emits(this, this.x) + Unit doStream(Int ts) emits(this.obs, this.obs.x, this.obs.ts) this.x = this.x + 1; + this.obs = new Observation(this.x, ts); end String windowToString() String s = ""; - List l = window(this.m); + List l = window(this.m); while l != null do s = s ++ l.content ++ " "; l = l.next; @@ -25,16 +32,27 @@ streamer class C(Int x, Monitor m) end main - C o = new C(0, null); - o.register(); - + // configure simulation clock Int i = 100; Int endAt = 200; + // initialize stream + C o = new C(0, null, null); + + // initialize monitor + o.register(); + + // run simulation while i < endAt do - o.doStream(); + + // put triples in the stream + o.doStream(i); + + // read window contents String res = o.windowToString(); print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) i = i + 1; end end @@ -42,12 +60,15 @@ end // Output: // >>100: // >>101: -// >>102: -// >>103: -// >>104: obj5 obj5 obj5 obj5 -// >>105: obj5 obj5 obj5 obj5 -// >>106: obj5 obj5 obj5 obj5 -// >>107: obj5 obj5 obj5 obj5 -// >>108: obj5 obj5 obj5 obj5 obj5 obj5 -// >>109: obj5 obj5 obj5 obj5 obj5 obj5 +// >>102: obj639 obj640 +// >>103: obj639 obj640 +// >>104: obj639 obj640 obj641 obj644 +// >>105: obj639 obj640 obj641 obj644 +// >>106: obj640 obj641 obj644 obj647 obj652 +// >>107: obj640 obj641 obj644 obj647 obj652 +// >>108: obj644 obj647 obj652 obj657 obj663 +// >>109: obj644 obj647 obj652 obj657 obj663 // .. + +// Notes: +// - Objects can be used as stream elements diff --git a/src/test/resources/csparql/08_use_window_results.smol b/src/test/resources/csparql/08_use_window_results.smol index 5f0cb597..4f20f0e6 100644 --- a/src/test/resources/csparql/08_use_window_results.smol +++ b/src/test/resources/csparql/08_use_window_results.smol @@ -1,16 +1,23 @@ -streamer class C(Int x, Monitor m) +// reada src/test/resources/csparql/08_use_window_results.smol + +class Observation(Int x, Int ts) end + +streamer class C(Int x, Observation obs, Monitor m) Unit register() Monitor m = monitor(" - SELECT (SUM(?x) as ?sumX) - FROM STREAM %1 [RANGE 5s STEP 2s] - WHERE { ?s prog:C_x ?x } + SELECT (SUM(?x) AS ?sumX) + FROM NAMED WINDOW ON %1 [RANGE PT5.001s STEP PT2s] + WHERE { + WINDOW ?w { ?s prog:Observation_x ?x } + } ", this); this.m = m; end - Unit doStream() emits(this.x) + Unit doStream(Int ts) emits(this.obs, this.obs.x, this.obs.ts) this.x = this.x + 1; + this.obs = new Observation(this.x, ts); end String windowToString() @@ -28,16 +35,27 @@ streamer class C(Int x, Monitor m) end main - C o = new C(0, null); - o.register(); - + // configure simulation clock Int i = 100; Int endAt = 200; + // initialize stream + C o = new C(0, null, null); + + // initialize monitor + o.register(); + + // run simulation while i < endAt do - o.doStream(); + + // put triples in the stream + o.doStream(i); + + // read window contents String res = o.windowToString(); print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) i = i + 1; end end @@ -56,6 +74,7 @@ end // >>110: 40 mult20 // >>111: 40 mult20 // >>112: 50 +// .. // Notes: // - Shows how to use the window results (e.g., check if the sum is multiple of 20) diff --git a/src/test/resources/csparql/10_multiple_streams.smol b/src/test/resources/csparql/10_multiple_streams.smol index 0152b747..9814e923 100644 --- a/src/test/resources/csparql/10_multiple_streams.smol +++ b/src/test/resources/csparql/10_multiple_streams.smol @@ -1,12 +1,23 @@ +// reada src/test/resources/csparql/10_multiple_streams.smol + +class Observation(Int x, Int ts) end + class Controller(List streamers, Monitor m) Unit register() Monitor m = monitor(" SELECT ?x - FROM STREAM %1 [RANGE TRIPLES 6] - FROM STREAM %2 [RANGE TRIPLES 6] - FROM STREAM %3 [RANGE TRIPLES 6] - WHERE { ?s prog:C_x ?x }", + FROM NAMED WINDOW ON %1 [RANGE PT4.001s STEP PT2s] + FROM NAMED WINDOW ON %2 [RANGE PT4.001s STEP PT2s] + FROM NAMED WINDOW ON %3 [RANGE PT4.001s STEP PT2s] + WHERE { + { WINDOW { ?s prog:C_x ?x. } } + UNION + { WINDOW { ?s prog:C_x ?x. } } + UNION + { WINDOW { ?s prog:C_x ?x. } } + } + ", this.streamers.content, this.streamers.next.content, this.streamers.next.next.content @@ -44,6 +55,11 @@ streamer class C(Int x) end main + // configure simulation + clock Int i = 100; + Int endAt = 200; + + // initialize stream C o1 = new C(0); C o2 = new C(10); C o3 = new C(20); @@ -53,15 +69,20 @@ main Controller ctrl = new Controller(streamers, null); - clock Int i = 100; - Int endAt = 200; - + // initialize monitor ctrl.register(); + // run simulation while i < endAt do - ctrl.doStream(); + + // put triples in the stream + ctrl.doStream(i); + + // read window contents String res = ctrl.windowToString(); print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) i = i + 1; end end @@ -69,13 +90,18 @@ end // Output: // >>100: // >>101: -// >>102: 1 11 21 2 12 22 -// >>103: 1 11 21 2 12 22 -// >>104: 3 13 23 4 14 24 -// >>105: 3 13 23 4 14 24 -// >>106: 5 15 25 6 16 26 -// >>107: 5 15 25 6 16 26 -// >>108: 7 17 27 8 18 28 +// >>102: +// >>103: 1 11 21 2 12 22 : 1-2, 11-12, 21-22 +// >>104: 1 11 21 2 12 22 +// >>105: 1 21 14 4 3 23 11 22 24 12 13 2 : 2-5, 12-15, 22-25 +// >>106: 1 21 14 4 3 23 11 22 24 12 13 2 +// >>107: 25 4 14 3 23 16 26 24 6 5 13 15 : 3-6, 13-16, 23-26 +// >>108: 25 4 14 3 23 16 26 24 6 5 13 15 +// >>109: 25 27 16 26 28 6 17 5 8 7 18 15 : 5-8, 15-18, 25-28 +// >>110: 25 27 16 26 28 6 17 5 8 7 18 15 +// >>111: 30 20 27 28 10 9 17 29 8 19 7 18 : 7-10, 17-20, 27-30 +// >>112: 30 20 27 28 10 9 17 29 8 19 7 18 +// >>113: 30 20 32 21 10 9 22 29 12 11 19 31 : 9-12, 19-22, 29-32 // .. // Notes: diff --git a/src/test/resources/csparql/11_use_domain.smol b/src/test/resources/csparql/11_use_domain.smol index bd0d2e5e..cf99c73b 100644 --- a/src/test/resources/csparql/11_use_domain.smol +++ b/src/test/resources/csparql/11_use_domain.smol @@ -1,16 +1,25 @@ // Commands: -// java -jar build/libs/smol.jar -b reada src/test/resources/csparql/example.owl -p ex=https://example.com/ontology# +// java -jar build/libs/smol.jar -b src/test/resources/csparql/example.owl -p ex=https://example.com/ontology# // reada src/test/resources/csparql/11_use_domain.smol +class Observation(Int x, Int ts) end + class Controller(List streamers, Monitor m) Unit register() Monitor m = monitor(" SELECT ?x - FROM STREAM %1 [RANGE TRIPLES 6] - FROM STREAM %2 [RANGE TRIPLES 6] - FROM STREAM %3 [RANGE TRIPLES 6] - WHERE { ?s prog:C_x ?x }", + FROM NAMED WINDOW ON %1 [RANGE PT4.001s STEP PT2s] + FROM NAMED WINDOW ON %2 [RANGE PT4.001s STEP PT2s] + FROM NAMED WINDOW ON %3 [RANGE PT4.001s STEP PT2s] + WHERE { + { WINDOW { ?s prog:C_x ?x. } } + UNION + { WINDOW { ?s prog:C_x ?x. } } + UNION + { WINDOW { ?s prog:C_x ?x. } } + } + ", this.streamers.content, this.streamers.next.content, this.streamers.next.next.content @@ -39,13 +48,20 @@ class Controller(List streamers, Monitor m) end -streamer class C(String id, Int x) models "a ex:Class."; +streamer class C(Int x) models "a ex:Class."; + Unit doStream() emits(this.x) this.x = this.x + 1; end + end main + // configure simulation + clock Int i = 100; + Int endAt = 200; + + // initialize stream List streamers = construct(" SELECT ?id ?x WHERE { @@ -56,15 +72,20 @@ main "); Controller ctrl = new Controller(streamers, null); - clock Int i = 100; - Int endAt = 200; - + // initialize monitor ctrl.register(); + // run simulation while i < endAt do - ctrl.doStream(); + + // put triples in the stream + ctrl.doStream(i); + + // read window contents String res = ctrl.windowToString(); print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) i = i + 1; end end @@ -72,13 +93,18 @@ end // Output: // >>100: // >>101: -// >>102: 1 11 21 2 12 22 -// >>103: 1 11 21 2 12 22 -// >>104: 3 13 23 4 14 24 -// >>105: 3 13 23 4 14 24 -// >>106: 5 15 25 6 16 26 -// >>107: 5 15 25 6 16 26 -// >>108: 7 17 27 8 18 28 +// >>102: +// >>103: 11 21 1 12 22 2 : 1-2, 11-12, 21-22 +// >>104: 11 21 1 12 22 2 +// >>105: 24 21 4 1 12 11 23 2 14 22 13 3 : 2-5, 12-15, 22-25 +// >>106: 24 21 4 1 12 11 23 2 14 22 13 3 +// >>107: 24 15 4 6 23 26 14 25 13 3 5 16 : 3-6, 13-16, 23-26 +// >>108: 24 15 4 6 23 26 14 25 13 3 5 16 +// >>109: 15 27 6 18 26 17 7 28 25 8 5 16 : 5-8, 15-18, 25-28 +// >>110: 15 27 6 18 26 17 7 28 25 8 5 16 +// >>111: 27 10 30 18 17 29 7 20 28 19 9 8 : 7-10, 17-20, 27-30 +// >>112: 27 10 30 18 17 29 7 20 28 19 9 8 +// >>113: 22 10 30 21 32 11 29 20 31 19 12 9 : 9-12, 19-22, 29-32 // .. // Notes: diff --git a/src/test/resources/csparql/12_different_ranges.smol b/src/test/resources/csparql/12_different_ranges.smol index 3f1a5759..f373dfb1 100644 --- a/src/test/resources/csparql/12_different_ranges.smol +++ b/src/test/resources/csparql/12_different_ranges.smol @@ -1,12 +1,23 @@ +// reada src/test/resources/csparql/12_different_ranges.smol + +class Observation(Int x, Int ts) end + class Controller(List streamers, Monitor m) Unit register() Monitor m = monitor(" SELECT ?x - FROM STREAM %1 [RANGE TRIPLES 2] - FROM STREAM %2 [RANGE TRIPLES 4] - FROM STREAM %3 [RANGE TRIPLES 6] - WHERE { ?s prog:C_x ?x }", + FROM NAMED WINDOW ON %1 [RANGE PT4.001s STEP PT2s] + FROM NAMED WINDOW ON %2 [RANGE PT3.001s STEP PT1s] + FROM NAMED WINDOW ON %3 [RANGE PT5.001s STEP PT4s] + WHERE { + { WINDOW { ?s prog:C_x ?x. } } + UNION + { WINDOW { ?s prog:C_x ?x. } } + UNION + { WINDOW { ?s prog:C_x ?x. } } + } + ", this.streamers.content, this.streamers.next.content, this.streamers.next.next.content @@ -44,6 +55,11 @@ streamer class C(Int x) end main + // configure simulation + clock Int i = 100; + Int endAt = 200; + + // initialize stream C o1 = new C(0); C o2 = new C(10); C o3 = new C(20); @@ -53,15 +69,20 @@ main Controller ctrl = new Controller(streamers, null); - clock Int i = 100; - Int endAt = 200; - + // initialize monitor ctrl.register(); + // run simulation while i < endAt do - ctrl.doStream(); + + // put triples in the stream + ctrl.doStream(i); + + // read window contents String res = ctrl.windowToString(); print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) i = i + 1; end end @@ -69,14 +90,19 @@ end // Output: // >>100: // >>101: -// >>102: 1 11 21 2 12 22 -// >>103: 1 11 21 2 12 22 -// >>104: 3 13 23 4 14 24 -// >>105: 3 13 23 4 14 24 -// >>106: 5 15 25 6 16 26 -// >>107: 5 15 25 6 16 26 -// >>108: 7 17 27 8 18 28 -// .. +// >>102: +// >>103: 11 21 1 12 22 2 : 1-2, 11-12, 21-22 +// >>104: 11 21 1 12 22 2 +// >>105: 22 4 21 24 12 23 1 11 14 13 2 3 : 2-5, 12-15, 22-25 +// >>106: 22 4 21 24 12 23 1 11 14 13 2 3 +// >>107: 26 4 25 6 24 23 14 13 16 3 5 15 : 3-6, 13-16, 23-26 +// >>108: 26 4 25 6 24 23 14 13 16 3 5 15 +// >>109: 18 26 17 25 6 7 28 27 8 16 5 15 : 5-8, 15-18, 25-28 +// >>110: 18 26 17 25 6 7 28 27 8 16 5 15 +// >>111: 18 17 10 20 7 28 19 27 30 8 29 9 : 7-10, 17-20, 27-30 +// >>112: 18 17 10 20 7 28 19 27 30 8 29 9 +// >>113: 10 11 20 19 22 12 30 21 29 32 31 9 : 9-12, 19-22, 29-32 + // Notes: -// - Error: Indeterministic! Windows have 2, 4, or 6 elements +// - First range and step applies: [RANGE 4s STEP 2s] From 44041f23aa3985eca232d42eaf9af38c7dd2899f Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 21 Sep 2025 22:22:11 +0200 Subject: [PATCH 14/19] multiple monitors enabled --- .../no/uio/microobject/data/StreamManager.kt | 19 +-- .../csparql/14_multiple_monitors.smol | 120 ++++++++++++++++++ 2 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 src/test/resources/csparql/14_multiple_monitors.smol diff --git a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt index c18b482f..33a9f00d 100644 --- a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt +++ b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt @@ -61,8 +61,9 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable var lastTimestamp: Long = 0 var lastWindowTs: Long? = null - var lastTriggerTs: Long? = null - var lastModel: Model? = null + // monitor state + var lastTriggerTs: MutableMap = mutableMapOf() + var lastModels: MutableMap = mutableMapOf() var nStaticGraphsPushed = 0 @@ -139,12 +140,12 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable val expressions = interpreter!!.staticInfo.streamersTable[className]!![methodName]!! val timestamp = getTimestamp() - if (lastTriggerTs == null || timestamp > lastTriggerTs!!) { + if (!lastTriggerTs.containsKey(obj) || timestamp > lastTriggerTs[obj]!!) { // put model only if time has advanced (guarantee one graph per timestamp) - if (lastTriggerTs != null) - stream.putGraph(lastModel!!.getGraph(), lastTriggerTs!!) - lastModel = ModelFactory.createDefaultModel() - lastTriggerTs = timestamp + if (lastTriggerTs.containsKey(obj)) + stream.putGraph(lastModels[obj]!!.getGraph(), lastTriggerTs[obj]!!) + lastModels[obj] = ModelFactory.createDefaultModel() + lastTriggerTs[obj] = timestamp } for (expr in expressions) { @@ -167,8 +168,8 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable predIri = "${settings.progPrefix}${(currentObj.tag as BaseType).name}_${exprParts.last()}" } - val stmt = lastModel!!.createStatement(lastModel!!.createResource(subjIri), lastModel!!.createProperty(predIri), literalToIri(lastModel!!, res, settings)) - lastModel!!.add(stmt) + val stmt = lastModels[obj]!!.createStatement(lastModels[obj]!!.createResource(subjIri), lastModels[obj]!!.createProperty(predIri), literalToIri(lastModels[obj]!!, res, settings)) + lastModels[obj]!!.add(stmt) } } diff --git a/src/test/resources/csparql/14_multiple_monitors.smol b/src/test/resources/csparql/14_multiple_monitors.smol new file mode 100644 index 00000000..58aff3c2 --- /dev/null +++ b/src/test/resources/csparql/14_multiple_monitors.smol @@ -0,0 +1,120 @@ +// reada src/test/resources/csparql/14_multiple_monitors.smol + +class Observation(Int x, Int ts) end + +class Controller(List streamers) + + Unit register() + List objs = this.streamers; + while objs != null do + C o = objs.content; + o.register(); + objs = objs.next; + end + end + + Unit doStream(Int ts) + List objs = this.streamers; + while objs != null do + C o = objs.content; + o.doStream(ts); + objs = objs.next; + end + end + + String windowToString() + String s = ""; + List objs = this.streamers; + while objs != null do + C o = objs.content; + String res = o.windowToString(); + s = s ++ o ++ " ( " ++ res ++ ") "; + objs = objs.next; + end + return s; + end +end + +streamer class C(Int x, Observation obs, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT ?x + FROM NAMED WINDOW ON %1 [RANGE PT4.001s STEP PT2s] + WHERE { + WINDOW ?w { ?s prog:Observation_x ?x. } + } + ", + this + ); + this.m = m; + end + + Unit doStream(Int ts) emits(this.obs, this.obs.x, this.obs.ts) + this.obs = new Observation(this.x, ts); + this.x = this.x + 1; + end + + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end + +end + +main + // configure simulation + clock Int i = 100; + Int endAt = 200; + + // initialize stream + C o1 = new C(0, null, null); + C o2 = new C(10, null, null); + C o3 = new C(20, null, null); + List l3 = new List(o3, null); + List l2 = new List(o2, l3); + List streamers = new List(o1, l2); + Controller ctrl = new Controller(streamers); + + // initialize monitor + ctrl.register(); + + // run simulation + while i < endAt do + + // put triples in the stream + ctrl.doStream(i); + + // read window contents + String res = ctrl.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) + i = i + 1; + end +end + +// Output: +// >>100: obj7 ( ) obj8 ( ) obj9 ( ) +// >>101: obj7 ( ) obj8 ( ) obj9 ( ) +// >>102: obj7 ( ) obj8 ( ) obj9 ( ) +// >>103: obj7 ( 0 1 ) obj8 ( 10 11 ) obj9 ( 20 21 ) +// >>104: obj7 ( 0 1 ) obj8 ( 10 11 ) obj9 ( 20 21 ) +// >>105: obj7 ( 0 1 2 3 ) obj8 ( 10 11 12 13 ) obj9 ( 20 21 22 23 ) +// >>106: obj7 ( 0 1 2 3 ) obj8 ( 10 11 12 13 ) obj9 ( 20 21 22 23 ) +// >>107: obj7 ( 2 3 4 5 ) obj8 ( 12 13 14 15 ) obj9 ( 22 23 24 25 ) +// >>108: obj7 ( 2 3 4 5 ) obj8 ( 12 13 14 15 ) obj9 ( 22 23 24 25 ) +// >>109: obj7 ( 4 5 6 7 ) obj8 ( 14 15 16 17 ) obj9 ( 24 25 26 27 ) +// >>110: obj7 ( 4 5 6 7 ) obj8 ( 14 15 16 17 ) obj9 ( 24 25 26 27 ) +// >>111: obj7 ( 6 7 8 9 ) obj8 ( 16 17 18 19 ) obj9 ( 26 27 28 29 ) +// >>112: obj7 ( 6 7 8 9 ) obj8 ( 16 17 18 19 ) obj9 ( 26 27 28 29 ) +// >>113: obj7 ( 8 9 10 11 ) obj8 ( 18 19 20 21 ) obj9 ( 28 29 30 31 ) + +// Notes: +// - Multiple monitors on multiple streams are supported From 057759f8440e840e2dad1515f12a1bbfaf29cb2b Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 21 Sep 2025 23:40:51 +0200 Subject: [PATCH 15/19] static join enabled --- .../microobject/ast/stmt/PushStaticStmt.kt | 12 +- .../no/uio/microobject/data/StreamManager.kt | 12 -- .../no/uio/microobject/type/TypeChecker.kt | 20 +++ .../resources/csparql/13_static_join.smol | 122 ++++++++++++++---- 4 files changed, 129 insertions(+), 37 deletions(-) diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt index 0a8084fc..f22040ec 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/PushStaticStmt.kt @@ -40,31 +40,37 @@ data class PushStaticStmt(val target : Location, val sources: Expression, val po "urlOntology" to false, "fmos" to false ) + var reasonerMode: ReasonerMode = ReasonerMode.off for (s in sourcesList) { val trimmed = s.trim() if (sourcesMap.containsKey(trimmed)) { sourcesMap[trimmed] = true + } else if (trimmed == "reasoner") { + reasonerMode = interpreter.settings.reasoner } else { - throw Exception("Unknown source '$trimmed' in pushStatic statement, only comma-separated [heap, staticTable, vocabularyFile, externalOntology, urlOntology, fmos] are allowed") + throw Exception("Unknown source '$trimmed' in pushStatic statement, only comma-separated [heap, staticTable, vocabularyFile, externalOntology, urlOntology, fmos (or reasoner)] are allowed") } } + // load settings from source map val ts = TripleSettings( sources = sourcesMap, guards = hashMapOf("heap" to true, "staticTable" to true), virtualization = hashMapOf("heap" to true, "staticTable" to true, "fmos" to true), - jenaReasoner = interpreter.settings.reasoner, + jenaReasoner = reasonerMode, cachedModel = null ) + // write output to a file val model = interpreter.tripleManager.getModel(ts) - // todo implement val file = "output.ttl" File(interpreter.settings.outdir).mkdirs() File("${interpreter.settings.outdir}/${file}").createNewFile() model.write(FileWriter("${interpreter.settings.outdir}/${file}"),"TTL") val resultPath = "" + // return the file path as a string literal + println("Saving static data to $resultPath") val resultLit = LiteralExpr(resultPath, STRINGTYPE) return replaceStmt(AssignStmt(target, resultLit, declares = declares), stackFrame) } diff --git a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt index 33a9f00d..ed50032e 100644 --- a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt +++ b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt @@ -230,18 +230,6 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable return queryWithPrefixes } - // todo implement - // public fun putStaticNamedGraph(iri: String, model: Model) { - // if (!engineInitialized) initEngine() - - // // serialize the model (RDF/XML matches the engine's first attempt) - // val sw = StringWriter() - // model.write(sw, "RDF/XML") - - // // hand it to the C-SPARQL engine - // engine.putStaticNamedModel(iri, sw.toString()) - // } - public fun getStaticNamedIri(): String { val s = "${settings.runPrefix}loadStatic${nStaticGraphsPushed}" nStaticGraphsPushed += 1 diff --git a/src/main/kotlin/no/uio/microobject/type/TypeChecker.kt b/src/main/kotlin/no/uio/microobject/type/TypeChecker.kt index 81a2dcf5..2b957e24 100644 --- a/src/main/kotlin/no/uio/microobject/type/TypeChecker.kt +++ b/src/main/kotlin/no/uio/microobject/type/TypeChecker.kt @@ -1106,6 +1106,26 @@ class TypeChecker(private val ctx: WhileParser.ProgramContext, private val setti if(ctx.target != null && ctx.target !is WhileParser.Var_expressionContext && inRule) log("Non-local access in rule method.", ctx) } + is WhileParser.PushStatic_statementContext -> { + val innerType = getType(ctx.sources, inner, vars, thisType, inRule) + if(innerType != ERRORTYPE && innerType != STRINGTYPE) + log("PushStatic expects a string as its parameter", ctx) + if (ctx.declType != null) { + val lhs = ctx.target + if (lhs !is WhileParser.Var_expressionContext) { + log("Variable declaration must declare a variable.", ctx) + } else { + val name = lhs.NAME().text + if (vars.keys.contains(name)) log("Variable $name declared twice.", ctx) + else { + val expType = translateType(ctx.type(), className, generics) + vars[name] = expType + } + } + if (translateType(ctx.declType, className, generics) != STRINGTYPE) + log("PushStatic expects a String variable to assign to", ctx) + } + } else -> { log("Statements with class ${ctx.javaClass} cannot be type checked",ctx) } diff --git a/src/test/resources/csparql/13_static_join.smol b/src/test/resources/csparql/13_static_join.smol index c560e79f..1a9107bc 100644 --- a/src/test/resources/csparql/13_static_join.smol +++ b/src/test/resources/csparql/13_static_join.smol @@ -1,17 +1,63 @@ -streamer class C(Int x, Monitor m) +// reada src/test/resources/csparql/13_static_join.smol - Unit register() - String static = pushStatic("heap,staticTable"); +class Observation(Int x, Int ts) end + +class Controller(List streamers) + + Unit register(String static) + List objs = this.streamers; + while objs != null do + C o = objs.content; + o.register(static); + objs = objs.next; + end + end + + Unit doStream(Int ts) + List objs = this.streamers; + while objs != null do + C o = objs.content; + o.doStream(ts); + objs = objs.next; + end + end + + String windowToString() + String s = ""; + List objs = this.streamers; + while objs != null do + C o = objs.content; + String res = o.windowToString(); + s = s ++ o ++ " ( " ++ res ++ ") "; + objs = objs.next; + end + return s; + end +end + +streamer class C(Int x, Int mult, Observation obs, Monitor m) + + Unit register(String static) Monitor m = monitor(" - SELECT (SUM(?x) as ?sumX) - FROM STREAM %1 [RANGE 3s TUMBLING] - FROM <%2> - WHERE { ?s prog:C_x ?x . ?s a prog:C . } - ", this, static); + SELECT (?x * ?k AS ?res) + FROM NAMED WINDOW ON %1 [RANGE PT5.001s STEP PT2s] + FROM %2 + WHERE { + ?streamer prog:C_mult ?k. + WINDOW ?w { + ?streamer prog:C_obs ?obs . + ?obs prog:Observation_x ?x . + } + } + ", + this, + static + ); this.m = m; end - Unit doStream() emits(this.x) + Unit doStream(Int ts) emits(this.obs, this.obs.x, this.obs.ts) + this.obs = new Observation(this.x, ts); this.x = this.x + 1; end @@ -28,29 +74,61 @@ streamer class C(Int x, Monitor m) end main - C o = new C(10, null); - o.register(); - + // configure simulation clock Int i = 100; Int endAt = 200; + C o1 = new C(0, 2, null, null); + C o2 = new C(10, 3, null, null); + C o3 = new C(20, 10, null, null); + + // push static data + String static = pushStatic("heap,staticTable"); + + // initialize stream + List l3 = new List(o3, null); + List l2 = new List(o2, l3); + List streamers = new List(o1, l2); + Controller ctrl = new Controller(streamers); + + // initialize monitor + ctrl.register(static); + + // run simulation while i < endAt do - o.doStream(); - String res = o.windowToString(); + + // put triples in the stream + ctrl.doStream(i); + + // read window contents + String res = ctrl.windowToString(); print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) i = i + 1; end end // Output: -// >>100: -// >>101: -// >>102: -// >>103: 0 -// >>104: 0 -// >>105: 0 -// >>106: 0 +// >>100: obj8 ( ) obj9 ( ) obj10 ( ) +// >>101: obj8 ( ) obj9 ( ) obj10 ( ) +// >>102: obj8 ( ) obj9 ( ) obj10 ( ) +// >>103: obj8 ( 0 2 ) obj9 ( 30 33 ) obj10 ( 200 210 ) +// >>104: obj8 ( 0 2 ) obj9 ( 30 33 ) obj10 ( 200 210 ) +// >>105: obj8 ( 0 2 4 6 ) obj9 ( 30 33 36 39 ) obj10 ( 200 210 220 230 ) +// >>106: obj8 ( 0 2 4 6 ) obj9 ( 30 33 36 39 ) obj10 ( 200 210 220 230 ) +// >>107: obj8 ( 2 4 6 8 10 ) obj9 ( 33 36 39 42 45 ) obj10 ( 210 220 230 240 250 ) +// >>108: obj8 ( 2 4 6 8 10 ) obj9 ( 33 36 39 42 45 ) obj10 ( 210 220 230 240 250 ) +// >>109: obj8 ( 6 8 10 12 14 ) obj9 ( 39 42 45 48 51 ) obj10 ( 230 240 250 260 270 ) +// >>110: obj8 ( 6 8 10 12 14 ) obj9 ( 39 42 45 48 51 ) obj10 ( 230 240 250 260 270 ) +// >>111: obj8 ( 10 12 14 16 18 ) obj9 ( 45 48 51 54 57 ) obj10 ( 250 260 270 280 290 ) // .. // Notes: -// Error: Static join does not work! +// - pushStatic("heap, staticTable") dumps static data to output.ttl +// - This file contains triples for C_mult property of each streamer object (static data) + +// - Results from streamers are multiplied by {2, 3, 10} respectively +// - Stream data is inside the window: ?x +// - Static data is outside the window: ?k +// - Static data is joined with stream data: ?x * ?k From d1d299be217ab3b972f5e13e9e3672f0744df12a Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Mon, 22 Sep 2025 21:21:10 +0200 Subject: [PATCH 16/19] clock var timing simplified, different periods enabled --- .../no/uio/microobject/ast/stmt/AssignStmt.kt | 2 +- .../no/uio/microobject/data/StreamManager.kt | 46 ++++-- .../csparql/15_different_periods.smol | 136 ++++++++++++++++++ 3 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 src/test/resources/csparql/15_different_periods.smol diff --git a/src/main/kotlin/no/uio/microobject/ast/stmt/AssignStmt.kt b/src/main/kotlin/no/uio/microobject/ast/stmt/AssignStmt.kt index 3a6ee500..955c6851 100644 --- a/src/main/kotlin/no/uio/microobject/ast/stmt/AssignStmt.kt +++ b/src/main/kotlin/no/uio/microobject/ast/stmt/AssignStmt.kt @@ -34,7 +34,7 @@ data class AssignStmt(val target : Location, val value : Expression, val isClock interpreter.streamManager.clockVar = target.name } if (interpreter.streamManager.clockVar != null && target.name.equals(interpreter.streamManager.clockVar) - && res.tag == INTTYPE) interpreter.streamManager.clockTimestampSec = res.literal + && res.tag == INTTYPE) interpreter.streamManager.updateClock(res.literal.toLong()) stackFrame.store[target.name] = res } is OwnVar -> { diff --git a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt index ed50032e..b082821e 100644 --- a/src/main/kotlin/no/uio/microobject/data/StreamManager.kt +++ b/src/main/kotlin/no/uio/microobject/data/StreamManager.kt @@ -50,6 +50,7 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable private var ec: EngineConfiguration? = null val LOG : Logger? = LoggerFactory.getLogger(StreamManager::class.java) + private var streamToClass: MutableMap = mutableMapOf() private var streams: MutableMap> = mutableMapOf() private var monitors: MutableMap = mutableMapOf() @@ -57,11 +58,9 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable private val queryResults = ConcurrentHashMap>() // read-only snapshots var clockVar : String? = null - var clockTimestampSec : String? = null - var lastTimestamp: Long = 0 - var lastWindowTs: Long? = null + var clockTimestampSec : Long? = null - // monitor state + // stream state var lastTriggerTs: MutableMap = mutableMapOf() var lastModels: MutableMap = mutableMapOf() @@ -117,24 +116,36 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable if (!streams.containsKey(className)) streams[className] = mutableMapOf() streams[className]!![obj] = stream + streamToClass[obj] = className } private fun getTimestamp(): Long { - var ms: Long - if (clockTimestampSec != null) { - ms = secToMs(clockTimestampSec!!.toLong()) - } else { - ms = System.currentTimeMillis() - } - - lastTimestamp = ms - return ms + if (clockTimestampSec != null) + return secToMs(clockTimestampSec!!) + return System.currentTimeMillis() } private fun secToMs(s: Long): Long { return s * 1000 } + fun updateClock(newTsSec: Long) { + if (clockTimestampSec != newTsSec) { + clockTimestampSec = newTsSec + + // push last models to streams if time has advanced + pushLastModels() + } + } + + private fun pushLastModels() { + for ((obj, model) in lastModels) { + if (model.isEmpty) continue + streams[streamToClass[obj]!!]!![obj]!!.putGraph(model.getGraph(), lastTriggerTs[obj]!!) + lastModels[obj] = ModelFactory.createDefaultModel() + } + } + public fun triggerStream(className: String, obj: LiteralExpr, methodName: String, stackEntry: StackEntry) { val stream = streams[className]!![obj]!! val expressions = interpreter!!.staticInfo.streamersTable[className]!![methodName]!! @@ -170,6 +181,15 @@ class StreamManager(private val settings: Settings, val staticTable: StaticTable val stmt = lastModels[obj]!!.createStatement(lastModels[obj]!!.createResource(subjIri), lastModels[obj]!!.createProperty(predIri), literalToIri(lastModels[obj]!!, res, settings)) lastModels[obj]!!.add(stmt) + // println("Added to stream $obj at $timestamp: $stmt") + } + + // if the clock variable is not used, push the model immediately + // assume system time (ms) has advanced or will advance before next trigger + if (clockVar == null) { + stream.putGraph(lastModels[obj]!!.getGraph(), lastTriggerTs[obj]!!) + // println("Pushed immediately to stream $obj at ${lastTriggerTs[obj]}: ${lastModels[obj]!!.size()} triples (now: ${getTimestamp()})") + lastModels[obj] = ModelFactory.createDefaultModel() } } diff --git a/src/test/resources/csparql/15_different_periods.smol b/src/test/resources/csparql/15_different_periods.smol new file mode 100644 index 00000000..57229941 --- /dev/null +++ b/src/test/resources/csparql/15_different_periods.smol @@ -0,0 +1,136 @@ +// reada src/test/resources/csparql/15_different_periods.smol + +class Observation(Int x, Int ts) end + +class Controller(List streamers) + + Unit register() + List objs = this.streamers; + while objs != null do + C o = objs.content; + o.register(); + objs = objs.next; + end + end + + // Unit doStream(Int ts) + // List objs = this.streamers; + // while objs != null do + // C o = objs.content; + // o.doStream(ts); + // objs = objs.next; + // end + // end + + String windowToString() + String s = ""; + List objs = this.streamers; + while objs != null do + C o = objs.content; + String res = o.windowToString(); + s = s ++ o ++ " ( " ++ res ++ ") "; + objs = objs.next; + end + return s; + end +end + +streamer class C(Int x, Int period, Observation obs, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT ?x + FROM NAMED WINDOW ON %1 [RANGE PT6.001s STEP PT2s] + WHERE { + WINDOW ?w { ?s prog:Observation_x ?x. } + } + ", + this + ); + this.m = m; + end + + Unit doStream(Int ts) emits(this.obs, this.obs.x, this.obs.ts) + this.obs = new Observation(this.x, ts); + this.x = this.x + 1; + end + + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end + +end + +main + // configure simulation + clock Int i = 100; + Int endAt = 200; + + // initialize stream + C o1 = new C(0, 1, null, null); + C o2 = new C(10, 2, null, null); + C o3 = new C(20, 3, null, null); + List l3 = new List(o3, null); + List l2 = new List(o2, l3); + List streamers = new List(o1, l2); + Controller ctrl = new Controller(streamers); + + // initialize monitor + ctrl.register(); + + // run simulation + List objs = null; + while i < endAt do + + // put triples in the stream + objs = streamers; + while objs != null do + C o = objs.content; + if (((i / o.period) * o.period) == i) then + o.doStream(i); + else + o.x = o.x + 1; // increment without streaming + end + objs = objs.next; + end + + // advance clock (timestamp) + i = i + 1; + + // read window contents + String res = ctrl.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + end +end + +// Output: +// >>101: obj6 ( ) obj7 ( ) obj8 ( ) +// >>102: obj6 ( ) obj7 ( ) obj8 ( ) +// >>103: obj6 ( 0 1 ) obj7 ( 10 ) obj8 ( ) +// >>104: obj6 ( 0 1 ) obj7 ( 10 ) obj8 ( ) +// >>105: obj6 ( 0 1 2 3 ) obj7 ( 10 12 ) obj8 ( 22 ) +// >>106: obj6 ( 0 1 2 3 ) obj7 ( 10 12 ) obj8 ( 22 ) +// >>107: obj6 ( 0 1 2 3 4 5 ) obj7 ( 10 12 14 ) obj8 ( 22 25 ) +// >>108: obj6 ( 0 1 2 3 4 5 ) obj7 ( 10 12 14 ) obj8 ( 22 25 ) +// >>109: obj6 ( 2 3 4 5 6 7 ) obj7 ( 12 14 16 ) obj8 ( 22 25 ) +// >>110: obj6 ( 2 3 4 5 6 7 ) obj7 ( 12 14 16 ) obj8 ( 22 25 ) +// >>111: obj6 ( 4 5 6 7 8 9 ) obj7 ( 14 16 18 ) obj8 ( 25 28 ) +// >>112: obj6 ( 4 5 6 7 8 9 ) obj7 ( 14 16 18 ) obj8 ( 25 28 ) +// >>113: obj6 ( 6 7 8 9 10 11 ) obj7 ( 16 18 20 ) obj8 ( 28 31 ) +// >>114: obj6 ( 6 7 8 9 10 11 ) obj7 ( 16 18 20 ) obj8 ( 28 31 ) +// >>115: obj6 ( 8 9 10 11 12 13 ) obj7 ( 18 20 22 ) obj8 ( 28 31 ) +// >>116: obj6 ( 8 9 10 11 12 13 ) obj7 ( 18 20 22 ) obj8 ( 28 31 ) +// >>117: obj6 ( 10 11 12 13 14 15 ) obj7 ( 20 22 24 ) obj8 ( 31 34 ) + +// Notes: +// - Clock variable is increased before reading the window contents (can only read past data) +// - First monitor streams every second: can fit 6 elements in a 6.001s window +// - Second monitor streams every 2 seconds: can fit 3 elements in a 6.001s window +// - Third monitor streams every 3 seconds: can fit 2 elements in a 6.001s window From fbd358885916dabb29e01d7e3ba3a1e0b5599d5b Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Mon, 22 Sep 2025 21:22:34 +0200 Subject: [PATCH 17/19] system time / aperiodic streaming test + analysis added --- .../resources/csparql/16_system_time.smol | 110 ++++++++++++++++++ .../csparql/16_system_time_full_output.txt | 51 ++++++++ 2 files changed, 161 insertions(+) create mode 100644 src/test/resources/csparql/16_system_time.smol create mode 100644 src/test/resources/csparql/16_system_time_full_output.txt diff --git a/src/test/resources/csparql/16_system_time.smol b/src/test/resources/csparql/16_system_time.smol new file mode 100644 index 00000000..5df173d7 --- /dev/null +++ b/src/test/resources/csparql/16_system_time.smol @@ -0,0 +1,110 @@ +// reada src/test/resources/csparql/16_system_time.smol + +class Observation(Int x, Int ts) end + +streamer class C(Int x, Observation obs, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT ?x + FROM NAMED WINDOW ON %1 [RANGE PT0.5s STEP PT0.2s] + WHERE { + WINDOW ?w { ?s prog:Observation_x ?x } + } + ", this); + this.m = m; + end + + Unit doStream(Int ts) emits(this.obs, this.obs.x, this.obs.ts) + this.x = this.x + 1; + this.obs = new Observation(this.x, ts); + end + + Unit wait(Int iter) + Int x = 0; + Int j = 0; + Int k = 0; + while j < 100 do + while k < 100 do + x = j * k; + x = 0; + while x < iter do + x = x + 1; + end + k = k + 1; + end + k = 0; + j = j + 1; + end + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end +end + +main + // configure simulation + Int i = 0; + Int endAt = 10; + + // initialize stream + C o = new C(0, null, null); + + // initialize monitor + o.register(); + + // run simulation + while i < endAt do + + // put triples in the stream + o.doStream(i); + + // advance i + i = i + 1; + + // wait + o.wait(i); + + // read window contents + String res = o.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + + end +end + +// Example run output: +// >>1: +// >>2: +// >>3: 1 2 +// >>4: 1 2 3 +// >>5: 1 2 3 +// >>6: 1 2 3 +// >>7: 2 3 4 5 6 +// >>8: 2 3 4 5 6 +// >>9: 2 3 4 5 6 +// >>10: 4 5 6 7 8 9 + +// Analysis of the example: +// - Window configuration: Range 500ms, Step 200ms +// - Consider first timestamp (1758566583488) as 0 point, listing (add, push) times in ms relative to that point +// - Ex. (1758566583488 - 1758566583488, 1758566583506 - 1758566583488) = (0, 18) +// - p.w.i = previous window includes (the elements in the last window are monitored) +// * 1: ( 0 , 18 ) -> window (0, 200] +// * 2: (137, 138) -> window (0, 200] +// * 3: (230, 357) -> window (0, 400]; p.w.i. 1, 2 +// * 4: (404, 408) -> window (100, 600]; p.w.i. 1, 2, 3 +// * 5: (451, 451) -> window (100, 600]; p.w.i. 1, 2, 3 +// * 6: (511, 511) -> window (100, 600]; p.w.i. 1, 2, 3 +// * 7: (647, 651) -> window (300, 800]; p.w.i. 2, 3, 4, 5, 6 +// * 8: (706, 707) -> window (300, 800]; p.w.i. 2, 3, 4, 5, 6 +// * 9: (749, 749) -> window (300, 800]; p.w.i. 2, 3, 4, 5, 6 +// * 10:(800, 803) -> window (500,1000]; p.w.i. 4, 5, 6, 7, 8, 9 +// - Full output given in 16_system_time_full_output.txt +// - Note: The actual times may vary based on system performance and load diff --git a/src/test/resources/csparql/16_system_time_full_output.txt b/src/test/resources/csparql/16_system_time_full_output.txt new file mode 100644 index 00000000..17e83cd4 --- /dev/null +++ b/src/test/resources/csparql/16_system_time_full_output.txt @@ -0,0 +1,51 @@ +Full output with debug prints: +Added to stream obj6 at 1758566583488: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj6, https://github.com/Edkamb/SemanticObjects/Program#C_obs, https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj8] +Added to stream obj6 at 1758566583488: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj8, https://github.com/Edkamb/SemanticObjects/Program#Observation_x, "1"^^http://www.w3.org/2001/XMLSchema#int] +Added to stream obj6 at 1758566583488: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj8, https://github.com/Edkamb/SemanticObjects/Program#Observation_ts, "0"^^http://www.w3.org/2001/XMLSchema#int] +Pushed immediately to stream obj6 at 1758566583488: 3 triples (now: 1758566583506) +>>1: +Added to stream obj6 at 1758566583625: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj6, https://github.com/Edkamb/SemanticObjects/Program#C_obs, https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj9] +Added to stream obj6 at 1758566583625: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj9, https://github.com/Edkamb/SemanticObjects/Program#Observation_x, "2"^^http://www.w3.org/2001/XMLSchema#int] +Added to stream obj6 at 1758566583625: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj9, https://github.com/Edkamb/SemanticObjects/Program#Observation_ts, "1"^^http://www.w3.org/2001/XMLSchema#int] +Pushed immediately to stream obj6 at 1758566583625: 3 triples (now: 1758566583626) +>>2: +Added to stream obj6 at 1758566583718: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj6, https://github.com/Edkamb/SemanticObjects/Program#C_obs, https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj10] +Added to stream obj6 at 1758566583718: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj10, https://github.com/Edkamb/SemanticObjects/Program#Observation_x, "3"^^http://www.w3.org/2001/XMLSchema#int] +Added to stream obj6 at 1758566583718: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj10, https://github.com/Edkamb/SemanticObjects/Program#Observation_ts, "2"^^http://www.w3.org/2001/XMLSchema#int] +Pushed immediately to stream obj6 at 1758566583718: 3 triples (now: 1758566583845) +>>3: 1 2 +Added to stream obj6 at 1758566583892: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj6, https://github.com/Edkamb/SemanticObjects/Program#C_obs, https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj13] +Added to stream obj6 at 1758566583892: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj13, https://github.com/Edkamb/SemanticObjects/Program#Observation_x, "4"^^http://www.w3.org/2001/XMLSchema#int] +Added to stream obj6 at 1758566583892: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj13, https://github.com/Edkamb/SemanticObjects/Program#Observation_ts, "3"^^http://www.w3.org/2001/XMLSchema#int] +Pushed immediately to stream obj6 at 1758566583892: 3 triples (now: 1758566583896) +>>4: 1 2 3 +Added to stream obj6 at 1758566583939: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj6, https://github.com/Edkamb/SemanticObjects/Program#C_obs, https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj17] +Added to stream obj6 at 1758566583939: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj17, https://github.com/Edkamb/SemanticObjects/Program#Observation_x, "5"^^http://www.w3.org/2001/XMLSchema#int] +Added to stream obj6 at 1758566583939: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj17, https://github.com/Edkamb/SemanticObjects/Program#Observation_ts, "4"^^http://www.w3.org/2001/XMLSchema#int] +Pushed immediately to stream obj6 at 1758566583939: 3 triples (now: 1758566583939) +>>5: 1 2 3 +Added to stream obj6 at 1758566583999: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj6, https://github.com/Edkamb/SemanticObjects/Program#C_obs, https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj21] +Added to stream obj6 at 1758566583999: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj21, https://github.com/Edkamb/SemanticObjects/Program#Observation_x, "6"^^http://www.w3.org/2001/XMLSchema#int] +Added to stream obj6 at 1758566583999: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj21, https://github.com/Edkamb/SemanticObjects/Program#Observation_ts, "5"^^http://www.w3.org/2001/XMLSchema#int] +Pushed immediately to stream obj6 at 1758566583999: 3 triples (now: 1758566583999) +>>6: 1 2 3 +Added to stream obj6 at 1758566584135: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj6, https://github.com/Edkamb/SemanticObjects/Program#C_obs, https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj25] +Added to stream obj6 at 1758566584135: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj25, https://github.com/Edkamb/SemanticObjects/Program#Observation_x, "7"^^http://www.w3.org/2001/XMLSchema#int] +Added to stream obj6 at 1758566584135: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj25, https://github.com/Edkamb/SemanticObjects/Program#Observation_ts, "6"^^http://www.w3.org/2001/XMLSchema#int] +Pushed immediately to stream obj6 at 1758566584135: 3 triples (now: 1758566584139) +>>7: 2 3 4 5 6 +Added to stream obj6 at 1758566584194: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj6, https://github.com/Edkamb/SemanticObjects/Program#C_obs, https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj31] +Added to stream obj6 at 1758566584194: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj31, https://github.com/Edkamb/SemanticObjects/Program#Observation_x, "8"^^http://www.w3.org/2001/XMLSchema#int] +Added to stream obj6 at 1758566584194: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj31, https://github.com/Edkamb/SemanticObjects/Program#Observation_ts, "7"^^http://www.w3.org/2001/XMLSchema#int] +Pushed immediately to stream obj6 at 1758566584194: 3 triples (now: 1758566584195) +>>8: 2 3 4 5 6 +Added to stream obj6 at 1758566584237: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj6, https://github.com/Edkamb/SemanticObjects/Program#C_obs, https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj37] +Added to stream obj6 at 1758566584237: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj37, https://github.com/Edkamb/SemanticObjects/Program#Observation_x, "9"^^http://www.w3.org/2001/XMLSchema#int] +Added to stream obj6 at 1758566584237: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj37, https://github.com/Edkamb/SemanticObjects/Program#Observation_ts, "8"^^http://www.w3.org/2001/XMLSchema#int] +Pushed immediately to stream obj6 at 1758566584237: 3 triples (now: 1758566584237) +>>9: 2 3 4 5 6 +Added to stream obj6 at 1758566584288: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj6, https://github.com/Edkamb/SemanticObjects/Program#C_obs, https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj43] +Added to stream obj6 at 1758566584288: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj43, https://github.com/Edkamb/SemanticObjects/Program#Observation_x, "10"^^http://www.w3.org/2001/XMLSchema#int] +Added to stream obj6 at 1758566584288: [https://github.com/Edkamb/SemanticObjects/Run1758566561585#obj43, https://github.com/Edkamb/SemanticObjects/Program#Observation_ts, "9"^^http://www.w3.org/2001/XMLSchema#int] +Pushed immediately to stream obj6 at 1758566584288: 3 triples (now: 1758566584291) +>>10: 4 5 6 7 8 9 From 6240615ab92934f8814bb1f68e1c5e71975b3672 Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Sun, 28 Sep 2025 12:22:12 +0200 Subject: [PATCH 18/19] influxdb test added, old tests removed --- ...ggregation.smol => 01_no_aggregation.smol} | 2 +- .../resources/csparql/01_physical_window.smol | 59 ---------- .../csparql/02_logical_window_tumbling.smol | 59 ---------- .../csparql/09_timestamp_function.smol | 59 ---------- src/test/resources/csparql/17_influxdb.smol | 109 ++++++++++++++++++ 5 files changed, 110 insertions(+), 178 deletions(-) rename src/test/resources/csparql/{04_no_aggregation.smol => 01_no_aggregation.smol} (96%) delete mode 100644 src/test/resources/csparql/01_physical_window.smol delete mode 100644 src/test/resources/csparql/02_logical_window_tumbling.smol delete mode 100644 src/test/resources/csparql/09_timestamp_function.smol create mode 100644 src/test/resources/csparql/17_influxdb.smol diff --git a/src/test/resources/csparql/04_no_aggregation.smol b/src/test/resources/csparql/01_no_aggregation.smol similarity index 96% rename from src/test/resources/csparql/04_no_aggregation.smol rename to src/test/resources/csparql/01_no_aggregation.smol index 38355d04..e83b0b5f 100644 --- a/src/test/resources/csparql/04_no_aggregation.smol +++ b/src/test/resources/csparql/01_no_aggregation.smol @@ -1,4 +1,4 @@ -// reada src/test/resources/csparql/04_no_aggregation.smol +// reada src/test/resources/csparql/01_no_aggregation.smol class Observation(Int x, Int ts) end diff --git a/src/test/resources/csparql/01_physical_window.smol b/src/test/resources/csparql/01_physical_window.smol deleted file mode 100644 index 37557c31..00000000 --- a/src/test/resources/csparql/01_physical_window.smol +++ /dev/null @@ -1,59 +0,0 @@ -// Commands: -// java -jar build/libs/smol.jar -// reada stream/01_physical_window.smol - -streamer class C(Int x, Monitor m) - - Unit register() - Monitor m = monitor(" - SELECT (SUM(?x) as ?sumX) - FROM STREAM %1 [RANGE TRIPLES 3] - WHERE { ?s prog:C_x ?x } - ", this); - this.m = m; - end - - Unit doStream() emits(this.x) - this.x = this.x + 1; - end - - String windowToString() - String s = ""; - List l = window(this.m); - while l != null do - s = s ++ intToString(l.content) ++ " "; - l = l.next; - end - return s; - end -end - -main - C o = new C(0, null); - o.register(); - - Int i = 100; - Int endAt = 200; - while i < endAt do - o.doStream(); - String res = o.windowToString(); - print(">>" ++ intToString(i) ++ ": " ++ res); - i = i + 1; - end -end - -// Output: -// >>100: -// >>101: -// >>102: 6 = 1+2+3 -// >>103: 6 -// >>104: 6 -// >>105: 15 = 4+5+6 -// >>106: 15 -// >>107: 15 -// >>108: 24 = 7+8+9 -// .. - -// Notes: -// - First 2 windows are empty because the window size is 3 triples -// - RANGE TRIPLES 3 = sliding window of 3 triples diff --git a/src/test/resources/csparql/02_logical_window_tumbling.smol b/src/test/resources/csparql/02_logical_window_tumbling.smol deleted file mode 100644 index 5054c0f9..00000000 --- a/src/test/resources/csparql/02_logical_window_tumbling.smol +++ /dev/null @@ -1,59 +0,0 @@ -streamer class C(Int x, Monitor m) - - Unit register() - Monitor m = monitor(" - SELECT (SUM(?x) as ?sumX) - FROM STREAM %1 [RANGE 3s TUMBLING] - WHERE { ?s prog:C_x ?x } - ", this); - this.m = m; - end - - Unit doStream() emits(this.x) - this.x = this.x + 1; - end - - String windowToString() - String s = ""; - List l = window(this.m); - while l != null do - s = s ++ intToString(l.content) ++ " "; - l = l.next; - end - return s; - end -end - -main - C o = new C(0, null); - o.register(); - - clock Int i = 100; - Int endAt = 200; - - while i < endAt do - o.doStream(); - String res = o.windowToString(); - print(">>" ++ intToString(i) ++ ": " ++ res); - i = i + 1; - end -end - -// Output: -// >>100: -// >>101: -// >>102: -// >>103: 6 = 1+2+3 -// >>104: 6 -// >>105: 6 -// >>106: 15 = 4+5+6 -// >>107: 15 -// >>108: 15 -// >>109: 24 = 7+8+9 -// .. - -// Notes: -// - External timestamp: clock variable increased by 1 second each iteration = timestamp increased by 1000 ms -// - RANGE 3s TUMBLING = tumbling window of 3 seconds -// - Similar output as in 01_physical_window.smol (3 seconds * 1 triple per second = 3 triples) -// - First 3 windows are empty because the window size is 3 seconds diff --git a/src/test/resources/csparql/09_timestamp_function.smol b/src/test/resources/csparql/09_timestamp_function.smol deleted file mode 100644 index 3fa1f027..00000000 --- a/src/test/resources/csparql/09_timestamp_function.smol +++ /dev/null @@ -1,59 +0,0 @@ -streamer class C(Int x, Monitor m) - - Unit register() - Monitor m = monitor(" - PREFIX ext: - SELECT (ext:timestamp(?s, prog:C_x, ?x) as ?t) - FROM STREAM %1 [RANGE 5s STEP 2s] - WHERE { ?s prog:C_x ?x } - ", this); - this.m = m; - end - - Unit doStream() emits(this.x) - this.x = this.x + 1; - end - - String windowToString() - String s = ""; - List l = window(this.m); - while l != null do - s = s ++ intToString(l.content) ++ " "; - l = l.next; - end - return s; - end -end - -main - C o = new C(0, null); - o.register(); - - clock Int i = 100; - Int endAt = 200; - - while i < endAt do - o.doStream(); - String res = o.windowToString(); - print(">>" ++ intToString(i) ++ ": " ++ res); - i = i + 1; - end -end - -// Output: -// >>100: -// >>101: -// >>102: 99999 101000 -// >>103: 99999 101000 -// >>104: 99999 101000 102000 103000 -// >>105: 99999 101000 102000 103000 -// >>106: 101000 102000 103000 104000 105000 -// >>107: 101000 102000 103000 104000 105000 -// >>108: 103000 104000 105000 106000 107000 -// >>109: 103000 104000 105000 106000 107000 -// >>110: 105000 106000 107000 108000 109000 -// .. - -// Notes: -// - Timestamps are in milliseconds -// - The first timestamp is decreased by 1 as a workaround to C-SPARQL external timestamp bug diff --git a/src/test/resources/csparql/17_influxdb.smol b/src/test/resources/csparql/17_influxdb.smol new file mode 100644 index 00000000..635cbb62 --- /dev/null +++ b/src/test/resources/csparql/17_influxdb.smol @@ -0,0 +1,109 @@ +// put config.yml in src/main/resources with InfluxDB connection details +// reada src/test/resources/csparql/17_influxdb.smol + +class Observation(Double x, Int ts) end + +streamer class C(Observation obs, Monitor m) + + Unit register() + Monitor m = monitor(" + SELECT ?x + FROM NAMED WINDOW ON %1 [RANGE PT1200.001s STEP PT120s] + WHERE { + WINDOW ?w { ?s prog:Observation_x ?x } + } + ", this); + this.m = m; + end + + Unit doStream(Double x, Int ts) emits(this.obs, this.obs.x, this.obs.ts) + this.obs = new Observation(x, ts); + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ doubleToString(l.content) ++ " "; + l = l.next; + end + return s; + end + +end + +class Reader() + List readInfluxDB(Int from, Int to) + List list = access(" + from(bucket: \"sr-demo\") + |> range(start: %1, stop: %2) + |> filter(fn: (r) => r._measurement == \"sensor\" and r._field == \"soil_moisture\" and r.field == \"FA01\") + |> yield(name: \"_value\") + ", INFLUXDB("src/main/resources/config.yml"), from, to); + return list; + end +end + +main + Reader r = new Reader(); + Int i = 0; + clock Int ts = 1727740800; + Int endAt = 1727745000; + Int tickS = 60; + + // initialize stream + C o = new C(null, null); + + // initialize monitor + o.register(); + + while ts < endAt do + // print("Querying InfluxDB from " ++ intToString(ts) ++ " to " ++ intToString(ts + tickS)); + List l = r.readInfluxDB(ts, ts + tickS); + while l != null do + o.doStream(l.content, ts); + String res = o.windowToString(); + print(intToString(i) ++ "[" ++ intToString(ts) ++ "]: " ++ res); + l = l.next; + end + i = i + 1; + ts = ts + tickS; + end + +end + +// Output: +// 0[1727740800]: +// 2[1727740920]: +// 4[1727741040]: 2.0 +// 6[1727741160]: 2.0 2.011940000178814 +// 8[1727741280]: 2.0 2.011940000178814 2.0358200005364417 +// 10[1727741400]: 2.0 2.011940000178814 2.0358200005364417 2.0597000008940696 +// 12[1727741520]: 2.0 2.011940000178814 2.0358200005364417 2.0597000008940696 2.0835800012516974 +// 14[1727741640]: 2.0 2.011940000178814 2.0358200005364417 2.0597000008940696 2.0835800012516974 2.1074600016093252 +// 16[1727741760]: 2.0 2.011940000178814 2.0358200005364417 2.0597000008940696 2.0835800012516974 2.1074600016093252 2.131340001966953 +// 18[1727741880]: 2.0 2.011940000178814 2.0358200005364417 2.0597000008940696 2.0835800012516974 2.1074600016093252 2.131340001966953 2.155220002324581 +// 20[1727742000]: 2.0 2.011940000178814 2.0358200005364417 2.0597000008940696 2.0835800012516974 2.1074600016093252 2.131340001966953 2.155220002324581 2.1791000026822087 +// 22[1727742120]: 2.0358200005364417 2.1791000026822087 2.0597000008940696 2.0835800012516974 2.0 2.2029800030398365 2.155220002324581 2.1074600016093252 2.011940000178814 2.131340001966953 +// 24[1727742240]: 2.0358200005364417 2.1791000026822087 2.0597000008940696 2.0835800012516974 2.2029800030398365 2.155220002324581 2.2268600033974644 2.1074600016093252 2.011940000178814 2.131340001966953 +// 26[1727742360]: 2.0358200005364417 2.1791000026822087 2.0597000008940696 2.0835800012516974 2.2029800030398365 2.155220002324581 2.1074600016093252 2.2268600033974644 2.250740003755092 2.131340001966953 +// 28[1727742480]: 2.1791000026822087 2.0597000008940696 2.0835800012516974 2.2029800030398365 2.27462000411272 2.155220002324581 2.1074600016093252 2.2268600033974644 2.250740003755092 2.131340001966953 +// 30[1727742600]: 2.1791000026822087 2.0835800012516974 2.298500004470348 2.2029800030398365 2.27462000411272 2.155220002324581 2.1074600016093252 2.2268600033974644 2.250740003755092 2.131340001966953 +// 32[1727742720]: 2.1791000026822087 2.298500004470348 2.2029800030398365 2.27462000411272 2.155220002324581 2.1074600016093252 2.2268600033974644 2.250740003755092 2.3223800048279757 2.131340001966953 +// 34[1727742840]: 2.1791000026822087 2.298500004470348 2.2029800030398365 2.27462000411272 2.155220002324581 2.2268600033974644 2.250740003755092 2.3462600051856035 2.3223800048279757 2.131340001966953 +// 36[1727742960]: 2.1791000026822087 2.298500004470348 2.3701400055432313 2.2029800030398365 2.27462000411272 2.155220002324581 2.2268600033974644 2.250740003755092 2.3462600051856035 2.3223800048279757 +// 38[1727743080]: 2.1791000026822087 2.298500004470348 2.3701400055432313 2.2029800030398365 2.394020005900859 2.27462000411272 2.2268600033974644 2.250740003755092 2.3462600051856035 2.3223800048279757 +// 40[1727743200]: 2.298500004470348 2.2029800030398365 2.3701400055432313 2.394020005900859 2.417900006258487 2.27462000411272 2.2268600033974644 2.250740003755092 2.3462600051856035 2.3223800048279757 +// 42[1727743320]: 2.298500004470348 2.3701400055432313 2.394020005900859 2.417900006258487 2.27462000411272 2.2268600033974644 2.250740003755092 2.3462600051856035 2.441780006616115 2.3223800048279757 +// 44[1727743440]: 2.298500004470348 2.3701400055432313 2.417900006258487 2.394020005900859 2.27462000411272 2.250740003755092 2.3462600051856035 2.441780006616115 2.3223800048279757 2.4656600069737427 +// 46[1727743560]: 2.298500004470348 2.3701400055432313 2.417900006258487 2.394020005900859 2.27462000411272 2.4895400073313705 2.3462600051856035 2.441780006616115 2.3223800048279757 2.4656600069737427 +// 48[1727743680]: 2.5134200076889983 2.298500004470348 2.3701400055432313 2.417900006258487 2.394020005900859 2.4895400073313705 2.3462600051856035 2.441780006616115 2.3223800048279757 2.4656600069737427 +// 50[1727743800]: 2.5134200076889983 2.537300008046626 2.3701400055432313 2.417900006258487 2.394020005900859 2.4895400073313705 2.3462600051856035 2.441780006616115 2.3223800048279757 2.4656600069737427 +// 52[1727743920]: 2.5134200076889983 2.537300008046626 2.3701400055432313 2.417900006258487 2.394020005900859 2.561180008404254 2.4895400073313705 2.3462600051856035 2.441780006616115 2.4656600069737427 +// 54[1727744040]: 2.5134200076889983 2.537300008046626 2.585060008761882 2.3701400055432313 2.417900006258487 2.394020005900859 2.561180008404254 2.4895400073313705 2.441780006616115 2.4656600069737427 +// 56[1727744160]: 2.5134200076889983 2.537300008046626 2.585060008761882 2.417900006258487 2.394020005900859 2.561180008404254 2.4895400073313705 2.441780006616115 2.4656600069737427 2.6089400091195096 +// 58[1727744280]: 2.5134200076889983 2.537300008046626 2.6328200094771375 2.585060008761882 2.417900006258487 2.561180008404254 2.4895400073313705 2.441780006616115 2.4656600069737427 2.6089400091195096 +// 60[1727744400]: 2.5134200076889983 2.537300008046626 2.6328200094771375 2.585060008761882 2.6567000098347653 2.561180008404254 2.4895400073313705 2.441780006616115 2.4656600069737427 2.6089400091195096 + +// Notes: +// - [RANGE 20m STEP 2m]: windows have {0, 0, 2, 3, 4, ..., 9, 10, 10, 10, ...} elements From 009d978a26d0651830a03cbadd692dfda7de329f Mon Sep 17 00:00:00 2001 From: Emre Kenar Date: Thu, 2 Oct 2025 01:07:25 +0200 Subject: [PATCH 19/19] reasoning test and example provided --- .../resources/default-csparql2.properties | 7 +- src/test/resources/csparql/18_reasoning.smol | 145 ++++++++++++++++++ .../resources/csparql/example-tbox-rdfxml.owl | 20 +++ src/test/resources/csparql/example2.owl | 29 ++++ 4 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 src/test/resources/csparql/18_reasoning.smol create mode 100644 src/test/resources/csparql/example-tbox-rdfxml.owl create mode 100644 src/test/resources/csparql/example2.owl diff --git a/src/main/resources/default-csparql2.properties b/src/main/resources/default-csparql2.properties index 06765796..e3d86f29 100644 --- a/src/main/resources/default-csparql2.properties +++ b/src/main/resources/default-csparql2.properties @@ -13,9 +13,6 @@ rsp_engine.report_grain=SINGLE rsp_engine.sds.mantainance=NAIVE rsp_engine.partialwindow=true +# # to enable reasoning, uncomment the following two lines (entailment options: RDFS, OWL) # jasper.entailment=RDFS -# rsp_engine.tbox_location=artist.tbox.owl -# rsp_engine.tbox_location="" - -#CSPARQLEngine.entailment=CUSTOM -#CSPARQLEngine.rules=/Users/riccardo/_Projects/csparql2/src/test/resources/rdfs-fb.rules +# rsp_engine.tbox_location=src/test/resources/csparql/example-tbox-rdfxml.owl diff --git a/src/test/resources/csparql/18_reasoning.smol b/src/test/resources/csparql/18_reasoning.smol new file mode 100644 index 00000000..8fb4625b --- /dev/null +++ b/src/test/resources/csparql/18_reasoning.smol @@ -0,0 +1,145 @@ +// uncomment the last 2 lines for reasoning in src/main/resources/default-csparql2.properties +// java -jar build/libs/smol.jar -b src/test/resources/csparql/example2.owl -p ex=https://example.com/ontology# +// reada src/test/resources/csparql/18_reasoning.smol + +class SensorData(Int x, Int ts) end + +class Observation extends SensorData() end + +class Controller(List streamers) + + Unit register(String static) + List objs = this.streamers; + while objs != null do + C o = objs.content; + o.register(static); + objs = objs.next; + end + end + + Unit doStream(Int ts) + List objs = this.streamers; + while objs != null do + C o = objs.content; + o.doStream(ts); + objs = objs.next; + end + end + + String windowToString() + String s = ""; + List objs = this.streamers; + while objs != null do + C o = objs.content; + String res = o.windowToString(); + s = s ++ o ++ " ( " ++ res ++ ") "; + objs = objs.next; + end + return s; + end +end + +// C is subclass of D +class D(Int x, Int mult, Observation obs, Monitor m) end + +// ex:StreamerClass is subclass of ex:Class +streamer class C extends D() models "a ex:StreamerClass."; + + Unit register(String static) + Monitor m = monitor(" + SELECT (?x * ?k AS ?res) + FROM NAMED WINDOW ON %1 [RANGE PT5.001s STEP PT2s] + FROM %2 + WHERE { + ?streamer prog:C_mult ?k . + ?streamer domain:models ?model . + + ?model a ex:Class . + ?streamer a prog:D . + + WINDOW ?w { + ?streamer prog:C_obs ?obs . + ?obs prog:Observation_x ?x . + } + } + ", + this, + static + ); + this.m = m; + end + + Unit doStream(Int ts) emits(this.obs, this.obs.x, this.obs.ts) + this.obs = new Observation(this.x, ts); + this.x = this.x + 1; + end + + String windowToString() + String s = ""; + List l = window(this.m); + while l != null do + s = s ++ intToString(l.content) ++ " "; + l = l.next; + end + return s; + end + +end + +main + // configure simulation + clock Int i = 100; + Int endAt = 200; + + C o1 = new C(0, 2, null, null); + C o2 = new C(10, 3, null, null); + C o3 = new C(20, 10, null, null); + + // push static data + String static = pushStatic("heap,staticTable,externalOntology"); + + breakpoint; + + // initialize stream + List l3 = new List(o3, null); + List l2 = new List(o2, l3); + List streamers = new List(o1, l2); + Controller ctrl = new Controller(streamers); + + // initialize monitor + ctrl.register(static); + + // run simulation + while i < endAt do + + // put triples in the stream + ctrl.doStream(i); + + // read window contents + String res = ctrl.windowToString(); + print(">>" ++ intToString(i) ++ ": " ++ res); + + // advance clock (timestamp) + i = i + 1; + end +end + +// Output: +// >>100: obj8 ( ) obj9 ( ) obj10 ( ) +// >>101: obj8 ( ) obj9 ( ) obj10 ( ) +// >>102: obj8 ( ) obj9 ( ) obj10 ( ) +// >>103: obj8 ( 0 2 ) obj9 ( 30 33 ) obj10 ( 200 210 ) +// >>104: obj8 ( 0 2 ) obj9 ( 30 33 ) obj10 ( 200 210 ) +// >>105: obj8 ( 0 2 4 6 ) obj9 ( 30 33 36 39 ) obj10 ( 200 210 220 230 ) +// >>106: obj8 ( 0 2 4 6 ) obj9 ( 30 33 36 39 ) obj10 ( 200 210 220 230 ) +// >>107: obj8 ( 2 4 6 8 10 ) obj9 ( 33 36 39 42 45 ) obj10 ( 210 220 230 240 250 ) +// >>108: obj8 ( 2 4 6 8 10 ) obj9 ( 33 36 39 42 45 ) obj10 ( 210 220 230 240 250 ) +// >>109: obj8 ( 6 8 10 12 14 ) obj9 ( 39 42 45 48 51 ) obj10 ( 230 240 250 260 270 ) +// >>110: obj8 ( 6 8 10 12 14 ) obj9 ( 39 42 45 48 51 ) obj10 ( 230 240 250 260 270 ) +// >>111: obj8 ( 10 12 14 16 18 ) obj9 ( 45 48 51 54 57 ) obj10 ( 250 260 270 280 290 ) +// .. + +// Notes: +// 1. ?model a ex:Class is inferred from ?streamer a ex:StreamerClass and ex:StreamerClass rdfs:subClassOf ex:Class +// 2. ?streamer a prog:D is inferred from ?streamer a prog:C and prog:C rdfs:subClassOf prog:D +// ?obs a prog:SensorData cannot be inferred because ?obs is null diff --git a/src/test/resources/csparql/example-tbox-rdfxml.owl b/src/test/resources/csparql/example-tbox-rdfxml.owl new file mode 100644 index 00000000..0761d8d8 --- /dev/null +++ b/src/test/resources/csparql/example-tbox-rdfxml.owl @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/src/test/resources/csparql/example2.owl b/src/test/resources/csparql/example2.owl new file mode 100644 index 00000000..510e74d6 --- /dev/null +++ b/src/test/resources/csparql/example2.owl @@ -0,0 +1,29 @@ +@prefix owl: . +@prefix rdf: . +@prefix xsd: . +@prefix rdfs: . +@prefix ex: . + +# class terminology +ex:Class a owl:Class . +ex:StreamerClass a owl:Class ; + rdfs:subClassOf ex:Class . + +# property terminology +ex:id a owl:DatatypeProperty ; + rdfs:domain ex:Class ; + rdfs:range xsd:string . +ex:x0 a owl:DatatypeProperty ; + rdfs:domain ex:Class ; + rdfs:range xsd:integer . + +# instances +ex:A a ex:StreamerClass ; + ex:id "A" ; + ex:x0 0 . +ex:B a ex:StreamerClass ; + ex:id "B" ; + ex:x0 10 . +ex:C a ex:StreamerClass ; + ex:id "C" ; + ex:x0 20 .