Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change TS generator to support serialization of communication in federated execution #1125

Merged
merged 13 commits into from
May 10, 2022
Merged
13 changes: 9 additions & 4 deletions org.lflang/src/org/lflang/generator/ts/TSActionGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ class TSActionGenerator (
// duplicate action if we included the one generated
// by LF.
if (action.name != "shutdown") {
stateClassProperties.add("${action.name}: __Action<${getActionType(action, federate)}>;")
stateClassProperties.add("${action.name}: __Action<${getActionType(action)}>;")
}
}
return stateClassProperties.joinToString("\n")
}

fun generateInstantiations(): String {
fun generateInstantiations(networkMessageActions: List<Action>): String {
val actionInstantiations = LinkedList<String>()
for (action in actions) {
// Shutdown actions are handled internally by the
Expand All @@ -50,8 +50,13 @@ class TSActionGenerator (
actionArgs+= ", " + action.minDelay.getTargetValue()
}
}
actionInstantiations.add(
"this.${action.name} = new __Action<${getActionType(action, federate)}>($actionArgs);")
if (action in networkMessageActions){
actionInstantiations.add(
"this.${action.name} = new __FederatePortAction<${getActionType(action)}>($actionArgs);")
} else {
actionInstantiations.add(
"this.${action.name} = new __Action<${getActionType(action)}>($actionArgs);")
}
}
}
return actionInstantiations.joinToString("\n")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class TSConstructorGenerator (
${" | "..timers.generateInstantiations()}
${" | "..parameters.generateInstantiations()}
${" | "..states.generateInstantiations()}
${" | "..actions.generateInstantiations()}
${" | "..actions.generateInstantiations(federate.networkMessageActions)}
${" | "..ports.generateInstantiations()}
${" | "..connections.generateInstantiations()}
${" | "..if (reactor.isFederated) generateFederatePortActionRegistrations(federate.networkMessageActions) else ""}
Expand Down
10 changes: 2 additions & 8 deletions org.lflang/src/org/lflang/generator/ts/TSExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,8 @@ fun Parameter.getTargetType(): String = TSTypes.getTargetType(this)
* @param action The action
* @return The TS type.
*/
fun getActionType(action: Action, federate: FederateInstance): String {
// Special handling for the networkMessage action created by
// FedASTUtils.makeCommunication(), by assigning TypeScript
// Buffer type for the action. Action<Buffer> is used as
// FederatePortAction in federation.ts.
if (action in federate.networkMessageActions) {
return "Buffer"
} else if (action.type != null) {
fun getActionType(action: Action): String {
if (action.type != null) {
return action.type.getTargetType()
} else {
return "Present"
Expand Down
9 changes: 2 additions & 7 deletions org.lflang/src/org/lflang/generator/ts/TSGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -531,11 +531,9 @@ class TSGenerator(
serializer: SupportedSerializers
): String {
return with(PrependOperator) {"""
|// FIXME: For now assume the data is a Buffer, but this is not checked.
|// Replace with ProtoBufs or MessagePack.
|// generateNetworkReceiverBody
|if (${action.name} !== undefined) {
| ${receivingPort.container.name}.${receivingPort.variable.name} = ${action.name}.toString(); // defaults to utf8 encoding
| ${receivingPort.container.name}.${receivingPort.variable.name} = ${action.name};
|}
""".trimMargin()}
}
Expand Down Expand Up @@ -570,11 +568,8 @@ class TSGenerator(
serializer: SupportedSerializers
): String {
return with(PrependOperator) {"""
|// FIXME: For now assume the data is a Buffer, but this is not checked.
|// Use SupportedSerializers for determining the serialization later.
|if (${sendingPort.container.name}.${sendingPort.variable.name} !== undefined) {
| let buf = Buffer.from(${sendingPort.container.name}.${sendingPort.variable.name})
| this.util.sendRTITimedMessage(buf, ${receivingFed.id}, ${receivingPortID});
| this.util.sendRTITimedMessage(${sendingPort.container.name}.${sendingPort.variable.name}, ${receivingFed.id}, ${receivingPortID});
|}
""".trimMargin()}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class TSImportPreambleGenerator(
|import commandLineArgs from 'command-line-args'
|import commandLineUsage from 'command-line-usage'
|import {Parameter as __Parameter, Timer as __Timer, Reactor as __Reactor, App as __App} from './core/reactor'
|import {Action as __Action, Startup as __Startup} from './core/action'
|import {Action as __Action, Startup as __Startup, FederatePortAction as __FederatePortAction} from './core/action'
|import {Bank as __Bank} from './core/bank'
|import {FederatedApp as __FederatedApp} from './core/federation'
|import {InPort as __InPort, OutPort as __OutPort, Port as __Port} from './core/port'
Expand Down
12 changes: 3 additions & 9 deletions org.lflang/src/org/lflang/generator/ts/TSReactionGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,10 @@ class TSReactionGenerator(
}

private fun generateReactionSignatureForTrigger(trigOrSource: VarRef): String {
var reactSignatureElementType = if (trigOrSource.variable.name.startsWith("networkMessage")) {
// Special handling for the networkMessage action created by
// FedASTUtils.makeCommunication(), by assigning TypeScript
// Buffer type for the action. Action<Buffer> is used as
// FederatePortAction in federation.ts.
"Buffer"
} else if (trigOrSource.variable is Timer) {
var reactSignatureElementType = if (trigOrSource.variable is Timer) {
"__Tag"
} else if (trigOrSource.variable is Action) {
getActionType(trigOrSource.variable as Action, federate)
getActionType(trigOrSource.variable as Action)
} else if (trigOrSource.variable is Port) {
getPortType(trigOrSource.variable as Port)
} else {
Expand Down Expand Up @@ -333,7 +327,7 @@ class TSReactionGenerator(
if (effect.variable is Timer) {
errorReporter.reportError("A timer cannot be an effect of a reaction")
} else if (effect.variable is Action){
reactSignatureElement += ": Sched<" + getActionType(effect.variable as Action, federate) + ">"
reactSignatureElement += ": Sched<" + getActionType(effect.variable as Action) + ">"
schedActionSet.add(effect.variable as Action)
} else if (effect.variable is Port){
reactSignatureElement += ": ${generateReactionSignatureElementForPortEffect(effect)}"
Expand Down
18 changes: 3 additions & 15 deletions test/TypeScript/src/federated/DistributedCount.lf
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,14 @@
timeout: 5 sec
};

// TODO(hokeun): Replace the following reactor with import Count from "../lib/Count.lf";
// once TypeScript serialization is implemented.
reactor Count(offset:time(0), period:time(1 sec)) {
output out:string;
timer t(offset, period);
state count:number(1);
reaction(t) -> out {=
out = String(count++);
=}
}

import Count from "../lib/Count.lf";
reactor Print {
// TODO(hokeun): Change type of inp to number once
// once TypeScript serialization is implemented.
input inp:string;
input inp:number;
state c:number(1);
reaction(inp) {=
const elapsedTime = util.getElapsedLogicalTime();
console.log("At time " + elapsedTime + ", received " + inp);
if (inp !== String(c)) {
if (inp !== c) {
util.requestErrorStop("Expected to receive " + c + ".");
}
if (elapsedTime.isEqualTo(TimeValue.msec(200).add(TimeValue.sec(c - 1)))) {
Expand Down
52 changes: 52 additions & 0 deletions test/TypeScript/src/federated/PingPongDistributed.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* This checks communication between federates
*/

target TypeScript;

reactor Ping(count:number(10)) {
input receive:number;
output send:number;
state pingsLeft:number(count);
logical action serve;
reaction (startup, serve) -> send {=
console.log(`At lo${util.getElapsedLogicalTime()}, Ping sending ${pingsLeft}`);
send = pingsLeft;
pingsLeft = pingsLeft - 1;
=}
reaction (receive) -> serve {=
if(pingsLeft > 0){
actions.serve.schedule(0, null);
}
else{
util.requestStop();
}
=}
}

lhstrh marked this conversation as resolved.
Show resolved Hide resolved
reactor Pong(expected:number(10)) {
input receive:number;
output send:number;
state count:number(0);
reaction(receive) -> send {=
count += 1;
console.log(`At logical time ${util.getElapsedLogicalTime()}, Pong received ${receive}`)
send = receive;
if (count == expected){
util.requestStop();
}
=}
reaction(shutdown) {=
if (count != expected){
util.requestErrorStop(`Pong expected to receive ${expected} inputs, but it received ${count}`);
}
console.log(`Pong received ${count} pings.`);
=}
}

lhstrh marked this conversation as resolved.
Show resolved Hide resolved
federated reactor PingPongDistributed(count: number(10)){
ping = new Ping(count=count);
pong = new Pong(expected=count);
ping.send -> pong.receive;
pong.send -> ping.receive;
}
39 changes: 39 additions & 0 deletions test/TypeScript/src/federated/StopAtShutdown.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Check that request_stop() doesn't cause
* any issues at the shutdown tag.
*
* Original bug discovered by Steven Wong <housengw@berkeley.edu>
*/
target TypeScript {
timeout: 2 sec
}

reactor A {
input inp:number;
reaction(startup) {=
console.log("Hello World!");
=}
reaction(inp) {=
console.log("Got it");
=}
reaction(shutdown) {=
util.requestStop();
=}
}

reactor B {
output out:number;
timer t(1 sec);
reaction(t) -> out {=
out = 1;
=}
reaction(shutdown) {=
util.requestStop();
=}
}

federated reactor {
a = new A();
b = new B();
b.out -> a.inp;
}