From 9f67ec3abdc3ddd6c92d400b61d245c2265fdada Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 5 Feb 2022 21:41:12 -0800 Subject: [PATCH 01/68] Add _connectMultiplePorts to support connection between multi ports. --- src/core/reactor.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 4e65989f0..0aa69047a 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -1447,6 +1447,33 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } } + /** + * Connect multiports. + * @param leftPorts The source ports to connect. + * @param rightPorts The destination ports to connect. + * @param repeatLeft Wheter left ports can be repeated when there are more right ports. + */ + protected _connectMultiplePorts ( + leftPorts: Array | IOPort>, + rightPorts: Array | IOPort>, + repeatLeft: boolean) { + if (repeatLeft) { + // TODO(hokeun): Handle repeat left case. + } + + if (leftPorts.length < rightPorts.length) { + Log.warn(null, () => "There are more right ports than left ports. ", + "Not all ports will be connected!") + } else if (leftPorts.length > rightPorts.length) { + Log.warn(null, () => "There are more left ports than right ports. ", + "Not all ports will be connected!") + } + + for (let i = 0; i < leftPorts.length && i < rightPorts.length; i++) { + this._connect(leftPorts[i], rightPorts[i]) + } + } + /** * Return a dependency graph consisting of only this reactor's own ports * and the dependencies between them. From d10b50332b6532696be35e9fce0d6cc22a7d80ea Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 5 Feb 2022 21:41:59 -0800 Subject: [PATCH 02/68] Extend Variable to Array of Read to support Array as a multiport in Args. --- src/core/reactor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 0aa69047a..06bb93655 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -48,7 +48,7 @@ export type ReadWrite = Read & Write; * @see Write * @see Sched */ -export type Variable = Read +export type Variable = Read | Array> //--------------------------------------------------------------------------// // Constants // From 9d223df96a091287d105eaf090d000c7e7027f6f Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 10 Feb 2022 16:21:33 -0800 Subject: [PATCH 03/68] Refactoring of connect function to treat regular IO ports and caller/callee ports separately. --- src/benchmark/PingPong.ts | 2 +- src/core/reactor.ts | 189 +++++++++++++++++++++----------------- 2 files changed, 105 insertions(+), 86 deletions(-) diff --git a/src/benchmark/PingPong.ts b/src/benchmark/PingPong.ts index ca3362526..2c8f7f5c8 100644 --- a/src/benchmark/PingPong.ts +++ b/src/benchmark/PingPong.ts @@ -79,7 +79,7 @@ export class PingPong extends App { super(timeout, keepAlive, fast, success, fail); this.ping = new Ping(this, 1000000) //1000000 this.pong = new Pong(this) - this._connect(this.ping.client, this.pong.server) + this._connectCall(this.ping.client, this.pong.server) } } // =============== END reactor class PingPong diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 06bb93655..c1cfc411f 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -776,8 +776,14 @@ export abstract class Reactor extends Component { * @param dst */ public connect - (src: CallerPort | IOPort, dst: CalleePort | IOPort) { - return this.reactor._connect(src, dst); + (src: CallerPort | IOPort, dst: CalleePort | IOPort) { + if (src instanceof CallerPort && dst instanceof CalleePort) { + return this.reactor._connectCall(src, dst); + } else if (src instanceof IOPort && dst instanceof IOPort) { + return this.reactor._connect(src, dst); + } else { + // ERROR + } } /** @@ -1250,6 +1256,25 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { return false; } + public canConnectCall + (src: CallerPort, dst: CalleePort) { + // FIXME: can we change the inheritance relationship so that we can overload? + + if (this._runtime.isRunning() == false) { + // console.log("Connecting before running") + // Validate connections between callers and callees. + + if (src._isContainedByContainerOf(this) && dst._isContainedByContainerOf(this)) { + return true + } + return false + + } else { + // FIXME + } + } + + /** * Returns true if a given source port can be connected to the * given destination port, false otherwise. Valid connections @@ -1266,8 +1291,8 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { * @param src The start point of a new connection. * @param dst The end point of a new connection. */ - public canConnect - (src: CallerPort | IOPort, dst: CalleePort | IOPort) { + public canConnect + (src: IOPort, dst: IOPort) { // Immediate rule out trivial self loops. if (src === dst) { return false @@ -1276,35 +1301,24 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { if (this._runtime.isRunning() == false) { // console.log("Connecting before running") // Validate connections between callers and callees. - if (src instanceof CalleePort) { - return false - } - if (src instanceof CallerPort) { - if (dst instanceof CalleePort && - src._isContainedByContainerOf(this) && dst._isContainedByContainerOf(this)) { - return true - } - return false - } // Additional checks for regular ports. - if (dst instanceof IOPort) { - console.log("IOPort") - // Rule out write conflicts. - // - (between reactors) - if (!(dst instanceof CalleePort) && - this._dependencyGraph.getBackEdges(dst).size > 0) { - return false; - } - // - between reactors and reactions (NOTE: check also needs to happen - // in addReaction) - var deps = this._dependencyGraph.getEdges(dst) // FIXME this will change with multiplex ports - if (deps != undefined && deps.size > 0) { - return false; - } + console.log("IOPort") + // Rule out write conflicts. + // - (between reactors) + if (this._dependencyGraph.getBackEdges(dst).size > 0) { + return false; + } - return this._isInScope(src, dst) + // - between reactors and reactions (NOTE: check also needs to happen + // in addReaction) + var deps = this._dependencyGraph.getEdges(dst) // FIXME this will change with multiplex ports + if (deps != undefined && deps.size > 0) { + return false; } + + return this._isInScope(src, dst) + } else { // Attempt to make a connection while executing. // Check the local dependency graph to figure out whether this change @@ -1318,7 +1332,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { for (let r of this._getOwnReactors()) { graph.merge(r._getCausalityInterface()) } - + // Add the new edge. graph.addEdge(dst, src) @@ -1388,75 +1402,78 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { * @param src The source port to connect. * @param dst The destination port to connect. */ - protected _connect - (src: CallerPort | IOPort, dst: CalleePort | IOPort) { + protected _connect(src: IOPort, dst:IOPort) { if (this.canConnect(src, dst)) { Log.debug(this, () => "connecting " + src + " and " + dst); - if (src instanceof CallerPort && dst instanceof CalleePort) { - // Treat connections between callers and callees separately. - // Note that because A extends T and S extends R, we can safely - // cast CalleePort to CalleePort. - src.remotePort = ((dst as unknown) as CalleePort); - // Register the caller in the callee reactor so that it can - // establish dependencies on the callers. - let calleeManager = dst.getManager(this._getKey(dst)) - let callerManager = src.getManager(this._getKey(src)) - let container = callerManager.getContainer() - let callers = new Set>() - container._dependencyGraph.getBackEdges(src).forEach((dep) => { - if (dep instanceof Reaction) { - callers.add(dep) - } - }) - let first = container._getFirst(callers) - let last = container._getLast(callers) - let lastCaller = calleeManager.getLastCaller() - if (lastCaller !== undefined) { - // This means the callee port is bound to a reaction and - // there may be zero or more callers. We now continue - // building a chain of callers. - if (first) { - this._dependencyGraph.addEdge(first, lastCaller) - } else { - this._dependencyGraph.addEdge(src, dst) - } - if (last) - calleeManager.setLastCaller(last) - } else { - throw new Error("No procedure linked to callee" - + " port `${procedure}`.") + // Add dependency implied by connection to local graph. + this._dependencyGraph.addEdge(dst, src); + // Register receiver for value propagation. + let writer = dst.asWritable(this._getKey(dst)); + src.getManager(this._getKey(src)).addReceiver + (writer as WritablePort); + let val = src.get() + if (this._runtime.isRunning() && val !== undefined) { + //console.log(">>>>>>>>>>>>>>>>>>>>>>>>><<<<<>>>>>>>>>>>>>>>>>>>>>") + writer.set(val) + } + } else { + throw new Error("ERROR connecting " + src + " to " + dst); + } + } + + protected _connectCall + (src: CallerPort, dst: CalleePort) { + if (this.canConnectCall(src, dst)) { + Log.debug(this, () => "connecting " + src + " and " + dst); + // Treat connections between callers and callees separately. + // Note that because A extends T and S extends R, we can safely + // cast CalleePort to CalleePort. + src.remotePort = ((dst as unknown) as CalleePort); + // Register the caller in the callee reactor so that it can + // establish dependencies on the callers. + let calleeManager = dst.getManager(this._getKey(dst)) + let callerManager = src.getManager(this._getKey(src)) + let container = callerManager.getContainer() + let callers = new Set>() + container._dependencyGraph.getBackEdges(src).forEach((dep) => { + if (dep instanceof Reaction) { + callers.add(dep) } - - } else if (src instanceof IOPort && dst instanceof IOPort) { - Log.debug(this, () => "connecting " + src + " and " + dst); - // Add dependency implied by connection to local graph. - this._dependencyGraph.addEdge(dst, src); - // Register receiver for value propagation. - let writer = dst.asWritable(this._getKey(dst)); - src.getManager(this._getKey(src)).addReceiver - (writer as WritablePort); - let val = src.get() - if (this._runtime.isRunning() && val !== undefined) { - //console.log(">>>>>>>>>>>>>>>>>>>>>>>>><<<<<>>>>>>>>>>>>>>>>>>>>>") - writer.set(val) + }) + let first = container._getFirst(callers) + let last = container._getLast(callers) + let lastCaller = calleeManager.getLastCaller() + if (lastCaller !== undefined) { + // This means the callee port is bound to a reaction and + // there may be zero or more callers. We now continue + // building a chain of callers. + if (first) { + this._dependencyGraph.addEdge(first, lastCaller) + } else { + this._dependencyGraph.addEdge(src, dst) } + if (last) + calleeManager.setLastCaller(last) + } else { + throw new Error("No procedure linked to callee" + + " port `${procedure}`.") } - } else { throw new Error("ERROR connecting " + src + " to " + dst); } } + /** * Connect multiports. * @param leftPorts The source ports to connect. * @param rightPorts The destination ports to connect. - * @param repeatLeft Wheter left ports can be repeated when there are more right ports. + * @param repeatLeft Whether left ports can be repeated when there are more right ports. */ - protected _connectMultiplePorts ( - leftPorts: Array | IOPort>, - rightPorts: Array | IOPort>, - repeatLeft: boolean) { + protected _connectMultiplePorts( + leftPorts: Array>, + rightPorts: Array>, + repeatLeft: boolean) { if (repeatLeft) { // TODO(hokeun): Handle repeat left case. } @@ -1470,9 +1487,11 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } for (let i = 0; i < leftPorts.length && i < rightPorts.length; i++) { + this._connect(leftPorts[i], rightPorts[i]) + } - } + } /** * Return a dependency graph consisting of only this reactor's own ports From 0e018a397a6674d0d44f3e951cf3d87a02f738e6 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 10 Feb 2022 17:22:40 -0800 Subject: [PATCH 04/68] WIP toward Banks as containers --- src/core/component.ts | 14 +++++++------- src/core/reactor.ts | 22 ++++++++++++++++++---- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/core/component.ts b/src/core/component.ts index 730aabe1f..7f72d00ba 100644 --- a/src/core/component.ts +++ b/src/core/component.ts @@ -1,4 +1,4 @@ -import {Reactor, App, Runtime} from "./reactor"; +import {Container, App, Runtime} from "./reactor"; /** * Base class for named objects embedded in a hierarchy of reactors. Each @@ -27,11 +27,11 @@ export abstract class Component { * reactor. Only instances of `App`, which denote top-level reactors, * are self-contained. */ - private _container: Reactor; + private _container: Container; /** * Create a new component and register it with the given container. - * @param container The reactor that will contain the new component, + * @param container The container that will contain the new component, * `null` if this is an instance of `App`, in which case the component * will be designated as its own container. * @@ -40,7 +40,7 @@ export abstract class Component { * constructor in order to establish a link with the runtime object. * @param alias An optional alias for the component. */ - constructor(container: Reactor | null, alias?:string) { + constructor(container: Container | null, alias?:string) { this._alias = alias if (container !== null) { @@ -88,7 +88,7 @@ export abstract class Component { * Confirm whether or not this component is contained by the given reactor. * @param reactor The presumptive container of this component. */ - public _isContainedBy(reactor: Reactor): boolean { + public _isContainedBy(reactor: Container): boolean { if (this instanceof App) return false else if (this._container === reactor) return true @@ -102,7 +102,7 @@ export abstract class Component { * @param reactor The container presumptive container of the container of * this component. */ - public _isContainedByContainerOf(reactor: Reactor): boolean { + public _isContainedByContainerOf(reactor: Container): boolean { if (this instanceof App) return false else if (this._container._isContainedBy(reactor)) return true; @@ -168,7 +168,7 @@ export abstract class Component { /** * Return the container of this component. */ - protected _getContainer(): Reactor { + protected _getContainer(): Container { return this._container } diff --git a/src/core/reactor.ts b/src/core/reactor.ts index c1cfc411f..325776ced 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -50,6 +50,8 @@ export type ReadWrite = Read & Write; */ export type Variable = Read | Array> +export type Container = Reactor | Bank + //--------------------------------------------------------------------------// // Constants // //--------------------------------------------------------------------------// @@ -195,7 +197,7 @@ abstract class Trigger extends Component { /** * Return the owner of this trigger. */ - public getContainer(): Reactor | null { + public getContainer(): Container | null { return this._getContainer() } @@ -267,7 +269,7 @@ abstract class ScheduledTrigger extends Trigger { protected manager = new class implements TriggerManager { constructor(private trigger: ScheduledTrigger) { } - getContainer(): Reactor { + getContainer(): Container { return this.trigger._getContainer() } addReaction(reaction: Reaction): void { @@ -288,6 +290,10 @@ abstract class ScheduledTrigger extends Trigger { } +export class Bank extends Component { + +} + /** * An action denotes a self-scheduled event. * An action, like an input, can cause reactions to be invoked. @@ -1421,6 +1427,10 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } } + protected _connectMulti(src:MultiPort, dest: MultiPort) { + + } + protected _connectCall (src: CallerPort, dst: CalleePort) { if (this.canConnectCall(src, dst)) { @@ -1463,7 +1473,6 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } } - /** * Connect multiports. * @param leftPorts The source ports to connect. @@ -1704,6 +1713,10 @@ export abstract class Port extends Trigger implements Read } } +export class MultiPort { + + +} export abstract class IOPort extends Port { protected receivers: Set> = new Set(); @@ -1955,7 +1968,7 @@ export class CalleePort extends Port im protected manager:CalleeManager = new class implements CalleeManager { constructor(private port:CalleePort) {} - getContainer(): Reactor { + getContainer(): Container { return this.port._getContainer() } addReaction(procedure: Reaction): void { @@ -2049,6 +2062,7 @@ interface UtilityFunctions { // } export interface MutationSandbox extends ReactionSandbox { + connect (src: CallerPort | IOPort, dst: CalleePort | IOPort):void; From ebd961186ed692faa359e8abb53f3007290d3acd Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 10 Feb 2022 17:45:14 -0800 Subject: [PATCH 05/68] Add Bank class implementation. --- src/core/reactor.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 325776ced..9befc9d45 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -291,7 +291,21 @@ abstract class ScheduledTrigger extends Trigger { } export class Bank extends Component { - + protected runtime!: Runtime; + public reactors!: Array + + constructor(container: Reactor, reactors: Array, alias?:string) { + super(container, alias); + this.reactors = reactors + } + + public _receiveRuntimeObject(runtime: Runtime) { + if (!this.runtime) { + this.runtime = runtime + } else { + throw new Error("Can only establish link to runtime once.") + } + } } /** From ba7a2d914db0f5741251abb1515fbba3b4c12b9b Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 10 Feb 2022 19:07:08 -0800 Subject: [PATCH 06/68] Add preliminary implementation of MultiPort class. --- src/core/reactor.ts | 66 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 9befc9d45..894a9f855 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -48,7 +48,7 @@ export type ReadWrite = Read & Write; * @see Write * @see Sched */ -export type Variable = Read | Array> +export type Variable = Read | Array> | MultiPort export type Container = Reactor | Bank @@ -1441,8 +1441,41 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } } - protected _connectMulti(src:MultiPort, dest: MultiPort) { - + protected _connectMulti( + src: Array>, + dest: Array>, + repeatLeft: boolean) { + let leftPorts = new Array>(0) + let rightPorts = new Array>(0) + + // TODO(hokeun): Check if the multiport's container is Bank when Bank is implemented. + src.forEach(multiPort => { + multiPort.ports.forEach(port => { + leftPorts.push(port) + }) + }) + + dest.forEach(multiPort => { + multiPort.ports.forEach(port => { + rightPorts.push(port) + }) + }) + + if (repeatLeft) { + // TODO(hokeun): Handle repeat left case. + } + + if (leftPorts.length < rightPorts.length) { + Log.warn(null, () => "There are more right ports than left ports. ", + "Not all ports will be connected!") + } else if (leftPorts.length > rightPorts.length) { + Log.warn(null, () => "There are more left ports than right ports. ", + "Not all ports will be connected!") + } + + for (let i = 0; i < leftPorts.length && i < rightPorts.length; i++) { + this._connect(leftPorts[i], rightPorts[i]) + } } protected _connectCall @@ -1510,9 +1543,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } for (let i = 0; i < leftPorts.length && i < rightPorts.length; i++) { - this._connect(leftPorts[i], rightPorts[i]) - } } @@ -1727,10 +1758,33 @@ export abstract class Port extends Trigger implements Read } } -export class MultiPort { +enum PortType { + INPUT = 0, + OUTPUT = 1 +} +export abstract class MultiPort extends Component { + public ports: Array> + constructor(container: Reactor, width: number, portType: PortType) { + super(container) + switch (portType) { + case PortType.INPUT: + this.ports = new Array>(width) + for (let i = 0; i < width; i++) { + this.ports[i] = new InPort(container) + } + break + case PortType.OUTPUT: + this.ports = new Array>(width) + for (let i = 0; i < width; i++) { + this.ports[i] = new OutPort(container) + } + break + } + } } + export abstract class IOPort extends Port { protected receivers: Set> = new Set(); From 01574c08c56db970657815703b959230eb5733e6 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 10 Feb 2022 19:22:32 -0800 Subject: [PATCH 07/68] Add InMultiPort and OutMultiPort. --- src/core/reactor.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 894a9f855..e45d91754 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -1764,8 +1764,9 @@ enum PortType { } export abstract class MultiPort extends Component { + protected runtime!: Runtime; public ports: Array> - + constructor(container: Reactor, width: number, portType: PortType) { super(container) switch (portType) { @@ -1783,6 +1784,25 @@ export abstract class MultiPort extends Component { break } } + + public _receiveRuntimeObject(runtime: Runtime) { + if (!this.runtime) { + this.runtime = runtime + } else { + throw new Error("Can only establish link to runtime once. Name: " + this._getFullyQualifiedName()) + } + } +} + +export class InMultiPort extends MultiPort { + constructor(container: Reactor, width: number) { + super(container, width, PortType.INPUT) + } +} +export class OutMultiPort extends MultiPort { + constructor(container: Reactor, width: number) { + super(container, width, PortType.OUTPUT) + } } export abstract class IOPort extends Port { From 9de0be31fa2398779ec7fabb86aaa96d21844a1a Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Fri, 11 Feb 2022 00:41:29 -0800 Subject: [PATCH 08/68] Fix MultiPort generic. --- src/core/reactor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index e45d91754..2713b297c 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -48,7 +48,7 @@ export type ReadWrite = Read & Write; * @see Write * @see Sched */ -export type Variable = Read | Array> | MultiPort +export type Variable = Read | Array> | MultiPort export type Container = Reactor | Bank From 5ac91bfaae3c8eaddfa7443fe10ba3986fa5754c Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Fri, 11 Feb 2022 01:02:29 -0800 Subject: [PATCH 09/68] Remove Container Type and make Bank inherit Reactor. --- src/core/component.ts | 14 ++++++------- src/core/reactor.ts | 47 +++++++++++++++++++++---------------------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/core/component.ts b/src/core/component.ts index 7f72d00ba..730aabe1f 100644 --- a/src/core/component.ts +++ b/src/core/component.ts @@ -1,4 +1,4 @@ -import {Container, App, Runtime} from "./reactor"; +import {Reactor, App, Runtime} from "./reactor"; /** * Base class for named objects embedded in a hierarchy of reactors. Each @@ -27,11 +27,11 @@ export abstract class Component { * reactor. Only instances of `App`, which denote top-level reactors, * are self-contained. */ - private _container: Container; + private _container: Reactor; /** * Create a new component and register it with the given container. - * @param container The container that will contain the new component, + * @param container The reactor that will contain the new component, * `null` if this is an instance of `App`, in which case the component * will be designated as its own container. * @@ -40,7 +40,7 @@ export abstract class Component { * constructor in order to establish a link with the runtime object. * @param alias An optional alias for the component. */ - constructor(container: Container | null, alias?:string) { + constructor(container: Reactor | null, alias?:string) { this._alias = alias if (container !== null) { @@ -88,7 +88,7 @@ export abstract class Component { * Confirm whether or not this component is contained by the given reactor. * @param reactor The presumptive container of this component. */ - public _isContainedBy(reactor: Container): boolean { + public _isContainedBy(reactor: Reactor): boolean { if (this instanceof App) return false else if (this._container === reactor) return true @@ -102,7 +102,7 @@ export abstract class Component { * @param reactor The container presumptive container of the container of * this component. */ - public _isContainedByContainerOf(reactor: Container): boolean { + public _isContainedByContainerOf(reactor: Reactor): boolean { if (this instanceof App) return false else if (this._container._isContainedBy(reactor)) return true; @@ -168,7 +168,7 @@ export abstract class Component { /** * Return the container of this component. */ - protected _getContainer(): Container { + protected _getContainer(): Reactor { return this._container } diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 2713b297c..64d8fc6d1 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -50,8 +50,6 @@ export type ReadWrite = Read & Write; */ export type Variable = Read | Array> | MultiPort -export type Container = Reactor | Bank - //--------------------------------------------------------------------------// // Constants // //--------------------------------------------------------------------------// @@ -197,7 +195,7 @@ abstract class Trigger extends Component { /** * Return the owner of this trigger. */ - public getContainer(): Container | null { + public getContainer(): Reactor | null { return this._getContainer() } @@ -269,7 +267,7 @@ abstract class ScheduledTrigger extends Trigger { protected manager = new class implements TriggerManager { constructor(private trigger: ScheduledTrigger) { } - getContainer(): Container { + getContainer(): Reactor { return this.trigger._getContainer() } addReaction(reaction: Reaction): void { @@ -290,24 +288,6 @@ abstract class ScheduledTrigger extends Trigger { } -export class Bank extends Component { - protected runtime!: Runtime; - public reactors!: Array - - constructor(container: Reactor, reactors: Array, alias?:string) { - super(container, alias); - this.reactors = reactors - } - - public _receiveRuntimeObject(runtime: Runtime) { - if (!this.runtime) { - this.runtime = runtime - } else { - throw new Error("Can only establish link to runtime once.") - } - } -} - /** * An action denotes a self-scheduled event. * An action, like an input, can cause reactions to be invoked. @@ -1713,6 +1693,25 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } } +export abstract class Bank extends Reactor { + protected runtime!: Runtime; + // TODO(hokeun): Use _keyChain to store an array of reactors. + // See _getOwnReactors() for hints. + public reactors!: Array + + constructor(container: Reactor, reactors: Array, alias?:string) { + super(container, alias); + this.reactors = reactors + } + + public _receiveRuntimeObject(runtime: Runtime) { + if (!this.runtime) { + this.runtime = runtime + } else { + throw new Error("Can only establish link to runtime once.") + } + } +} export abstract class Port extends Trigger implements Read { protected runtime!: Runtime; @@ -1924,7 +1923,7 @@ interface ComponentManager { } interface TriggerManager { - getContainer():Reactor; + getContainer(): Reactor; addReaction(reaction: Reaction): void; delReaction(reaction: Reaction): void; } @@ -2056,7 +2055,7 @@ export class CalleePort extends Port im protected manager:CalleeManager = new class implements CalleeManager { constructor(private port:CalleePort) {} - getContainer(): Container { + getContainer(): Reactor { return this.port._getContainer() } addReaction(procedure: Reaction): void { From 45fce1b60ef65ff2ce648cd5c2182d7259a2cbfd Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Fri, 11 Feb 2022 02:10:43 -0800 Subject: [PATCH 10/68] Add helper functions to be used by generated code in MultiPort class. --- src/core/reactor.ts | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 64d8fc6d1..d024f8114 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -868,7 +868,7 @@ export abstract class Reactor extends Component { // return this._active // } - protected writable(port: IOPort): ReadWrite { + public writable(port: IOPort): ReadWrite { return port.asWritable(this._getKey(port)); } @@ -1422,23 +1422,31 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } protected _connectMulti( - src: Array>, - dest: Array>, + src: Array | IOPort>, + dest: Array | IOPort>, repeatLeft: boolean) { let leftPorts = new Array>(0) let rightPorts = new Array>(0) // TODO(hokeun): Check if the multiport's container is Bank when Bank is implemented. - src.forEach(multiPort => { - multiPort.ports.forEach(port => { + src.forEach(port => { + if (port instanceof MultiPort) { + port.ports.forEach(singlePort => { + leftPorts.push(singlePort) + }) + } else if (port instanceof IOPort) { leftPorts.push(port) - }) + } }) - dest.forEach(multiPort => { - multiPort.ports.forEach(port => { + dest.forEach(port => { + if (port instanceof MultiPort) { + port.ports.forEach(singlePort => { + rightPorts.push(singlePort) + }) + } else if (port instanceof IOPort) { rightPorts.push(port) - }) + } }) if (repeatLeft) { @@ -1784,6 +1792,10 @@ export abstract class MultiPort extends Component { } } + public width(): number { + return this.ports.length + } + public _receiveRuntimeObject(runtime: Runtime) { if (!this.runtime) { this.runtime = runtime @@ -1797,10 +1809,22 @@ export class InMultiPort extends MultiPort { constructor(container: Reactor, width: number) { super(container, width, PortType.INPUT) } + public getValueArray() : Array { + let valueArray = new Array(this.width()); + for (let i = 0; i < valueArray.length; i++) { + valueArray[i] = this.ports[i].get(); + } + return valueArray + } } export class OutMultiPort extends MultiPort { + public writablePorts: Array> constructor(container: Reactor, width: number) { super(container, width, PortType.OUTPUT) + this.writablePorts = new Array>(width) + for (let i = 0; i < width; i++) { + this.writablePorts[i] = container.writable(this.ports[i]) + } } } From f53a5a832b1efdfb1abcf7eaf08810cf2019ce93 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 12 Feb 2022 09:21:08 -0800 Subject: [PATCH 11/68] Added Bank with type-safe higher-order instantiation --- src/core/bank.ts | 50 +++++++++++++++++++++++++++++++++++++++++ src/core/reactor.ts | 54 ++++++++++++++++++++++----------------------- 2 files changed, 77 insertions(+), 27 deletions(-) create mode 100644 src/core/bank.ts diff --git a/src/core/bank.ts b/src/core/bank.ts new file mode 100644 index 000000000..2716cb154 --- /dev/null +++ b/src/core/bank.ts @@ -0,0 +1,50 @@ +import { Reactor } from './reactor' +export type ReactorArgs = T extends any[] ? T : never; + +// class Reactor { + +// } + +// class SomeClass extends Reactor { +// constructor(public x:number, public y: string) { +// super() +// } +// } + +type ReactorConstructor = { + new (...args:ReactorArgs): T; +} + +// function foo(cls:ReactorConstructor, ...args:ReactorArgs):T { +// return Reflect.construct(cls, args, cls) +// } + +// let inst = foo(SomeClass, 1, "foo") +// console.log(inst instanceof SomeClass) + +// console.log(inst.x) +// console.log(inst.y) + +export class Bank { + private readonly members: Array = new Array(); + constructor(width: number, cls:ReactorConstructor, ...args:ReactorArgs) { + + for (let i=0; i < width; i++) { + this.members.push(Reflect.construct(cls, args, cls)); + } + + } + + public get(index:number):T { + return this.members[index] + } + + public all():Array { + return this.members + } +} + +// let bank = new Bank(3, SomeClass, 4, "foo") +// console.log(bank.get(0).x) +// console.log(bank.get(1).x) +// console.log(bank.get(2).x) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index d024f8114..0379f0c26 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -9,6 +9,7 @@ import {PrioritySetElement, PrioritySet, SortableDependencyGraph, Log, Dependenc import {TimeValue, TimeUnit, Tag, Origin, getCurrentPhysicalTime, Alarm} from './time'; import {Component} from "./component" import {Reaction, Priority, Mutation, Procedure} from "./reaction" +import {Bank} from './bank' // Set the default log level. Log.global.level = Log.levels.ERROR; @@ -31,6 +32,8 @@ export type Absent = undefined; */ export type ArgList = T extends Variable[] ? T : never; +export type ParmList = T extends any[] ? T : never; + /** * Type for data exchanged between ports. */ @@ -526,6 +529,8 @@ export class Triggers { } } + + /** * A reactor is a software component that reacts to input events, timer events, * and action events. It has private state variables that are not visible to any @@ -536,26 +541,26 @@ export class Triggers { export abstract class Reactor extends Component { /** - * Data structure to keep track of register components. + * Data structure to keep track of registered components. * Note: declare this class member before any other ones as they may * attempt to access it. */ private _keyChain: Map = new Map() /** - * Collection of priviledged functions that are passed down from the - * container. + * This graph has in it all the dependencies implied by this container's + * ports, reactions, and connections. */ - private _runtime!: Runtime; + protected _dependencyGraph: DependencyGraph | Reaction> = new DependencyGraph() /** - * This graph has in it all the dependencies implied by this reactor's - * ports, reactions, and connections. + * The runtime object, which has a collection of privileged functions that are passed down from the + * container. */ - private _dependencyGraph: DependencyGraph | Reaction> = new DependencyGraph() + private _runtime!: Runtime; /** - * This graph has some overlap with the reactors dependency, but is + * This graph has some overlap with the reactors dependency graph, but is * different in two respects: * - transitive dependencies between ports have been collapsed; and * - it incorporates the causality interfaces of all contained reactors. @@ -602,6 +607,11 @@ export abstract class Reactor extends Component { */ private _mutationScope: MutationSandbox; + /** + * A mapping from reactor instance to bank index. + */ + private _bankMap: Map = new Map() + /** * Receive the runtime object from the container of this reactor. * Invoking this method in any user-written code will result in a @@ -638,6 +648,15 @@ export abstract class Reactor extends Component { if (component !== this && !this._keyChain.has(component)) { this._keyChain.set(component, key) } + // Record bank members so that their index can be looked up. + if (component instanceof Bank) { + let idx = 0 + component.all().forEach(inst => {this._bankMap.set(inst, idx); idx++}) + } + } + + public _bankIndex(r: Reactor): number | undefined { + return this._bankMap.get(r) } public _requestRuntimeObject(component: Component): void { @@ -1701,25 +1720,6 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } } -export abstract class Bank extends Reactor { - protected runtime!: Runtime; - // TODO(hokeun): Use _keyChain to store an array of reactors. - // See _getOwnReactors() for hints. - public reactors!: Array - - constructor(container: Reactor, reactors: Array, alias?:string) { - super(container, alias); - this.reactors = reactors - } - - public _receiveRuntimeObject(runtime: Runtime) { - if (!this.runtime) { - this.runtime = runtime - } else { - throw new Error("Can only establish link to runtime once.") - } - } -} export abstract class Port extends Trigger implements Read { protected runtime!: Runtime; From 102ac73d7fac8438622161088d38617a865d4cfe Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 16 Feb 2022 14:07:05 -0800 Subject: [PATCH 12/68] Add support for handling of the special bank_index parameter of the reactor. --- src/core/bank.ts | 25 +++++++++++++++++++++---- src/core/reactor.ts | 13 ++++++++----- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/core/bank.ts b/src/core/bank.ts index 2716cb154..5fddeffbd 100644 --- a/src/core/bank.ts +++ b/src/core/bank.ts @@ -1,4 +1,5 @@ -import { Reactor } from './reactor' +import { Component } from './component'; +import { MultiPort, IOPort, Reactor, Runtime, Present } from './reactor' export type ReactorArgs = T extends any[] ? T : never; // class Reactor { @@ -25,17 +26,33 @@ type ReactorConstructor = { // console.log(inst.x) // console.log(inst.y) -export class Bank { +export class Bank extends Component { + private runtime!: Runtime; + + public _receiveRuntimeObject(runtime: Runtime): void { + if (!this.runtime) { + this.runtime = runtime + } else { + throw new Error("Can only establish link to runtime once. Name: " + this._getFullyQualifiedName()) + } + } private readonly members: Array = new Array(); - constructor(width: number, cls:ReactorConstructor, ...args:ReactorArgs) { + constructor(container: Reactor, width: number, cls:ReactorConstructor, bankIndexArgIndex?: number, ...args:ReactorArgs) { + super(container) for (let i=0; i < width; i++) { + if (bankIndexArgIndex !== undefined) { + // When bankIndexArgIndex is not null, + // bankIndexArgIndex indicates the index of "bank_index" paramter in the argument list of the reactor as in LF program. + // +1 is added since the 0th arg to the constructor of the code-generated TS reactor is actually the container (parent). + args[bankIndexArgIndex + 1] = i + } this.members.push(Reflect.construct(cls, args, cls)); } } - public get(index:number):T { + public get(index:number): T { return this.members[index] } diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 0379f0c26..f14228375 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -2,7 +2,8 @@ * Core of the reactor runtime. * * @author Marten Lohstroh (marten@berkeley.edu), - * @author Matt Weber (matt.weber@berkeley.edu) + * @author Matt Weber (matt.weber@berkeley.edu), + * @author Hokeun Kim (hokeunkim@berkeley.edu) */ import {PrioritySetElement, PrioritySet, SortableDependencyGraph, Log, DependencyGraph} from './util'; @@ -649,10 +650,12 @@ export abstract class Reactor extends Component { this._keyChain.set(component, key) } // Record bank members so that their index can be looked up. - if (component instanceof Bank) { - let idx = 0 - component.all().forEach(inst => {this._bankMap.set(inst, idx); idx++}) - } + // FIXME: Disabled this due to the following error: + // TypeError: Cannot read properties of undefined (reading 'forEach') + // if (component instanceof Bank) { + // let idx = 0 + // component.all().forEach(inst => {this._bankMap.set(inst, idx); idx++}) + // } } public _bankIndex(r: Reactor): number | undefined { From 3cb1021ebb5b0c34b0d847d8929746ba76dd2eb3 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 16 Feb 2022 14:29:16 -0800 Subject: [PATCH 13/68] Update lingua franca version. --- lingua-franca-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index a499ec2ef..57512a364 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -a768c680dc39b5ff7344bd9253916bf6e77c0d68 +dcbe092ccbf65598ee4f91f03b13c0b9a6900940 From ad9338cb28235382d0b203cb32ba93b240bae742 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 17 Feb 2022 12:47:13 -0800 Subject: [PATCH 14/68] Simplified implementation of banks with access to bank index --- __tests__/bank.ts | 37 +++++++++++++++++++++++++++++++ src/core/bank.ts | 49 +++++++++++++++++++++++------------------ src/core/reactor.ts | 53 +++++++++++++++++++++++++++++++-------------- 3 files changed, 102 insertions(+), 37 deletions(-) create mode 100644 __tests__/bank.ts diff --git a/__tests__/bank.ts b/__tests__/bank.ts new file mode 100644 index 000000000..264bd2a4a --- /dev/null +++ b/__tests__/bank.ts @@ -0,0 +1,37 @@ +import { Bank } from "../src/core/bank"; +import { Reactor, App, Timer, Triggers, Args} from "../src/core/reactor"; +import { TimeValue } from "../src/core/time"; + + class Periodic extends Reactor { + + t: Timer = new Timer(this, 0, TimeValue.sec(1)); + constructor(parent: Reactor) { + super(parent) + this.addReaction( + new Triggers(this.t), + new Args(this.t), + function (this) { + console.log(this.getBankIndex()); + } + ); + } + } + + +describe('Check bank index', () => { + + class myApp extends App { + b = new Bank(this, 3, Periodic, this) + constructor() { + super(); + it('contained actor name', () => { + expect(this.b.get(0).getBankIndex()).toBe(0); + expect(this.b.get(1).getBankIndex()).toBe(1); + expect(this.b.get(2).getBankIndex()).toBe(2); + }); + } + } + + new myApp(); + +}); diff --git a/src/core/bank.ts b/src/core/bank.ts index 2716cb154..57722017b 100644 --- a/src/core/bank.ts +++ b/src/core/bank.ts @@ -1,6 +1,31 @@ -import { Reactor } from './reactor' +import { Component } from './component'; +import { Reactor, Runtime } from './reactor' + +type ReactorConstructor = { + new (...args:ReactorArgs): T; + } export type ReactorArgs = T extends any[] ? T : never; +export class Bank { + + private readonly members: Array = new Array(); + constructor(parent: Reactor, width: number, cls:ReactorConstructor, ...args:ReactorArgs) { + for (let i=0; i < width; i++) { + this.members.push(Reflect.construct(cls, args, cls)); + this.members[i].setBankIndex(i) + } + } + + public get(index:number):T { + return this.members[index] + } + + public all():Array { + return this.members + } +} + + // class Reactor { // } @@ -11,9 +36,8 @@ export type ReactorArgs = T extends any[] ? T : never; // } // } -type ReactorConstructor = { - new (...args:ReactorArgs): T; -} + + // function foo(cls:ReactorConstructor, ...args:ReactorArgs):T { // return Reflect.construct(cls, args, cls) @@ -25,24 +49,7 @@ type ReactorConstructor = { // console.log(inst.x) // console.log(inst.y) -export class Bank { - private readonly members: Array = new Array(); - constructor(width: number, cls:ReactorConstructor, ...args:ReactorArgs) { - - for (let i=0; i < width; i++) { - this.members.push(Reflect.construct(cls, args, cls)); - } - - } - - public get(index:number):T { - return this.members[index] - } - public all():Array { - return this.members - } -} // let bank = new Bank(3, SomeClass, 4, "foo") // console.log(bank.get(0).x) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 0379f0c26..c75271bcc 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -9,7 +9,6 @@ import {PrioritySetElement, PrioritySet, SortableDependencyGraph, Log, Dependenc import {TimeValue, TimeUnit, Tag, Origin, getCurrentPhysicalTime, Alarm} from './time'; import {Component} from "./component" import {Reaction, Priority, Mutation, Procedure} from "./reaction" -import {Bank} from './bank' // Set the default log level. Log.global.level = Log.levels.ERROR; @@ -176,6 +175,7 @@ export class TaggedEvent implements PrioritySetElement { return this.tag; } } + /** * Abstract class for a trigger. A trigger may be an action, port, or timer. */ @@ -559,6 +559,18 @@ export abstract class Reactor extends Component { */ private _runtime!: Runtime; + private _bankIndex: number = -1; + + public getBankIndex(): number { + return this._bankIndex + } + + public setBankIndex(index: number): void { + if (this._bankIndex == -1) { + this._bankIndex = index + } + } + /** * This graph has some overlap with the reactors dependency graph, but is * different in two respects: @@ -607,11 +619,6 @@ export abstract class Reactor extends Component { */ private _mutationScope: MutationSandbox; - /** - * A mapping from reactor instance to bank index. - */ - private _bankMap: Map = new Map() - /** * Receive the runtime object from the container of this reactor. * Invoking this method in any user-written code will result in a @@ -638,6 +645,9 @@ export abstract class Reactor extends Component { * @param key The component's key. */ public _register(component: Component, key: Symbol) { + if (component === undefined || component === null) { + throw new Error("Unable to register undefined or null component") + } if (component._isRegistered()) { throw new Error("Unable to register " + component._getFullyQualifiedName() @@ -648,15 +658,6 @@ export abstract class Reactor extends Component { if (component !== this && !this._keyChain.has(component)) { this._keyChain.set(component, key) } - // Record bank members so that their index can be looked up. - if (component instanceof Bank) { - let idx = 0 - component.all().forEach(inst => {this._bankMap.set(inst, idx); idx++}) - } - } - - public _bankIndex(r: Reactor): number | undefined { - return this._bankMap.get(r) } public _requestRuntimeObject(component: Component): void { @@ -787,7 +788,9 @@ export abstract class Reactor extends Component { constructor(private reactor: Reactor) { this.reactor = reactor this.util = reactor.util + this.getBankIndex = () => reactor.getBankIndex() } + getBankIndex: () => number; /** * @@ -827,9 +830,12 @@ export abstract class Reactor extends Component { */ private _ReactionSandbox = class implements ReactionSandbox { public util: UtilityFunctions; + public getBankIndex: () => number; constructor(public reactor: Reactor) { this.util = reactor.util + this.getBankIndex = () => reactor._bankIndex } + } /** @@ -2193,7 +2199,7 @@ export interface ReactionSandbox { * Collection of utility functions accessible from within a `react` function. */ util: UtilityFunctions - + getBankIndex: () => number } export class App extends Reactor { @@ -2883,3 +2889,18 @@ export class App extends Reactor { this._startExecuting() } } + +class X extends Reactor { + a = new InPort(this); + constructor(parent: Reactor) { + super(parent) + this.getBankIndex() + this.addReaction( + new Triggers(this.a), // replace this with `server` and an error is thrown. + new Args(this.a), + function (this) { + + } + ); + } +} \ No newline at end of file From fc44748ebea91b3fcc6d7fd891db5ec0c0fc1566 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 17 Feb 2022 12:52:26 -0800 Subject: [PATCH 15/68] Remove bogus test code --- src/core/reactor.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index c75271bcc..980800ab0 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -2889,18 +2889,3 @@ export class App extends Reactor { this._startExecuting() } } - -class X extends Reactor { - a = new InPort(this); - constructor(parent: Reactor) { - super(parent) - this.getBankIndex() - this.addReaction( - new Triggers(this.a), // replace this with `server` and an error is thrown. - new Args(this.a), - function (this) { - - } - ); - } -} \ No newline at end of file From 5d2d00f7953aa4ca3735548c45b8311472016d96 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 14:19:41 -0800 Subject: [PATCH 16/68] Update lingua franca version. --- lingua-franca-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index 57512a364..fbc85a172 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -dcbe092ccbf65598ee4f91f03b13c0b9a6900940 +3d991dfd807a51c03c27ecb3d40a7949005f7b70 From 6d2659c601a4e1bba8c289d9c66919860b2399c8 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 15:21:42 -0800 Subject: [PATCH 17/68] Implement repeatLeft for ()+ -> syntax, e.g., (source.out)+ -> sink.inp, and remove unused code. --- src/core/reactor.ts | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 62e362530..7e48a5bdb 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -1476,7 +1476,10 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { }) if (repeatLeft) { - // TODO(hokeun): Handle repeat left case. + const leftPortsSize = leftPorts.length + for (let i = 0; leftPorts.length < rightPorts.length; i++) { + leftPorts.push(leftPorts[i % leftPortsSize]) + } } if (leftPorts.length < rightPorts.length) { @@ -1534,33 +1537,6 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } } - /** - * Connect multiports. - * @param leftPorts The source ports to connect. - * @param rightPorts The destination ports to connect. - * @param repeatLeft Whether left ports can be repeated when there are more right ports. - */ - protected _connectMultiplePorts( - leftPorts: Array>, - rightPorts: Array>, - repeatLeft: boolean) { - if (repeatLeft) { - // TODO(hokeun): Handle repeat left case. - } - - if (leftPorts.length < rightPorts.length) { - Log.warn(null, () => "There are more right ports than left ports. ", - "Not all ports will be connected!") - } else if (leftPorts.length > rightPorts.length) { - Log.warn(null, () => "There are more left ports than right ports. ", - "Not all ports will be connected!") - } - - for (let i = 0; i < leftPorts.length && i < rightPorts.length; i++) { - this._connect(leftPorts[i], rightPorts[i]) - } - } - /** * Return a dependency graph consisting of only this reactor's own ports * and the dependencies between them. From 6d1b2997689e0a59b70ba05d008b52bf3594937b Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 16:21:35 -0800 Subject: [PATCH 18/68] Update lingua franca version. --- lingua-franca-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index fbc85a172..cc6abd5c9 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -3d991dfd807a51c03c27ecb3d40a7949005f7b70 +2aef559e12bbf5352c13d66e754e20ebd44bdbc8 From 981fd128851d9707784044ac2886a266a95598a3 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 17 Feb 2022 19:01:25 -0800 Subject: [PATCH 19/68] Propose syntax for generic banks --- __tests__/bank.ts | 38 +++++++++++++++++++++++--------------- src/core/bank.ts | 40 +++++----------------------------------- 2 files changed, 28 insertions(+), 50 deletions(-) diff --git a/__tests__/bank.ts b/__tests__/bank.ts index 264bd2a4a..18ea0c8fa 100644 --- a/__tests__/bank.ts +++ b/__tests__/bank.ts @@ -1,27 +1,31 @@ import { Bank } from "../src/core/bank"; -import { Reactor, App, Timer, Triggers, Args} from "../src/core/reactor"; +import { Reactor, App, Timer, Triggers, Args, InPort, Present} from "../src/core/reactor"; import { TimeValue } from "../src/core/time"; - class Periodic extends Reactor { - - t: Timer = new Timer(this, 0, TimeValue.sec(1)); - constructor(parent: Reactor) { - super(parent) - this.addReaction( - new Triggers(this.t), - new Args(this.t), - function (this) { - console.log(this.getBankIndex()); - } - ); - } +class Periodic extends Reactor { + + t: Timer = new Timer(this, 0, TimeValue.sec(1)); + constructor(parent: Reactor) { + super(parent) + this.addReaction( + new Triggers(this.t), + new Args(this.t), + function (this) { + console.log(this.getBankIndex()); + } + ); } +} +class Generic extends Reactor { + input: InPort = new InPort(this); +} describe('Check bank index', () => { class myApp extends App { - b = new Bank(this, 3, Periodic, this) + b = new Bank(3, Periodic, this) + c = new Bank, [Reactor]>(2, Generic, this); constructor() { super(); it('contained actor name', () => { @@ -29,6 +33,10 @@ describe('Check bank index', () => { expect(this.b.get(1).getBankIndex()).toBe(1); expect(this.b.get(2).getBankIndex()).toBe(2); }); + + it('generic bank', () => { + this.c.all().forEach(r => expect(typeof r.input == "number")) + }); } } diff --git a/src/core/bank.ts b/src/core/bank.ts index 57722017b..41fde5444 100644 --- a/src/core/bank.ts +++ b/src/core/bank.ts @@ -1,15 +1,16 @@ -import { Component } from './component'; -import { Reactor, Runtime } from './reactor' +import { InPort, Present, Reactor } from './reactor' type ReactorConstructor = { new (...args:ReactorArgs): T; - } +} +// FIXME(marten) +// Also see: https://www.typescriptlang.org/docs/handbook/utility-types.html#constructorparameterstype export type ReactorArgs = T extends any[] ? T : never; export class Bank { private readonly members: Array = new Array(); - constructor(parent: Reactor, width: number, cls:ReactorConstructor, ...args:ReactorArgs) { + constructor(width: number, cls:ReactorConstructor, ...args:ReactorArgs) { for (let i=0; i < width; i++) { this.members.push(Reflect.construct(cls, args, cls)); this.members[i].setBankIndex(i) @@ -24,34 +25,3 @@ export class Bank { return this.members } } - - -// class Reactor { - -// } - -// class SomeClass extends Reactor { -// constructor(public x:number, public y: string) { -// super() -// } -// } - - - - -// function foo(cls:ReactorConstructor, ...args:ReactorArgs):T { -// return Reflect.construct(cls, args, cls) -// } - -// let inst = foo(SomeClass, 1, "foo") -// console.log(inst instanceof SomeClass) - -// console.log(inst.x) -// console.log(inst.y) - - - -// let bank = new Bank(3, SomeClass, 4, "foo") -// console.log(bank.get(0).x) -// console.log(bank.get(1).x) -// console.log(bank.get(2).x) From 3954e8f4ddf39bcdabc284d23c02cf7a460e6270 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 17 Feb 2022 21:26:37 -0800 Subject: [PATCH 20/68] Update lingua franca version. --- lingua-franca-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index cc6abd5c9..8974a1cce 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -2aef559e12bbf5352c13d66e754e20ebd44bdbc8 +6fab59ae64673d9e103914342680f517c88d58f5 From d446e9e44f3fce79f6e0fc802f1c2df60086f1a4 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 17 Feb 2022 22:50:52 -0800 Subject: [PATCH 21/68] Added `select` function to collect ports from bank. Not sure it has the right name. --- __tests__/bank.ts | 10 +++++++++- src/core/bank.ts | 8 ++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/__tests__/bank.ts b/__tests__/bank.ts index 18ea0c8fa..6282fd1d6 100644 --- a/__tests__/bank.ts +++ b/__tests__/bank.ts @@ -1,10 +1,11 @@ import { Bank } from "../src/core/bank"; -import { Reactor, App, Timer, Triggers, Args, InPort, Present} from "../src/core/reactor"; +import { Reactor, App, Timer, Triggers, Args, InPort, Present, OutPort} from "../src/core/reactor"; import { TimeValue } from "../src/core/time"; class Periodic extends Reactor { t: Timer = new Timer(this, 0, TimeValue.sec(1)); + o: OutPort = new OutPort(this) constructor(parent: Reactor) { super(parent) this.addReaction( @@ -37,6 +38,13 @@ describe('Check bank index', () => { it('generic bank', () => { this.c.all().forEach(r => expect(typeof r.input == "number")) }); + var foo = this.b.select((member) => member.o) + var bar = [this.b.get(0).o, this.b.get(1).o, this.b.get(2).o] + it('select port', () => { + for (let i=0; i < foo.length; i++) { + expect(foo[i]).toBe(bar[i]); + } + }); } } diff --git a/src/core/bank.ts b/src/core/bank.ts index c992c594f..2ba0bbc45 100644 --- a/src/core/bank.ts +++ b/src/core/bank.ts @@ -1,4 +1,4 @@ -import { InPort, Present, Reactor } from './reactor' +import { InPort, Port, Present, Reactor } from './reactor' type ReactorConstructor = { new (...args:ReactorArgs): T; @@ -24,4 +24,8 @@ export class Bank { public all():Array { return this.members } -} \ No newline at end of file + + public select

>(selector: (reactor:T) => P):Array

{ + return [...this.all().reduce((acc, val) => acc.concat(selector(val)), new Array

(0))] + } +} From 7c2a06515bff75038d0cb2307dbd44267db54e8a Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 17 Feb 2022 23:23:54 -0800 Subject: [PATCH 22/68] Renamed `select` to `port` and removed unnecessary spread --- __tests__/bank.ts | 2 +- src/core/bank.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/__tests__/bank.ts b/__tests__/bank.ts index 6282fd1d6..95fc1ff1b 100644 --- a/__tests__/bank.ts +++ b/__tests__/bank.ts @@ -38,7 +38,7 @@ describe('Check bank index', () => { it('generic bank', () => { this.c.all().forEach(r => expect(typeof r.input == "number")) }); - var foo = this.b.select((member) => member.o) + var foo = this.b.port((member) => member.o) var bar = [this.b.get(0).o, this.b.get(1).o, this.b.get(2).o] it('select port', () => { for (let i=0; i < foo.length; i++) { diff --git a/src/core/bank.ts b/src/core/bank.ts index 2ba0bbc45..6b76f1d13 100644 --- a/src/core/bank.ts +++ b/src/core/bank.ts @@ -25,7 +25,7 @@ export class Bank { return this.members } - public select

>(selector: (reactor:T) => P):Array

{ - return [...this.all().reduce((acc, val) => acc.concat(selector(val)), new Array

(0))] + public port

>(selector: (reactor:T) => P):Array

{ + return this.all().reduce((acc, val) => acc.concat(selector(val)), new Array

(0)) } } From ffe855fdfa89bd6cccd3e7171cd30313466f6643 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 18 Feb 2022 17:39:06 -0800 Subject: [PATCH 23/68] Started refactoring --- __tests__/ActionTrigger.test.ts | 3 +- __tests__/Adder.test.ts | 3 +- __tests__/Clock.test.ts | 3 +- __tests__/Logger.test.ts | 5 +- __tests__/dependencies.ts | 4 +- __tests__/mutations.test.ts | 4 +- __tests__/reactors.errors.test.ts | 2 +- __tests__/reactors.test.ts | 3 +- src/core/action.ts | 139 +++++++ src/core/bank.ts | 67 +++- src/core/container.ts | 11 + src/core/event.ts | 64 ++++ src/core/port.ts | 244 +++++++++++++ src/core/reaction.ts | 3 +- src/core/reactor.ts | 579 +----------------------------- src/core/state.ts | 33 ++ src/core/trigger.ts | 130 +++++++ src/share/Logger.ts | 3 +- src/share/SingleEvent.ts | 2 +- 19 files changed, 696 insertions(+), 606 deletions(-) create mode 100644 src/core/action.ts create mode 100644 src/core/container.ts create mode 100644 src/core/event.ts create mode 100644 src/core/port.ts create mode 100644 src/core/state.ts create mode 100644 src/core/trigger.ts diff --git a/__tests__/ActionTrigger.test.ts b/__tests__/ActionTrigger.test.ts index 772495693..91e01bf6c 100644 --- a/__tests__/ActionTrigger.test.ts +++ b/__tests__/ActionTrigger.test.ts @@ -1,6 +1,7 @@ import {App, Triggers, Args} from '../src/core/reactor'; import {Origin, TimeValue} from '../src/core/time'; -import {Reactor, Timer, Action, Sched} from '../src/core/reactor'; +import {Reactor, Timer, Sched} from '../src/core/reactor'; +import { Action } from '../src/core/action'; //Upon initialization, this reactor should produce an //output event diff --git a/__tests__/Adder.test.ts b/__tests__/Adder.test.ts index 8e67c875b..cc65d2ae1 100644 --- a/__tests__/Adder.test.ts +++ b/__tests__/Adder.test.ts @@ -1,5 +1,6 @@ -import {App, IOPort, Reactor, InPort, OutPort, Present, Args, Triggers} from '../src/core/reactor'; +import { IOPort } from '../src/core/port'; +import {App, Reactor, InPort, OutPort, Present, Args, Triggers} from '../src/core/reactor'; export class Adder extends Reactor { diff --git a/__tests__/Clock.test.ts b/__tests__/Clock.test.ts index 4b3762965..092bfb283 100644 --- a/__tests__/Clock.test.ts +++ b/__tests__/Clock.test.ts @@ -1,6 +1,7 @@ 'use strict'; -import {Timer, Action, App, Sched, Triggers, Args} from '../src/core/reactor'; +import { Action } from '../src/core/action'; +import {Timer, App, Sched, Triggers, Args} from '../src/core/reactor'; import {TimeValue, TimeUnit, Origin} from "../src/core/time" /** diff --git a/__tests__/Logger.test.ts b/__tests__/Logger.test.ts index dfb357749..a556ea80e 100644 --- a/__tests__/Logger.test.ts +++ b/__tests__/Logger.test.ts @@ -1,10 +1,7 @@ import {Logger} from '../src/share/Logger' -import {Reactor, InPort, Read, Triggers, Args, State, Present, ReactionSandbox, App} from '../src/core/reactor'; -import { TimeValue, TimeUnit } from '../src/core/time'; +import {Reactor, App} from '../src/core/reactor'; import { Log, LogLevel } from '../src/core/util' - - const _reactor:Reactor = new App() const lg:Logger = new Logger(_reactor , 10) diff --git a/__tests__/dependencies.ts b/__tests__/dependencies.ts index 88fa3e607..6a0274380 100644 --- a/__tests__/dependencies.ts +++ b/__tests__/dependencies.ts @@ -1,5 +1,5 @@ -import {SortableDependencyGraph, Sortable, PrioritySetElement, PrioritySet, Log, LogLevel} from '../src/core/util'; -import {Reactor, App, Triggers, InPort, Args, ArgList, Startup, Shutdown} from '../src/core/reactor'; +import {SortableDependencyGraph, Sortable, PrioritySet, Log} from '../src/core/util'; +import {Reactor, App, Triggers, InPort, Args} from '../src/core/reactor'; import {Reaction, Priority} from "../src/core/reaction" //Log.setGlobalLevel(Log.levels.DEBUG); diff --git a/__tests__/mutations.test.ts b/__tests__/mutations.test.ts index 86486c4ee..63adfaca7 100644 --- a/__tests__/mutations.test.ts +++ b/__tests__/mutations.test.ts @@ -1,7 +1,5 @@ -import {Reactor, App, Triggers, InPort, Args, OutPort, Timer, State} from '../src/core/reactor'; +import {Reactor, App, Triggers, InPort, Args, OutPort, Timer} from '../src/core/reactor'; import {TimeValue} from '../src/core/time'; -import {Log, LogLevel} from '../src/core/util'; - class Source extends Reactor { diff --git a/__tests__/reactors.errors.test.ts b/__tests__/reactors.errors.test.ts index 11002a976..cc752fe29 100644 --- a/__tests__/reactors.errors.test.ts +++ b/__tests__/reactors.errors.test.ts @@ -1,4 +1,4 @@ -import {Reactor, App, Triggers, InPort, Args, ArgList, Startup, Shutdown, CalleePort, CallerPort, Present} from '../src/core/reactor'; +import {Reactor, App, Triggers, InPort, Args, CalleePort, CallerPort, Present} from '../src/core/reactor'; import {TimeUnit, TimeValue} from '../src/core/time'; import { Log, LogLevel, SortableDependencyGraph, Sortable } from '../src/core/util'; import { writer } from 'repl'; diff --git a/__tests__/reactors.test.ts b/__tests__/reactors.test.ts index c22f44510..bedc23085 100644 --- a/__tests__/reactors.test.ts +++ b/__tests__/reactors.test.ts @@ -1,7 +1,8 @@ -import {Reactor, App, Triggers, InPort, Args, ArgList, Startup, Shutdown, CalleePort, CallerPort, Port, Present, OutPort, Action, Timer} from '../src/core/reactor'; +import {Reactor, App, Triggers, InPort, Args, OutPort, Timer} from '../src/core/reactor'; import {TimeUnit, TimeValue, Origin } from '../src/core/time'; import { Log, LogLevel, SortableDependencyGraph, Sortable } from '../src/core/util'; import { doesNotMatch } from 'assert'; +import { Action } from '../src/core/action'; /* Set a port in startup to get thing going */ class Starter extends Reactor { diff --git a/src/core/action.ts b/src/core/action.ts new file mode 100644 index 000000000..4b86f6978 --- /dev/null +++ b/src/core/action.ts @@ -0,0 +1,139 @@ +import { TaggedEvent } from "./event"; +import { Absent, Present, Reactor, Read, Sched, SchedulableAction } from "./reactor"; +import { getCurrentPhysicalTime, Origin, Tag, TimeUnit, TimeValue } from "./time"; +import { ScheduledTrigger, TriggerManager } from "./trigger"; +import { Log } from "./util"; + +const defaultMIT = TimeValue.withUnits(1, TimeUnit.nsec); // FIXME + + +/** + * An action denotes a self-scheduled event. + * An action, like an input, can cause reactions to be invoked. + * Whereas inputs are provided by other reactors, actions are scheduled + * by this reactor itself, either in response to some observed external + * event or as a delayed response to some input event. The action can be + * scheduled by a reactor by invoking the schedule function in a reaction + * or in an asynchronous callback that has been set up in a reaction. + */ + export class Action extends ScheduledTrigger implements Read { + + readonly origin: Origin; + readonly minDelay: TimeValue; + readonly minInterArrival: TimeValue = defaultMIT; + + public get(): T | Absent { + if (this.isPresent()) { + return this.value; + } else { + return undefined; + } + } + + public asSchedulable(key: Symbol | undefined): Sched { + if (this._key === key) { + return this.scheduler + } + throw Error("Invalid reference to container.") + } + + public getManager(key: Symbol | undefined): TriggerManager { + if (this._key == key) { + return this.manager + } + throw Error("Unable to grant access to manager.") + } + + protected scheduler = new class extends SchedulableAction { + get(): T | undefined { + return this.action.get() + } + constructor(private action: Action) { + super() + } + schedule(extraDelay: 0 | TimeValue, value: T, intendedTag?: Tag): void { + if (!(extraDelay instanceof TimeValue)) { + extraDelay = TimeValue.secs(0); + } + + var tag = this.action.runtime.util.getCurrentTag(); + var delay = this.action.minDelay.add(extraDelay); + + tag = tag.getLaterTag(delay); + + if (this.action.origin == Origin.physical) { + // If the resulting timestamp from delay is less than the current physical time + // on the platform, then the timestamp becomes the current physical time. + // Otherwise the tag is computed like a logical action's tag. + + let physicalTime = getCurrentPhysicalTime(); + if (tag.time.isEarlierThan(physicalTime)) { + tag = new Tag(getCurrentPhysicalTime(), 0); + } else { + tag = tag.getMicroStepLater(); + } + } + + if (this.action instanceof FederatePortAction) { + if (intendedTag === undefined) { + throw new Error("FederatedPortAction must have an intended tag from RTI."); + } + if (intendedTag <= this.action.runtime.util.getCurrentTag()) { + throw new Error("Intended tag must be greater than current tag. Intended tag" + + intendedTag + " Current tag: " + this.action.runtime.util.getCurrentTag()); + } + Log.debug(this, () => "Using intended tag from RTI, similar to schedule_at_tag(tag) with an intended tag: " + + intendedTag); + tag = intendedTag; + } else if (this.action.origin == Origin.logical && !(this.action instanceof Startup)) { + tag = tag.getMicroStepLater(); + } + + Log.debug(this, () => "Scheduling " + this.action.origin + + " action " + this.action._getFullyQualifiedName() + " with tag: " + tag); + + this.action.runtime.schedule(new TaggedEvent(this.action, tag, value)); + } + }(this) + + /** + * Construct a new action. + * @param __container__ The reactor containing this action. + * @param origin Optional. If physical, then the hardware clock on the local + * platform is used to determine the tag of the resulting event. If logical, + * the current logical time (plus one microstep) is used as the offset. + * @param minDelay Optional. Defaults to 0. Specifies the intrinsic delay of + * any events resulting from scheduling this action. + * @param minInterArrival Optional. Defaults to 1 nsec. Specifies the minimum + * intrinsic delay between to occurrences of this action. + */ + constructor(__container__: Reactor, origin: Origin, minDelay: TimeValue = TimeValue.secs(0), minInterArrival: TimeValue = defaultMIT) { + super(__container__); + this.origin = origin; + this.minDelay = minDelay; + } + + public toString() { + return this._getFullyQualifiedName(); + } +} + +// FIXME(marten): move these to trigger.ts and let them extend trigger + +export class Startup extends Action { // FIXME: this should not be a schedulable trigger, just a trigger + constructor(__parent__: Reactor) { + super(__parent__, Origin.logical) + } +} + +export class Shutdown extends Action { + constructor(__parent__: Reactor) { + super(__parent__, Origin.logical) + } +} + +class FederatePortAction extends Action { + constructor(__parent__: Reactor) { + super(__parent__, Origin.logical) + } +} \ No newline at end of file diff --git a/src/core/bank.ts b/src/core/bank.ts index 6b76f1d13..9de14b320 100644 --- a/src/core/bank.ts +++ b/src/core/bank.ts @@ -1,31 +1,66 @@ -import { InPort, Port, Present, Reactor } from './reactor' +import { Port } from './port'; +import { Present, Reactor } from './reactor' -type ReactorConstructor = { - new (...args:ReactorArgs): T; +/** + * Type that describes a class with a constructor of which the arguments + * are of type `ReactorArgs`. + */ +export type ReactorClass = { + new(...args: ReactorArgs): T; } -// FIXME(marten) -// Also see: https://www.typescriptlang.org/docs/handbook/utility-types.html#constructorparameterstype + +/** + * Type that describes a tuple of arguments passed into the constructor + * of a reactor class. + */ export type ReactorArgs = T extends any[] ? T : never; -export class Bank { +/** + * A bank of reactor instances. + */ +export class Bank { + + /** + * Array of reactor instances that constitute the bank. + */ + private readonly members: Array = new Array(); - private readonly members: Array = new Array(); - constructor(width: number, cls:ReactorConstructor, ...args:ReactorArgs) { - for (let i=0; i < width; i++) { - this.members.push(Reflect.construct(cls, args, cls)); + /** + * Construct a new bank of given width on the basis of a given reactor class and a list of arguments. + * @param width the width of the bank + * @param cls the class to construct reactor instances of that will populate the bank + * @param args the arguments to pass into the constructor of the given reactor class + */ + constructor(width: number, cls: ReactorClass, ...args: ReactorArgs) { + for (let i = 0; i < width; i++) { + this.members.push(Reflect.construct(cls, args, cls)); this.members[i].setBankIndex(i) } } - - public get(index:number):T { - return this.members[index] - } - public all():Array { + /** + * Return all reactor instances in this bank. + * @returns all reactor instances in this bank + */ + public all(): Array { return this.members } - public port

>(selector: (reactor:T) => P):Array

{ + /** + * Return the reactor instance that corresponds to the given index. + * @param index index of the reactor instance inside this bank + * @returns the reactor instances that corresponds to the given index + */ + public get(index: number): T { + return this.members[index] + } + + /** + * Return a list of ports selected across all bank members by the given lambda. + * @param selector lambda function that takes a reactor of type T and return a port of type P + * @returns a list of ports selected across all bank members by the given lambda + */ + public port

>(selector: (reactor: T) => P): Array

{ return this.all().reduce((acc, val) => acc.concat(selector(val)), new Array

(0)) } } diff --git a/src/core/container.ts b/src/core/container.ts new file mode 100644 index 000000000..ea2d6cf32 --- /dev/null +++ b/src/core/container.ts @@ -0,0 +1,11 @@ +import {Component} from './component'; +import { Reaction } from './reaction'; +import { Port, Present } from './reactor'; +import { DependencyGraph } from './util'; + +export abstract class Container extends Component { + + + + +} \ No newline at end of file diff --git a/src/core/event.ts b/src/core/event.ts new file mode 100644 index 000000000..2d686d76e --- /dev/null +++ b/src/core/event.ts @@ -0,0 +1,64 @@ +import { Present } from "./reactor"; +import { Tag } from "./time"; +import { ScheduledTrigger } from "./trigger"; +import { PrioritySetElement } from "./util"; + +/** + * An event is caused by a timer or a scheduled action. Each event is tagged + * with a time instant and may carry a value of arbitrary type. The tag will + * determine the event's position with respect to other events in the event + * queue. + */ + export class TaggedEvent implements PrioritySetElement { + + /** + * Pointer to the next element of the priority set that this event might + * be hooked into. + */ + public next: PrioritySetElement | undefined; + + /** + * Construct a new tagged event. + * @param trigger The trigger of this event. + * @param tag The tag at which this event occurs. + * @param value The value associated with this event. + * + */ + constructor(public trigger: ScheduledTrigger, public tag: Tag, public value: T) { + } + + /** + * Return true if this event has a smaller tag than the given event, false + * otherwise. + * @param node The event to compare this event's tag against. + */ + hasPriorityOver(node: PrioritySetElement | undefined) { + if (node) { + return this.getPriority().isSmallerThan(node.getPriority()); + } else { + return false; + } + } + + /** + * Determine whether the given event is a duplicate of this one. If so, assign the + * value this event to the given one. Otherwise, return false. + * @param node The event adopt the value from if it is a duplicate of this one. + */ + updateIfDuplicateOf(node: PrioritySetElement | undefined) { + if (node && node instanceof TaggedEvent) { + if (this.trigger === node.trigger && this.tag.isSimultaneousWith(node.tag)) { + node.value = this.value; // update the value + return true; + } + } + return false; + } + + /** + * Return the tag associated with this event. + */ + getPriority(): Tag { + return this.tag; + } +} \ No newline at end of file diff --git a/src/core/port.ts b/src/core/port.ts new file mode 100644 index 000000000..50c11ff19 --- /dev/null +++ b/src/core/port.ts @@ -0,0 +1,244 @@ +import { Component } from "./component"; +import { Reaction } from "./reaction"; +import { Absent, InPort, OutPort, Present, Reactor, Read, ReadWrite, Runtime } from "./reactor"; +import { Tag } from "./time"; +import { Trigger, TriggerManager } from "./trigger"; +import { Log } from "./util"; + +export abstract class Port extends Trigger implements Read { + + protected runtime!: Runtime; + + constructor(container: Reactor, alias?: string) { + super(container, alias) + this._linkToRuntimeObject() + } + + /** The time stamp associated with this port's value. */ + protected tag: Tag | undefined; + + /** The value associated with this port. */ + protected value: T | Absent; + + abstract get(): T | undefined; + + public _receiveRuntimeObject(runtime: Runtime) { + if (!this.runtime) { + this.runtime = runtime + } else { + throw new Error("Can only establish link to runtime once. Name: " + this._getFullyQualifiedName()) + } + } + + /** + * Returns true if the connected port's value has been set; false otherwise + */ + public isPresent() { + + Log.debug(this, () => "In isPresent()...") + Log.debug(this, () => "value: " + this.value); + Log.debug(this, () => "tag: " + this.tag); + Log.debug(this, () => "time: " + this.runtime.util.getCurrentLogicalTime()) + + if (this.value !== undefined + && this.tag !== undefined + && this.tag.isSimultaneousWith(this.runtime.util.getCurrentTag())) { + return true; + } else { + return false; + } + } +} + +enum PortType { + INPUT = 0, + OUTPUT = 1 +} + +export abstract class MultiPort extends Component { + protected runtime!: Runtime; + public ports: Array> + + constructor(container: Reactor, width: number, portType: PortType) { + super(container) + switch (portType) { + case PortType.INPUT: + this.ports = new Array>(width) + for (let i = 0; i < width; i++) { + this.ports[i] = new InPort(container) + } + break + case PortType.OUTPUT: + this.ports = new Array>(width) + for (let i = 0; i < width; i++) { + this.ports[i] = new OutPort(container) + } + break + } + } + + public width(): number { + return this.ports.length + } + + public _receiveRuntimeObject(runtime: Runtime) { + if (!this.runtime) { + this.runtime = runtime + } else { + throw new Error("Can only establish link to runtime once. Name: " + this._getFullyQualifiedName()) + } + } +} + +/** + * Abstract class for a writable port. It is intended as a wrapper for a + * regular port. In addition to a get method, it also has a set method and + * a method for retrieving the port that it wraps. + */ + export abstract class WritablePort implements ReadWrite { + abstract get(): T | undefined; + abstract set(value: T): void; + abstract getPort(): IOPort +} + + +interface IOPortManager extends TriggerManager { + addReceiver(port: WritablePort): void; + delReceiver(port: WritablePort): void; +} + +export abstract class IOPort extends Port { + + protected receivers: Set> = new Set(); + + /** + * Return the value set to this port. Return `Absent` if the connected + * output did not have its value set at the current logical time. + */ + public get(): T | Absent { + if (this.isPresent()) { + return this.value; + } else { + return undefined; + } + } + + /** + * Only the holder of the key may obtain a writable port. + * @param key + */ + public asWritable(key: Symbol | undefined): WritablePort { + if (this._key === key) { + return this.writer + } + throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) // FIXME: adjust messages for other methods as well + // FIXME: we could potentially do this for reads/triggers as well just for scope rule enforcement + } + + /** + * + * @param container Reference to the container of this port + * (or the container thereof). + */ + public getManager(key: Symbol | undefined): IOPortManager { + if (this._key == key) { + return this.manager + } + throw Error("Unable to grant access to manager.") + } + + /** + * Inner class instance to gain access to Write interface. + */ + protected writer = new class extends WritablePort { + constructor(private port:IOPort) { + super() + } + + public set(value: T): void { + this.port.value = value; + this.port.tag = this.port.runtime.util.getCurrentTag(); + // Set values in downstream receivers. + this.port.receivers.forEach(p => p.set(value)) + //console.log("Set called. The number of reactions is: " + this.port.reactions.size) + // Stage triggered reactions for execution. + this.port.reactions.forEach(r => this.port.runtime.stage(r)) + } + + public get(): T | Absent { + return this.port.get() + } + + public getPort(): IOPort { + return this.port + } + + public toString(): string { + return this.port.toString() + } + + }(this) + + /** + * Inner class instance to let the container configure this port. + */ + protected manager:IOPortManager = new class implements IOPortManager { + constructor(private port:IOPort) {} + getContainer(): Reactor { + return this.port._getContainer() + } + + /** + * Add the given port to the list of receivers. If the connection was + * established at runtime and the upstream port already has a value, + * immediately propagate the value to the newly connected receiver. + * @param port A newly connected downstream port. + */ + addReceiver(port: WritablePort): void { + this.port.receivers.add(port) + if (this.port.runtime.isRunning()) { + let val = this.port.get() + if (val !== undefined) { + port.set(val) + } + } + } + delReceiver(port: WritablePort): void { + this.port.receivers.delete(port) + } + addReaction(reaction: Reaction): void { + this.port.reactions.add(reaction) + //console.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + } + delReaction(reaction: Reaction): void { + this.port.reactions.delete(reaction) + } + }(this) + + toString(): string { + return this._getFullyQualifiedName(); + } +} + +export class InMultiPort extends MultiPort { + constructor(container: Reactor, width: number) { + super(container, width, PortType.INPUT) + } + public getValueArray() : Array { + let valueArray = new Array(this.width()); + for (let i = 0; i < valueArray.length; i++) { + valueArray[i] = this.ports[i].get(); + } + return valueArray + } +} +export class OutMultiPort extends MultiPort { + public writablePorts: Array> + constructor(container: Reactor, width: number) { + super(container, width, PortType.OUTPUT) + this.writablePorts = new Array>(width) + for (let i = 0; i < width; i++) { + this.writablePorts[i] = container.writable(this.ports[i]) + } + } +} diff --git a/src/core/reaction.ts b/src/core/reaction.ts index f1f873672..45506af55 100644 --- a/src/core/reaction.ts +++ b/src/core/reaction.ts @@ -1,6 +1,7 @@ import {Sortable, PrioritySetElement, Log} from "./util" -import {Reactor, ReactionSandbox, Triggers, Args, ArgList, Startup, Timer, MutationSandbox} from "./reactor" +import {Reactor, ReactionSandbox, Triggers, Args, ArgList, Timer, MutationSandbox} from "./reactor" import {TimeValue, Tag} from "./time"; +import { Startup } from "./action"; /** * A number that indicates a reaction's position with respect to other diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 7e48a5bdb..3c8685a9a 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -10,6 +10,10 @@ import {PrioritySetElement, PrioritySet, SortableDependencyGraph, Log, Dependenc import {TimeValue, TimeUnit, Tag, Origin, getCurrentPhysicalTime, Alarm} from './time'; import {Component} from "./component" import {Reaction, Priority, Mutation, Procedure} from "./reaction" +import { IOPort, MultiPort, Port, WritablePort } from './port'; +import { Action, Shutdown, Startup } from './action'; +import { ScheduledTrigger, Trigger, TriggerManager } from './trigger'; +import { TaggedEvent } from './event'; // Set the default log level. Log.global.level = Log.levels.ERROR; @@ -53,11 +57,6 @@ export type ReadWrite = Read & Write; */ export type Variable = Read | Array> | MultiPort -//--------------------------------------------------------------------------// -// Constants // -//--------------------------------------------------------------------------// - -const defaultMIT = TimeValue.withUnits(1, TimeUnit.nsec); // FIXME //--------------------------------------------------------------------------// // Interfaces // @@ -92,16 +91,7 @@ export interface Write { set: (value: T) => void; } -/** - * Abstract class for a writable port. It is intended as a wrapper for a - * regular port. In addition to a get method, it also has a set method and - * a method for retrieving the port that it wraps. - */ -export abstract class WritablePort implements ReadWrite { - abstract get(): T | undefined; - abstract set(value: T): void; - abstract getPort(): IOPort -} + /** * Abstract class for a schedulable action. It is intended as a wrapper for a @@ -117,309 +107,6 @@ export abstract class SchedulableAction implements Sched { // Core Reactor Classes // //--------------------------------------------------------------------------// -/** - * An event is caused by a timer or a scheduled action. Each event is tagged - * with a time instant and may carry a value of arbitrary type. The tag will - * determine the event's position with respect to other events in the event - * queue. - */ -export class TaggedEvent implements PrioritySetElement { - - /** - * Pointer to the next element of the priority set that this event might - * be hooked into. - */ - public next: PrioritySetElement | undefined; - - /** - * Construct a new tagged event. - * @param trigger The trigger of this event. - * @param tag The tag at which this event occurs. - * @param value The value associated with this event. - * - */ - constructor(public trigger: ScheduledTrigger, public tag: Tag, public value: T) { - } - - /** - * Return true if this event has a smaller tag than the given event, false - * otherwise. - * @param node The event to compare this event's tag against. - */ - hasPriorityOver(node: PrioritySetElement | undefined) { - if (node) { - return this.getPriority().isSmallerThan(node.getPriority()); - } else { - return false; - } - } - - /** - * Determine whether the given event is a duplicate of this one. If so, assign the - * value this event to the given one. Otherwise, return false. - * @param node The event adopt the value from if it is a duplicate of this one. - */ - updateIfDuplicateOf(node: PrioritySetElement | undefined) { - if (node && node instanceof TaggedEvent) { - if (this.trigger === node.trigger && this.tag.isSimultaneousWith(node.tag)) { - node.value = this.value; // update the value - return true; - } - } - return false; - } - - /** - * Return the tag associated with this event. - */ - getPriority(): Tag { - return this.tag; - } -} - -/** - * Abstract class for a trigger. A trigger may be an action, port, or timer. - */ -abstract class Trigger extends Component { - - /** - * Reactions to trigger. - */ - protected reactions: Set> = new Set(); - - /** - * Request the manager of this trigger. The request will only be honored - * if the correct key is given. Each component has a unique symbol (a key) - * that is handed to the owner upon instantiation of the component. If the - * wrong key is supplied, return undefined. - * @param key The private key embedded in this trigger. - */ - abstract getManager(key: Symbol | undefined): TriggerManager; - - /** - * Return the owner of this trigger. - */ - public getContainer(): Reactor | null { - return this._getContainer() - } - - /** - * Return whether or not this trigger is present. - */ - abstract isPresent(): boolean; - -} - -/** - * - */ -abstract class ScheduledTrigger extends Trigger { - protected value: T | Absent = undefined; - protected tag: Tag | undefined; - - protected runtime!: Runtime; - - constructor(container: Reactor, alias?: string) { // FIXME: do we really want the alias here? Probably not. - super(container, alias) - this._linkToRuntimeObject() - } - - /** - * Update the current value of this timer in accordance with the given - * event, and trigger any reactions that list this timer as their trigger. - * @param e Timestamped event. - */ - public update(e: TaggedEvent):void { - - if (!e.tag.isSimultaneousWith(this.runtime.util.getCurrentTag())) { - throw new Error("Time of event does not match current logical time."); - } - if (e.trigger === this) { - this.value = e.value - this.tag = e.tag; - for (let r of this.reactions) { - this.runtime.stage(r) - } - } else { - throw new Error("Attempt to update action using incompatible event."); - } - } - - public getManager(key: Symbol | undefined): TriggerManager { - if (this._key == key) { - return this.manager - } - throw Error("Unable to grant access to manager.") - } - - /** - * Returns true if this action was scheduled for the current - * logical time. This result is not affected by whether it - * has a value. - */ - public isPresent() { - if (this.tag === undefined) { - // This action has never been scheduled before. - return false; - } - if (this.tag.isSimultaneousWith(this.runtime.util.getCurrentTag())) { - return true; - } else { - return false; - } - } - - protected manager = new class implements TriggerManager { - constructor(private trigger: ScheduledTrigger) { } - getContainer(): Reactor { - return this.trigger._getContainer() - } - addReaction(reaction: Reaction): void { - this.trigger.reactions.add(reaction) - } - delReaction(reaction: Reaction): void { - this.trigger.reactions.delete(reaction) - } - }(this) - - public _receiveRuntimeObject(runtime: Runtime) { - if (!this.runtime) { - this.runtime = runtime - } else { - throw new Error("Can only establish link to runtime once.") - } - } - -} - -/** - * An action denotes a self-scheduled event. - * An action, like an input, can cause reactions to be invoked. - * Whereas inputs are provided by other reactors, actions are scheduled - * by this reactor itself, either in response to some observed external - * event or as a delayed response to some input event. The action can be - * scheduled by a reactor by invoking the schedule function in a reaction - * or in an asynchronous callback that has been set up in a reaction. - */ -export class Action extends ScheduledTrigger implements Read { - - readonly origin: Origin; - readonly minDelay: TimeValue; - readonly minInterArrival: TimeValue = defaultMIT; - - public get(): T | Absent { - if (this.isPresent()) { - return this.value; - } else { - return undefined; - } - } - - public asSchedulable(key: Symbol | undefined): Sched { - if (this._key === key) { - return this.scheduler - } - throw Error("Invalid reference to container.") - } - - public getManager(key: Symbol | undefined): TriggerManager { - if (this._key == key) { - return this.manager - } - throw Error("Unable to grant access to manager.") - } - - protected scheduler = new class extends SchedulableAction { - get(): T | undefined { - return this.action.get() - } - constructor(private action: Action) { - super() - } - schedule(extraDelay: 0 | TimeValue, value: T, intendedTag?: Tag): void { - if (!(extraDelay instanceof TimeValue)) { - extraDelay = TimeValue.secs(0); - } - - var tag = this.action.runtime.util.getCurrentTag(); - var delay = this.action.minDelay.add(extraDelay); - - tag = tag.getLaterTag(delay); - - if (this.action.origin == Origin.physical) { - // If the resulting timestamp from delay is less than the current physical time - // on the platform, then the timestamp becomes the current physical time. - // Otherwise the tag is computed like a logical action's tag. - - let physicalTime = getCurrentPhysicalTime(); - if (tag.time.isEarlierThan(physicalTime)) { - tag = new Tag(getCurrentPhysicalTime(), 0); - } else { - tag = tag.getMicroStepLater(); - } - } - - if (this.action instanceof FederatePortAction) { - if (intendedTag === undefined) { - throw new Error("FederatedPortAction must have an intended tag from RTI."); - } - if (intendedTag <= this.action.runtime.util.getCurrentTag()) { - throw new Error("Intended tag must be greater than current tag. Intended tag" + - intendedTag + " Current tag: " + this.action.runtime.util.getCurrentTag()); - } - Log.debug(this, () => "Using intended tag from RTI, similar to schedule_at_tag(tag) with an intended tag: " + - intendedTag); - tag = intendedTag; - } else if (this.action.origin == Origin.logical && !(this.action instanceof Startup)) { - tag = tag.getMicroStepLater(); - } - - Log.debug(this, () => "Scheduling " + this.action.origin + - " action " + this.action._getFullyQualifiedName() + " with tag: " + tag); - - this.action.runtime.schedule(new TaggedEvent(this.action, tag, value)); - } - }(this) - - /** - * Construct a new action. - * @param __container__ The reactor containing this action. - * @param origin Optional. If physical, then the hardware clock on the local - * platform is used to determine the tag of the resulting event. If logical, - * the current logical time (plus one microstep) is used as the offset. - * @param minDelay Optional. Defaults to 0. Specifies the intrinsic delay of - * any events resulting from scheduling this action. - * @param minInterArrival Optional. Defaults to 1 nsec. Specifies the minimum - * intrinsic delay between to occurrences of this action. - */ - constructor(__container__: Reactor, origin: Origin, minDelay: TimeValue = TimeValue.secs(0), minInterArrival: TimeValue = defaultMIT) { - super(__container__); - this.origin = origin; - this.minDelay = minDelay; - } - - public toString() { - return this._getFullyQualifiedName(); - } -} - -export class Startup extends Action { // FIXME: this should not be a schedulable trigger, just a trigger - constructor(__parent__: Reactor) { - super(__parent__, Origin.logical) - } -} - -export class Shutdown extends Action { - constructor(__parent__: Reactor) { - super(__parent__, Origin.logical) - } -} - -export class FederatePortAction extends Action { - constructor(__parent__: Reactor) { - super(__parent__, Origin.logical) - } -} export class Parameter implements Read { constructor(private value: T) { @@ -429,37 +116,7 @@ export class Parameter implements Read { } } -/** - * A state variable. This class refines the Read interface by letting `get` - * return T rather than T | Absent. If the state should be nullable or - * uninitialized, this has to be reflected explicitly in T. - */ -export class State implements Read, Write { - - /** - * Create a new state variable and assign it an initial value. - * @param value The initial value to assign to this state variable. - */ - constructor(private value: T) { - - } - - /** - * Return the current value of this state variable. - */ - get(): T { - return this.value; - }; - - /** - * Set the current value of this state variable. - * @param value - */ - set(value: T) { - this.value = value; - }; -} /** * A timer is an attribute of a reactor which periodically (or just once) @@ -1703,242 +1360,18 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } } -export abstract class Port extends Trigger implements Read { - - protected runtime!: Runtime; - - constructor(container: Reactor, alias?: string) { - super(container, alias) - this._linkToRuntimeObject() - } - - /** The time stamp associated with this port's value. */ - protected tag: Tag | undefined; - - /** The value associated with this port. */ - protected value: T | Absent; - - abstract get(): T | undefined; - - public _receiveRuntimeObject(runtime: Runtime) { - if (!this.runtime) { - this.runtime = runtime - } else { - throw new Error("Can only establish link to runtime once. Name: " + this._getFullyQualifiedName()) - } - } - - /** - * Returns true if the connected port's value has been set; false otherwise - */ - public isPresent() { - - Log.debug(this, () => "In isPresent()...") - Log.debug(this, () => "value: " + this.value); - Log.debug(this, () => "tag: " + this.tag); - Log.debug(this, () => "time: " + this.runtime.util.getCurrentLogicalTime()) - - if (this.value !== undefined - && this.tag !== undefined - && this.tag.isSimultaneousWith(this.runtime.util.getCurrentTag())) { - return true; - } else { - return false; - } - } -} - -enum PortType { - INPUT = 0, - OUTPUT = 1 -} - -export abstract class MultiPort extends Component { - protected runtime!: Runtime; - public ports: Array> - - constructor(container: Reactor, width: number, portType: PortType) { - super(container) - switch (portType) { - case PortType.INPUT: - this.ports = new Array>(width) - for (let i = 0; i < width; i++) { - this.ports[i] = new InPort(container) - } - break - case PortType.OUTPUT: - this.ports = new Array>(width) - for (let i = 0; i < width; i++) { - this.ports[i] = new OutPort(container) - } - break - } - } - - public width(): number { - return this.ports.length - } - - public _receiveRuntimeObject(runtime: Runtime) { - if (!this.runtime) { - this.runtime = runtime - } else { - throw new Error("Can only establish link to runtime once. Name: " + this._getFullyQualifiedName()) - } - } -} - -export class InMultiPort extends MultiPort { - constructor(container: Reactor, width: number) { - super(container, width, PortType.INPUT) - } - public getValueArray() : Array { - let valueArray = new Array(this.width()); - for (let i = 0; i < valueArray.length; i++) { - valueArray[i] = this.ports[i].get(); - } - return valueArray - } -} -export class OutMultiPort extends MultiPort { - public writablePorts: Array> - constructor(container: Reactor, width: number) { - super(container, width, PortType.OUTPUT) - this.writablePorts = new Array>(width) - for (let i = 0; i < width; i++) { - this.writablePorts[i] = container.writable(this.ports[i]) - } - } -} - -export abstract class IOPort extends Port { - - protected receivers: Set> = new Set(); - - /** - * Return the value set to this port. Return `Absent` if the connected - * output did not have its value set at the current logical time. - */ - public get(): T | Absent { - if (this.isPresent()) { - return this.value; - } else { - return undefined; - } - } - - /** - * Only the holder of the key may obtain a writable port. - * @param key - */ - public asWritable(key: Symbol | undefined): WritablePort { - if (this._key === key) { - return this.writer - } - throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) // FIXME: adjust messages for other methods as well - // FIXME: we could potentially do this for reads/triggers as well just for scope rule enforcement - } - - /** - * - * @param container Reference to the container of this port - * (or the container thereof). - */ - public getManager(key: Symbol | undefined): IOPortManager { - if (this._key == key) { - return this.manager - } - throw Error("Unable to grant access to manager.") - } - - /** - * Inner class instance to gain access to Write interface. - */ - protected writer = new class extends WritablePort { - constructor(private port:IOPort) { - super() - } - public set(value: T): void { - this.port.value = value; - this.port.tag = this.port.runtime.util.getCurrentTag(); - // Set values in downstream receivers. - this.port.receivers.forEach(p => p.set(value)) - //console.log("Set called. The number of reactions is: " + this.port.reactions.size) - // Stage triggered reactions for execution. - this.port.reactions.forEach(r => this.port.runtime.stage(r)) - } - - public get(): T | Absent { - return this.port.get() - } - - public getPort(): IOPort { - return this.port - } - - public toString(): string { - return this.port.toString() - } - - }(this) - /** - * Inner class instance to let the container configure this port. - */ - protected manager:IOPortManager = new class implements IOPortManager { - constructor(private port:IOPort) {} - getContainer(): Reactor { - return this.port._getContainer() - } - /** - * Add the given port to the list of receivers. If the connection was - * established at runtime and the upstream port already has a value, - * immediately propagate the value to the newly connected receiver. - * @param port A newly connected downstream port. - */ - addReceiver(port: WritablePort): void { - this.port.receivers.add(port) - if (this.port.runtime.isRunning()) { - let val = this.port.get() - if (val !== undefined) { - port.set(val) - } - } - } - delReceiver(port: WritablePort): void { - this.port.receivers.delete(port) - } - addReaction(reaction: Reaction): void { - this.port.reactions.add(reaction) - //console.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") - } - delReaction(reaction: Reaction): void { - this.port.reactions.delete(reaction) - } - }(this) - toString(): string { - return this._getFullyQualifiedName(); - } -} interface ComponentManager { getOwner(): Reactor; } -interface TriggerManager { - getContainer(): Reactor; - addReaction(reaction: Reaction): void; - delReaction(reaction: Reaction): void; -} -interface IOPortManager extends TriggerManager { - addReceiver(port: WritablePort): void; - delReceiver(port: WritablePort): void; -} + export class OutPort extends IOPort { diff --git a/src/core/state.ts b/src/core/state.ts new file mode 100644 index 000000000..576575969 --- /dev/null +++ b/src/core/state.ts @@ -0,0 +1,33 @@ +import { Read, Write } from "./reactor"; + +/** + * A state variable. This class refines the Read interface by letting `get` + * return T rather than T | Absent. If the state should be nullable or + * uninitialized, this has to be reflected explicitly in T. + */ + export class State implements Read, Write { + + /** + * Create a new state variable and assign it an initial value. + * @param value The initial value to assign to this state variable. + */ + constructor(private value: T) { + + } + + /** + * Return the current value of this state variable. + */ + get(): T { + return this.value; + }; + + /** + * Set the current value of this state variable. + * @param value + */ + set(value: T) { + this.value = value; + }; + +} \ No newline at end of file diff --git a/src/core/trigger.ts b/src/core/trigger.ts new file mode 100644 index 000000000..1cc4bb862 --- /dev/null +++ b/src/core/trigger.ts @@ -0,0 +1,130 @@ +import { Component } from "./component"; +import { TaggedEvent } from "./event"; +import { Reaction } from "./reaction"; +import { Absent, Present, Reactor, Runtime } from "./reactor"; +import { Tag } from "./time"; + +export interface TriggerManager { + getContainer(): Reactor; + addReaction(reaction: Reaction): void; + delReaction(reaction: Reaction): void; +} + + + + +/** + * Abstract class for a trigger. A trigger may be an action, port, or timer. + */ + export abstract class Trigger extends Component { + + /** + * Reactions to trigger. + */ + protected reactions: Set> = new Set(); + + /** + * Request the manager of this trigger. The request will only be honored + * if the correct key is given. Each component has a unique symbol (a key) + * that is handed to the owner upon instantiation of the component. If the + * wrong key is supplied, return undefined. + * @param key The private key embedded in this trigger. + */ + abstract getManager(key: Symbol | undefined): TriggerManager; + + /** + * Return the owner of this trigger. + */ + public getContainer(): Reactor | null { + return this._getContainer() + } + + /** + * Return whether or not this trigger is present. + */ + abstract isPresent(): boolean; + +} + +/** + * + */ + export abstract class ScheduledTrigger extends Trigger { + protected value: T | Absent = undefined; + protected tag: Tag | undefined; + + protected runtime!: Runtime; + + constructor(container: Reactor, alias?: string) { // FIXME: do we really want the alias here? Probably not. + super(container, alias) + this._linkToRuntimeObject() + } + + /** + * Update the current value of this timer in accordance with the given + * event, and trigger any reactions that list this timer as their trigger. + * @param e Timestamped event. + */ + public update(e: TaggedEvent):void { + + if (!e.tag.isSimultaneousWith(this.runtime.util.getCurrentTag())) { + throw new Error("Time of event does not match current logical time."); + } + if (e.trigger === this) { + this.value = e.value + this.tag = e.tag; + for (let r of this.reactions) { + this.runtime.stage(r) + } + } else { + throw new Error("Attempt to update action using incompatible event."); + } + } + + public getManager(key: Symbol | undefined): TriggerManager { + if (this._key == key) { + return this.manager + } + throw Error("Unable to grant access to manager.") + } + + /** + * Returns true if this action was scheduled for the current + * logical time. This result is not affected by whether it + * has a value. + */ + public isPresent() { + if (this.tag === undefined) { + // This action has never been scheduled before. + return false; + } + if (this.tag.isSimultaneousWith(this.runtime.util.getCurrentTag())) { + return true; + } else { + return false; + } + } + + protected manager = new class implements TriggerManager { + constructor(private trigger: ScheduledTrigger) { } + getContainer(): Reactor { + return this.trigger._getContainer() + } + addReaction(reaction: Reaction): void { + this.trigger.reactions.add(reaction) + } + delReaction(reaction: Reaction): void { + this.trigger.reactions.delete(reaction) + } + }(this) + + public _receiveRuntimeObject(runtime: Runtime) { + if (!this.runtime) { + this.runtime = runtime + } else { + throw new Error("Can only establish link to runtime once.") + } + } + +} + diff --git a/src/share/Logger.ts b/src/share/Logger.ts index 06ea2604c..8fc19c0bd 100644 --- a/src/share/Logger.ts +++ b/src/share/Logger.ts @@ -1,4 +1,5 @@ -import {Reactor, InPort, Read, Triggers, Args, State, Present, ReactionSandbox} from '../core/reactor'; +import {Reactor, InPort, Read, Triggers, Args, Present, ReactionSandbox} from '../core/reactor'; +import { State } from '../core/state'; function print(this:ReactionSandbox, i: Read, expected: State) { const received = i.get(); diff --git a/src/share/SingleEvent.ts b/src/share/SingleEvent.ts index ab05659e6..ce9b7b9c2 100644 --- a/src/share/SingleEvent.ts +++ b/src/share/SingleEvent.ts @@ -1,5 +1,5 @@ -import {Reactor, OutPort, Timer, Write, Triggers, Args, ArgList, ReactionSandbox, Present, State, Parameter, Variable} from '../core/reactor'; +import {Reactor, OutPort, Timer, Write, Triggers, Args, ArgList, ReactionSandbox, Present, Parameter, Variable} from '../core/reactor'; function produceOutput(this: ReactionSandbox, o: Write, payload:Parameter) { o.set(payload.get()); From 9f16fd5a941a3042df06918427cf730897035247 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 18 Feb 2022 22:53:10 -0800 Subject: [PATCH 24/68] Breaking more things while fixing others --- __tests__/Adder.test.ts | 4 +- __tests__/HierarchicalSingleEvent.test.ts | 2 +- __tests__/OutputGet.test.ts | 3 +- __tests__/bank.ts | 2 +- __tests__/dependencies.ts | 2 +- __tests__/hierarchy.ts | 2 +- __tests__/mutations.test.ts | 2 +- __tests__/reactors.errors.test.ts | 2 +- __tests__/reactors.test.ts | 2 +- __tests__/simple.ts | 2 +- src/core/port.ts | 104 ++++++++++++---------- src/core/reactor.ts | 20 +++-- src/core/trigger.ts | 2 +- src/share/Logger.ts | 2 +- src/share/SingleEvent.ts | 2 +- 15 files changed, 84 insertions(+), 69 deletions(-) diff --git a/__tests__/Adder.test.ts b/__tests__/Adder.test.ts index cc65d2ae1..83151aee4 100644 --- a/__tests__/Adder.test.ts +++ b/__tests__/Adder.test.ts @@ -1,6 +1,6 @@ -import { IOPort } from '../src/core/port'; -import {App, Reactor, InPort, OutPort, Present, Args, Triggers} from '../src/core/reactor'; +import { IOPort} from '../src/core/port'; +import {App, Reactor, Present, Args, Triggers, InPort, OutPort} from '../src/core/reactor'; export class Adder extends Reactor { diff --git a/__tests__/HierarchicalSingleEvent.test.ts b/__tests__/HierarchicalSingleEvent.test.ts index 711c36836..ceb49c0ad 100644 --- a/__tests__/HierarchicalSingleEvent.test.ts +++ b/__tests__/HierarchicalSingleEvent.test.ts @@ -1,4 +1,4 @@ -import {Reactor, OutPort, InPort, App, Parameter} from '../src/core/reactor'; +import {Reactor, App, Parameter, OutPort, InPort} from '../src/core/reactor'; import {TimeValue} from "../src/core/time" import {SingleEvent} from '../src/share/SingleEvent'; import {Logger} from '../src/share/Logger'; diff --git a/__tests__/OutputGet.test.ts b/__tests__/OutputGet.test.ts index 18a1f5785..83c39ed28 100644 --- a/__tests__/OutputGet.test.ts +++ b/__tests__/OutputGet.test.ts @@ -1,4 +1,5 @@ -import {OutPort, App, Timer, Write, Triggers, Args} from '../src/core/reactor'; + +import {App, Timer, Write, Triggers, Args, OutPort} from '../src/core/reactor'; import {TimeValue} from "../src/core/time"; import { Log } from '../src/core/util'; diff --git a/__tests__/bank.ts b/__tests__/bank.ts index 95fc1ff1b..86716736b 100644 --- a/__tests__/bank.ts +++ b/__tests__/bank.ts @@ -1,5 +1,5 @@ import { Bank } from "../src/core/bank"; -import { Reactor, App, Timer, Triggers, Args, InPort, Present, OutPort} from "../src/core/reactor"; +import { Reactor, App, Timer, Triggers, Args, Present, OutPort, InPort} from "../src/core/reactor"; import { TimeValue } from "../src/core/time"; class Periodic extends Reactor { diff --git a/__tests__/dependencies.ts b/__tests__/dependencies.ts index 6a0274380..2b931a918 100644 --- a/__tests__/dependencies.ts +++ b/__tests__/dependencies.ts @@ -1,5 +1,5 @@ import {SortableDependencyGraph, Sortable, PrioritySet, Log} from '../src/core/util'; -import {Reactor, App, Triggers, InPort, Args} from '../src/core/reactor'; +import {Reactor, App, Triggers, Args, InPort} from '../src/core/reactor'; import {Reaction, Priority} from "../src/core/reaction" //Log.setGlobalLevel(Log.levels.DEBUG); diff --git a/__tests__/hierarchy.ts b/__tests__/hierarchy.ts index 99d9f6626..ffd7f7372 100644 --- a/__tests__/hierarchy.ts +++ b/__tests__/hierarchy.ts @@ -1,4 +1,4 @@ -import {Reactor, OutPort, InPort, App} from '../src/core/reactor'; +import {Reactor, App, InPort, OutPort} from '../src/core/reactor'; var app = new App(); diff --git a/__tests__/mutations.test.ts b/__tests__/mutations.test.ts index 63adfaca7..adc904f81 100644 --- a/__tests__/mutations.test.ts +++ b/__tests__/mutations.test.ts @@ -1,4 +1,4 @@ -import {Reactor, App, Triggers, InPort, Args, OutPort, Timer} from '../src/core/reactor'; +import {Reactor, App, Triggers, Args, Timer, OutPort, InPort} from '../src/core/reactor'; import {TimeValue} from '../src/core/time'; class Source extends Reactor { diff --git a/__tests__/reactors.errors.test.ts b/__tests__/reactors.errors.test.ts index cc752fe29..5e38c90f8 100644 --- a/__tests__/reactors.errors.test.ts +++ b/__tests__/reactors.errors.test.ts @@ -1,4 +1,4 @@ -import {Reactor, App, Triggers, InPort, Args, CalleePort, CallerPort, Present} from '../src/core/reactor'; +import {Reactor, App, Triggers, Args, CalleePort, CallerPort, Present, InPort} from '../src/core/reactor'; import {TimeUnit, TimeValue} from '../src/core/time'; import { Log, LogLevel, SortableDependencyGraph, Sortable } from '../src/core/util'; import { writer } from 'repl'; diff --git a/__tests__/reactors.test.ts b/__tests__/reactors.test.ts index bedc23085..c8fc802c1 100644 --- a/__tests__/reactors.test.ts +++ b/__tests__/reactors.test.ts @@ -1,4 +1,4 @@ -import {Reactor, App, Triggers, InPort, Args, OutPort, Timer} from '../src/core/reactor'; +import {Reactor, App, Triggers, Args, Timer, OutPort, InPort} from '../src/core/reactor'; import {TimeUnit, TimeValue, Origin } from '../src/core/time'; import { Log, LogLevel, SortableDependencyGraph, Sortable } from '../src/core/util'; import { doesNotMatch } from 'assert'; diff --git a/__tests__/simple.ts b/__tests__/simple.ts index 696c1796a..d3a9f47cc 100644 --- a/__tests__/simple.ts +++ b/__tests__/simple.ts @@ -1,4 +1,4 @@ -import {Reactor, OutPort, InPort, App} from "../src/core/reactor"; +import {Reactor, App, InPort, OutPort} from "../src/core/reactor"; class MyActor extends Reactor { diff --git a/src/core/port.ts b/src/core/port.ts index 50c11ff19..e1ea03079 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -5,6 +5,7 @@ import { Tag } from "./time"; import { Trigger, TriggerManager } from "./trigger"; import { Log } from "./util"; + export abstract class Port extends Trigger implements Read { protected runtime!: Runtime; @@ -55,40 +56,6 @@ enum PortType { OUTPUT = 1 } -export abstract class MultiPort extends Component { - protected runtime!: Runtime; - public ports: Array> - - constructor(container: Reactor, width: number, portType: PortType) { - super(container) - switch (portType) { - case PortType.INPUT: - this.ports = new Array>(width) - for (let i = 0; i < width; i++) { - this.ports[i] = new InPort(container) - } - break - case PortType.OUTPUT: - this.ports = new Array>(width) - for (let i = 0; i < width; i++) { - this.ports[i] = new OutPort(container) - } - break - } - } - - public width(): number { - return this.ports.length - } - - public _receiveRuntimeObject(runtime: Runtime) { - if (!this.runtime) { - this.runtime = runtime - } else { - throw new Error("Can only establish link to runtime once. Name: " + this._getFullyQualifiedName()) - } - } -} /** * Abstract class for a writable port. It is intended as a wrapper for a @@ -215,30 +182,75 @@ export abstract class IOPort extends Port { } }(this) - toString(): string { + toString(): string {InMultiPort return this._getFullyQualifiedName(); } } + +export abstract class MultiPort extends Component { + protected runtime!: Runtime; + + constructor(container: Reactor, readonly width: number) { + super(container) + } + public abstract channels(): Array> + + public _receiveRuntimeObject(runtime: Runtime) { + if (!this.runtime) { + this.runtime = runtime + } else { + throw new Error("Can only establish link to runtime once. Name: " + this._getFullyQualifiedName()) + } + } + + public static values(ports: Array>): Array { + let values = new Array(ports.length); + for (let i = 0; i < values.length; i++) { + values[i] = ports[i].get(); + } + return values + } + + public abstract values(): Array +} + export class InMultiPort extends MultiPort { - constructor(container: Reactor, width: number) { - super(container, width, PortType.INPUT) + + private _channels: Array> + + public channels() { + return this._channels } - public getValueArray() : Array { - let valueArray = new Array(this.width()); - for (let i = 0; i < valueArray.length; i++) { - valueArray[i] = this.ports[i].get(); + + constructor(container: Reactor, width: number) { + super(container, width) + this._channels = new Array>(width) + for (let i = 0; i < width; i++) { + this._channels[i] = new InPort(container) } - return valueArray + } + + public values(): Array { + return MultiPort.values(this._channels) } } export class OutMultiPort extends MultiPort { - public writablePorts: Array> + private _channels: Array> + constructor(container: Reactor, width: number) { - super(container, width, PortType.OUTPUT) - this.writablePorts = new Array>(width) + super(container, width) + this._channels = new Array>(width) for (let i = 0; i < width; i++) { - this.writablePorts[i] = container.writable(this.ports[i]) + this._channels[i] = new InPort(container) } } + + public values(): Array { + return MultiPort.values(this._channels) + } + + public channels() { + return this._channels + } } diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 3c8685a9a..4e3670e69 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -18,6 +18,15 @@ import { TaggedEvent } from './event'; // Set the default log level. Log.global.level = Log.levels.ERROR; +// FIXME(marten): moving these two to port.ts results in a circular import problem with many test files: +export class OutPort extends IOPort { + +} + +export class InPort extends IOPort { + +} + //--------------------------------------------------------------------------// // Types // //--------------------------------------------------------------------------// @@ -1114,7 +1123,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { // TODO(hokeun): Check if the multiport's container is Bank when Bank is implemented. src.forEach(port => { if (port instanceof MultiPort) { - port.ports.forEach(singlePort => { + port.channels().forEach(singlePort => { leftPorts.push(singlePort) }) } else if (port instanceof IOPort) { @@ -1124,7 +1133,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { dest.forEach(port => { if (port instanceof MultiPort) { - port.ports.forEach(singlePort => { + port.channels().forEach(singlePort => { rightPorts.push(singlePort) }) } else if (port instanceof IOPort) { @@ -1373,13 +1382,6 @@ interface ComponentManager { -export class OutPort extends IOPort { - -} - -export class InPort extends IOPort { - -} /** * A caller port sends arguments of type T and receives a response of type R. diff --git a/src/core/trigger.ts b/src/core/trigger.ts index 1cc4bb862..892142e13 100644 --- a/src/core/trigger.ts +++ b/src/core/trigger.ts @@ -1,7 +1,7 @@ +import { Absent, Present, Reactor, Runtime } from "./reactor"; import { Component } from "./component"; import { TaggedEvent } from "./event"; import { Reaction } from "./reaction"; -import { Absent, Present, Reactor, Runtime } from "./reactor"; import { Tag } from "./time"; export interface TriggerManager { diff --git a/src/share/Logger.ts b/src/share/Logger.ts index 8fc19c0bd..6765decc1 100644 --- a/src/share/Logger.ts +++ b/src/share/Logger.ts @@ -1,4 +1,4 @@ -import {Reactor, InPort, Read, Triggers, Args, Present, ReactionSandbox} from '../core/reactor'; +import {Reactor, Read, Triggers, Args, Present, ReactionSandbox, InPort} from '../core/reactor'; import { State } from '../core/state'; function print(this:ReactionSandbox, i: Read, expected: State) { diff --git a/src/share/SingleEvent.ts b/src/share/SingleEvent.ts index ce9b7b9c2..87b7fbf70 100644 --- a/src/share/SingleEvent.ts +++ b/src/share/SingleEvent.ts @@ -1,5 +1,5 @@ -import {Reactor, OutPort, Timer, Write, Triggers, Args, ArgList, ReactionSandbox, Present, Parameter, Variable} from '../core/reactor'; +import {Reactor, Timer, Write, Triggers, Args, ArgList, ReactionSandbox, Present, Parameter, Variable, OutPort} from '../core/reactor'; function produceOutput(this: ReactionSandbox, o: Write, payload:Parameter) { o.set(payload.get()); From b368361378e5bb1d32714baa948b02adcbe0b53e Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 19 Feb 2022 21:31:38 -0800 Subject: [PATCH 25/68] Fix import and export errors. --- src/core/action.ts | 2 +- src/core/federation.ts | 6 ++++-- src/core/reactor.ts | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/core/action.ts b/src/core/action.ts index 4b86f6978..c07818f76 100644 --- a/src/core/action.ts +++ b/src/core/action.ts @@ -132,7 +132,7 @@ export class Shutdown extends Action { } } -class FederatePortAction extends Action { +export class FederatePortAction extends Action { constructor(__parent__: Reactor) { super(__parent__, Origin.logical) } diff --git a/src/core/federation.ts b/src/core/federation.ts index 1088984fb..62e6ee7b1 100644 --- a/src/core/federation.ts +++ b/src/core/federation.ts @@ -1,8 +1,10 @@ import {Log} from './util'; -import {Tag, TimeValue, TimeUnit, Origin, getCurrentPhysicalTime, Alarm} from './time'; +import {Tag, TimeValue, Origin, getCurrentPhysicalTime, Alarm} from './time'; import {Socket, createConnection, SocketConnectOpts} from 'net' import {EventEmitter} from 'events'; -import {Action, Present, TaggedEvent, App, FederatePortAction} from './reactor' +import {Present, App} from './reactor'; +import {Action, FederatePortAction} from './action'; +import {TaggedEvent} from './event'; //---------------------------------------------------------------------// // Federated Execution Constants and Enums // diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 4e3670e69..653791421 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -6,8 +6,8 @@ * @author Hokeun Kim (hokeunkim@berkeley.edu) */ -import {PrioritySetElement, PrioritySet, SortableDependencyGraph, Log, DependencyGraph} from './util'; -import {TimeValue, TimeUnit, Tag, Origin, getCurrentPhysicalTime, Alarm} from './time'; +import {PrioritySet, SortableDependencyGraph, Log, DependencyGraph} from './util'; +import {TimeValue, Tag, Origin, getCurrentPhysicalTime, Alarm} from './time'; import {Component} from "./component" import {Reaction, Priority, Mutation, Procedure} from "./reaction" import { IOPort, MultiPort, Port, WritablePort } from './port'; From c6b47574f3bc0ba934d78c97b273591d8c95f7d0 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 22 Feb 2022 11:57:58 -0800 Subject: [PATCH 26/68] Add _uncheckedConnect and tests for connections. --- __tests__/connection.test.ts | 101 +++++++++++++++++++++++++++++++++++ src/core/reactor.ts | 34 +++++++----- 2 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 __tests__/connection.test.ts diff --git a/__tests__/connection.test.ts b/__tests__/connection.test.ts new file mode 100644 index 000000000..257ebc12f --- /dev/null +++ b/__tests__/connection.test.ts @@ -0,0 +1,101 @@ +import { Reactor, App, Triggers, Args, Timer, OutPort, InPort } from '../src/core/reactor'; +import { State } from '../src/core/state'; +import { TimeUnit, TimeValue } from '../src/core/time'; + +describe('Check canConnect', () => { + class Source extends Reactor { + out: OutPort = new OutPort(this) + } + class Destination extends Reactor { + in: InPort = new InPort(this) + out: InPort = new InPort(this) + } + + class TestApp extends App { + source: Source + destination: Destination + + constructor() { + super() + this.source = new Source(this) + this.destination = new Destination(this) + + it('canConnect success out->in', () => { + expect(this.canConnect(this.source.out, this.destination.in)).toBe(true) + }) + + it('canConnect success out->out', () => { + expect(this.canConnect(this.source.out, this.destination.out)).toBe(true) + }) + + it('canConnect failure', () => { + expect(this.canConnect(this.destination.in, this.source.out)).toBe(false) + }) + } + } + var testApp = new TestApp() +}) + +describe('Check _connect', () => { + jest.setTimeout(5000); + + class Source extends Reactor { + out: OutPort = new OutPort(this) + constructor(container: Reactor) { + super(container); + this.addReaction( + new Triggers(this.startup), + new Args(this.writable(this.out)), + function(this, __out) { + __out.set(100); + + } + ); + } + } + class Destination extends Reactor { + in: InPort = new InPort(this) + received: State = new State(0) + constructor(container: Reactor) { + super(container) + this.addReaction( + new Triggers(this.in), + new Args(this.in, this.received), + function(this, __in, __received) { + let tmp = __in.get(); + try + { + if(tmp) + { + __received.set(tmp) + } + } finally { + + } + } + ) + } + } + + class TestApp extends App { + source: Source + destination: Destination + + constructor(timeout: TimeValue, success?: () => void, fail?: () => void) { + super(timeout, false, false, success, fail) + this.source = new Source(this) + this.destination = new Destination(this) + this._connect(this.source.out, this.destination.in) + } + } + + it("_connect success", done => { + function fail() { + throw new Error("Test has failed."); + }; + + let testApp = new TestApp(TimeValue.withUnits(1,TimeUnit.nsec), done, fail) + testApp._start() + expect(testApp.destination.received.get()).toBe(100) + }) +}) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 653791421..fd98f1487 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -1082,6 +1082,27 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { } } + /** + * Connect a source port to a downstream destination port without canConnect() check. + * This must be used with caution after checking canConnect for the given ports. + * @param src The source port to connect. + * @param dst The destination port to connect. + */ + private _uncheckedConnect(src: IOPort, dst:IOPort) { + Log.debug(this, () => "connecting " + src + " and " + dst); + // Add dependency implied by connection to local graph. + this._dependencyGraph.addEdge(dst, src); + // Register receiver for value propagation. + let writer = dst.asWritable(this._getKey(dst)); + src.getManager(this._getKey(src)).addReceiver + (writer as WritablePort); + let val = src.get() + if (this._runtime.isRunning() && val !== undefined) { + //console.log(">>>>>>>>>>>>>>>>>>>>>>>>><<<<<>>>>>>>>>>>>>>>>>>>>>") + writer.set(val) + } + } + /** * Connect a source port to a downstream destination port. If a source is a * regular port, then the type variable of the source has to be a subtype of @@ -1096,18 +1117,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { */ protected _connect(src: IOPort, dst:IOPort) { if (this.canConnect(src, dst)) { - Log.debug(this, () => "connecting " + src + " and " + dst); - // Add dependency implied by connection to local graph. - this._dependencyGraph.addEdge(dst, src); - // Register receiver for value propagation. - let writer = dst.asWritable(this._getKey(dst)); - src.getManager(this._getKey(src)).addReceiver - (writer as WritablePort); - let val = src.get() - if (this._runtime.isRunning() && val !== undefined) { - //console.log(">>>>>>>>>>>>>>>>>>>>>>>>><<<<<>>>>>>>>>>>>>>>>>>>>>") - writer.set(val) - } + this._uncheckedConnect(src, dst); } else { throw new Error("ERROR connecting " + src + " to " + dst); } From f70086b870d0a2d178f234206379dc11f833873d Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 22 Feb 2022 12:17:03 -0800 Subject: [PATCH 27/68] Add canConnect check for _connectMulti. --- src/core/reactor.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/reactor.ts b/src/core/reactor.ts index fd98f1487..f625e3c23 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -1166,8 +1166,15 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { "Not all ports will be connected!") } + + for (let i = 0; i < leftPorts.length && i < rightPorts.length; i++) { + if (!this.canConnect(leftPorts[i], rightPorts[i])) { + throw new Error("ERROR connecting " + leftPorts[i] + " to " + rightPorts[i] + + "in multiple connections from " + src + " to " + dest) + } + } for (let i = 0; i < leftPorts.length && i < rightPorts.length; i++) { - this._connect(leftPorts[i], rightPorts[i]) + this._uncheckedConnect(leftPorts[i], rightPorts[i]) } } From b980f0dab4c5c4204e4f3875a4be2ddcfb375dfc Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Tue, 22 Feb 2022 22:46:20 -0800 Subject: [PATCH 28/68] Interim commit before pulling --- src/core/port.ts | 110 +++++++++++++++++++++++++++++++------------- src/core/reactor.ts | 24 ++++++++-- src/core/trigger.ts | 22 +++++---- 3 files changed, 112 insertions(+), 44 deletions(-) diff --git a/src/core/port.ts b/src/core/port.ts index e1ea03079..c00a10111 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -1,13 +1,15 @@ import { Component } from "./component"; import { Reaction } from "./reaction"; -import { Absent, InPort, OutPort, Present, Reactor, Read, ReadWrite, Runtime } from "./reactor"; +import { Absent, InPort, MultiReadWrite, OutPort, Present, Reactor, Read, ReadWrite, Runtime } from "./reactor"; import { Tag } from "./time"; -import { Trigger, TriggerManager } from "./trigger"; +import { NonComposite, Trigger, TriggerManager } from "./trigger"; import { Log } from "./util"; -export abstract class Port extends Trigger implements Read { +export abstract class Port extends Trigger { + protected receivers: Set> = new Set(); + protected runtime!: Runtime; constructor(container: Reactor, alias?: string) { @@ -15,13 +17,13 @@ export abstract class Port extends Trigger implements Read this._linkToRuntimeObject() } - /** The time stamp associated with this port's value. */ + /** The tag associated with this port's value, or undefined is there is none. */ protected tag: Tag | undefined; - /** The value associated with this port. */ + /** The current value associated with this port. */ protected value: T | Absent; - abstract get(): T | undefined; + // abstract get(): T | undefined; public _receiveRuntimeObject(runtime: Runtime) { if (!this.runtime) { @@ -51,12 +53,6 @@ export abstract class Port extends Trigger implements Read } } -enum PortType { - INPUT = 0, - OUTPUT = 1 -} - - /** * Abstract class for a writable port. It is intended as a wrapper for a * regular port. In addition to a get method, it also has a set method and @@ -65,7 +61,12 @@ enum PortType { export abstract class WritablePort implements ReadWrite { abstract get(): T | undefined; abstract set(value: T): void; - abstract getPort(): IOPort + abstract getPort(): IOPort // FIXME: just extend interface instead. +} + +export interface WritableMultiPort extends MultiReadWrite { + getWriters(): Array> + getPorts(): Array> } @@ -76,8 +77,6 @@ interface IOPortManager extends TriggerManager { export abstract class IOPort extends Port { - protected receivers: Set> = new Set(); - /** * Return the value set to this port. Return `Absent` if the connected * output did not have its value set at the current logical time. @@ -182,29 +181,19 @@ export abstract class IOPort extends Port { } }(this) - toString(): string {InMultiPort + toString(): string { return this._getFullyQualifiedName(); } } -export abstract class MultiPort extends Component { - protected runtime!: Runtime; - +export abstract class MultiPort extends NonComposite { + constructor(container: Reactor, readonly width: number) { super(container) } - public abstract channels(): Array> - - public _receiveRuntimeObject(runtime: Runtime) { - if (!this.runtime) { - this.runtime = runtime - } else { - throw new Error("Can only establish link to runtime once. Name: " + this._getFullyQualifiedName()) - } - } - - public static values(ports: Array>): Array { + + public static values(ports: Array>): Array { let values = new Array(ports.length); for (let i = 0; i < values.length; i++) { values[i] = ports[i].get(); @@ -213,10 +202,61 @@ export abstract class MultiPort extends Component { } public abstract values(): Array + + + /** + * Inner class instance to gain access to Write interface. + */ + protected writers = new class implements MultiReadWrite { + + private readonly bla: Array> = new Array(); + + constructor(private port:MultiPort, private container: Reactor | null) { + if (container !== null) { + port.channels().forEach(channel => { + this.bla.push(container.writable(channel)) + }); + } + } + + get(index: number): T | undefined { + return this.port.channel(index).get() + } + + public set(index: number, value: T): void { + this.bla[index].set(value) + } + +}(this, this.getContainer()) + + public abstract channels(): Array> + + public abstract channel(index: number): IOPort + + + /** + * Only the holder of the key may obtain a writable port. + * @param key + */ + public asWritable(key: Symbol | undefined): MultiReadWrite { + if (this._key === key) { + return this.writers + } + throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) } -export class InMultiPort extends MultiPort { +public _receiveRuntimeObject(runtime: Runtime): void { + throw new Error("Method not implemented."); +} + +} +export class InMultiPort extends MultiPort { + + public channel(index: number): IOPort { + return this._channels[index] + } + private _channels: Array> public channels() { @@ -227,7 +267,7 @@ export class InMultiPort extends MultiPort { super(container, width) this._channels = new Array>(width) for (let i = 0; i < width; i++) { - this._channels[i] = new InPort(container) + let port = new InPort(container, this._getName()) } } @@ -236,6 +276,12 @@ export class InMultiPort extends MultiPort { } } export class OutMultiPort extends MultiPort { + public channel(index: number): IOPort { + throw new Error("Method not implemented."); + } + public _receiveRuntimeObject(runtime: Runtime): void { + throw new Error("Method not implemented."); + } private _channels: Array> constructor(container: Reactor, width: number) { diff --git a/src/core/reactor.ts b/src/core/reactor.ts index f625e3c23..c76754a2f 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -12,7 +12,7 @@ import {Component} from "./component" import {Reaction, Priority, Mutation, Procedure} from "./reaction" import { IOPort, MultiPort, Port, WritablePort } from './port'; import { Action, Shutdown, Startup } from './action'; -import { ScheduledTrigger, Trigger, TriggerManager } from './trigger'; +import { NonComposite, ScheduledTrigger, Trigger, TriggerManager } from './trigger'; import { TaggedEvent } from './event'; // Set the default log level. @@ -57,6 +57,8 @@ export type Present = (number | bigint | string | boolean | symbol | object | nu */ export type ReadWrite = Read & Write; +export type MultiReadWrite = MultiRead & MultiWrite; + /** * A variable can be read, written to, or scheduled. Variables may be passed to * reactions in an argument list. @@ -64,7 +66,7 @@ export type ReadWrite = Read & Write; * @see Write * @see Sched */ -export type Variable = Read | Array> | MultiPort +export type Variable = Read | Array> | MultiPort // FIXME(marten): this looks suspicious //--------------------------------------------------------------------------// @@ -85,6 +87,14 @@ export interface Read { get(): T | Absent; } +export interface MultiRead { + get(index: number): T | Absent +} + +export interface MultiWrite { + set: (index: number, value: T) => void; +} + /** * Interface for schedulable actions. */ @@ -417,7 +427,7 @@ export abstract class Reactor extends Component { * @param key The key that verifies the containment relation between this * reactor and the component, with at most one level of indirection. */ - protected _getKey(component: Trigger, key?: Symbol): Symbol | undefined { + protected _getKey(component: NonComposite, key?: Symbol): Symbol | undefined { if (component._isContainedBy(this) || this._key === key) { return this._keyChain.get(component) } else if (!(component instanceof Action) && @@ -560,7 +570,13 @@ export abstract class Reactor extends Component { // return this._active // } - public writable(port: IOPort): ReadWrite { + // + + public allWritable(port: MultiPort): MultiReadWrite { + return port.asWritable(this._getKey(port)); + } + + public writable(port: IOPort): WritablePort { return port.asWritable(this._getKey(port)); } diff --git a/src/core/trigger.ts b/src/core/trigger.ts index 892142e13..ee2a40be6 100644 --- a/src/core/trigger.ts +++ b/src/core/trigger.ts @@ -1,7 +1,7 @@ -import { Absent, Present, Reactor, Runtime } from "./reactor"; import { Component } from "./component"; import { TaggedEvent } from "./event"; import { Reaction } from "./reaction"; +import { Absent, Present, Reactor, Runtime } from "./reactor"; import { Tag } from "./time"; export interface TriggerManager { @@ -12,11 +12,21 @@ export interface TriggerManager { +export abstract class Atom extends Component { + + /** + * Return the owner of this trigger. + */ + public getContainer(): Reactor | null { + return this._getContainer() + } + +} /** * Abstract class for a trigger. A trigger may be an action, port, or timer. */ - export abstract class Trigger extends Component { + export abstract class Trigger extends Atom { /** * Reactions to trigger. @@ -32,12 +42,6 @@ export interface TriggerManager { */ abstract getManager(key: Symbol | undefined): TriggerManager; - /** - * Return the owner of this trigger. - */ - public getContainer(): Reactor | null { - return this._getContainer() - } /** * Return whether or not this trigger is present. @@ -46,6 +50,8 @@ export interface TriggerManager { } + + /** * */ From a92a67ce4423dbe1c2681e83c3afc2cc0df000eb Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 14:32:07 -0800 Subject: [PATCH 29/68] Refactoring --- package-lock.json | 308 ++++++++++------------------------------- package.json | 4 +- src/core/action.ts | 17 ++- src/core/bank.ts | 3 +- src/core/cli.ts | 3 +- src/core/component.ts | 2 +- src/core/container.ts | 11 -- src/core/event.ts | 5 +- src/core/federation.ts | 9 +- src/core/port.ts | 50 ++++--- src/core/reaction.ts | 11 +- src/core/reactor.ts | 117 ++-------------- src/core/state.ts | 2 +- src/core/trigger.ts | 16 ++- 14 files changed, 157 insertions(+), 401 deletions(-) delete mode 100644 src/core/container.ts diff --git a/package-lock.json b/package-lock.json index 41d2ba7b2..6c14b6eb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3386,9 +3386,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", - "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -4416,29 +4416,6 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -5689,9 +5666,9 @@ } }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -6314,6 +6291,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", @@ -9139,15 +9125,6 @@ "node": ">=8" } }, - "node_modules/jest-runtime/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-runtime/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -9157,20 +9134,6 @@ "node": ">=8" } }, - "node_modules/jest-runtime/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-runtime/node_modules/strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -9983,15 +9946,6 @@ "node": ">=8" } }, - "node_modules/jest/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/jest/node_modules/jest-cli": { "version": "27.0.6", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.0.6.tgz", @@ -10026,20 +9980,6 @@ } } }, - "node_modules/jest/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest/node_modules/strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -11847,6 +11787,32 @@ "node": ">=8" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -12747,29 +12713,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -12892,15 +12835,6 @@ "node": ">=8" } }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/yargs/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -12933,32 +12867,6 @@ "engines": { "node": ">=8" } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } } }, "dependencies": { @@ -15439,9 +15347,9 @@ "dev": true }, "acorn": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", - "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true }, "acorn-globals": { @@ -16248,23 +16156,6 @@ "wrap-ansi": "^6.2.0" }, "dependencies": { - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -17258,9 +17149,9 @@ "optional": true }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -17733,6 +17624,12 @@ "dev": true, "optional": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "is-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", @@ -18058,12 +17955,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "jest-cli": { "version": "27.0.6", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.0.6.tgz", @@ -18084,17 +17975,6 @@ "yargs": "^16.0.3" } }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -20048,29 +19928,12 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -22009,6 +21872,28 @@ } } }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -22692,23 +22577,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -22797,12 +22665,6 @@ "path-exists": "^4.0.0" } }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -22826,26 +22688,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } } } }, diff --git a/package.json b/package.json index 8c51e21bf..f7151d82f 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,11 @@ "@types/node": "^16.3.3", "dtslint": "^3.4.2", "jest": "^27.0.6", + "marked": ">=4.0.10", "parsimmon": "1.13.0", "ts-jest": "^27.0.3", "typedoc": "^0.21.4", - "typescript": "^4.3.5", - "marked": ">=4.0.10" + "typescript": "^4.3.5" }, "files": [ "lib" diff --git a/src/core/action.ts b/src/core/action.ts index c07818f76..551d7fdc7 100644 --- a/src/core/action.ts +++ b/src/core/action.ts @@ -1,11 +1,16 @@ -import { TaggedEvent } from "./event"; -import { Absent, Present, Reactor, Read, Sched, SchedulableAction } from "./reactor"; -import { getCurrentPhysicalTime, Origin, Tag, TimeUnit, TimeValue } from "./time"; -import { ScheduledTrigger, TriggerManager } from "./trigger"; -import { Log } from "./util"; +import type {Absent, Present, Read, Sched} from "./internal" +import { + Reactor, Log, TaggedEvent, + getCurrentPhysicalTime, Origin, Tag, TimeUnit, TimeValue, + ScheduledTrigger, TriggerManager +} from "./internal"; const defaultMIT = TimeValue.withUnits(1, TimeUnit.nsec); // FIXME +export abstract class SchedulableAction implements Sched { + abstract get(): T | undefined; + abstract schedule(extraDelay: 0 | TimeValue, value: T, intendedTag?: Tag): void; +} /** * An action denotes a self-scheduled event. @@ -118,8 +123,6 @@ const defaultMIT = TimeValue.withUnits(1, TimeUnit.nsec); // FIXME } } -// FIXME(marten): move these to trigger.ts and let them extend trigger - export class Startup extends Action { // FIXME: this should not be a schedulable trigger, just a trigger constructor(__parent__: Reactor) { super(__parent__, Origin.logical) diff --git a/src/core/bank.ts b/src/core/bank.ts index 9de14b320..02a554e8b 100644 --- a/src/core/bank.ts +++ b/src/core/bank.ts @@ -1,5 +1,4 @@ -import { Port } from './port'; -import { Present, Reactor } from './reactor' +import { Port, Present, Reactor } from './internal'; /** * Type that describes a class with a constructor of which the arguments diff --git a/src/core/cli.ts b/src/core/cli.ts index 945b1816b..7a3c52561 100644 --- a/src/core/cli.ts +++ b/src/core/cli.ts @@ -1,7 +1,6 @@ import commandLineArgs from 'command-line-args'; import commandLineUsage from 'command-line-usage'; -import {TimeUnit, TimeValue} from './time'; -import { LogLevel } from './util'; +import {TimeUnit, TimeValue, LogLevel} from './internal'; //---------------------------------------------------------------------// // Command Line Arguments Helper Functions // diff --git a/src/core/component.ts b/src/core/component.ts index 730aabe1f..6ecc613f1 100644 --- a/src/core/component.ts +++ b/src/core/component.ts @@ -1,4 +1,4 @@ -import {Reactor, App, Runtime} from "./reactor"; +import {Reactor, App, Runtime} from "./internal" /** * Base class for named objects embedded in a hierarchy of reactors. Each diff --git a/src/core/container.ts b/src/core/container.ts deleted file mode 100644 index ea2d6cf32..000000000 --- a/src/core/container.ts +++ /dev/null @@ -1,11 +0,0 @@ -import {Component} from './component'; -import { Reaction } from './reaction'; -import { Port, Present } from './reactor'; -import { DependencyGraph } from './util'; - -export abstract class Container extends Component { - - - - -} \ No newline at end of file diff --git a/src/core/event.ts b/src/core/event.ts index 2d686d76e..1be6c90af 100644 --- a/src/core/event.ts +++ b/src/core/event.ts @@ -1,7 +1,4 @@ -import { Present } from "./reactor"; -import { Tag } from "./time"; -import { ScheduledTrigger } from "./trigger"; -import { PrioritySetElement } from "./util"; +import type { Tag, ScheduledTrigger, Present, PrioritySetElement } from "./internal"; /** * An event is caused by a timer or a scheduled action. Each event is tagged diff --git a/src/core/federation.ts b/src/core/federation.ts index 62e6ee7b1..e98b1b17b 100644 --- a/src/core/federation.ts +++ b/src/core/federation.ts @@ -1,10 +1,9 @@ -import {Log} from './util'; -import {Tag, TimeValue, Origin, getCurrentPhysicalTime, Alarm} from './time'; +import { + Log, Tag, TimeValue, Origin, getCurrentPhysicalTime, Alarm, + Present, App, Action, FederatePortAction, TaggedEvent +} from './internal'; import {Socket, createConnection, SocketConnectOpts} from 'net' import {EventEmitter} from 'events'; -import {Present, App} from './reactor'; -import {Action, FederatePortAction} from './action'; -import {TaggedEvent} from './event'; //---------------------------------------------------------------------// // Federated Execution Constants and Enums // diff --git a/src/core/port.ts b/src/core/port.ts index c00a10111..ff8079a4e 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -1,11 +1,11 @@ -import { Component } from "./component"; -import { Reaction } from "./reaction"; -import { Absent, InPort, MultiReadWrite, OutPort, Present, Reactor, Read, ReadWrite, Runtime } from "./reactor"; -import { Tag } from "./time"; -import { NonComposite, Trigger, TriggerManager } from "./trigger"; -import { Log } from "./util"; +import { + Reaction, Reactor, Runtime, Tag, NonComposite, Trigger, TriggerManager, + Absent, MultiRead, MultiReadWrite, Present, ReadWrite, Log +} from "./internal" +// FIXME(marten): moving these two to port.ts results in a circular import problem with many test files: + export abstract class Port extends Trigger { protected receivers: Set> = new Set(); @@ -187,12 +187,22 @@ export abstract class IOPort extends Port { } -export abstract class MultiPort extends NonComposite { +export class OutPort extends IOPort { + +} + +export class InPort extends IOPort { + +} + +export abstract class MultiPort extends NonComposite implements MultiRead { constructor(container: Reactor, readonly width: number) { super(container) } + abstract get(index: number): T | Absent; + public static values(ports: Array>): Array { let values = new Array(ports.length); for (let i = 0; i < values.length; i++) { @@ -211,15 +221,14 @@ export abstract class MultiPort extends NonComposite { private readonly bla: Array> = new Array(); - constructor(private port:MultiPort, private container: Reactor | null) { - if (container !== null) { - port.channels().forEach(channel => { - this.bla.push(container.writable(channel)) - }); - } - } + constructor(private port: MultiPort) { + port.channels().forEach(channel => { + let writer = port.getContainer()?.writable(channel) + if (writer) this.bla.push(writer) + }); + } - get(index: number): T | undefined { + public get(index: number): T | undefined { return this.port.channel(index).get() } @@ -227,7 +236,7 @@ export abstract class MultiPort extends NonComposite { this.bla[index].set(value) } -}(this, this.getContainer()) +}(this) public abstract channels(): Array> @@ -253,6 +262,10 @@ public _receiveRuntimeObject(runtime: Runtime): void { export class InMultiPort extends MultiPort { + public get(index: number): T | undefined { + return this._channels[index].get() + } + public channel(index: number): IOPort { return this._channels[index] } @@ -276,6 +289,11 @@ export class InMultiPort extends MultiPort { } } export class OutMultiPort extends MultiPort { + + public get(index: number): T | undefined { + return this._channels[index].get() + } + public channel(index: number): IOPort { throw new Error("Method not implemented."); } diff --git a/src/core/reaction.ts b/src/core/reaction.ts index 45506af55..791d71976 100644 --- a/src/core/reaction.ts +++ b/src/core/reaction.ts @@ -1,7 +1,10 @@ -import {Sortable, PrioritySetElement, Log} from "./util" -import {Reactor, ReactionSandbox, Triggers, Args, ArgList, Timer, MutationSandbox} from "./reactor" -import {TimeValue, Tag} from "./time"; -import { Startup } from "./action"; +import { + Sortable, PrioritySetElement, Log, + ReactionSandbox, Timer, MutationSandbox, + Reactor, TimeValue, Tag, + ArgList, Args, Triggers, + Startup +} from "./internal" /** * A number that indicates a reaction's position with respect to other diff --git a/src/core/reactor.ts b/src/core/reactor.ts index c76754a2f..75b4224ed 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -6,69 +6,18 @@ * @author Hokeun Kim (hokeunkim@berkeley.edu) */ -import {PrioritySet, SortableDependencyGraph, Log, DependencyGraph} from './util'; -import {TimeValue, Tag, Origin, getCurrentPhysicalTime, Alarm} from './time'; -import {Component} from "./component" -import {Reaction, Priority, Mutation, Procedure} from "./reaction" -import { IOPort, MultiPort, Port, WritablePort } from './port'; -import { Action, Shutdown, Startup } from './action'; -import { NonComposite, ScheduledTrigger, Trigger, TriggerManager } from './trigger'; -import { TaggedEvent } from './event'; +import { + TimeValue, Tag, Origin, getCurrentPhysicalTime, Alarm, PrioritySet, + SortableDependencyGraph, Log, DependencyGraph, Reaction, Priority, + Mutation, Procedure, Absent, ArgList, Args, MultiReadWrite, Present, + Read, Sched, SchedulableAction, Triggers, Variable, Write, TaggedEvent, + Component, NonComposite, ScheduledTrigger, Trigger, TriggerManager, + Action, InPort, IOPort, MultiPort, OutPort, Port, WritablePort, Startup, Shutdown +} from "./internal" // Set the default log level. Log.global.level = Log.levels.ERROR; -// FIXME(marten): moving these two to port.ts results in a circular import problem with many test files: -export class OutPort extends IOPort { - -} - -export class InPort extends IOPort { - -} - -//--------------------------------------------------------------------------// -// Types // -//--------------------------------------------------------------------------// - -/** - * Type that denotes the absence of a value. - * @see Variable - */ -export type Absent = undefined; - -/** - * Conditional type for argument lists of reactions. If the type variable - * `T` is inferred to be a subtype of `Variable[]` it will yield `T`; it - * will yield `never` if `T` is not a subtype of `Variable[]`. - * @see Reaction - */ -export type ArgList = T extends Variable[] ? T : never; - -export type ParmList = T extends any[] ? T : never; - -/** - * Type for data exchanged between ports. - */ -export type Present = (number | bigint | string | boolean | symbol | object | null); - -/** - * Type for simple variables that are both readable and writable. - */ -export type ReadWrite = Read & Write; - -export type MultiReadWrite = MultiRead & MultiWrite; - -/** - * A variable can be read, written to, or scheduled. Variables may be passed to - * reactions in an argument list. - * @see Read - * @see Write - * @see Sched - */ -export type Variable = Read | Array> | MultiPort // FIXME(marten): this looks suspicious - - //--------------------------------------------------------------------------// // Interfaces // //--------------------------------------------------------------------------// @@ -80,36 +29,6 @@ export interface Call extends Write, Read { invoke(args: A): R | undefined; } -/** - * Interface for readable variables. - */ -export interface Read { - get(): T | Absent; -} - -export interface MultiRead { - get(index: number): T | Absent -} - -export interface MultiWrite { - set: (index: number, value: T) => void; -} - -/** - * Interface for schedulable actions. - */ -export interface Sched extends Read { - schedule: (extraDelay: TimeValue | 0, value: T, intendedTag?: Tag) => void; - // FIXME: it makes sense to be able to check the presence of a (re)schedulable action. -} - -/** - * Interface for writable ports. - */ -export interface Write { - set: (value: T) => void; -} - /** @@ -117,10 +36,7 @@ export interface Write { * regular action. In addition to a get method, it also has a schedule method * that allows for the action to be scheduled. */ -export abstract class SchedulableAction implements Sched { - abstract get(): T | undefined; - abstract schedule(extraDelay: 0 | TimeValue, value: T, intendedTag?: Tag): void; -} + //--------------------------------------------------------------------------// // Core Reactor Classes // @@ -192,21 +108,6 @@ export class Timer extends ScheduledTrigger implements Read { } -export class Args { - tuple: T; - constructor(...args: T) { - this.tuple = args; - } -} - -export class Triggers { - list: Variable[]; - constructor(trigger: Variable, ...triggers: Variable[]) { - this.list = triggers.concat(trigger) - } -} - - /** * A reactor is a software component that reacts to input events, timer events, diff --git a/src/core/state.ts b/src/core/state.ts index 576575969..f6d8aa015 100644 --- a/src/core/state.ts +++ b/src/core/state.ts @@ -1,4 +1,4 @@ -import { Read, Write } from "./reactor"; +import { Read, Write } from "./internal"; /** * A state variable. This class refines the Read interface by letting `get` diff --git a/src/core/trigger.ts b/src/core/trigger.ts index ee2a40be6..1dc33c530 100644 --- a/src/core/trigger.ts +++ b/src/core/trigger.ts @@ -1,8 +1,10 @@ import { Component } from "./component"; -import { TaggedEvent } from "./event"; +import type { TaggedEvent } from "./event"; import { Reaction } from "./reaction"; -import { Absent, Present, Reactor, Runtime } from "./reactor"; -import { Tag } from "./time"; +import { Origin, Tag } from "./time"; + +import type { Absent, Present } from "./types"; +import type { Reactor, Runtime } from "./reactor"; export interface TriggerManager { getContainer(): Reactor; @@ -12,7 +14,7 @@ export interface TriggerManager { -export abstract class Atom extends Component { +export abstract class NonComposite extends Component { /** * Return the owner of this trigger. @@ -26,7 +28,7 @@ export abstract class Atom extends Component { /** * Abstract class for a trigger. A trigger may be an action, port, or timer. */ - export abstract class Trigger extends Atom { + export abstract class Trigger extends NonComposite { /** * Reactions to trigger. @@ -134,3 +136,7 @@ export abstract class Atom extends Component { } +// FIXME(marten): move these to trigger.ts and let them extend trigger + + + From c4b57488591126b1bbc555467ecf4f3ea8526a97 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 14:32:58 -0800 Subject: [PATCH 30/68] Add missing files --- src/core/internal.ts | 12 ++++++ src/core/types.ts | 90 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 src/core/internal.ts create mode 100644 src/core/types.ts diff --git a/src/core/internal.ts b/src/core/internal.ts new file mode 100644 index 000000000..5240f46dd --- /dev/null +++ b/src/core/internal.ts @@ -0,0 +1,12 @@ +export * from "./types" +export * from "./time" +export * from "./util" +export * from "./reaction" +export * from "./component" +export * from "./trigger"; +export * from "./action" +export * from "./port"; +export * from "./reactor" +export * from "./event"; +export * from "./federation" +export * from "./cli" diff --git a/src/core/types.ts b/src/core/types.ts new file mode 100644 index 000000000..e61b89c26 --- /dev/null +++ b/src/core/types.ts @@ -0,0 +1,90 @@ + +import { Tag, TimeValue } from "./time"; + +/** + * A variable can be read, written to, or scheduled. Variables may be passed to + * reactions in an argument list. + * @see Read + * @see Write + * @see Sched + */ + export type Variable = Read | MultiRead + +/** + * Interface for writable ports. + */ + export interface Write { + set: (value: T) => void; +} + +/** + * Type for simple variables that are both readable and writable. + */ + export type ReadWrite = Read & Write; + + export type MultiReadWrite = MultiRead & MultiWrite; + + /** + * Interface for readable variables. + */ + export interface Read { + get(): T | Absent; + } + + export interface MultiRead { + get(index: number): T | Absent + } + + export interface MultiWrite { + set: (index: number, value: T) => void; + } + + //--------------------------------------------------------------------------// +// Types // +//--------------------------------------------------------------------------// + +/** + * Type that denotes the absence of a value. + * @see Variable + */ +export type Absent = undefined; + +/** + * Conditional type for argument lists of reactions. If the type variable + * `T` is inferred to be a subtype of `Variable[]` it will yield `T`; it + * will yield `never` if `T` is not a subtype of `Variable[]`. + * @see Reaction + */ +export type ArgList = T extends Variable[] ? T : never; + +export type ParmList = T extends any[] ? T : never; + +/** + * Type for data exchanged between ports. + */ +export type Present = (number | bigint | string | boolean | symbol | object | null); + + +export class Args { + tuple: T; + constructor(...args: T) { + this.tuple = args; + } +} + +export class Triggers { + list: Variable[]; + constructor(trigger: Variable, ...triggers: Variable[]) { + this.list = triggers.concat(trigger) + } +} + + +/** + * Interface for schedulable actions. + */ + export interface Sched extends Read { + schedule: (extraDelay: TimeValue | 0, value: T, intendedTag?: Tag) => void; + // FIXME: it makes sense to be able to check the presence of a (re)schedulable action. +} + From 87cd681c631f7761ffa3f0bf2ba01ad550048f35 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 23 Feb 2022 14:46:00 -0800 Subject: [PATCH 31/68] Fix type error in federation.ts. --- src/core/federation.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/federation.ts b/src/core/federation.ts index e98b1b17b..ee9877b52 100644 --- a/src/core/federation.ts +++ b/src/core/federation.ts @@ -372,7 +372,7 @@ class RTIClient extends EventEmitter { try { this.socket?.write(msg); } catch (e) { - Log.error(this, () => {return e}); + Log.error(this, () => {return `${e}`}); } } @@ -383,7 +383,7 @@ class RTIClient extends EventEmitter { try { this.socket?.write(msg); } catch (e) { - Log.error(this, () => {return e}); + Log.error(this, () => {return `${e}`}); } } @@ -406,7 +406,7 @@ class RTIClient extends EventEmitter { Log.debug(this, () => {return `Sending RTI start time: ${myPhysicalTime}`}); this.socket?.write(msg); } catch (e) { - Log.error(this, () => {return e}); + Log.error(this, () => {return `${e}`}); } } @@ -431,7 +431,7 @@ class RTIClient extends EventEmitter { + `federate ID: ${destFederateID} and port ID: ${destPortID}.`}); this.socket?.write(msg); } catch (e) { - Log.error(this, () => {return e}); + Log.error(this, () => {return `${e}`}); } } @@ -461,7 +461,7 @@ class RTIClient extends EventEmitter { + `, time: ${time.toString('hex')}.`}); this.socket?.write(msg); } catch (e) { - Log.error(this, () => {return e}); + Log.error(this, () => {return `${e}`}); } } @@ -482,7 +482,7 @@ class RTIClient extends EventEmitter { Log.debug(this, () => {return "Sending RTI logical time complete: " + completeTime.toString('hex');}); this.socket?.write(msg); } catch (e) { - Log.error(this, () => {return e}); + Log.error(this, () => {return `${e}`}); } } @@ -497,7 +497,7 @@ class RTIClient extends EventEmitter { Log.debug(this, () => {return "Sending RTI resign.";}); this.socket?.write(msg); } catch (e) { - Log.error(this, () => {return e}); + Log.error(this, () => {return `${e}`}); } } @@ -517,7 +517,7 @@ class RTIClient extends EventEmitter { Log.debug(this, () => {return "Sending RTI Next Event Time.";}); this.socket?.write(msg); } catch (e) { - Log.error(this, () => {return e}); + Log.error(this, () => {return `${e}`}); } } From b164f428a5d044385ee671b97d8755d77b909e7a Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 14:47:11 -0800 Subject: [PATCH 32/68] Quickfix to allow test to run --- src/core/port.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/core/port.ts b/src/core/port.ts index ff8079a4e..98804333e 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -222,10 +222,11 @@ export abstract class MultiPort extends NonComposite implemen private readonly bla: Array> = new Array(); constructor(private port: MultiPort) { - port.channels().forEach(channel => { - let writer = port.getContainer()?.writable(channel) - if (writer) this.bla.push(writer) - }); + // FIXME: won't work because channels have not been created yet + // port.channels().forEach(channel => { + // let writer = port.getContainer()?.writable(channel) + // if (writer) this.bla.push(writer) + // }); } public get(index: number): T | undefined { @@ -233,7 +234,8 @@ export abstract class MultiPort extends NonComposite implemen } public set(index: number, value: T): void { - this.bla[index].set(value) + this.port.getContainer()?.writable(this.port.channel(index)).set(value) + //this.bla[index].set(value) } }(this) From f535ce06eb718bcffdd8ba99a55f75d810f032be Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 14:51:31 -0800 Subject: [PATCH 33/68] Further use of "internal" pattern --- src/core/trigger.ts | 8 ++------ src/core/types.ts | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/core/trigger.ts b/src/core/trigger.ts index 1dc33c530..2ea1402f2 100644 --- a/src/core/trigger.ts +++ b/src/core/trigger.ts @@ -1,10 +1,6 @@ -import { Component } from "./component"; -import type { TaggedEvent } from "./event"; -import { Reaction } from "./reaction"; -import { Origin, Tag } from "./time"; +import { Component, TaggedEvent, Reaction, Tag } from "./internal"; -import type { Absent, Present } from "./types"; -import type { Reactor, Runtime } from "./reactor"; +import type { Reactor, Runtime, Absent, Present } from "./internal"; export interface TriggerManager { getContainer(): Reactor; diff --git a/src/core/types.ts b/src/core/types.ts index e61b89c26..8d12a12ea 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -1,5 +1,5 @@ -import { Tag, TimeValue } from "./time"; +import { Tag, TimeValue } from "./internal"; /** * A variable can be read, written to, or scheduled. Variables may be passed to From 4aefa8e3fd7f18388bbc052ae4879c8a5a098329 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Wed, 23 Feb 2022 14:59:28 -0800 Subject: [PATCH 34/68] Fix type error in federation.ts. --- src/core/federation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/federation.ts b/src/core/federation.ts index ee9877b52..e22c63742 100644 --- a/src/core/federation.ts +++ b/src/core/federation.ts @@ -312,7 +312,7 @@ class RTIClient extends EventEmitter { this.socket?.write(buffer); this.socket?.write(this.federationID); } catch (e) { - Log.error(this, () => {return e.toString()}); + Log.error(this, () => {return `${e}`}); } // Finally, emit a connected event. From c075ac7fd504a695c31d861bb1cd7381f3873e8b Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 15:13:18 -0800 Subject: [PATCH 35/68] add missing file --- __tests__/ActionTrigger.test.ts | 5 +-- __tests__/Adder.test.ts | 3 +- __tests__/alarm.ts | 2 +- __tests__/bank.ts | 4 +-- __tests__/multiport.test.ts | 54 +++++++++++++++++++++++++++++++++ __tests__/mutations.test.ts | 3 +- src/core/internal.ts | 1 + src/core/port.ts | 4 ++- 8 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 __tests__/multiport.test.ts diff --git a/__tests__/ActionTrigger.test.ts b/__tests__/ActionTrigger.test.ts index 91e01bf6c..7df66f8ea 100644 --- a/__tests__/ActionTrigger.test.ts +++ b/__tests__/ActionTrigger.test.ts @@ -1,7 +1,4 @@ -import {App, Triggers, Args} from '../src/core/reactor'; -import {Origin, TimeValue} from '../src/core/time'; -import {Reactor, Timer, Sched} from '../src/core/reactor'; -import { Action } from '../src/core/action'; +import {App, Triggers, Args, Origin, TimeValue, Reactor, Timer, Sched, Action} from '../src/core/internal'; //Upon initialization, this reactor should produce an //output event diff --git a/__tests__/Adder.test.ts b/__tests__/Adder.test.ts index 83151aee4..500709f0e 100644 --- a/__tests__/Adder.test.ts +++ b/__tests__/Adder.test.ts @@ -1,6 +1,5 @@ -import { IOPort} from '../src/core/port'; -import {App, Reactor, Present, Args, Triggers, InPort, OutPort} from '../src/core/reactor'; +import { IOPort, App, Reactor, Present, Args, Triggers, InPort, OutPort} from '../src/core/internal'; export class Adder extends Reactor { diff --git a/__tests__/alarm.ts b/__tests__/alarm.ts index 17134a932..77ec2b642 100644 --- a/__tests__/alarm.ts +++ b/__tests__/alarm.ts @@ -1,4 +1,4 @@ -import { TimeValue, TimeUnit, Alarm } from '../src/core/time'; +import { TimeValue, TimeUnit, Alarm } from '../src/core/internal'; import NanoTimer from 'nanotimer'; var timerA = new NanoTimer(); diff --git a/__tests__/bank.ts b/__tests__/bank.ts index 86716736b..5f6a91446 100644 --- a/__tests__/bank.ts +++ b/__tests__/bank.ts @@ -1,6 +1,4 @@ -import { Bank } from "../src/core/bank"; -import { Reactor, App, Timer, Triggers, Args, Present, OutPort, InPort} from "../src/core/reactor"; -import { TimeValue } from "../src/core/time"; +import { Bank , Reactor, App, Timer, Triggers, Args, Present, OutPort, InPort, TimeValue } from "../src/core/internal"; class Periodic extends Reactor { diff --git a/__tests__/multiport.test.ts b/__tests__/multiport.test.ts new file mode 100644 index 000000000..8e9594fb8 --- /dev/null +++ b/__tests__/multiport.test.ts @@ -0,0 +1,54 @@ +import { Args, Triggers, Reactor, App, InMultiPort, InPort, OutMultiPort } from "../src/core/internal"; + +//describe('Connect two multiports and check values', () => { + + class TwoInTwoOut extends Reactor { + inp = new InMultiPort(this, 2); + out = new OutMultiPort(this, 2); + + constructor(parent: Reactor) { + super(parent) + this.addReaction( + new Triggers(this.inp), + new Args(this.inp, this.allWritable(this.out)), + function (this, inp) { + test('check values', () => { + expect(inp.channel(0).get()).toBe(42); + expect(inp.channel(1).get()).toBe(69); + }); + console.log("Channel 0:" + inp.channel(0).get()) + console.log("Channel 1:" + inp.channel(1).get()) + } + ); + this.addReaction( + new Triggers(this.startup), + new Args(this.allWritable(this.out)), + function (out) { + test('start up reaction triggered', () => { + expect(true).toBe(true); + }); + out.set(0, 42) + out.set(0, 69) + } + ); + } + } + + + class myApp extends App { + port: InPort = new InPort(this); + + x = new TwoInTwoOut(this); + y = new TwoInTwoOut(this); + + constructor() { + super(); + this._connectMulti([this.x.out], [this.y.inp], false) + } + + } + + var app = new myApp(); + app._start() + +//}); diff --git a/__tests__/mutations.test.ts b/__tests__/mutations.test.ts index adc904f81..71e4b1060 100644 --- a/__tests__/mutations.test.ts +++ b/__tests__/mutations.test.ts @@ -1,5 +1,4 @@ -import {Reactor, App, Triggers, Args, Timer, OutPort, InPort} from '../src/core/reactor'; -import {TimeValue} from '../src/core/time'; +import {Reactor, App, Triggers, Args, Timer, OutPort, InPort, TimeValue} from '../src/core/internal'; class Source extends Reactor { diff --git a/src/core/internal.ts b/src/core/internal.ts index 5240f46dd..8fccf756b 100644 --- a/src/core/internal.ts +++ b/src/core/internal.ts @@ -10,3 +10,4 @@ export * from "./reactor" export * from "./event"; export * from "./federation" export * from "./cli" +export * from "./bank" \ No newline at end of file diff --git a/src/core/port.ts b/src/core/port.ts index 98804333e..1af68ce79 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -297,11 +297,13 @@ export class OutMultiPort extends MultiPort { } public channel(index: number): IOPort { - throw new Error("Method not implemented."); + return this._channels[index] } + public _receiveRuntimeObject(runtime: Runtime): void { throw new Error("Method not implemented."); } + private _channels: Array> constructor(container: Reactor, width: number) { From a20a7ba4984c47c52dc6a26c6cf832f964cbd7c3 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 15:28:39 -0800 Subject: [PATCH 36/68] All tests except one passing again --- __tests__/Clock.test.ts | 4 +--- __tests__/HierarchicalSingleEvent.test.ts | 4 ++-- __tests__/Logger.test.ts | 3 +-- __tests__/OutputEvent.test.ts | 3 +-- __tests__/OutputGet.test.ts | 4 +--- __tests__/connection.test.ts | 4 +--- __tests__/dependencies.ts | 5 ++--- __tests__/hierarchy.ts | 2 +- __tests__/reactors.errors.test.ts | 9 ++++----- __tests__/reactors.test.ts | 6 +----- __tests__/simple.ts | 2 +- src/core/internal.ts | 3 ++- src/share/Logger.ts | 4 ++-- src/share/SingleEvent.ts | 2 +- 14 files changed, 21 insertions(+), 34 deletions(-) diff --git a/__tests__/Clock.test.ts b/__tests__/Clock.test.ts index 092bfb283..1f2fb7bc4 100644 --- a/__tests__/Clock.test.ts +++ b/__tests__/Clock.test.ts @@ -1,8 +1,6 @@ 'use strict'; -import { Action } from '../src/core/action'; -import {Timer, App, Sched, Triggers, Args} from '../src/core/reactor'; -import {TimeValue, TimeUnit, Origin} from "../src/core/time" +import { Action,Timer, App, Sched, Triggers, Args,TimeValue, TimeUnit, Origin } from '../src/core/internal'; /** * This app tests simultaneous events. diff --git a/__tests__/HierarchicalSingleEvent.test.ts b/__tests__/HierarchicalSingleEvent.test.ts index ceb49c0ad..67768b4db 100644 --- a/__tests__/HierarchicalSingleEvent.test.ts +++ b/__tests__/HierarchicalSingleEvent.test.ts @@ -1,5 +1,5 @@ -import {Reactor, App, Parameter, OutPort, InPort} from '../src/core/reactor'; -import {TimeValue} from "../src/core/time" +import {Reactor, App, Parameter, OutPort, InPort, TimeValue} from '../src/core/internal'; + import {SingleEvent} from '../src/share/SingleEvent'; import {Logger} from '../src/share/Logger'; diff --git a/__tests__/Logger.test.ts b/__tests__/Logger.test.ts index a556ea80e..1d899d653 100644 --- a/__tests__/Logger.test.ts +++ b/__tests__/Logger.test.ts @@ -1,6 +1,5 @@ import {Logger} from '../src/share/Logger' -import {Reactor, App} from '../src/core/reactor'; -import { Log, LogLevel } from '../src/core/util' +import {Reactor, App, Log, LogLevel} from '../src/core/internal'; const _reactor:Reactor = new App() const lg:Logger = new Logger(_reactor , 10) diff --git a/__tests__/OutputEvent.test.ts b/__tests__/OutputEvent.test.ts index 943e1b5d4..bbc8241c6 100644 --- a/__tests__/OutputEvent.test.ts +++ b/__tests__/OutputEvent.test.ts @@ -1,5 +1,4 @@ -import {App, Reactor, Parameter, Args, Triggers} from '../src/core/reactor'; -import {TimeValue} from "../src/core/time" +import {App, Reactor, Parameter, Args, Triggers, TimeValue} from '../src/core/internal'; import {SingleEvent} from '../src/share/SingleEvent'; /** diff --git a/__tests__/OutputGet.test.ts b/__tests__/OutputGet.test.ts index 83c39ed28..234613114 100644 --- a/__tests__/OutputGet.test.ts +++ b/__tests__/OutputGet.test.ts @@ -1,7 +1,5 @@ -import {App, Timer, Write, Triggers, Args, OutPort} from '../src/core/reactor'; -import {TimeValue} from "../src/core/time"; -import { Log } from '../src/core/util'; +import {App, Timer, Write, Triggers, Args, OutPort, TimeValue, Log} from '../src/core/internal'; class OutputGetTest extends App { diff --git a/__tests__/connection.test.ts b/__tests__/connection.test.ts index 257ebc12f..2ae0cf62d 100644 --- a/__tests__/connection.test.ts +++ b/__tests__/connection.test.ts @@ -1,6 +1,4 @@ -import { Reactor, App, Triggers, Args, Timer, OutPort, InPort } from '../src/core/reactor'; -import { State } from '../src/core/state'; -import { TimeUnit, TimeValue } from '../src/core/time'; +import { Reactor, App, Triggers, Args, State, OutPort, InPort, TimeUnit, TimeValue } from '../src/core/internal'; describe('Check canConnect', () => { class Source extends Reactor { diff --git a/__tests__/dependencies.ts b/__tests__/dependencies.ts index 2b931a918..7fb65384f 100644 --- a/__tests__/dependencies.ts +++ b/__tests__/dependencies.ts @@ -1,6 +1,5 @@ -import {SortableDependencyGraph, Sortable, PrioritySet, Log} from '../src/core/util'; -import {Reactor, App, Triggers, Args, InPort} from '../src/core/reactor'; -import {Reaction, Priority} from "../src/core/reaction" +import {Reactor, App, Triggers, Args, InPort, Reaction, Priority, + SortableDependencyGraph, Sortable, PrioritySet, Log} from '../src/core/internal'; //Log.setGlobalLevel(Log.levels.DEBUG); diff --git a/__tests__/hierarchy.ts b/__tests__/hierarchy.ts index ffd7f7372..e9534665b 100644 --- a/__tests__/hierarchy.ts +++ b/__tests__/hierarchy.ts @@ -1,4 +1,4 @@ -import {Reactor, App, InPort, OutPort} from '../src/core/reactor'; +import {Reactor, App, InPort, OutPort} from '../src/core/internal'; var app = new App(); diff --git a/__tests__/reactors.errors.test.ts b/__tests__/reactors.errors.test.ts index 5e38c90f8..93da0af0b 100644 --- a/__tests__/reactors.errors.test.ts +++ b/__tests__/reactors.errors.test.ts @@ -1,8 +1,7 @@ -import {Reactor, App, Triggers, Args, CalleePort, CallerPort, Present, InPort} from '../src/core/reactor'; -import {TimeUnit, TimeValue} from '../src/core/time'; -import { Log, LogLevel, SortableDependencyGraph, Sortable } from '../src/core/util'; -import { writer } from 'repl'; -import { doesNotMatch } from 'assert'; +import { + Reactor, App, Triggers, Args, CalleePort, CallerPort, Present, InPort, + TimeUnit, TimeValue, Log, LogLevel +} from '../src/core/internal'; class R extends Reactor { diff --git a/__tests__/reactors.test.ts b/__tests__/reactors.test.ts index c8fc802c1..fa3ae082b 100644 --- a/__tests__/reactors.test.ts +++ b/__tests__/reactors.test.ts @@ -1,8 +1,4 @@ -import {Reactor, App, Triggers, Args, Timer, OutPort, InPort} from '../src/core/reactor'; -import {TimeUnit, TimeValue, Origin } from '../src/core/time'; -import { Log, LogLevel, SortableDependencyGraph, Sortable } from '../src/core/util'; -import { doesNotMatch } from 'assert'; -import { Action } from '../src/core/action'; +import { Reactor, App, Triggers, Args, Timer, OutPort, InPort, TimeUnit, TimeValue, Origin, Log, LogLevel, Action } from '../src/core/internal'; /* Set a port in startup to get thing going */ class Starter extends Reactor { diff --git a/__tests__/simple.ts b/__tests__/simple.ts index d3a9f47cc..4fbd1f137 100644 --- a/__tests__/simple.ts +++ b/__tests__/simple.ts @@ -1,4 +1,4 @@ -import {Reactor, App, InPort, OutPort} from "../src/core/reactor"; +import {Reactor, App, InPort, OutPort} from "../src/core/internal"; class MyActor extends Reactor { diff --git a/src/core/internal.ts b/src/core/internal.ts index 8fccf756b..b0246cd6d 100644 --- a/src/core/internal.ts +++ b/src/core/internal.ts @@ -5,9 +5,10 @@ export * from "./reaction" export * from "./component" export * from "./trigger"; export * from "./action" +export * from "./state" export * from "./port"; export * from "./reactor" +export * from "./bank" export * from "./event"; export * from "./federation" export * from "./cli" -export * from "./bank" \ No newline at end of file diff --git a/src/share/Logger.ts b/src/share/Logger.ts index 6765decc1..8703b5f84 100644 --- a/src/share/Logger.ts +++ b/src/share/Logger.ts @@ -1,5 +1,5 @@ -import {Reactor, Read, Triggers, Args, Present, ReactionSandbox, InPort} from '../core/reactor'; -import { State } from '../core/state'; +import {Reactor, Read, Triggers, Args, Present, ReactionSandbox, InPort,State} from '../core/internal'; + function print(this:ReactionSandbox, i: Read, expected: State) { const received = i.get(); diff --git a/src/share/SingleEvent.ts b/src/share/SingleEvent.ts index 87b7fbf70..89349389b 100644 --- a/src/share/SingleEvent.ts +++ b/src/share/SingleEvent.ts @@ -1,5 +1,5 @@ -import {Reactor, Timer, Write, Triggers, Args, ArgList, ReactionSandbox, Present, Parameter, Variable, OutPort} from '../core/reactor'; +import {Reactor, Timer, Write, Triggers, Args, ArgList, ReactionSandbox, Present, Parameter, OutPort} from '../core/internal'; function produceOutput(this: ReactionSandbox, o: Write, payload:Parameter) { o.set(payload.get()); From aa10a906c60bb716568cdad02901dccaef58204e Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 17:05:44 -0800 Subject: [PATCH 37/68] Fixed bug --- __tests__/multiport.test.ts | 12 +-- src/core/internal.ts | 6 +- src/core/port.ts | 162 +++++++++++++++++++++++------------- src/core/reactor.ts | 20 ++++- 4 files changed, 129 insertions(+), 71 deletions(-) diff --git a/__tests__/multiport.test.ts b/__tests__/multiport.test.ts index 8e9594fb8..274bc3476 100644 --- a/__tests__/multiport.test.ts +++ b/__tests__/multiport.test.ts @@ -9,9 +9,10 @@ import { Args, Triggers, Reactor, App, InMultiPort, InPort, OutMultiPort } from constructor(parent: Reactor) { super(parent) this.addReaction( - new Triggers(this.inp), - new Args(this.inp, this.allWritable(this.out)), + new Triggers(this.inp.channel(0), this.inp.channel(1)), + new Args(this.inp), function (this, inp) { + console.log("Getting triggered") test('check values', () => { expect(inp.channel(0).get()).toBe(42); expect(inp.channel(1).get()).toBe(69); @@ -36,14 +37,15 @@ import { Args, Triggers, Reactor, App, InMultiPort, InPort, OutMultiPort } from class myApp extends App { - port: InPort = new InPort(this); - + x = new TwoInTwoOut(this); y = new TwoInTwoOut(this); constructor() { super(); - this._connectMulti([this.x.out], [this.y.inp], false) + //this._connectMulti([this.x.out], [this.y.inp], false) + this._connect(this.x.out.channel(0), this.y.inp.channel(0)) + this._connect(this.x.out.channel(1), this.y.inp.channel(1)) } } diff --git a/src/core/internal.ts b/src/core/internal.ts index b0246cd6d..fb8cd4d59 100644 --- a/src/core/internal.ts +++ b/src/core/internal.ts @@ -3,12 +3,12 @@ export * from "./time" export * from "./util" export * from "./reaction" export * from "./component" -export * from "./trigger"; +export * from "./trigger" export * from "./action" export * from "./state" -export * from "./port"; +export * from "./port" export * from "./reactor" export * from "./bank" -export * from "./event"; +export * from "./event" export * from "./federation" export * from "./cli" diff --git a/src/core/port.ts b/src/core/port.ts index 1af68ce79..06081c9b1 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -117,7 +117,7 @@ export abstract class IOPort extends Port { * Inner class instance to gain access to Write interface. */ protected writer = new class extends WritablePort { - constructor(private port:IOPort) { + constructor(private port: IOPort) { super() } @@ -138,18 +138,18 @@ export abstract class IOPort extends Port { public getPort(): IOPort { return this.port } - + public toString(): string { return this.port.toString() } - + }(this) /** * Inner class instance to let the container configure this port. */ - protected manager:IOPortManager = new class implements IOPortManager { - constructor(private port:IOPort) {} + protected manager: IOPortManager = new class implements IOPortManager { + constructor(private port: IOPort) { } getContainer(): Reactor { return this.port._getContainer() } @@ -196,13 +196,13 @@ export class InPort extends IOPort { } export abstract class MultiPort extends NonComposite implements MultiRead { - + constructor(container: Reactor, readonly width: number) { super(container) } - + abstract get(index: number): T | Absent; - + public static values(ports: Array>): Array { let values = new Array(ports.length); for (let i = 0; i < values.length; i++) { @@ -213,65 +213,28 @@ export abstract class MultiPort extends NonComposite implemen public abstract values(): Array - - /** - * Inner class instance to gain access to Write interface. - */ - protected writers = new class implements MultiReadWrite { - - private readonly bla: Array> = new Array(); - - constructor(private port: MultiPort) { - // FIXME: won't work because channels have not been created yet - // port.channels().forEach(channel => { - // let writer = port.getContainer()?.writable(channel) - // if (writer) this.bla.push(writer) - // }); - } - - public get(index: number): T | undefined { - return this.port.channel(index).get() - } - - public set(index: number, value: T): void { - this.port.getContainer()?.writable(this.port.channel(index)).set(value) - //this.bla[index].set(value) - } - -}(this) - - public abstract channels(): Array> - - public abstract channel(index: number): IOPort - - /** * Only the holder of the key may obtain a writable port. * @param key */ - public asWritable(key: Symbol | undefined): MultiReadWrite { - if (this._key === key) { - return this.writers - } - throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) -} + public abstract asWritable(key: Symbol | undefined): MultiReadWrite; -public _receiveRuntimeObject(runtime: Runtime): void { - throw new Error("Method not implemented."); -} + public _receiveRuntimeObject(runtime: Runtime): void { + throw new Error("Method not implemented."); + } } export class InMultiPort extends MultiPort { - + public get(index: number): T | undefined { return this._channels[index].get() } - - public channel(index: number): IOPort { + + public channel(index: number): InPort { return this._channels[index] } - + private _channels: Array> public channels() { @@ -282,43 +245,122 @@ export class InMultiPort extends MultiPort { super(container, width) this._channels = new Array>(width) for (let i = 0; i < width; i++) { - let port = new InPort(container, this._getName()) + this._channels[i] = new InPort(container, this._getName() + "[" + i + "]") } } public values(): Array { return MultiPort.values(this._channels) } + + /** + * Inner class instance to gain access to Write interface. + */ + protected writers = new class implements MultiReadWrite { + + private readonly bla: Array> = new Array(); + + constructor(private port: InMultiPort) { + // FIXME: won't work because channels have not been created yet + // port.channels().forEach(channel => { + // let writer = port.getContainer()?.writable(channel) + // if (writer) this.bla.push(writer) + // }); + } + + public get(index: number): T | undefined { + return this.port.channel(index).get() + } + + public set(index: number, value: T): void { + console.log("Container: " + this.port.getContainer()) + this.port.getContainer()?.writable(this.port.channel(index)).set(value) + //this.bla[index].set(value) + } + + + + }(this) + + + /** + * Only the holder of the key may obtain a writable port. + * @param key + */ + public asWritable(key: Symbol | undefined): MultiReadWrite { + if (this._key === key) { + return this.writers + } + throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) + } } export class OutMultiPort extends MultiPort { public get(index: number): T | undefined { return this._channels[index].get() } - - public channel(index: number): IOPort { + + public channel(index: number): OutPort { return this._channels[index] } public _receiveRuntimeObject(runtime: Runtime): void { throw new Error("Method not implemented."); } - + private _channels: Array> constructor(container: Reactor, width: number) { super(container, width) - this._channels = new Array>(width) + this._channels = new Array>(width) for (let i = 0; i < width; i++) { - this._channels[i] = new InPort(container) + this._channels[i] = new OutPort(container, this._getName() + "[" + i + "]") } } public values(): Array { return MultiPort.values(this._channels) } - + public channels() { return this._channels } + + /** +* Inner class instance to gain access to Write interface. +*/ + protected writers = new class implements MultiReadWrite { + + private readonly bla: Array> = new Array(); + + constructor(private port: OutMultiPort) { + // FIXME: won't work because channels have not been created yet + // port.channels().forEach(channel => { + // let writer = port.getContainer()?.writable(channel) + // if (writer) this.bla.push(writer) + // }); + } + + public get(index: number): T | undefined { + return this.port.channel(index).get() + } + + public set(index: number, value: T): void { + console.log("Container: " + this.port.getContainer()) + this.port.getContainer()?.writable(this.port.channel(index)).set(value) + //this.bla[index].set(value) + } + + }(this) + + /** + * Only the holder of the key may obtain a writable port. + * @param key + */ + public asWritable(key: Symbol | undefined): MultiReadWrite { + if (this._key === key) { + return this.writers + } + throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) + } } diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 75b4224ed..3b17980f4 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -14,6 +14,7 @@ import { Component, NonComposite, ScheduledTrigger, Trigger, TriggerManager, Action, InPort, IOPort, MultiPort, OutPort, Port, WritablePort, Startup, Shutdown } from "./internal" +import { InMultiPort, OutMultiPort } from "./port"; // Set the default log level. Log.global.level = Log.levels.ERROR; @@ -524,6 +525,13 @@ export abstract class Reactor extends Component { if (t instanceof Trigger) { t.getManager(this._getKey(t)).addReaction(reaction) } + if (t instanceof MultiPort) { + console.log("Encountered multiport!! >>>>>>>>>-----") + let key = this._getKey(t) + if (t instanceof InMultiPort || t instanceof OutMultiPort) { + t.channels().forEach(channel => channel.getManager(key).addReaction(reaction)) + } + } // Also record this trigger as a dependency. if (t instanceof IOPort) { @@ -925,7 +933,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { if (deps != undefined && deps.size > 0) { return false; } - + return this._isInScope(src, dst) } else { @@ -1033,6 +1041,12 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { * @param dst The destination port to connect. */ protected _connect(src: IOPort, dst:IOPort) { + if (src === undefined || src === null) { + throw new Error("Cannot connect unspecified source"); + } + if (dst === undefined || dst === null) { + throw new Error("Cannot connect unspecified destination"); + } if (this.canConnect(src, dst)) { this._uncheckedConnect(src, dst); } else { @@ -1049,7 +1063,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { // TODO(hokeun): Check if the multiport's container is Bank when Bank is implemented. src.forEach(port => { - if (port instanceof MultiPort) { + if (port instanceof InMultiPort || port instanceof OutMultiPort) { port.channels().forEach(singlePort => { leftPorts.push(singlePort) }) @@ -1059,7 +1073,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { }) dest.forEach(port => { - if (port instanceof MultiPort) { + if (port instanceof InMultiPort || port instanceof OutMultiPort) { port.channels().forEach(singlePort => { rightPorts.push(singlePort) }) From 1daf7060e17be0ec4977c0dd337e911a07d40265 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 17:09:06 -0800 Subject: [PATCH 38/68] Multiport test now passes but using regular _connect --- __tests__/multiport.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/multiport.test.ts b/__tests__/multiport.test.ts index 274bc3476..1f63cab69 100644 --- a/__tests__/multiport.test.ts +++ b/__tests__/multiport.test.ts @@ -29,7 +29,7 @@ import { Args, Triggers, Reactor, App, InMultiPort, InPort, OutMultiPort } from expect(true).toBe(true); }); out.set(0, 42) - out.set(0, 69) + out.set(1, 69) } ); } From 6883e983b816ab73b115a40e7757369ad145f175 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 17:11:28 -0800 Subject: [PATCH 39/68] Test now passes with _connectMulti --- __tests__/multiport.test.ts | 8 ++++---- src/core/reactor.ts | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/__tests__/multiport.test.ts b/__tests__/multiport.test.ts index 1f63cab69..60db71d1f 100644 --- a/__tests__/multiport.test.ts +++ b/__tests__/multiport.test.ts @@ -9,7 +9,7 @@ import { Args, Triggers, Reactor, App, InMultiPort, InPort, OutMultiPort } from constructor(parent: Reactor) { super(parent) this.addReaction( - new Triggers(this.inp.channel(0), this.inp.channel(1)), + new Triggers(this.inp), new Args(this.inp), function (this, inp) { console.log("Getting triggered") @@ -43,9 +43,9 @@ import { Args, Triggers, Reactor, App, InMultiPort, InPort, OutMultiPort } from constructor() { super(); - //this._connectMulti([this.x.out], [this.y.inp], false) - this._connect(this.x.out.channel(0), this.y.inp.channel(0)) - this._connect(this.x.out.channel(1), this.y.inp.channel(1)) + this._connectMulti([this.x.out], [this.y.inp], false) + //this._connect(this.x.out.channel(0), this.y.inp.channel(0)) + //this._connect(this.x.out.channel(1), this.y.inp.channel(1)) } } diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 3b17980f4..92854cc05 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -527,9 +527,8 @@ export abstract class Reactor extends Component { } if (t instanceof MultiPort) { console.log("Encountered multiport!! >>>>>>>>>-----") - let key = this._getKey(t) if (t instanceof InMultiPort || t instanceof OutMultiPort) { - t.channels().forEach(channel => channel.getManager(key).addReaction(reaction)) + t.channels().forEach(channel => channel.getManager(this._getKey(channel)).addReaction(reaction)) } } From ca7d7dc55d875e58bf11e2aa0f8fad5a44c929c2 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 17:20:02 -0800 Subject: [PATCH 40/68] Also fixed remaining failing test --- __tests__/SingleEvent.test.ts | 3 +-- src/core/federation.ts | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/__tests__/SingleEvent.test.ts b/__tests__/SingleEvent.test.ts index be86d15cb..219c6b922 100644 --- a/__tests__/SingleEvent.test.ts +++ b/__tests__/SingleEvent.test.ts @@ -1,5 +1,4 @@ -import {App, Parameter} from '../src/core/reactor'; -import {TimeValue} from "../src/core/time" +import {App, Parameter, TimeValue} from '../src/core/internal'; import {SingleEvent} from '../src/share/SingleEvent'; import {Logger} from '../src/share/Logger'; diff --git a/src/core/federation.ts b/src/core/federation.ts index e22c63742..65c365d28 100644 --- a/src/core/federation.ts +++ b/src/core/federation.ts @@ -1,9 +1,10 @@ + +import {Socket, createConnection, SocketConnectOpts} from 'net' +import {EventEmitter} from 'events'; import { Log, Tag, TimeValue, Origin, getCurrentPhysicalTime, Alarm, Present, App, Action, FederatePortAction, TaggedEvent } from './internal'; -import {Socket, createConnection, SocketConnectOpts} from 'net' -import {EventEmitter} from 'events'; //---------------------------------------------------------------------// // Federated Execution Constants and Enums // From c1da6dc10dbf8dd289e9413eba3b643b9ac4f67d Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 22:43:26 -0800 Subject: [PATCH 41/68] Some simplifications and cleanups --- src/core/internal.ts | 1 + src/core/port.ts | 170 ------------------------------------------- src/core/reactor.ts | 12 +-- 3 files changed, 8 insertions(+), 175 deletions(-) diff --git a/src/core/internal.ts b/src/core/internal.ts index fb8cd4d59..2245f2d42 100644 --- a/src/core/internal.ts +++ b/src/core/internal.ts @@ -7,6 +7,7 @@ export * from "./trigger" export * from "./action" export * from "./state" export * from "./port" +export * from "./multiport" export * from "./reactor" export * from "./bank" export * from "./event" diff --git a/src/core/port.ts b/src/core/port.ts index 06081c9b1..495bcc81d 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -194,173 +194,3 @@ export class OutPort extends IOPort { export class InPort extends IOPort { } - -export abstract class MultiPort extends NonComposite implements MultiRead { - - constructor(container: Reactor, readonly width: number) { - super(container) - } - - abstract get(index: number): T | Absent; - - public static values(ports: Array>): Array { - let values = new Array(ports.length); - for (let i = 0; i < values.length; i++) { - values[i] = ports[i].get(); - } - return values - } - - public abstract values(): Array - - /** - * Only the holder of the key may obtain a writable port. - * @param key - */ - public abstract asWritable(key: Symbol | undefined): MultiReadWrite; - - public _receiveRuntimeObject(runtime: Runtime): void { - throw new Error("Method not implemented."); - } - -} - -export class InMultiPort extends MultiPort { - - public get(index: number): T | undefined { - return this._channels[index].get() - } - - public channel(index: number): InPort { - return this._channels[index] - } - - private _channels: Array> - - public channels() { - return this._channels - } - - constructor(container: Reactor, width: number) { - super(container, width) - this._channels = new Array>(width) - for (let i = 0; i < width; i++) { - this._channels[i] = new InPort(container, this._getName() + "[" + i + "]") - } - } - - public values(): Array { - return MultiPort.values(this._channels) - } - - /** - * Inner class instance to gain access to Write interface. - */ - protected writers = new class implements MultiReadWrite { - - private readonly bla: Array> = new Array(); - - constructor(private port: InMultiPort) { - // FIXME: won't work because channels have not been created yet - // port.channels().forEach(channel => { - // let writer = port.getContainer()?.writable(channel) - // if (writer) this.bla.push(writer) - // }); - } - - public get(index: number): T | undefined { - return this.port.channel(index).get() - } - - public set(index: number, value: T): void { - console.log("Container: " + this.port.getContainer()) - this.port.getContainer()?.writable(this.port.channel(index)).set(value) - //this.bla[index].set(value) - } - - - - }(this) - - - /** - * Only the holder of the key may obtain a writable port. - * @param key - */ - public asWritable(key: Symbol | undefined): MultiReadWrite { - if (this._key === key) { - return this.writers - } - throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) - } -} -export class OutMultiPort extends MultiPort { - - public get(index: number): T | undefined { - return this._channels[index].get() - } - - public channel(index: number): OutPort { - return this._channels[index] - } - - public _receiveRuntimeObject(runtime: Runtime): void { - throw new Error("Method not implemented."); - } - - private _channels: Array> - - constructor(container: Reactor, width: number) { - super(container, width) - this._channels = new Array>(width) - for (let i = 0; i < width; i++) { - this._channels[i] = new OutPort(container, this._getName() + "[" + i + "]") - } - } - - public values(): Array { - return MultiPort.values(this._channels) - } - - public channels() { - return this._channels - } - - /** -* Inner class instance to gain access to Write interface. -*/ - protected writers = new class implements MultiReadWrite { - - private readonly bla: Array> = new Array(); - - constructor(private port: OutMultiPort) { - // FIXME: won't work because channels have not been created yet - // port.channels().forEach(channel => { - // let writer = port.getContainer()?.writable(channel) - // if (writer) this.bla.push(writer) - // }); - } - - public get(index: number): T | undefined { - return this.port.channel(index).get() - } - - public set(index: number, value: T): void { - console.log("Container: " + this.port.getContainer()) - this.port.getContainer()?.writable(this.port.channel(index)).set(value) - //this.bla[index].set(value) - } - - }(this) - - /** - * Only the holder of the key may obtain a writable port. - * @param key - */ - public asWritable(key: Symbol | undefined): MultiReadWrite { - if (this._key === key) { - return this.writers - } - throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) - } -} diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 92854cc05..434c8f628 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -12,9 +12,9 @@ import { Mutation, Procedure, Absent, ArgList, Args, MultiReadWrite, Present, Read, Sched, SchedulableAction, Triggers, Variable, Write, TaggedEvent, Component, NonComposite, ScheduledTrigger, Trigger, TriggerManager, - Action, InPort, IOPort, MultiPort, OutPort, Port, WritablePort, Startup, Shutdown + Action, InPort, IOPort, MultiPort, OutPort, Port, WritablePort, Startup, Shutdown, + InMultiPort, OutMultiPort } from "./internal" -import { InMultiPort, OutMultiPort } from "./port"; // Set the default log level. Log.global.level = Log.levels.ERROR; @@ -527,7 +527,7 @@ export abstract class Reactor extends Component { } if (t instanceof MultiPort) { console.log("Encountered multiport!! >>>>>>>>>-----") - if (t instanceof InMultiPort || t instanceof OutMultiPort) { + if (t instanceof MultiPort) { t.channels().forEach(channel => channel.getManager(this._getKey(channel)).addReaction(reaction)) } } @@ -536,6 +536,8 @@ export abstract class Reactor extends Component { if (t instanceof IOPort) { this._dependencyGraph.addEdge(reaction, t) //this._addDependency(t, reaction); + } else if (t instanceof MultiPort) { + // FIXME(marten) } else { Log.debug(this, () => ">>>>>>>> not a dependency: " + t); } @@ -1062,7 +1064,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { // TODO(hokeun): Check if the multiport's container is Bank when Bank is implemented. src.forEach(port => { - if (port instanceof InMultiPort || port instanceof OutMultiPort) { + if (port instanceof MultiPort) { port.channels().forEach(singlePort => { leftPorts.push(singlePort) }) @@ -1072,7 +1074,7 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { }) dest.forEach(port => { - if (port instanceof InMultiPort || port instanceof OutMultiPort) { + if (port instanceof MultiPort) { port.channels().forEach(singlePort => { rightPorts.push(singlePort) }) From 8ca16dc349714bb26e195cfee7dc61688a86a2b5 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 22:47:23 -0800 Subject: [PATCH 42/68] Improve code coverage --- __tests__/multiport.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/__tests__/multiport.test.ts b/__tests__/multiport.test.ts index 60db71d1f..cf2e56145 100644 --- a/__tests__/multiport.test.ts +++ b/__tests__/multiport.test.ts @@ -13,12 +13,10 @@ import { Args, Triggers, Reactor, App, InMultiPort, InPort, OutMultiPort } from new Args(this.inp), function (this, inp) { console.log("Getting triggered") - test('check values', () => { + test('check read values', () => { expect(inp.channel(0).get()).toBe(42); expect(inp.channel(1).get()).toBe(69); }); - console.log("Channel 0:" + inp.channel(0).get()) - console.log("Channel 1:" + inp.channel(1).get()) } ); this.addReaction( @@ -30,6 +28,10 @@ import { Args, Triggers, Reactor, App, InMultiPort, InPort, OutMultiPort } from }); out.set(0, 42) out.set(1, 69) + test('check written values', () => { + expect(out.get(0)).toBe(42); + expect(out.get(1)).toBe(69); + }); } ); } From 9b9cdead16a5fb3571b04995a23fe60826d2bee3 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 23:19:24 -0800 Subject: [PATCH 43/68] Lookup names for channels of multiports. Added test. --- __tests__/multiport.test.ts | 98 ++++++++++++++++++------------------- src/core/component.ts | 15 +++++- 2 files changed, 62 insertions(+), 51 deletions(-) diff --git a/__tests__/multiport.test.ts b/__tests__/multiport.test.ts index cf2e56145..8faba0c94 100644 --- a/__tests__/multiport.test.ts +++ b/__tests__/multiport.test.ts @@ -1,58 +1,56 @@ -import { Args, Triggers, Reactor, App, InMultiPort, InPort, OutMultiPort } from "../src/core/internal"; - -//describe('Connect two multiports and check values', () => { - - class TwoInTwoOut extends Reactor { - inp = new InMultiPort(this, 2); - out = new OutMultiPort(this, 2); - - constructor(parent: Reactor) { - super(parent) - this.addReaction( - new Triggers(this.inp), - new Args(this.inp), - function (this, inp) { - console.log("Getting triggered") - test('check read values', () => { - expect(inp.channel(0).get()).toBe(42); - expect(inp.channel(1).get()).toBe(69); - }); - } - ); - this.addReaction( - new Triggers(this.startup), - new Args(this.allWritable(this.out)), - function (out) { - test('start up reaction triggered', () => { - expect(true).toBe(true); - }); - out.set(0, 42) - out.set(1, 69) - test('check written values', () => { - expect(out.get(0)).toBe(42); - expect(out.get(1)).toBe(69); - }); - } - ); - } +import { Args, Triggers, Reactor, App, InMultiPort, OutMultiPort } from "../src/core/internal"; +class TwoInTwoOut extends Reactor { + inp = new InMultiPort(this, 2); + out = new OutMultiPort(this, 2); + + constructor(parent: Reactor) { + super(parent) + this.addReaction( + new Triggers(this.inp), + new Args(this.inp), + function (this, inp) { + console.log("Getting triggered") + test('check read values', () => { + expect(inp.channel(0).get()).toBe(42); + expect(inp.channel(1).get()).toBe(69); + }); + test('print input port names', () => { + expect(inp._getName()).toBe("inp"); + expect(inp.channel(0)._getName()).toBe("inp[0]"); + expect(inp.channel(1)._getName()).toBe("inp[1]"); + }); + } + ); + this.addReaction( + new Triggers(this.startup), + new Args(this.allWritable(this.out)), + function (out) { + test('start up reaction triggered', () => { + expect(true).toBe(true); + }); + out.set(0, 42) + out.set(1, 69) + test('check written values', () => { + expect(out.get(0)).toBe(42); + expect(out.get(1)).toBe(69); + }); + } + ); } +} - class myApp extends App { - - x = new TwoInTwoOut(this); - y = new TwoInTwoOut(this); +class myApp extends App { - constructor() { - super(); - this._connectMulti([this.x.out], [this.y.inp], false) - //this._connect(this.x.out.channel(0), this.y.inp.channel(0)) - //this._connect(this.x.out.channel(1), this.y.inp.channel(1)) - } + x = new TwoInTwoOut(this); + y = new TwoInTwoOut(this); + constructor() { + super(); + this._connectMulti([this.x.out], [this.y.inp], false) } - var app = new myApp(); - app._start() +} -//}); +var app = new myApp(); +app._start() diff --git a/src/core/component.ts b/src/core/component.ts index 6ecc613f1..f8574846b 100644 --- a/src/core/component.ts +++ b/src/core/component.ts @@ -1,4 +1,4 @@ -import {Reactor, App, Runtime} from "./internal" +import {Reactor, App, Runtime, MultiPort, IOPort} from "./internal" /** * Base class for named objects embedded in a hierarchy of reactors. Each @@ -148,6 +148,19 @@ export abstract class Component { } } } + // Handle multiports + if (this instanceof IOPort) { + for (const [key, value] of Object.entries(this._container)) { + if (value instanceof MultiPort) { + let channels = value.channels() + for (let i=0; i < channels.length; i++) { + if (channels[i] === this) { + name = `${key}[${i}]` + } + } + } + } + } if (this._alias) { if (name == "") { From 3954bac8f49b770810cc0887ef8cb6eddd65d780 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 23:21:35 -0800 Subject: [PATCH 44/68] Also check fully qualified name --- __tests__/multiport.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/__tests__/multiport.test.ts b/__tests__/multiport.test.ts index 8faba0c94..f1155ea4a 100644 --- a/__tests__/multiport.test.ts +++ b/__tests__/multiport.test.ts @@ -18,6 +18,8 @@ class TwoInTwoOut extends Reactor { expect(inp._getName()).toBe("inp"); expect(inp.channel(0)._getName()).toBe("inp[0]"); expect(inp.channel(1)._getName()).toBe("inp[1]"); + expect(inp.channel(0)._getFullyQualifiedName()).toBe("myApp/y/inp[0]"); + expect(inp.channel(1)._getFullyQualifiedName()).toBe("myApp/y/inp[1]"); }); } ); From 462fe3eb83adea603b19749acf9a2303d7a00f90 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Wed, 23 Feb 2022 23:25:50 -0800 Subject: [PATCH 45/68] Added missing file --- src/core/multiport.ts | 124 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 src/core/multiport.ts diff --git a/src/core/multiport.ts b/src/core/multiport.ts new file mode 100644 index 000000000..611f6a951 --- /dev/null +++ b/src/core/multiport.ts @@ -0,0 +1,124 @@ +import { Absent, InPort, IOPort, MultiRead, MultiReadWrite, NonComposite, OutPort, Present, Reactor, Runtime, WritablePort } from "./internal"; + + +export abstract class MultiPort extends NonComposite implements MultiRead { + + protected _channels: Array> + + abstract channels(): Array> + + abstract channel(index: number): IOPort + + constructor(container: Reactor, readonly width: number) { + super(container) + this._channels = new Array(width) + } + + /** + * Inner class instance to gain access to Write interface. + */ + protected writer = new class implements MultiReadWrite { + + private readonly cache: Array> + + constructor(private port: MultiPort) { + this.cache = new Array(); + } + + public get(index: number): T | undefined { + return this.port._channels[index].get() + } + + public set(index: number, value: T): void { + let writableChannel = this.cache[index] + if (writableChannel === undefined) { + let container = this.port.getContainer() + if (container) { + writableChannel = container.writable(this.port._channels[index]) + this.cache[index] = writableChannel + } else { + throw new Error("Attempt to set channel of multiport that has no container") + } + } + writableChannel.set(value) + } + }(this) + + public get(index: number): T | undefined { + return this._channels[index].get() + } + + public static values(ports: Array>): Array { + let values = new Array(ports.length); + for (let i = 0; i < values.length; i++) { + values[i] = ports[i].get(); + } + return values + } + + public abstract values(): Array + + /** + * Only the holder of the key may obtain a writable port. + * @param key + */ + public asWritable(key: Symbol | undefined): MultiReadWrite { + if (this._key === key) { + return this.writer + } + throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) + } + + public _receiveRuntimeObject(runtime: Runtime): void { + throw new Error("Method not implemented."); + } +} + +export class InMultiPort extends MultiPort { + + public channel(index: number): InPort { + return this._channels[index] + } + + public channels(): Array> { + return this._channels + } + + constructor(container: Reactor, width: number) { + super(container, width) + this._channels = new Array>(width) + for (let i = 0; i < width; i++) { + this._channels[i] = new InPort(container) + } + } + + public values(): Array { + return MultiPort.values(this._channels) + } +} +export class OutMultiPort extends MultiPort { + + public channel(index: number): OutPort { + return this._channels[index] + } + + public _receiveRuntimeObject(runtime: Runtime): void { + throw new Error("Method not implemented."); + } + + constructor(container: Reactor, width: number) { + super(container, width) + this._channels = new Array>(width) + for (let i = 0; i < width; i++) { + this._channels[i] = new OutPort(container) + } + } + + public values(): Array { + return MultiPort.values(this._channels) + } + + public channels(): Array> { + return this._channels + } +} From 5dd7175ac224fc58fdf482facd9f96281e5ce8a1 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Thu, 24 Feb 2022 02:02:02 -0800 Subject: [PATCH 46/68] Remove federation from internal's exports as there's no other .ts file using classes from federation.ts. --- src/core/internal.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/internal.ts b/src/core/internal.ts index 2245f2d42..7d4a7e42d 100644 --- a/src/core/internal.ts +++ b/src/core/internal.ts @@ -11,5 +11,4 @@ export * from "./multiport" export * from "./reactor" export * from "./bank" export * from "./event" -export * from "./federation" export * from "./cli" From fb761bb49fa4eaf6e524e805e5c337b9cb4622d7 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 24 Feb 2022 15:58:29 -0800 Subject: [PATCH 47/68] Removed aliases. Revised naming scheme. --- __tests__/ActionTrigger.test.ts | 5 +- __tests__/Clock.test.ts | 5 +- __tests__/HierarchicalSingleEvent.test.ts | 5 +- __tests__/OutputGet.test.ts | 1 - __tests__/SingleEvent.test.ts | 1 - __tests__/dependencies.ts | 42 ++++---- __tests__/hierarchy.ts | 123 ++++++++-------------- __tests__/multiport.test.ts | 4 +- __tests__/mutations.test.ts | 2 +- __tests__/simple.ts | 49 ++++----- src/core/component.ts | 118 ++++++++++----------- src/core/internal.ts | 2 + src/core/port.ts | 4 +- src/core/reactor.ts | 8 +- src/core/strings.ts | 9 ++ src/core/trigger.ts | 4 +- 16 files changed, 171 insertions(+), 211 deletions(-) create mode 100644 src/core/strings.ts diff --git a/__tests__/ActionTrigger.test.ts b/__tests__/ActionTrigger.test.ts index 7df66f8ea..77ee7752c 100644 --- a/__tests__/ActionTrigger.test.ts +++ b/__tests__/ActionTrigger.test.ts @@ -65,9 +65,8 @@ export class ActionTrigger extends Reactor { class ActionTriggerTest extends App { aTrigger: ActionTrigger; - constructor(name: string, timeout: TimeValue, success?: ()=> void, fail?: ()=>void){ + constructor(timeout: TimeValue, success?: ()=> void, fail?: ()=>void){ super(timeout, false, false, success, fail); - this._setAlias(name); this.aTrigger = new ActionTrigger(this); } } @@ -84,7 +83,7 @@ describe('ActionTrigger', function () { }; //Tell the reactor runtime to successfully terminate after 3 seconds. - var aTriggerTest = new ActionTriggerTest("ActionTriggerTest", TimeValue.secs(3), done, failure); + var aTriggerTest = new ActionTriggerTest(TimeValue.secs(3), done, failure); //Don't give the runtime the done callback because we don't care if it terminates aTriggerTest._start(); diff --git a/__tests__/Clock.test.ts b/__tests__/Clock.test.ts index 1f2fb7bc4..2659153e0 100644 --- a/__tests__/Clock.test.ts +++ b/__tests__/Clock.test.ts @@ -22,9 +22,8 @@ export class Clock extends App { a2 = new Action(this, Origin.logical); a3 = new Action(this, Origin.logical); - constructor(name: string, timeout: TimeValue, success: () => void, fail: () => void) { + constructor(timeout: TimeValue, success: () => void, fail: () => void) { super(timeout, false, false, success, fail); - this._alias = name; this.addReaction( new Triggers(this.t1), new Args(this.schedulable(this.a1)), @@ -101,7 +100,7 @@ describe('clock', function () { }; //Tell the reactor runtime to successfully terminate after 6 seconds. - var clock = new Clock("Clock", TimeValue.secs(6), done, fail); + var clock = new Clock(TimeValue.secs(6), done, fail); //Don't give the runtime the done callback because we don't care if it terminates clock._start(); diff --git a/__tests__/HierarchicalSingleEvent.test.ts b/__tests__/HierarchicalSingleEvent.test.ts index 67768b4db..a94700ff7 100644 --- a/__tests__/HierarchicalSingleEvent.test.ts +++ b/__tests__/HierarchicalSingleEvent.test.ts @@ -31,9 +31,8 @@ class SETest extends App { seContainer: SEContainer; logContainer: LogContainer; - constructor(name:string, timeout: TimeValue, keepAlive: boolean = false, fast: boolean = false, success: ()=> void, fail: ()=>void ){ + constructor(timeout: TimeValue, keepAlive: boolean = false, fast: boolean = false, success: ()=> void, fail: ()=>void ){ super(timeout, keepAlive, fast, success, fail) - this._setAlias(name); this.seContainer = new SEContainer(this); this.logContainer = new LogContainer(this); @@ -61,7 +60,7 @@ describe('HierarchicalSingleEvent', function () { }; // Tell the reactor runtime to successfully terminate after 3 seconds. - let seTest = new SETest("SingleEventTesterApp", TimeValue.secs(3), false, false, done, failReactor); + let seTest = new SETest(TimeValue.secs(3), false, false, done, failReactor); // Normally _setAllParents would be called as part of the initialization // process for starting an app, but we call it directly here to set diff --git a/__tests__/OutputGet.test.ts b/__tests__/OutputGet.test.ts index 234613114..570f30607 100644 --- a/__tests__/OutputGet.test.ts +++ b/__tests__/OutputGet.test.ts @@ -9,7 +9,6 @@ class OutputGetTest extends App { constructor(timeout: TimeValue, name:string, success: ()=> void, failure: ()=>void){ super(timeout, true, false, success, failure); Log.global.debug(">>>>>>>>----" + this.util) - this._setAlias(name); this.addReaction( new Triggers(this.t), new Args(this.writable(this.o)), diff --git a/__tests__/SingleEvent.test.ts b/__tests__/SingleEvent.test.ts index 219c6b922..a9e4fdbf7 100644 --- a/__tests__/SingleEvent.test.ts +++ b/__tests__/SingleEvent.test.ts @@ -8,7 +8,6 @@ class SETest extends App { constructor(timeout: TimeValue, success: ()=> void, failure: ()=>void ) { super(timeout, false, false, success, failure); - this._setAlias("SETest"); this.singleEvent = new SingleEvent(this, new Parameter("foo")); this.logger = new Logger(this, "foo"); diff --git a/__tests__/dependencies.ts b/__tests__/dependencies.ts index 7fb65384f..a63217912 100644 --- a/__tests__/dependencies.ts +++ b/__tests__/dependencies.ts @@ -1,5 +1,5 @@ import {Reactor, App, Triggers, Args, InPort, Reaction, Priority, - SortableDependencyGraph, Sortable, PrioritySet, Log} from '../src/core/internal'; + SortableDependencyGraph, Sortable, PrioritySet, Log, StringUtil} from '../src/core/internal'; //Log.setGlobalLevel(Log.levels.DEBUG); @@ -102,11 +102,13 @@ describe('Manually constructed precedence graphs', () => { expect(graph.size()[0]).toEqual(6); // V expect(graph.size()[1]).toEqual(7); // E expect(graph.toString()).toBe( -`digraph G { -"App/R[R0]"->"App/R[R1]"->"App/R[R4]"->"App/R[R3]"->"App/R[R5]"; -"App/R[R0]"->"App/R[R4]"; -"App/R[R1]"->"App/R[R2]"->"App/R[R3]"; -}`); + StringUtil.dontIndent + `digraph G { + "App.R[R0]"->"App.R[R1]"->"App.R[R4]"->"App.R[R3]"->"App.R[R5]"; + "App.R[R0]"->"App.R[R4]"; + "App.R[R1]"->"App.R[R2]"->"App.R[R3]"; + }` + ); }); it('initial priorities', () => { @@ -125,9 +127,9 @@ describe('Manually constructed precedence graphs', () => { expect(graph.size()[1]).toEqual(6); // E expect(graph.toString()).toBe( `digraph G { -"App/R[R0]"->"App/R[R1]"->"App/R[R2]"->"App/R[R3]"->"App/R[R5]"; -"App/R[R1]"->"App/R[R4]"; -"App/R[R0]"->"App/R[R4]"; +"App.R[R0]"->"App.R[R1]"->"App.R[R2]"->"App.R[R3]"->"App.R[R5]"; +"App.R[R1]"->"App.R[R4]"; +"App.R[R0]"->"App.R[R4]"; }`); }); @@ -137,10 +139,12 @@ describe('Manually constructed precedence graphs', () => { expect(graph.size()[1]).toEqual(3); // E Log.global.debug(graph.toString()); expect(graph.toString()).toBe( -`digraph G { -"App/R[R2]"->"App/R[R3]"->"App/R[R5]"; -"App/R[R0]"->"App/R[R4]"; -}`); + StringUtil.dontIndent + `digraph G { + "App.R[R2]"->"App.R[R3]"->"App.R[R5]"; + "App.R[R0]"->"App.R[R4]"; + }` + ); }); it('add node 7, make 3 dependent on it', () => { @@ -150,11 +154,13 @@ describe('Manually constructed precedence graphs', () => { expect(graph.size()[1]).toEqual(4); // E Log.global.debug(graph.toString()); expect(graph.toString()).toBe( -`digraph G { -"App/R[R2]"->"App/R[R3]"->"App/R[R5]"; -"App/R[R0]"->"App/R[R4]"; -"App/R[R2]"->"App/R[R6]"; -}`); + StringUtil.dontIndent + `digraph G { + "App.R[R2]"->"App.R[R3]"->"App.R[R5]"; + "App.R[R0]"->"App.R[R4]"; + "App.R[R2]"->"App.R[R6]"; + }` + ); }); it('reassign priorities', () => { diff --git a/__tests__/hierarchy.ts b/__tests__/hierarchy.ts index e9534665b..9d93bd861 100644 --- a/__tests__/hierarchy.ts +++ b/__tests__/hierarchy.ts @@ -1,108 +1,73 @@ import {Reactor, App, InPort, OutPort} from '../src/core/internal'; -var app = new App(); - -class Component extends Reactor { +class InOut extends Reactor { a: InPort = new InPort(this); b: OutPort = new OutPort(this); - constructor(parent: Reactor, alias:string) { + constructor(parent: Reactor) { super(parent); - this._setAlias(alias); } - child: Reactor | undefined; } -describe('Container to Contained', () => { - - var container = new Component(app, "Container"); - var contained = new Component(container, "Contained"); - var grandcontained = new Component(contained, "GrandContained"); - - container.child = contained; - contained.child = grandcontained; - - var container2 = new Component(app, "Container2"); - var contained2 = new Component(container2, "Contained2"); - - container2.child = contained2; - - // Normally _setAllParents would be called as part of the initialization - // process for starting an app, but we call it directly here to set - // parent attributes needed for this test. - // container._setAllParents(null); - // container2._setAllParents(null); - - // it('reactor with self as child', () => { - // expect(() => { - // let loopy = new Component(app, "Loopy"); - // loopy.child = loopy; - // loopy._checkAllParents(null); - // }).toThrowError(); - // }); - - // it('reactor with a port constructed with the wrong parent', () => { - // expect(() => { - // let badPortComponent = new Component(app, "BadPortComponent"); - // let otherComponent = new Component(app, "OtherComponent"); - - // // this port has been incorrectly constructed because it - // // is an attribute of badPortComponent, but is set in the constructor - // // with otherComponent as its parent - // badPortComponent.a = new InPort(otherComponent); - - // // _setAllParents should throw an error - // badPortComponent._checkAllParents(null); - // }).toThrowError(); - // }); +var app = new class extends App { + container = new class extends InOut { + contained = new class extends InOut { + containedAgain = new InOut(this) + }(this) + }(this) + foo = new InOut(this) +}() +describe('Container to Contained', () => { + it('app name', () => { + expect(app.toString()).toBe(""); + }); + it('contained reactor name', () => { - // expect(contained._getName()).toBe("Contained"); - expect(contained.toString()).toBe("App/Container/child (Contained)"); + expect(app.container.contained.toString()).toBe(".container.contained"); }); it('container reactor name', () =>{ - // expect(container._getName()).toBe("Container"); - expect(container.toString()).toBe("App/Container"); + expect(app.container.toString()).toBe(".container"); }) it('testing canConnect', () => { - expect(container.canConnect(container.a, contained.a)).toBe(true); - expect(container.canConnect(contained.a, container.a)).toBe(false); - expect(container.canConnect(contained.a, contained.b)).toBe(false); - expect(container.canConnect(contained.b, contained.a)).toBe(true); + expect(app.container.canConnect(app.container.a, app.container.contained.a)).toBe(true); + expect(app.container.canConnect(app.container.contained.a, app.container.a)).toBe(false); + expect(app.container.canConnect(app.container.contained.a, app.container.contained.b)).toBe(false); + expect(app.container.canConnect(app.container.contained.b, app.container.contained.a)).toBe(true); - expect(container.canConnect(container.a, contained.b)).toBe(false); - expect(container.canConnect(contained.b, container.a)).toBe(false); + expect(app.container.canConnect(app.container.a, app.container.contained.b)).toBe(false); + expect(app.container.canConnect(app.container.contained.b, app.container.a)).toBe(false); - expect(container.canConnect(container.b, contained.a)).toBe(false); - expect(container.canConnect(contained.a, container.b)).toBe(false); + expect(app.container.canConnect(app.container.b, app.container.contained.a)).toBe(false); + expect(app.container.canConnect(app.container.contained.a, app.container.b)).toBe(false); - expect(container.canConnect(container.b, contained.b)).toBe(false); - expect(container.canConnect(contained.b, container.b)).toBe(true); + expect(app.container.canConnect(app.container.b, app.container.contained.b)).toBe(false); + expect(app.container.canConnect(app.container.contained.b, app.container.b)).toBe(true); - expect(container.canConnect(contained.a, contained2.a)).toBe(false); - expect(container.canConnect(contained.a, contained2.b)).toBe(false); - expect(container.canConnect(contained2.a, contained.a)).toBe(false); - expect(container.canConnect(contained2.a, contained.a)).toBe(false); + expect(app.container.canConnect(app.container.contained.a, app.foo.a)).toBe(false); + expect(app.container.canConnect(app.container.contained.a, app.foo.b)).toBe(false); + expect(app.container.canConnect(app.foo.a, app.container.contained.a)).toBe(false); + expect(app.container.canConnect(app.foo.a, app.container.contained.a)).toBe(false); - expect(container.canConnect(contained2.a, container.b)).toBe(false); - expect(container.canConnect(contained2.a, container.a)).toBe(false); + expect(app.container.canConnect(app.foo.a, app.container.b)).toBe(false); + expect(app.container.canConnect(app.foo.a, app.container.a)).toBe(false); - expect(container.child).toBeDefined(); + // expect(app.container.contained).toBeDefined(); - if (container.child) { - expect(container.child.canConnect(grandcontained.a, contained.a)).toBe(false); - expect(container.child.canConnect(grandcontained.b, contained.b)).toBe(true); - expect(container.child.canConnect(grandcontained.a, container.a)).toBe(false); - expect(container.child.canConnect(grandcontained.b, container.b)).toBe(false); - expect(container.child.canConnect(grandcontained.a, container2.a)).toBe(false); - expect(container.child.canConnect(grandcontained.b, container2.b)).toBe(false); - expect(container.child.canConnect(grandcontained.a, contained2.a)).toBe(false); - expect(container.child.canConnect(grandcontained.b, contained2.b)).toBe(false); - } + // if (container.child) { + expect(app.container.contained.canConnect(app.container.contained.containedAgain.a, app.container.contained.a)).toBe(false); + expect(app.container.contained.canConnect(app.container.contained.containedAgain.b, app.container.contained.b)).toBe(true); + expect(app.container.contained.canConnect(app.container.contained.containedAgain.a, app.container.a)).toBe(false); + expect(app.container.contained.canConnect(app.container.contained.containedAgain.b, app.container.b)).toBe(false); + expect(app.container.contained.canConnect(app.container.contained.containedAgain.a, app.foo.a)).toBe(false); + expect(app.container.contained.canConnect(app.container.contained.containedAgain.b, app.foo.b)).toBe(false); + expect(app.container.contained.canConnect(app.container.contained.containedAgain.a, app.foo.a)).toBe(false); + expect(app.container.contained.canConnect(app.container.contained.containedAgain.b, app.foo.b)).toBe(false); + //} }); }); \ No newline at end of file diff --git a/__tests__/multiport.test.ts b/__tests__/multiport.test.ts index f1155ea4a..1efbb6fda 100644 --- a/__tests__/multiport.test.ts +++ b/__tests__/multiport.test.ts @@ -18,8 +18,8 @@ class TwoInTwoOut extends Reactor { expect(inp._getName()).toBe("inp"); expect(inp.channel(0)._getName()).toBe("inp[0]"); expect(inp.channel(1)._getName()).toBe("inp[1]"); - expect(inp.channel(0)._getFullyQualifiedName()).toBe("myApp/y/inp[0]"); - expect(inp.channel(1)._getFullyQualifiedName()).toBe("myApp/y/inp[1]"); + expect(inp.channel(0)._getFullyQualifiedName()).toBe("myApp.y.inp[0]"); + expect(inp.channel(1)._getFullyQualifiedName()).toBe("myApp.y.inp[1]"); }); } ); diff --git a/__tests__/mutations.test.ts b/__tests__/mutations.test.ts index 71e4b1060..2af96077a 100644 --- a/__tests__/mutations.test.ts +++ b/__tests__/mutations.test.ts @@ -109,7 +109,7 @@ class ScatterGather extends App { class ZenoClock extends Reactor { tick:Timer; constructor(owner: Reactor, iteration: number) { - super(owner, "ZenoClock(" + iteration + ")") + super(owner) console.log("Creating ZenoClock " + iteration) this.tick = new Timer(this, 0, 0) this.addReaction(new Triggers(this.tick), new Args(this.tick), function(this, tick) { diff --git a/__tests__/simple.ts b/__tests__/simple.ts index 4fbd1f137..cccc6d16e 100644 --- a/__tests__/simple.ts +++ b/__tests__/simple.ts @@ -1,4 +1,4 @@ -import {Reactor, App, InPort, OutPort} from "../src/core/internal"; +import {Reactor, App, InPort, OutPort, StringUtil} from "../src/core/internal"; class MyActor extends Reactor { @@ -13,8 +13,8 @@ import {Reactor, App, InPort, OutPort} from "../src/core/internal"; a: InPort<{t: number}> = new InPort(this); b: OutPort<{t: number, y: string}> = new OutPort(this); - constructor(parent:Reactor, alias: string) { - super(parent, alias) + constructor(parent:Reactor) { + super(parent) } } @@ -25,45 +25,36 @@ describe('Test names for contained reactors', () => { port: InPort = new InPort(this); x = new MyActor(this); - y = new MyActor2(this, "Foo"); + y = new MyActor2(this); - constructor(name: string, someParam: string) { + constructor() { super(undefined); - this._setAlias(name); it('contained actor name', () => { expect(this.x._getName()).toBe("x"); }); - it('contained actor Alias', () => { - expect(this.x._getAlias()).toBe(""); - }); - it('contained actor FQN', () => { - expect(this.x._getFullyQualifiedName()).toBe("Hello World/x"); + expect(this.x._getFullyQualifiedName()).toBe("myApp.x"); }); it('contained actor toString', () => { - expect(this.x.toString()).toBe("Hello World/x"); + expect(this.x.toString()).toBe("myApp.x"); }); it('contained actor FQN', () => { - expect(this.x.toString()).toBe("Hello World/x"); + expect(this.x.toString()).toBe("myApp.x"); }); it('contained actor with alias FQN', () => { - expect(this.y.toString()).toBe("Hello World/y (Foo)"); + expect(this.y.toString()).toBe("myApp.y"); }); it('uncontained actor name', () => { - expect(this.toString()).toBe("Hello World"); + expect(this.toString()).toBe("myApp"); }); - it('uncontained actor Alias', () => { - expect(this._getAlias()).toBe(name); - }); - - it('check whehter App is not contained by itself', () => { + it('check whether App is not contained by itself', () => { expect(this._isContainedBy(this)).toBeFalsy(); }); @@ -81,8 +72,8 @@ describe('Test names for contained reactors', () => { it('graph before connect', () => { expect(this._getPrecedenceGraph().toString()).toBe( "digraph G {" + "\n" + - "\"Hello World/x[M0]\"->\"Hello World[M0]\";" + "\n" + - "\"Hello World/y (Foo)[M0]\"->\"Hello World[M0]\";" + "\n" + + "\"myApp.x[M0]\"->\"myApp[M0]\";" + "\n" + + "\"myApp.y[M0]\"->\"myApp[M0]\";" + "\n" + "}"); }); @@ -100,11 +91,12 @@ describe('Test names for contained reactors', () => { it('graph after connect and before disconnect', () => { expect(this._getPrecedenceGraph().toString()).toBe( - "digraph G {" + "\n" + - "\"Hello World/x/a\"->\"Hello World/y (Foo)/b\";" + "\n" + - "\"Hello World/x[M0]\"->\"Hello World[M0]\";" + "\n" + - "\"Hello World/y (Foo)[M0]\"->\"Hello World[M0]\";" + "\n" + - "}"); + StringUtil.dontIndent + `digraph G { + "myApp.x.a"->"myApp.y.b"; + "myApp.x[M0]"->"myApp[M0]"; + "myApp.y[M0]"->"myApp[M0]"; + }`); }); @@ -115,7 +107,6 @@ describe('Test names for contained reactors', () => { } } - var app = new myApp("Hello World", "!"); - + var app = new myApp(); }); diff --git a/src/core/component.ts b/src/core/component.ts index f8574846b..16c48eae3 100644 --- a/src/core/component.ts +++ b/src/core/component.ts @@ -11,10 +11,7 @@ import {Reactor, App, Runtime, MultiPort, IOPort} from "./internal" */ export abstract class Component { - /** - * An optional alias for this component. - */ - protected _alias: string | undefined; + public static pathSeparator = '.' /** * A symbol that identifies this component, and it also used to selectively @@ -40,8 +37,7 @@ export abstract class Component { * constructor in order to establish a link with the runtime object. * @param alias An optional alias for the component. */ - constructor(container: Reactor | null, alias?:string) { - this._alias = alias + constructor(container: Reactor | null) { if (container !== null) { // Register. @@ -75,7 +71,7 @@ export abstract class Component { } /** - * Report whether this component has been registed with its container or not. + * Report whether this component has been registered with its container or not. * In principle, all components should have a container, but at the time of * their construction there is a brief period where they are not. This is the * only moment that a component is allowed register with its container. @@ -109,73 +105,78 @@ export abstract class Component { return false; } - /** - * Return the alias of this component, or an empty string if none was set. - */ - public _getAlias(): string { - if (this._alias) return this._alias - else return "" - } - /** * Return a string that identifies this component. * The name is a path constructed as `[App]/[..]/[Container]/[This]`. */ - _getFullyQualifiedName(): string { - var path = ""; + _getFullyQualifiedName(): string { if (!(this instanceof App)) { - path = this._container._getFullyQualifiedName(); - } - if (path != "") { - path += "/" + this._getName(); + return this._container._getFullyQualifiedName() + Component.pathSeparator + this._getName(); } else { - path = this._getName(); + return this._getName() } - return path; } /** - * Return a string that identifies this component within its container. + * Given a component and its container (the global object if the component + * is an `App`), return the key of the entry that matches the component. + * @param component a component of which the object is assumed to be its + * container + * @param object the assumed container of the component + * @returns the key of the entry that matches the component */ - public _getName(): string { - - var name = "" - if (!(this instanceof App)) { - for (const [key, value] of Object.entries(this._container)) { - if (value === this) { - name = `${key}` - break - } + private static keyOfMatchingEntry(component: Component, object: Object) { + for (const [key, value] of Object.entries(object)) { + if (value === component) { + return `${key}` } } - // Handle multiports - if (this instanceof IOPort) { - for (const [key, value] of Object.entries(this._container)) { - if (value instanceof MultiPort) { - let channels = value.channels() - for (let i=0; i < channels.length; i++) { - if (channels[i] === this) { - name = `${key}[${i}]` - } + } + + /** + * Given a port and its containing reactor, return the key of the entry that matches + * a multiport found in the reactor that matches the port. + * @param port a port that is assumed to be a constituent of a multiport declared on + * the given reactor + * @param reactor a reactor that is assumed to have a multiport of which one of the + * constituents is the given port + * @returns an identifier for the port based on its location in a matching multiport + */ + public static keyOfMatchingMultiport(port: Component, reactor: Reactor) { + for (const [key, value] of Object.entries(reactor)) { + if (value instanceof MultiPort) { + let channels = value.channels() + for (let i=0; i < channels.length; i++) { + if (channels[i] === port) { + return `${key}[${i}]` } } } } + } - if (this._alias) { - if (name == "") { - name = this._alias - } else { - name += ` (${this._alias})` - } + /** + * Return a string that identifies this component within its container. + * If no such string was found, return the name of the constructor. + */ + public _getName(): string { + var name + + if (this instanceof App) { + name = Component.keyOfMatchingEntry(this, global) + } else { + name = Component.keyOfMatchingEntry(this, this._container) } - // Return the constructor name in case the component wasn't found in - // its container and doesn't have an alias. - if (name == "") { - name = this.constructor.name + + if (!name && this instanceof IOPort) { + name = Component.keyOfMatchingMultiport(this, this._container) + } + + if (name) { + return name + } else { + return this.constructor.name } - - return name } /** @@ -184,13 +185,4 @@ export abstract class Component { protected _getContainer(): Reactor { return this._container } - - /** - * Set an alias to override the name assigned to this component by its - * container. - * @param alias An alternative name. - */ - protected _setAlias(alias: string) { - this._alias = alias - } } diff --git a/src/core/internal.ts b/src/core/internal.ts index 7d4a7e42d..60564a36b 100644 --- a/src/core/internal.ts +++ b/src/core/internal.ts @@ -1,4 +1,5 @@ export * from "./types" +export * from "./strings" export * from "./time" export * from "./util" export * from "./reaction" @@ -12,3 +13,4 @@ export * from "./reactor" export * from "./bank" export * from "./event" export * from "./cli" + diff --git a/src/core/port.ts b/src/core/port.ts index 495bcc81d..019e63de3 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -12,8 +12,8 @@ export abstract class Port extends Trigger { protected runtime!: Runtime; - constructor(container: Reactor, alias?: string) { - super(container, alias) + constructor(container: Reactor) { + super(container) this._linkToRuntimeObject() } diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 434c8f628..17fa31753 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -12,8 +12,7 @@ import { Mutation, Procedure, Absent, ArgList, Args, MultiReadWrite, Present, Read, Sched, SchedulableAction, Triggers, Variable, Write, TaggedEvent, Component, NonComposite, ScheduledTrigger, Trigger, TriggerManager, - Action, InPort, IOPort, MultiPort, OutPort, Port, WritablePort, Startup, Shutdown, - InMultiPort, OutMultiPort + Action, InPort, IOPort, MultiPort, OutPort, Port, WritablePort, Startup, Shutdown } from "./internal" // Set the default log level. @@ -422,8 +421,8 @@ export abstract class Reactor extends Component { * @param container The container of this reactor. */ - constructor(container: Reactor | null, alias?:string) { - super(container, alias); + constructor(container: Reactor | null) { + super(container); this._linkToRuntimeObject() this.shutdown = new Shutdown(this); @@ -1518,6 +1517,7 @@ export interface Runtime { isRunning(): boolean; } + // interface CoreFunctions { // stage(reaction: Reaction): void; // initialize(timer: Timer): void; diff --git a/src/core/strings.ts b/src/core/strings.ts new file mode 100644 index 000000000..c275aad67 --- /dev/null +++ b/src/core/strings.ts @@ -0,0 +1,9 @@ + +/** + * + */ +export class StringUtil { + public static dontIndent(template: TemplateStringsArray){ + return ('' + template.toString()).replace(/(\n)\s+/g, '$1'); + } +} diff --git a/src/core/trigger.ts b/src/core/trigger.ts index 2ea1402f2..a83c52b12 100644 --- a/src/core/trigger.ts +++ b/src/core/trigger.ts @@ -59,8 +59,8 @@ export abstract class NonComposite extends Component { protected runtime!: Runtime; - constructor(container: Reactor, alias?: string) { // FIXME: do we really want the alias here? Probably not. - super(container, alias) + constructor(container: Reactor) { + super(container) this._linkToRuntimeObject() } From 6c1ce2a0e45094c324eccc758f998d1bbe1a3d74 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Thu, 24 Feb 2022 17:01:53 -0800 Subject: [PATCH 48/68] Improved code coverage ot multiport.ts to 90% --- __tests__/multiport.test.ts | 24 ++++++++++++++++++++++++ src/core/component.ts | 2 +- src/core/multiport.ts | 12 ++++-------- src/core/trigger.ts | 2 +- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/__tests__/multiport.test.ts b/__tests__/multiport.test.ts index 1efbb6fda..4c0b57482 100644 --- a/__tests__/multiport.test.ts +++ b/__tests__/multiport.test.ts @@ -3,6 +3,27 @@ class TwoInTwoOut extends Reactor { inp = new InMultiPort(this, 2); out = new OutMultiPort(this, 2); + foo = new class extends InMultiPort { + constructor(container: Reactor) { + + test('create multiport with no container', () => { + expect( + () => { + // @ts-ignore + super(null, 4) + } + ).toThrowError("Cannot instantiate component without a parent.") + }) + super(container, 4) + test('create multiport with no container', () => { + expect( + () => { + this.asWritable(Symbol()) + } + ).toThrowError(`Referenced port is out of scope: myApp.${container._getName()}.foo`) + }) + } + }(this) constructor(parent: Reactor) { super(parent) this.addReaction( @@ -12,7 +33,10 @@ class TwoInTwoOut extends Reactor { console.log("Getting triggered") test('check read values', () => { expect(inp.channel(0).get()).toBe(42); + expect(inp.get(0)).toBe(42); expect(inp.channel(1).get()).toBe(69); + expect(inp.get(1)).toBe(69); + expect(inp.values()).toEqual([42, 69]) }); test('print input port names', () => { expect(inp._getName()).toBe("inp"); diff --git a/src/core/component.ts b/src/core/component.ts index 16c48eae3..1f9613991 100644 --- a/src/core/component.ts +++ b/src/core/component.ts @@ -49,7 +49,7 @@ export abstract class Component { // Apps are self-contained. this._container = this } else { - throw Error("Cannot instantiate component without a parent.") + throw new Error("Cannot instantiate component without a parent.") } } } diff --git a/src/core/multiport.ts b/src/core/multiport.ts index 611f6a951..7e34ecbc0 100644 --- a/src/core/multiport.ts +++ b/src/core/multiport.ts @@ -32,13 +32,9 @@ export abstract class MultiPort extends NonComposite implemen public set(index: number, value: T): void { let writableChannel = this.cache[index] if (writableChannel === undefined) { - let container = this.port.getContainer() - if (container) { - writableChannel = container.writable(this.port._channels[index]) - this.cache[index] = writableChannel - } else { - throw new Error("Attempt to set channel of multiport that has no container") - } + writableChannel = this.port.getContainer() + .writable(this.port._channels[index]) + this.cache[index] = writableChannel } writableChannel.set(value) } @@ -70,7 +66,7 @@ export abstract class MultiPort extends NonComposite implemen } public _receiveRuntimeObject(runtime: Runtime): void { - throw new Error("Method not implemented."); + throw new Error("Method not implemented."); // FIXME(marten): extend Trigger instead? } } diff --git a/src/core/trigger.ts b/src/core/trigger.ts index a83c52b12..1651458a9 100644 --- a/src/core/trigger.ts +++ b/src/core/trigger.ts @@ -15,7 +15,7 @@ export abstract class NonComposite extends Component { /** * Return the owner of this trigger. */ - public getContainer(): Reactor | null { + public getContainer(): Reactor { return this._getContainer() } From 226ee69a54f1b3423bdf7f26bbdd15633b8a0fbc Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 26 Feb 2022 14:52:25 -0800 Subject: [PATCH 49/68] Add width() interface to MultiWrite so that we can get width for MultiReadWrite. --- src/core/multiport.ts | 4 ++++ src/core/types.ts | 9 +++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/multiport.ts b/src/core/multiport.ts index 7e34ecbc0..0628090bf 100644 --- a/src/core/multiport.ts +++ b/src/core/multiport.ts @@ -38,6 +38,10 @@ export abstract class MultiPort extends NonComposite implemen } writableChannel.set(value) } + + public width(): number { + return this.port.width + } }(this) public get(index: number): T | undefined { diff --git a/src/core/types.ts b/src/core/types.ts index 8d12a12ea..fdd3893bc 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -27,16 +27,17 @@ import { Tag, TimeValue } from "./internal"; /** * Interface for readable variables. */ - export interface Read { - get(): T | Absent; +export interface Read { + get(): T | Absent; } export interface MultiRead { - get(index: number): T | Absent + get(index: number): T | Absent } export interface MultiWrite { - set: (index: number, value: T) => void; + set: (index: number, value: T) => void; + width(): number } //--------------------------------------------------------------------------// From fbc49bc9283de6b6767031a0fd99346bcaf2e692 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 26 Feb 2022 23:28:02 -0800 Subject: [PATCH 50/68] Add values() interface to MultiWrite so that we can get width for MultiReadWrite. --- src/core/multiport.ts | 4 ++++ src/core/types.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/src/core/multiport.ts b/src/core/multiport.ts index 0628090bf..cb642c00e 100644 --- a/src/core/multiport.ts +++ b/src/core/multiport.ts @@ -42,6 +42,10 @@ export abstract class MultiPort extends NonComposite implemen public width(): number { return this.port.width } + + public values(): Array { + return this.port.values() + } }(this) public get(index: number): T | undefined { diff --git a/src/core/types.ts b/src/core/types.ts index fdd3893bc..db57f8821 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -38,6 +38,7 @@ export interface Read { export interface MultiWrite { set: (index: number, value: T) => void; width(): number + values(): Array } //--------------------------------------------------------------------------// From 103ef870a115145aa018d5f394a918a4078bcefa Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Sat, 26 Feb 2022 23:31:32 -0800 Subject: [PATCH 51/68] Update lingua franca version. --- lingua-franca-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index 8974a1cce..968cb1431 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -6fab59ae64673d9e103914342680f517c88d58f5 +62afeb012c6551b4f9a81867540131f46332bbab From e7d1dff215516eaad9f09327e1b14a475a194d31 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sun, 27 Feb 2022 18:10:13 -0800 Subject: [PATCH 52/68] Simplification of dealings with (multiport) triggers. --- __tests__/multiport.test.ts | 3 +-- src/core/multiport.ts | 54 ++++++++++++++++++++++++++++--------- src/core/port.ts | 4 +-- src/core/reactor.ts | 15 +++-------- src/core/trigger.ts | 21 ++++----------- src/core/types.ts | 6 ++--- 6 files changed, 56 insertions(+), 47 deletions(-) diff --git a/__tests__/multiport.test.ts b/__tests__/multiport.test.ts index 4c0b57482..6440cb9ac 100644 --- a/__tests__/multiport.test.ts +++ b/__tests__/multiport.test.ts @@ -15,7 +15,7 @@ class TwoInTwoOut extends Reactor { ).toThrowError("Cannot instantiate component without a parent.") }) super(container, 4) - test('create multiport with no container', () => { + test('make port writable with invalid key', () => { expect( () => { this.asWritable(Symbol()) @@ -30,7 +30,6 @@ class TwoInTwoOut extends Reactor { new Triggers(this.inp), new Args(this.inp), function (this, inp) { - console.log("Getting triggered") test('check read values', () => { expect(inp.channel(0).get()).toBe(42); expect(inp.get(0)).toBe(42); diff --git a/src/core/multiport.ts b/src/core/multiport.ts index cb642c00e..0095c0075 100644 --- a/src/core/multiport.ts +++ b/src/core/multiport.ts @@ -1,7 +1,6 @@ -import { Absent, InPort, IOPort, MultiRead, MultiReadWrite, NonComposite, OutPort, Present, Reactor, Runtime, WritablePort } from "./internal"; +import { Absent, InPort, IOPort, MultiRead, MultiReadWrite, OutPort, Present, Reactor, Runtime, WritablePort, Trigger, TriggerManager, Reaction } from "./internal"; - -export abstract class MultiPort extends NonComposite implements MultiRead { +export abstract class MultiPort extends Trigger implements MultiRead { protected _channels: Array> @@ -33,7 +32,7 @@ export abstract class MultiPort extends NonComposite implemen let writableChannel = this.cache[index] if (writableChannel === undefined) { writableChannel = this.port.getContainer() - .writable(this.port._channels[index]) + .writable(this.port._channels[index]) this.cache[index] = writableChannel } writableChannel.set(value) @@ -60,7 +59,9 @@ export abstract class MultiPort extends NonComposite implemen return values } - public abstract values(): Array + public values(): Array { + return MultiPort.values(this._channels) + } /** * Only the holder of the key may obtain a writable port. @@ -73,9 +74,42 @@ export abstract class MultiPort extends NonComposite implemen throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) } + + /** + * Inner class instance to let the container configure this port. + */ + protected manager = new class implements TriggerManager { + constructor(private port: MultiPort) { } + + getContainer(): Reactor { + return this.port.getContainer() + } + + addReaction(reaction: Reaction): void { + this.port.channels().forEach(channel => channel.getManager(this.getContainer()._getKey(channel)).addReaction(reaction)) + } + + delReaction(reaction: Reaction): void { + this.port.channels().forEach(channel => channel.getManager(this.port._key).delReaction(reaction)) + } + }(this) + + getManager(key: Symbol | undefined): TriggerManager { + if (this._key == key) { + return this.manager + } + throw Error("Unable to grant access to manager.") + } + + isPresent(): boolean { + return this.channels().some(channel => channel.isPresent()) + } + public _receiveRuntimeObject(runtime: Runtime): void { - throw new Error("Method not implemented."); // FIXME(marten): extend Trigger instead? + throw new Error("Multiports do not request to be linked to the runtime object, hence this method shall not be invoked."); } + + } export class InMultiPort extends MultiPort { @@ -96,9 +130,7 @@ export class InMultiPort extends MultiPort { } } - public values(): Array { - return MultiPort.values(this._channels) - } + } export class OutMultiPort extends MultiPort { @@ -106,10 +138,6 @@ export class OutMultiPort extends MultiPort { return this._channels[index] } - public _receiveRuntimeObject(runtime: Runtime): void { - throw new Error("Method not implemented."); - } - constructor(container: Reactor, width: number) { super(container, width) this._channels = new Array>(width) diff --git a/src/core/port.ts b/src/core/port.ts index 019e63de3..aafd0aed6 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -1,6 +1,6 @@ import { - Reaction, Reactor, Runtime, Tag, NonComposite, Trigger, TriggerManager, - Absent, MultiRead, MultiReadWrite, Present, ReadWrite, Log + Reaction, Reactor, Runtime, Tag, Trigger, TriggerManager, + Absent, MultiReadWrite, Present, ReadWrite, Log } from "./internal" diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 17fa31753..dbe087ea0 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -11,7 +11,7 @@ import { SortableDependencyGraph, Log, DependencyGraph, Reaction, Priority, Mutation, Procedure, Absent, ArgList, Args, MultiReadWrite, Present, Read, Sched, SchedulableAction, Triggers, Variable, Write, TaggedEvent, - Component, NonComposite, ScheduledTrigger, Trigger, TriggerManager, + Component, ScheduledTrigger, Trigger, TriggerManager, Action, InPort, IOPort, MultiPort, OutPort, Port, WritablePort, Startup, Shutdown } from "./internal" @@ -328,7 +328,7 @@ export abstract class Reactor extends Component { * @param key The key that verifies the containment relation between this * reactor and the component, with at most one level of indirection. */ - protected _getKey(component: NonComposite, key?: Symbol): Symbol | undefined { + public _getKey(component: Trigger, key?: Symbol): Symbol | undefined { if (component._isContainedBy(this) || this._key === key) { return this._keyChain.get(component) } else if (!(component instanceof Action) && @@ -524,19 +524,12 @@ export abstract class Reactor extends Component { if (t instanceof Trigger) { t.getManager(this._getKey(t)).addReaction(reaction) } - if (t instanceof MultiPort) { - console.log("Encountered multiport!! >>>>>>>>>-----") - if (t instanceof MultiPort) { - t.channels().forEach(channel => channel.getManager(this._getKey(channel)).addReaction(reaction)) - } - } - + // Also record this trigger as a dependency. if (t instanceof IOPort) { this._dependencyGraph.addEdge(reaction, t) - //this._addDependency(t, reaction); } else if (t instanceof MultiPort) { - // FIXME(marten) + t.channels().forEach(channel => this._dependencyGraph.addEdge(reaction, channel)) } else { Log.debug(this, () => ">>>>>>>> not a dependency: " + t); } diff --git a/src/core/trigger.ts b/src/core/trigger.ts index 1651458a9..4be8d4c1a 100644 --- a/src/core/trigger.ts +++ b/src/core/trigger.ts @@ -8,23 +8,10 @@ export interface TriggerManager { delReaction(reaction: Reaction): void; } - - -export abstract class NonComposite extends Component { - - /** - * Return the owner of this trigger. - */ - public getContainer(): Reactor { - return this._getContainer() - } - -} - /** * Abstract class for a trigger. A trigger may be an action, port, or timer. */ - export abstract class Trigger extends NonComposite { + export abstract class Trigger extends Component { /** * Reactions to trigger. @@ -46,8 +33,10 @@ export abstract class NonComposite extends Component { */ abstract isPresent(): boolean; -} - + public getContainer() { + return this._getContainer() + } + } /** diff --git a/src/core/types.ts b/src/core/types.ts index db57f8821..26a3b5382 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -1,5 +1,5 @@ -import { Tag, TimeValue } from "./internal"; +import { Tag, TimeValue, Trigger } from "./internal"; /** * A variable can be read, written to, or scheduled. Variables may be passed to @@ -75,8 +75,8 @@ export class Args { } export class Triggers { - list: Variable[]; - constructor(trigger: Variable, ...triggers: Variable[]) { + list: Trigger[]; + constructor(trigger: Trigger, ...triggers: Trigger[]) { this.list = triggers.concat(trigger) } } From 7779b12f6ecb9c46508b42c551b80706cbcf6bcd Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 28 Feb 2022 15:35:02 -0800 Subject: [PATCH 53/68] Fixed unit tests --- __tests__/dependencies.ts | 22 +++++++++++----------- __tests__/hierarchy.ts | 6 +++--- __tests__/mutations.test.ts | 3 ++- package-lock.json | 29 ++++++++++++++++++++++++++++- package.json | 4 +++- src/core/component.ts | 2 +- src/core/reactor.ts | 15 ++++++++++++++- src/core/strings.ts | 14 ++++++++++++++ 8 files changed, 76 insertions(+), 19 deletions(-) diff --git a/__tests__/dependencies.ts b/__tests__/dependencies.ts index a63217912..efe36bfdc 100644 --- a/__tests__/dependencies.ts +++ b/__tests__/dependencies.ts @@ -104,9 +104,9 @@ describe('Manually constructed precedence graphs', () => { expect(graph.toString()).toBe( StringUtil.dontIndent `digraph G { - "App.R[R0]"->"App.R[R1]"->"App.R[R4]"->"App.R[R3]"->"App.R[R5]"; - "App.R[R0]"->"App.R[R4]"; - "App.R[R1]"->"App.R[R2]"->"App.R[R3]"; + "app.R[R0]"->"app.R[R1]"->"app.R[R4]"->"app.R[R3]"->"app.R[R5]"; + "app.R[R0]"->"app.R[R4]"; + "app.R[R1]"->"app.R[R2]"->"app.R[R3]"; }` ); }); @@ -127,9 +127,9 @@ describe('Manually constructed precedence graphs', () => { expect(graph.size()[1]).toEqual(6); // E expect(graph.toString()).toBe( `digraph G { -"App.R[R0]"->"App.R[R1]"->"App.R[R2]"->"App.R[R3]"->"App.R[R5]"; -"App.R[R1]"->"App.R[R4]"; -"App.R[R0]"->"App.R[R4]"; +"app.R[R0]"->"app.R[R1]"->"app.R[R2]"->"app.R[R3]"->"app.R[R5]"; +"app.R[R1]"->"app.R[R4]"; +"app.R[R0]"->"app.R[R4]"; }`); }); @@ -141,8 +141,8 @@ describe('Manually constructed precedence graphs', () => { expect(graph.toString()).toBe( StringUtil.dontIndent `digraph G { - "App.R[R2]"->"App.R[R3]"->"App.R[R5]"; - "App.R[R0]"->"App.R[R4]"; + "app.R[R2]"->"app.R[R3]"->"app.R[R5]"; + "app.R[R0]"->"app.R[R4]"; }` ); }); @@ -156,9 +156,9 @@ describe('Manually constructed precedence graphs', () => { expect(graph.toString()).toBe( StringUtil.dontIndent `digraph G { - "App.R[R2]"->"App.R[R3]"->"App.R[R5]"; - "App.R[R0]"->"App.R[R4]"; - "App.R[R2]"->"App.R[R6]"; + "app.R[R2]"->"app.R[R3]"->"app.R[R5]"; + "app.R[R0]"->"app.R[R4]"; + "app.R[R2]"->"app.R[R6]"; }` ); }); diff --git a/__tests__/hierarchy.ts b/__tests__/hierarchy.ts index 9d93bd861..56ce25604 100644 --- a/__tests__/hierarchy.ts +++ b/__tests__/hierarchy.ts @@ -21,15 +21,15 @@ var app = new class extends App { describe('Container to Contained', () => { it('app name', () => { - expect(app.toString()).toBe(""); + expect(app.toString()).toBe("app"); }); it('contained reactor name', () => { - expect(app.container.contained.toString()).toBe(".container.contained"); + expect(app.container.contained.toString()).toBe("app.container.contained"); }); it('container reactor name', () =>{ - expect(app.container.toString()).toBe(".container"); + expect(app.container.toString()).toBe("app.container"); }) diff --git a/__tests__/mutations.test.ts b/__tests__/mutations.test.ts index 2af96077a..acf696e71 100644 --- a/__tests__/mutations.test.ts +++ b/__tests__/mutations.test.ts @@ -129,9 +129,10 @@ class ZenoClock extends Reactor { } class Zeno extends App { + readonly zeno = new ZenoClock(this, 1) constructor(timeout: TimeValue, success: () => void, fail: () => void) { super(timeout, false, false, success, fail); - new ZenoClock(this, 1) + var self = this; this.addReaction(new Triggers(this.shutdown), new Args(), function(this) { diff --git a/package-lock.json b/package-lock.json index 6c14b6eb4..01aa86229 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,8 @@ "heap": "^0.2.6", "microtime": "^3.0.0", "nanotimer": "^0.3.15", - "ulog": "^1.1.0" + "ulog": "^1.1.0", + "uuid": "^8.3.2" }, "devDependencies": { "@babel/cli": "^7.4.3", @@ -32,6 +33,7 @@ "@babel/preset-typescript": "^7.7.7", "@types/jest": "^26.0.24", "@types/node": "^16.3.3", + "@types/uuid": "^8.3.4", "dtslint": "^3.4.2", "jest": "^27.0.6", "marked": ">=4.0.10", @@ -3364,6 +3366,12 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, "node_modules/@types/yargs": { "version": "15.0.14", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", @@ -12505,6 +12513,14 @@ "dev": true, "optional": true }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.0.0.tgz", @@ -15325,6 +15341,12 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, "@types/yargs": { "version": "15.0.14", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", @@ -22409,6 +22431,11 @@ "dev": true, "optional": true }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "v8-to-istanbul": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.0.0.tgz", diff --git a/package.json b/package.json index f7151d82f..cca3d8fce 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "heap": "^0.2.6", "microtime": "^3.0.0", "nanotimer": "^0.3.15", - "ulog": "^1.1.0" + "ulog": "^1.1.0", + "uuid": "^8.3.2" }, "devDependencies": { "@babel/cli": "^7.4.3", @@ -26,6 +27,7 @@ "@babel/preset-typescript": "^7.7.7", "@types/jest": "^26.0.24", "@types/node": "^16.3.3", + "@types/uuid": "^8.3.4", "dtslint": "^3.4.2", "jest": "^27.0.6", "marked": ">=4.0.10", diff --git a/src/core/component.ts b/src/core/component.ts index 1f9613991..c3263e837 100644 --- a/src/core/component.ts +++ b/src/core/component.ts @@ -163,7 +163,7 @@ export abstract class Component { var name if (this instanceof App) { - name = Component.keyOfMatchingEntry(this, global) + name = this._name } else { name = Component.keyOfMatchingEntry(this, this._container) } diff --git a/src/core/reactor.ts b/src/core/reactor.ts index dbe087ea0..373bf51e4 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -14,6 +14,7 @@ import { Component, ScheduledTrigger, Trigger, TriggerManager, Action, InPort, IOPort, MultiPort, OutPort, Port, WritablePort, Startup, Shutdown } from "./internal" +import { v4 as uuidv4 } from 'uuid'; // Set the default log level. Log.global.level = Log.levels.ERROR; @@ -1556,12 +1557,14 @@ export interface ReactionSandbox { getBankIndex: () => number } + export class App extends Reactor { readonly _alarm = new Alarm(); private _errored = false private _errorMessage?: string + readonly _uuid = uuidv4() /** * Set of reactions to stage when this app starts executing. @@ -1820,6 +1823,8 @@ export class App extends Reactor { private snooze: Action; + readonly _name:string + /** * Create a new top-level reactor. * @param executionTimeout Optional parameter to let the execution of the app time out. @@ -1834,7 +1839,15 @@ export class App extends Reactor { public success: () => void = () => {}, public failure: () => void = () => {}) { super(null); - + + let name = this.constructor.name + if (name == "") { + name = "app" + } else { + name = name.charAt(0).toLowerCase() + name.slice(1) + } + this._name = name + // Update pointer to runtime object for this reactor and // its startup and shutdown action since the inner class // instance this.__runtime isn't initialized up until here. diff --git a/src/core/strings.ts b/src/core/strings.ts index c275aad67..af481a035 100644 --- a/src/core/strings.ts +++ b/src/core/strings.ts @@ -6,4 +6,18 @@ export class StringUtil { public static dontIndent(template: TemplateStringsArray){ return ('' + template.toString()).replace(/(\n)\s+/g, '$1'); } + + public static toRegex(template: TemplateStringsArray, ...keys:any[]) { + return (function(...values:any[]) { + let dict = values[values.length - 1] || {}; + let result = [template[0]]; + keys.forEach(function(key, i) { + let value = Number.isInteger(key) ? values[key] : dict[key]; + result.push(value, template[i + 1]); + }); + return result.join('').replace(/(\n)\s+/g, '$1'); + }); + } + + } From 54145d3b351c9a75b7ccb4015bf27237041efdee Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Mon, 28 Feb 2022 16:43:17 -0800 Subject: [PATCH 54/68] Clean up code and add missing comments. --- src/core/port.ts | 10 +++------- src/core/reaction.ts | 2 +- src/core/reactor.ts | 21 ++++----------------- src/core/strings.ts | 8 +++++++- 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/core/port.ts b/src/core/port.ts index aafd0aed6..e42c2a527 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -3,9 +3,6 @@ import { Absent, MultiReadWrite, Present, ReadWrite, Log } from "./internal" - -// FIXME(marten): moving these two to port.ts results in a circular import problem with many test files: - export abstract class Port extends Trigger { protected receivers: Set> = new Set(); @@ -23,8 +20,6 @@ export abstract class Port extends Trigger { /** The current value associated with this port. */ protected value: T | Absent; - // abstract get(): T | undefined; - public _receiveRuntimeObject(runtime: Runtime) { if (!this.runtime) { this.runtime = runtime @@ -64,6 +59,9 @@ export abstract class Port extends Trigger { abstract getPort(): IOPort // FIXME: just extend interface instead. } +/** + * Interface for a writable multi port, intended as a wrapper for a multi port. + */ export interface WritableMultiPort extends MultiReadWrite { getWriters(): Array> getPorts(): Array> @@ -126,7 +124,6 @@ export abstract class IOPort extends Port { this.port.tag = this.port.runtime.util.getCurrentTag(); // Set values in downstream receivers. this.port.receivers.forEach(p => p.set(value)) - //console.log("Set called. The number of reactions is: " + this.port.reactions.size) // Stage triggered reactions for execution. this.port.reactions.forEach(r => this.port.runtime.stage(r)) } @@ -174,7 +171,6 @@ export abstract class IOPort extends Port { } addReaction(reaction: Reaction): void { this.port.reactions.add(reaction) - //console.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") } delReaction(reaction: Reaction): void { this.port.reactions.delete(reaction) diff --git a/src/core/reaction.ts b/src/core/reaction.ts index 791d71976..ca1da3e67 100644 --- a/src/core/reaction.ts +++ b/src/core/reaction.ts @@ -162,7 +162,7 @@ export class Reaction implements Sortable, PrioritySetElement>>>>>>>>>>> last caller:" + lastCaller) for (let e of effects) { if (!(e instanceof CalleePort)) { // Also add edge to the local graph. @@ -1017,7 +1016,6 @@ protected _getFirstReactionOrMutation(): Reaction | undefined { (writer as WritablePort); let val = src.get() if (this._runtime.isRunning() && val !== undefined) { - //console.log(">>>>>>>>>>>>>>>>>>>>>>>>><<<<<>>>>>>>>>>>>>>>>>>>>>") writer.set(val) } } @@ -1503,24 +1501,13 @@ class ReactionQueue extends PrioritySet { export interface Runtime { util:UtilityFunctions; - //core: CoreFunctions; stage(reaction: Reaction): void; initialize(timer: Timer): void; schedule(e: TaggedEvent): void; delete(r: Reactor): void; isRunning(): boolean; } - - -// interface CoreFunctions { -// stage(reaction: Reaction): void; -// initialize(timer: Timer): void; -// schedule(e: TaggedEvent): void; -// mark(r: Reactor): void; -// isRunning(): boolean; -// } - -interface UtilityFunctions { // +interface UtilityFunctions { requestStop(): void; reportError(message?: string): void; requestErrorStop(message?: string): void; @@ -1636,9 +1623,8 @@ export class App extends Reactor { }(this); - /** - * + * Inner class that provides access to the Runtime object. */ private __runtime: Runtime = new class implements Runtime { util: UtilityFunctions @@ -1704,6 +1690,7 @@ export class App extends Reactor { this.app._timersToSchedule.add(timer) } } + /** * Push an event onto the event queue. * @param e Tagged event to push onto the event queue. @@ -1731,7 +1718,7 @@ export class App extends Reactor { * Mark a reactor for deletion. At the end of logical time at which * this method was invoked the reactor will be removed from its * container. - * @param r + * @param r The reactor to be deleted. */ public delete(r: Reactor): void { this.app._reactorsToRemove.push(r) diff --git a/src/core/strings.ts b/src/core/strings.ts index af481a035..f82e14793 100644 --- a/src/core/strings.ts +++ b/src/core/strings.ts @@ -1,8 +1,14 @@ /** - * + * Utility class for handling strings, for example, to format diagraph + * string representation. */ export class StringUtil { + /** + * Remove indentation in the multi-line string. + * @param template Multi-line string whose indentation should be removed. + * @returns String without indentation. + */ public static dontIndent(template: TemplateStringsArray){ return ('' + template.toString()).replace(/(\n)\s+/g, '$1'); } From 681283f93b83c2881277cf2d94df21ba0f37a592 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 28 Feb 2022 17:05:18 -0800 Subject: [PATCH 55/68] Improving code coverage of multiports --- __tests__/multiport.test.ts | 21 ++++++++++++++++----- src/core/multiport.ts | 11 +++++++++-- src/core/types.ts | 3 ++- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/__tests__/multiport.test.ts b/__tests__/multiport.test.ts index 6440cb9ac..c4c4e8282 100644 --- a/__tests__/multiport.test.ts +++ b/__tests__/multiport.test.ts @@ -24,8 +24,17 @@ class TwoInTwoOut extends Reactor { }) } }(this) + constructor(parent: Reactor) { super(parent) + let writer = this.allWritable(this.inp) + test('check inactive during construction', () => { + expect(this._active).toBe(false) + }) + test('check multiport width', () => { + expect(this.inp.width()).toBe(2) + expect(writer.width()).toBe(2) + }) this.addReaction( new Triggers(this.inp), new Args(this.inp), @@ -52,13 +61,15 @@ class TwoInTwoOut extends Reactor { function (out) { test('start up reaction triggered', () => { expect(true).toBe(true); - }); - out.set(0, 42) - out.set(1, 69) - test('check written values', () => { + }) + test('check multiport values before and after writing', () => { + expect(out.values()).toEqual([undefined, undefined]) + out.set(0, 42) + out.set(1, 69) expect(out.get(0)).toBe(42); expect(out.get(1)).toBe(69); - }); + expect(out.values()).toEqual([42, 69]) + }) } ); } diff --git a/src/core/multiport.ts b/src/core/multiport.ts index 0095c0075..c4c4f9637 100644 --- a/src/core/multiport.ts +++ b/src/core/multiport.ts @@ -7,10 +7,17 @@ export abstract class MultiPort extends Trigger implements Mu abstract channels(): Array> abstract channel(index: number): IOPort + + private readonly _width - constructor(container: Reactor, readonly width: number) { + public width(): number { + return this._width + } + + constructor(container: Reactor, width: number) { super(container) this._channels = new Array(width) + this._width = width } /** @@ -39,7 +46,7 @@ export abstract class MultiPort extends Trigger implements Mu } public width(): number { - return this.port.width + return this.port.width() } public values(): Array { diff --git a/src/core/types.ts b/src/core/types.ts index 26a3b5382..cbba9eff8 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -32,12 +32,13 @@ export interface Read { } export interface MultiRead { + width(): number get(index: number): T | Absent } export interface MultiWrite { - set: (index: number, value: T) => void; width(): number + set: (index: number, value: T) => void; values(): Array } From 980074a0d17a2c09a4ac812a6fac36cf9ebeced9 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Mon, 28 Feb 2022 18:02:01 -0800 Subject: [PATCH 56/68] Update lingua-franca version. --- lingua-franca-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index 968cb1431..83b273bda 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -62afeb012c6551b4f9a81867540131f46332bbab +f5de3b5fb138187a424312931f4bf24114e6d37a From 54249f511b627be9313ae89760c7649b74c1419d Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 28 Feb 2022 18:07:39 -0800 Subject: [PATCH 57/68] Added comments --- src/core/multiport.ts | 238 ++++++++++++++++++++++++++++-------------- src/core/types.ts | 16 +++ 2 files changed, 177 insertions(+), 77 deletions(-) diff --git a/src/core/multiport.ts b/src/core/multiport.ts index c4c4f9637..2fd7779e5 100644 --- a/src/core/multiport.ts +++ b/src/core/multiport.ts @@ -1,19 +1,55 @@ -import { Absent, InPort, IOPort, MultiRead, MultiReadWrite, OutPort, Present, Reactor, Runtime, WritablePort, Trigger, TriggerManager, Reaction } from "./internal"; - +import { + Absent, InPort, IOPort, MultiRead, MultiReadWrite, OutPort, Present, + Reactor, Runtime, WritablePort, Trigger, TriggerManager, Reaction +} from "./internal"; + +/** + * A trigger that represents a multiport. All of channels of a multiport can be read. + * To obtain a writable version, see @link{Reactor.allWritable()}. + * + * @author Marten Lohstroh + * @author Hokeun Kim + */ export abstract class MultiPort extends Trigger implements MultiRead { - protected _channels: Array> - + /** + * Return all channels of this multiport. + */ abstract channels(): Array> + /** + * Return the channel identified by the given index. + * @param index the index of the requested channel + */ abstract channel(index: number): IOPort - + + /** + * The channels of this multiport. + */ + protected _channels: Array> + + /** @inheritdoc */ private readonly _width - public width(): number { - return this._width + /** + * Given an array of ports (channels), return an array holding the ports' + * current values. + * @param ports the ports to return the values of + * @returns the current values of the given ports + */ + public static values(ports: Array>): Array { + let values = new Array(ports.length); + for (let i = 0; i < values.length; i++) { + values[i] = ports[i].get(); + } + return values } + /** + * Construct a new multiport. + * @param container the reactor that will contain the new instance + * @param width the number of channels of newly created instance + */ constructor(container: Reactor, width: number) { super(container) this._channels = new Array(width) @@ -21,143 +57,191 @@ export abstract class MultiPort extends Trigger implements Mu } /** - * Inner class instance to gain access to Write interface. - */ - protected writer = new class implements MultiReadWrite { - - private readonly cache: Array> - - constructor(private port: MultiPort) { - this.cache = new Array(); - } - - public get(index: number): T | undefined { - return this.port._channels[index].get() - } - - public set(index: number, value: T): void { - let writableChannel = this.cache[index] - if (writableChannel === undefined) { - writableChannel = this.port.getContainer() - .writable(this.port._channels[index]) - this.cache[index] = writableChannel - } - writableChannel.set(value) - } - - public width(): number { - return this.port.width() - } - - public values(): Array { - return this.port.values() + * Obtain a writable version of this port, provided that the caller holds the required key. + * @param key + */ + public asWritable(key: Symbol | undefined): MultiReadWrite { + if (this._key === key) { + return this.writer } - }(this) + throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) + } + /** @inheritdoc */ public get(index: number): T | undefined { return this._channels[index].get() } - public static values(ports: Array>): Array { - let values = new Array(ports.length); - for (let i = 0; i < values.length; i++) { - values[i] = ports[i].get(); + /** @inheritdoc */ + getManager(key: Symbol | undefined): TriggerManager { + if (this._key == key) { + return this.manager } - return values + throw Error("Unable to grant access to manager.") } - public values(): Array { - return MultiPort.values(this._channels) + /** + * Return whether this multiport has any channels that are present. + * @returns true if there are any present channels + */ + isPresent(): boolean { + return this.channels().some(channel => channel.isPresent()) } /** - * Only the holder of the key may obtain a writable port. - * @param key + * Return the number of channels of this multiport. + * @returns the number of channels */ - public asWritable(key: Symbol | undefined): MultiReadWrite { - if (this._key === key) { - return this.writer - } - throw Error("Referenced port is out of scope: " + this._getFullyQualifiedName()) + public width(): number { + return this._width } + /** + * Return an array of which the elements represent the current value of + * each channel, which may either be present or absent (i.e., undefined). + * @returns an array of values + */ + public values(): Array { + return MultiPort.values(this._channels) + } /** * Inner class instance to let the container configure this port. */ - protected manager = new class implements TriggerManager { + protected manager = new class implements TriggerManager { + /** @inheritdoc */ constructor(private port: MultiPort) { } - + + /** @inheritdoc */ getContainer(): Reactor { return this.port.getContainer() } + /** @inheritdoc */ addReaction(reaction: Reaction): void { - this.port.channels().forEach(channel => channel.getManager(this.getContainer()._getKey(channel)).addReaction(reaction)) + this.port.channels().forEach( + channel => channel.getManager( + this.getContainer()._getKey(channel) + ).addReaction(reaction)) } + /** @inheritdoc */ delReaction(reaction: Reaction): void { - this.port.channels().forEach(channel => channel.getManager(this.port._key).delReaction(reaction)) + this.port.channels().forEach( + channel => channel.getManager( + this.port._key + ).delReaction(reaction) + ) } }(this) - getManager(key: Symbol | undefined): TriggerManager { - if (this._key == key) { - return this.manager - } - throw Error("Unable to grant access to manager.") + /** + * Unimplemented method (multiports require not access to the runtime object). + */ + public _receiveRuntimeObject(runtime: Runtime): void { + throw new Error("Multiports do not request to be linked to the" + + " runtime object, hence this method shall not be invoked."); } - isPresent(): boolean { - return this.channels().some(channel => channel.isPresent()) - } + /** + * Inner class instance to gain access to MultiWrite interface. + */ + protected writer = new class implements MultiReadWrite { - public _receiveRuntimeObject(runtime: Runtime): void { - throw new Error("Multiports do not request to be linked to the runtime object, hence this method shall not be invoked."); - } + /** + * Storage for obtained writers. + */ + private readonly cache: Array> + + /** @inheritdoc */ + constructor(private port: MultiPort) { + this.cache = new Array(); + } + /** @inheritdoc */ + public get(index: number): T | undefined { + return this.port._channels[index].get() + } + + /** @inheritdoc */ + public set(index: number, value: T): void { + let writableChannel = this.cache[index] + if (writableChannel === undefined) { + writableChannel = this.port.getContainer() + .writable(this.port._channels[index]) + this.cache[index] = writableChannel + } + writableChannel.set(value) + } + + /** @inheritdoc */ + public width(): number { + return this.port.width() + } + + /** @inheritdoc */ + public values(): Array { + return this.port.values() + } + }(this) } +/** + * A trigger that represents an input port that is also multiport. + * + * @author Marten Lohstroh + * @author Hokeun Kim + */ export class InMultiPort extends MultiPort { + /** @inheritdoc */ public channel(index: number): InPort { return this._channels[index] } + /** @inheritdoc */ public channels(): Array> { return this._channels } + /** @inheritdoc */ constructor(container: Reactor, width: number) { super(container, width) - this._channels = new Array>(width) for (let i = 0; i < width; i++) { this._channels[i] = new InPort(container) } } - - } -export class OutMultiPort extends MultiPort { - public channel(index: number): OutPort { - return this._channels[index] - } +/** + * A trigger that represents an output port that is also multiport. + * + * @author Marten Lohstroh + * @author Hokeun Kim + */ +export class OutMultiPort extends MultiPort { + /** @inheritdoc */ constructor(container: Reactor, width: number) { super(container, width) - this._channels = new Array>(width) for (let i = 0; i < width; i++) { this._channels[i] = new OutPort(container) } } - public values(): Array { - return MultiPort.values(this._channels) + /** @inheritdoc */ + public channel(index: number): OutPort { + return this._channels[index] } + /** @inheritdoc */ public channels(): Array> { return this._channels } + + /** @inheritdoc */ + public values(): Array { + return MultiPort.values(this._channels) + } } diff --git a/src/core/types.ts b/src/core/types.ts index cbba9eff8..03589b26f 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -32,13 +32,29 @@ export interface Read { } export interface MultiRead { + + /** + * Return the number of channels. + */ width(): number + + /** + * Given an index that identifies a particular channel, return the current + * value of the identified channel. + * @param index the index that identifies the channel to return the value of + * @returns the value that corresponds to the identified channel + */ get(index: number): T | Absent } export interface MultiWrite { + /** + * Return the number of channels. + */ width(): number + set: (index: number, value: T) => void; + values(): Array } From 4b4e51c01a8a64e09ea6c249292b080bd2b591ab Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 28 Feb 2022 21:55:48 -0800 Subject: [PATCH 58/68] Added type annotation --- src/core/multiport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/multiport.ts b/src/core/multiport.ts index 2fd7779e5..68e1bd3b3 100644 --- a/src/core/multiport.ts +++ b/src/core/multiport.ts @@ -29,7 +29,7 @@ export abstract class MultiPort extends Trigger implements Mu protected _channels: Array> /** @inheritdoc */ - private readonly _width + private readonly _width: number /** * Given an array of ports (channels), return an array holding the ports' From 6d0028caabfd45a53598fd1333cf46964d440b1c Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 28 Feb 2022 22:38:36 -0800 Subject: [PATCH 59/68] Adjust LF pointer --- lingua-franca-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index 83b273bda..c8db45156 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -f5de3b5fb138187a424312931f4bf24114e6d37a +7662f6fc11b29915d11d334beb422d2ddfe05357 From 1fb6e3e7c2538a9911b8e0843885e1f18271a4f7 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Tue, 1 Mar 2022 21:54:27 -0800 Subject: [PATCH 60/68] Update lingua franca version. --- lingua-franca-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index c8db45156..8d5f3f581 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -7662f6fc11b29915d11d334beb422d2ddfe05357 +d5874d12ed6ec330caf514b87995ff39fe104847 From 0341e3de9e4571fff593a540493fe992826df5c7 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Tue, 1 Mar 2022 23:20:02 -0800 Subject: [PATCH 61/68] Fix for the bank-index-not-available-in-constructor-problem --- src/core/bank.ts | 17 ++++++++++++++++- src/core/reactor.ts | 26 +++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/core/bank.ts b/src/core/bank.ts index 02a554e8b..9674c9f7b 100644 --- a/src/core/bank.ts +++ b/src/core/bank.ts @@ -24,6 +24,12 @@ export class Bank { */ private readonly members: Array = new Array(); + /** + * Index of the bank member that is currently being initialized, + * or -1 if there is not initialization happening. + */ + private initializing = -1 + /** * Construct a new bank of given width on the basis of a given reactor class and a list of arguments. * @param width the width of the bank @@ -33,8 +39,17 @@ export class Bank { constructor(width: number, cls: ReactorClass, ...args: ReactorArgs) { for (let i = 0; i < width; i++) { this.members.push(Reflect.construct(cls, args, cls)); - this.members[i].setBankIndex(i) + this.initializing = i } + this.initializing = -1 + } + + /** + * Return the index of the member that is currently being initialized. + * @returns the index of the currently initializing member + */ + public initializingMember() { + return this.initializing } /** diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 2b8a4ab34..4b8ee0ab1 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -15,6 +15,7 @@ import { Action, InPort, IOPort, MultiPort, OutPort, Port, WritablePort, Startup, Shutdown } from "./internal" import { v4 as uuidv4 } from 'uuid'; +import { Bank } from "./bank"; // Set the default log level. Log.global.level = Log.levels.ERROR; @@ -237,6 +238,30 @@ export abstract class Reactor extends Component { if (component !== this && !this._keyChain.has(component)) { this._keyChain.set(component, key) } + // See if the newly registered component is a bank member + // and set its index if so. + if (component instanceof Reactor) { + component.setBankIndex(this.findBankIndex(component)) + } + } + + /** + * Return the bank index of a newly registered reactor if it happens + * to be a bank member. If it is not, return -1. + * @param component a reactor that is in the process of being initialized + * @returns an index representing the reactor's position in a bank, is it + * is a member of one. + */ + private findBankIndex(component: Reactor) { + for (const [key, value] of Object.entries(this)) { + if (value instanceof Bank) { + let index = value.initializingMember() + if (index >= 0) { + return index + } + } + } + return -1 } public _requestRuntimeObject(component: Component): void { @@ -421,7 +446,6 @@ export abstract class Reactor extends Component { * Create a new reactor. * @param container The container of this reactor. */ - constructor(container: Reactor | null) { super(container); From 4c49de80b888949517a7201b2b99597ac3b0c6ad Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 4 Mar 2022 10:43:08 -0800 Subject: [PATCH 62/68] Fix for the bank index problem that actually works --- src/core/bank.ts | 17 +++++------------ src/core/reactor.ts | 23 ++++------------------- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/src/core/bank.ts b/src/core/bank.ts index 9674c9f7b..e4824e372 100644 --- a/src/core/bank.ts +++ b/src/core/bank.ts @@ -28,7 +28,7 @@ export class Bank { * Index of the bank member that is currently being initialized, * or -1 if there is not initialization happening. */ - private initializing = -1 + public static readonly initializationMap: Map = new Map() /** * Construct a new bank of given width on the basis of a given reactor class and a list of arguments. @@ -36,20 +36,13 @@ export class Bank { * @param cls the class to construct reactor instances of that will populate the bank * @param args the arguments to pass into the constructor of the given reactor class */ - constructor(width: number, cls: ReactorClass, ...args: ReactorArgs) { + constructor(container: Reactor, width: number, cls: ReactorClass, ...args: ReactorArgs) { for (let i = 0; i < width; i++) { + Bank.initializationMap.set(container, i) + console.log("Setting initializing to " + i) this.members.push(Reflect.construct(cls, args, cls)); - this.initializing = i } - this.initializing = -1 - } - - /** - * Return the index of the member that is currently being initialized. - * @returns the index of the currently initializing member - */ - public initializingMember() { - return this.initializing + Bank.initializationMap.delete(container) } /** diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 4b8ee0ab1..a917afc74 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -241,27 +241,12 @@ export abstract class Reactor extends Component { // See if the newly registered component is a bank member // and set its index if so. if (component instanceof Reactor) { - component.setBankIndex(this.findBankIndex(component)) - } - } - - /** - * Return the bank index of a newly registered reactor if it happens - * to be a bank member. If it is not, return -1. - * @param component a reactor that is in the process of being initialized - * @returns an index representing the reactor's position in a bank, is it - * is a member of one. - */ - private findBankIndex(component: Reactor) { - for (const [key, value] of Object.entries(this)) { - if (value instanceof Bank) { - let index = value.initializingMember() - if (index >= 0) { - return index - } + let index = Bank.initializationMap.get(this) + if (index) { + component.setBankIndex(index) } + // Not in a bank. } - return -1 } public _requestRuntimeObject(component: Component): void { From f0b6c2371b2f19c278ae3f4b97220c85270cbee9 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 4 Mar 2022 11:14:34 -0800 Subject: [PATCH 63/68] Adjusted test but still stuck with index not being readable --- __tests__/bank.ts | 6 +++--- src/core/bank.ts | 4 ++-- src/core/reactor.ts | 17 +++++++++++++---- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/__tests__/bank.ts b/__tests__/bank.ts index 5f6a91446..512fb490a 100644 --- a/__tests__/bank.ts +++ b/__tests__/bank.ts @@ -23,11 +23,11 @@ class Generic extends Reactor { describe('Check bank index', () => { class myApp extends App { - b = new Bank(3, Periodic, this) - c = new Bank, [Reactor]>(2, Generic, this); + b = new Bank(this, 3, Periodic, this) + c = new Bank, [Reactor]>(this, 2, Generic, this); constructor() { super(); - it('contained actor name', () => { + it('contained member bank index', () => { expect(this.b.get(0).getBankIndex()).toBe(0); expect(this.b.get(1).getBankIndex()).toBe(1); expect(this.b.get(2).getBankIndex()).toBe(2); diff --git a/src/core/bank.ts b/src/core/bank.ts index e4824e372..5d73946fa 100644 --- a/src/core/bank.ts +++ b/src/core/bank.ts @@ -25,8 +25,8 @@ export class Bank { private readonly members: Array = new Array(); /** - * Index of the bank member that is currently being initialized, - * or -1 if there is not initialization happening. + * A mapping from containing reactors to indices corresponding to the member + * of a contained bank that is currently being initialized (if there is one). */ public static readonly initializationMap: Map = new Map() diff --git a/src/core/reactor.ts b/src/core/reactor.ts index a917afc74..17b953c8c 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -139,15 +139,21 @@ export abstract class Reactor extends Component { */ private _runtime!: Runtime; - private _bankIndex: number = -1; + private _bankIndex: number | undefined; public getBankIndex(): number { + if (this._bankIndex === undefined) { + return -1 + } return this._bankIndex } public setBankIndex(index: number): void { - if (this._bankIndex == -1) { + console.log("Setting the bank index >>>>>>>>>>>>>>>>>>>") + if (this._bankIndex === undefined) { this._bankIndex = index + } else { + throw new Error("Attempt to set bank index twice") } } @@ -242,9 +248,12 @@ export abstract class Reactor extends Component { // and set its index if so. if (component instanceof Reactor) { let index = Bank.initializationMap.get(this) - if (index) { + if (index !== undefined) { + console.log("Found bank index: " + index) component.setBankIndex(index) + console.log("Reading index again: " + component.getBankIndex()) } + console.log("Unable to find bank index for component that is contained by " + this._getName()) // Not in a bank. } } @@ -422,7 +431,7 @@ export abstract class Reactor extends Component { public getBankIndex: () => number; constructor(public reactor: Reactor) { this.util = reactor.util - this.getBankIndex = () => reactor._bankIndex + this.getBankIndex = () => reactor.getBankIndex() } } From 24e82b5c47feff48b3cc0e36521452a3782f733c Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 4 Mar 2022 14:08:59 -0800 Subject: [PATCH 64/68] Added test and fixed getFullyQualifiedName for bank members. Still diagnosing the bankIndex issue. --- __tests__/bank.ts | 16 +++++++++++----- src/core/component.ts | 19 ++++++++++++++++++- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/__tests__/bank.ts b/__tests__/bank.ts index 512fb490a..a9ca7283e 100644 --- a/__tests__/bank.ts +++ b/__tests__/bank.ts @@ -6,6 +6,7 @@ class Periodic extends Reactor { o: OutPort = new OutPort(this) constructor(parent: Reactor) { super(parent) + console.log("Bank index inside of constructor: " + this.getBankIndex()) this.addReaction( new Triggers(this.t), new Args(this.t), @@ -27,11 +28,16 @@ describe('Check bank index', () => { c = new Bank, [Reactor]>(this, 2, Generic, this); constructor() { super(); - it('contained member bank index', () => { - expect(this.b.get(0).getBankIndex()).toBe(0); - expect(this.b.get(1).getBankIndex()).toBe(1); - expect(this.b.get(2).getBankIndex()).toBe(2); - }); + test('contained bank member name', () => { + expect(this.b.get(0)._getFullyQualifiedName()).toBe("myApp.b[0]") + expect(this.b.get(1)._getFullyQualifiedName()).toBe("myApp.b[1]") + expect(this.b.get(2)._getFullyQualifiedName()).toBe("myApp.b[2]") + }) + it('contained bank member index', () => { + expect(this.b.get(0).getBankIndex()).toBe(0); + expect(this.b.get(1).getBankIndex()).toBe(1); + expect(this.b.get(2).getBankIndex()).toBe(2); + }); it('generic bank', () => { this.c.all().forEach(r => expect(typeof r.input == "number")) diff --git a/src/core/component.ts b/src/core/component.ts index c3263e837..53049f1ca 100644 --- a/src/core/component.ts +++ b/src/core/component.ts @@ -1,4 +1,4 @@ -import {Reactor, App, Runtime, MultiPort, IOPort} from "./internal" +import {Reactor, App, Runtime, MultiPort, IOPort, Bank} from "./internal" /** * Base class for named objects embedded in a hierarchy of reactors. Each @@ -155,6 +155,19 @@ export abstract class Component { } } + public static keyOfMatchingBank(member: Component, reactor: Reactor) { + for (const [key, value] of Object.entries(reactor)) { + if (value instanceof Bank) { + let members = value.all() + for (let i=0; i < members.length; i++) { + if (members[i] === member) { + return `${key}[${i}]` + } + } + } + } + } + /** * Return a string that identifies this component within its container. * If no such string was found, return the name of the constructor. @@ -172,6 +185,10 @@ export abstract class Component { name = Component.keyOfMatchingMultiport(this, this._container) } + if (!name && this instanceof Reactor) { + name = Component.keyOfMatchingBank(this, this._container) + } + if (name) { return name } else { From 0c75655b62701c87afcfc1e0cffed9d492fb37df Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 4 Mar 2022 14:40:08 -0800 Subject: [PATCH 65/68] Fix for the bank index issue --- __tests__/bank.ts | 1 - src/core/reactor.ts | 38 ++++++++++++++++---------------------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/__tests__/bank.ts b/__tests__/bank.ts index a9ca7283e..8f068fb3e 100644 --- a/__tests__/bank.ts +++ b/__tests__/bank.ts @@ -6,7 +6,6 @@ class Periodic extends Reactor { o: OutPort = new OutPort(this) constructor(parent: Reactor) { super(parent) - console.log("Bank index inside of constructor: " + this.getBankIndex()) this.addReaction( new Triggers(this.t), new Args(this.t), diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 17b953c8c..5563004c2 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -139,8 +139,16 @@ export abstract class Reactor extends Component { */ private _runtime!: Runtime; - private _bankIndex: number | undefined; + /** + * Index that specifies the location of the reactor instance in a bank, + * if it is a member of one. + */ + private _bankIndex: number; + /** + * Return the location of the reactor instance in a bank, + * if it is a member of one; return -1 otherwise. + */ public getBankIndex(): number { if (this._bankIndex === undefined) { return -1 @@ -148,15 +156,6 @@ export abstract class Reactor extends Component { return this._bankIndex } - public setBankIndex(index: number): void { - console.log("Setting the bank index >>>>>>>>>>>>>>>>>>>") - if (this._bankIndex === undefined) { - this._bankIndex = index - } else { - throw new Error("Attempt to set bank index twice") - } - } - /** * This graph has some overlap with the reactors dependency graph, but is * different in two respects: @@ -244,18 +243,6 @@ export abstract class Reactor extends Component { if (component !== this && !this._keyChain.has(component)) { this._keyChain.set(component, key) } - // See if the newly registered component is a bank member - // and set its index if so. - if (component instanceof Reactor) { - let index = Bank.initializationMap.get(this) - if (index !== undefined) { - console.log("Found bank index: " + index) - component.setBankIndex(index) - console.log("Reading index again: " + component.getBankIndex()) - } - console.log("Unable to find bank index for component that is contained by " + this._getName()) - // Not in a bank. - } } public _requestRuntimeObject(component: Component): void { @@ -442,6 +429,13 @@ export abstract class Reactor extends Component { */ constructor(container: Reactor | null) { super(container); + this._bankIndex = -1 + if (container !== null) { + let index = Bank.initializationMap.get(container) + if (index !== undefined) { + this._bankIndex = index + } + } this._linkToRuntimeObject() this.shutdown = new Shutdown(this); From b2c7dd6fea10f1c7db9f20b80347f5787872761c Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Fri, 4 Mar 2022 15:16:46 -0800 Subject: [PATCH 66/68] Update LF version. --- lingua-franca-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index 8d5f3f581..03d16af6a 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -d5874d12ed6ec330caf514b87995ff39fe104847 +e996c5e5f3ac02e85fd330c82cd759b1c76f372f From 70f472a144c2668485f63cf0afa445357e9e40b9 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 4 Mar 2022 15:44:03 -0800 Subject: [PATCH 67/68] Adding missing dependencies for multiports --- src/core/port.ts | 11 ++++++++--- src/core/reactor.ts | 26 +++++++++++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/core/port.ts b/src/core/port.ts index e42c2a527..aa10234b5 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -52,11 +52,16 @@ export abstract class Port extends Trigger { * Abstract class for a writable port. It is intended as a wrapper for a * regular port. In addition to a get method, it also has a set method and * a method for retrieving the port that it wraps. + * We have this abstract class so that we can do `instanceof` checks. */ - export abstract class WritablePort implements ReadWrite { +export abstract class WritablePort implements ReadWrite { abstract get(): T | undefined; abstract set(value: T): void; - abstract getPort(): IOPort // FIXME: just extend interface instead. + abstract getPort(): IOPort // FIXME: why not call this channel? +} + +export abstract class WritableMultiPort implements MultiReadWrite { + } /** @@ -64,7 +69,7 @@ export abstract class Port extends Trigger { */ export interface WritableMultiPort extends MultiReadWrite { getWriters(): Array> - getPorts(): Array> + getPorts(): Array> // FIXME: why not call this channels()? } diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 5563004c2..aaa44d40a 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -12,7 +12,7 @@ import { Mutation, Procedure, Absent, ArgList, Args, MultiReadWrite, Present, Read, Sched, SchedulableAction, Triggers, Variable, Write, TaggedEvent, Component, ScheduledTrigger, Trigger, TriggerManager, - Action, InPort, IOPort, MultiPort, OutPort, Port, WritablePort, Startup, Shutdown + Action, InPort, IOPort, MultiPort, OutPort, Port, WritablePort, Startup, Shutdown, WritableMultiPort } from "./internal" import { v4 as uuidv4 } from 'uuid'; import { Bank } from "./bank"; @@ -555,23 +555,31 @@ export abstract class Reactor extends Component { if (a instanceof IOPort) { this._dependencyGraph.addEdge(reaction, a) sources.add(a) - } - if (a instanceof CalleePort) { + } else if (a instanceof MultiPort) { + a.channels().forEach(channel => { + this._dependencyGraph.addEdge(reaction, a) + sources.add(channel) + }) + } else if (a instanceof CalleePort) { this._dependencyGraph.addEdge(a, reaction) - } - if (a instanceof CallerPort) { + } else if (a instanceof CallerPort) { this._dependencyGraph.addEdge(reaction, a) } // Only necessary if we want to add actions to the dependency graph. - if (a instanceof Action) { + else if (a instanceof Action) { // dep } - if (a instanceof SchedulableAction) { + else if (a instanceof SchedulableAction) { // antidep - } - if (a instanceof WritablePort) { + } else if (a instanceof WritablePort) { this._dependencyGraph.addEdge(a.getPort(), reaction) effects.add(a.getPort()) + } else if (a instanceof WritableMultiPort) { + a.getPorts().forEach(channel => { + this._dependencyGraph.addEdge(reaction, a) + effects.add(channel) + }) + } } // Make effects dependent on sources. From 21393554b8b3446b6b77206e6d28e97ef48ffca7 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Mon, 7 Mar 2022 12:05:25 -0800 Subject: [PATCH 68/68] Fixed problems with wrong/missing dependencies --- src/core/bank.ts | 4 ++++ src/core/component.ts | 2 +- src/core/multiport.ts | 18 ++++++++++++++---- src/core/port.ts | 18 ++++++++---------- src/core/reactor.ts | 6 +++--- src/core/util.ts | 3 +++ 6 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/core/bank.ts b/src/core/bank.ts index 5d73946fa..04d662a6f 100644 --- a/src/core/bank.ts +++ b/src/core/bank.ts @@ -70,4 +70,8 @@ export class Bank { public port

>(selector: (reactor: T) => P): Array

{ return this.all().reduce((acc, val) => acc.concat(selector(val)), new Array

(0)) } + + public toString() { + return "bank(" + this.members.length + ")" + } } diff --git a/src/core/component.ts b/src/core/component.ts index 53049f1ca..2929f95e8 100644 --- a/src/core/component.ts +++ b/src/core/component.ts @@ -125,7 +125,7 @@ export abstract class Component { * @param object the assumed container of the component * @returns the key of the entry that matches the component */ - private static keyOfMatchingEntry(component: Component, object: Object) { + public static keyOfMatchingEntry(component: Component, object: Object) { for (const [key, value] of Object.entries(object)) { if (value === component) { return `${key}` diff --git a/src/core/multiport.ts b/src/core/multiport.ts index 68e1bd3b3..1ce3ac0a9 100644 --- a/src/core/multiport.ts +++ b/src/core/multiport.ts @@ -1,7 +1,8 @@ import { Absent, InPort, IOPort, MultiRead, MultiReadWrite, OutPort, Present, - Reactor, Runtime, WritablePort, Trigger, TriggerManager, Reaction + Reactor, Runtime, WritablePort, Trigger, TriggerManager, Reaction, Component } from "./internal"; +import { WritableMultiPort } from "./port"; /** * A trigger that represents a multiport. All of channels of a multiport can be read. @@ -50,7 +51,7 @@ export abstract class MultiPort extends Trigger implements Mu * @param container the reactor that will contain the new instance * @param width the number of channels of newly created instance */ - constructor(container: Reactor, width: number) { + constructor(private container: Reactor, width: number) { super(container) this._channels = new Array(width) this._width = width @@ -60,7 +61,7 @@ export abstract class MultiPort extends Trigger implements Mu * Obtain a writable version of this port, provided that the caller holds the required key. * @param key */ - public asWritable(key: Symbol | undefined): MultiReadWrite { + public asWritable(key: Symbol | undefined): WritableMultiPort { if (this._key === key) { return this.writer } @@ -146,7 +147,11 @@ export abstract class MultiPort extends Trigger implements Mu /** * Inner class instance to gain access to MultiWrite interface. */ - protected writer = new class implements MultiReadWrite { + protected writer = new class extends WritableMultiPort { + + getPorts(): IOPort[] { + return this.port._channels + } /** * Storage for obtained writers. @@ -155,6 +160,7 @@ export abstract class MultiPort extends Trigger implements Mu /** @inheritdoc */ constructor(private port: MultiPort) { + super() this.cache = new Array(); } @@ -185,6 +191,10 @@ export abstract class MultiPort extends Trigger implements Mu } }(this) + public toString() { + return this.container.toString() + "." + Component.keyOfMatchingEntry(this, this.container) + } + } /** diff --git a/src/core/port.ts b/src/core/port.ts index aa10234b5..ea1163237 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -55,24 +55,22 @@ export abstract class Port extends Trigger { * We have this abstract class so that we can do `instanceof` checks. */ export abstract class WritablePort implements ReadWrite { - abstract get(): T | undefined; - abstract set(value: T): void; - abstract getPort(): IOPort // FIXME: why not call this channel? + abstract get(): T | undefined + abstract set(value: T): void + abstract getPort(): IOPort } export abstract class WritableMultiPort implements MultiReadWrite { - + abstract get(index: number): T | undefined + abstract set(index: number, value: T): void + abstract width(): number + abstract values(): Array + abstract getPorts(): Array> } /** * Interface for a writable multi port, intended as a wrapper for a multi port. */ -export interface WritableMultiPort extends MultiReadWrite { - getWriters(): Array> - getPorts(): Array> // FIXME: why not call this channels()? -} - - interface IOPortManager extends TriggerManager { addReceiver(port: WritablePort): void; delReceiver(port: WritablePort): void; diff --git a/src/core/reactor.ts b/src/core/reactor.ts index aaa44d40a..a3a7e0163 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -486,7 +486,7 @@ export abstract class Reactor extends Component { // - public allWritable(port: MultiPort): MultiReadWrite { + public allWritable(port: MultiPort): WritableMultiPort { return port.asWritable(this._getKey(port)); } @@ -557,7 +557,7 @@ export abstract class Reactor extends Component { sources.add(a) } else if (a instanceof MultiPort) { a.channels().forEach(channel => { - this._dependencyGraph.addEdge(reaction, a) + this._dependencyGraph.addEdge(reaction, channel) sources.add(channel) }) } else if (a instanceof CalleePort) { @@ -576,7 +576,7 @@ export abstract class Reactor extends Component { effects.add(a.getPort()) } else if (a instanceof WritableMultiPort) { a.getPorts().forEach(channel => { - this._dependencyGraph.addEdge(reaction, a) + this._dependencyGraph.addEdge(channel, reaction) effects.add(channel) }) diff --git a/src/core/util.ts b/src/core/util.ts index a2843bc51..12deeb457 100644 --- a/src/core/util.ts +++ b/src/core/util.ts @@ -328,6 +328,9 @@ export class DependencyGraph { function printChain(node: T, chain: Array) { dot += "\n"; dot += '"' + node + '"' + if ((node as Object).toString() == "[object Object]") { + console.error("Encountered node with no toString() implementation: " + (node as Object).constructor) + } while (chain.length > 0) { dot += "->" + '"' + chain.pop() + '"'; }