diff --git a/core/src/main/java/org/lflang/AttributeUtils.java b/core/src/main/java/org/lflang/AttributeUtils.java index 147a4b6d0d..79447cb440 100644 --- a/core/src/main/java/org/lflang/AttributeUtils.java +++ b/core/src/main/java/org/lflang/AttributeUtils.java @@ -36,17 +36,7 @@ import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.resource.XtextResource; import org.lflang.ast.ASTUtils; -import org.lflang.lf.Action; -import org.lflang.lf.AttrParm; -import org.lflang.lf.Attribute; -import org.lflang.lf.Input; -import org.lflang.lf.Instantiation; -import org.lflang.lf.Output; -import org.lflang.lf.Parameter; -import org.lflang.lf.Reaction; -import org.lflang.lf.Reactor; -import org.lflang.lf.StateVar; -import org.lflang.lf.Timer; +import org.lflang.lf.*; import org.lflang.util.StringUtil; /** @@ -83,6 +73,8 @@ public static List getAttributes(EObject node) { return ((Output) node).getAttributes(); } else if (node instanceof Instantiation) { return ((Instantiation) node).getAttributes(); + } else if (node instanceof Watchdog) { + return ((Watchdog) node).getAttributes(); } throw new IllegalArgumentException("Not annotatable: " + node); } diff --git a/core/src/main/java/org/lflang/LinguaFranca.xtext b/core/src/main/java/org/lflang/LinguaFranca.xtext index 7f9e74d491..6b3e6c8f7f 100644 --- a/core/src/main/java/org/lflang/LinguaFranca.xtext +++ b/core/src/main/java/org/lflang/LinguaFranca.xtext @@ -218,6 +218,7 @@ Deadline: 'deadline' '(' delay=Expression ')' code=Code; Watchdog: + (attributes+=Attribute)* 'watchdog' name=ID '(' timeout=Expression ')' ('->' effects+=VarRefOrModeTransition (',' effects+=VarRefOrModeTransition)*)? code=Code; diff --git a/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java b/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java index a5b4b8e314..7d8dfb1c10 100644 --- a/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java +++ b/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java @@ -129,6 +129,7 @@ import org.lflang.generator.SendRange; import org.lflang.generator.TimerInstance; import org.lflang.generator.TriggerInstance; +import org.lflang.generator.WatchdogInstance; import org.lflang.lf.Connection; import org.lflang.lf.LfPackage; import org.lflang.lf.Model; @@ -1022,6 +1023,8 @@ private Collection transformReactorNetwork( Multimap actionDestinations = HashMultimap.create(); Multimap actionSources = HashMultimap.create(); Map timerNodes = new HashMap<>(); + Multimap watchdogDestinations = HashMultimap.create(); + Multimap watchdogSources = HashMultimap.create(); KNode startupNode = _kNodeExtensions.createNode(); TriggerInstance startup = null; KNode shutdownNode = _kNodeExtensions.createNode(); @@ -1145,6 +1148,8 @@ private Collection transformReactorNetwork( if (src != null) { connect(createDependencyEdge(trigger.getDefinition()), src, port); } + } else if (trigger instanceof WatchdogInstance) { + watchdogDestinations.put(((WatchdogInstance) trigger), port); } } @@ -1204,6 +1209,8 @@ private Collection transformReactorNetwork( if (dst != null) { connect(createDependencyEdge(effect), port, dst); } + } else if (effect instanceof WatchdogInstance) { + watchdogSources.put((WatchdogInstance) effect, port); } } } @@ -1251,6 +1258,34 @@ private Collection transformReactorNetwork( } } + // Connect watchdogs + Set watchdogs = new HashSet<>(); + watchdogs.addAll(watchdogSources.keySet()); + watchdogs.addAll(watchdogDestinations.keySet()); + + for (WatchdogInstance watchdog : watchdogs) { + KNode node = associateWith(_kNodeExtensions.createNode(), watchdog.getDefinition()); + NamedInstanceUtil.linkInstance(node, watchdog); + _utilityExtensions.setID(node, watchdog.uniqueID()); + nodes.add(node); + setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE); + Pair ports = _linguaFrancaShapeExtensions.addWatchdogFigureAndPorts(node); + setAnnotatedLayoutOptions(watchdog.getDefinition(), node); + if (watchdog.getTimeout() != null) { + _kLabelExtensions.addOutsideBottomCenteredNodeLabel( + node, String.format("timeout: %s", watchdog.getTimeout().toString()), 7); + } + // connect source + for (KPort source : watchdogSources.get(watchdog)) { + connect(createDelayEdge(watchdog), source, ports.getKey()); + } + + // connect targets + for (KPort target : watchdogDestinations.get(watchdog)) { + connect(createDelayEdge(watchdog), ports.getValue(), target); + } + } + // Transform connections. // First, collect all the source ports. List sourcePorts = new LinkedList<>(reactorInstance.inputs); diff --git a/core/src/main/java/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java b/core/src/main/java/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java index b7ef406ee5..299370a532 100644 --- a/core/src/main/java/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java +++ b/core/src/main/java/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java @@ -40,7 +40,6 @@ import de.cau.cs.kieler.klighd.krendering.KArc; import de.cau.cs.kieler.klighd.krendering.KAreaPlacementData; import de.cau.cs.kieler.klighd.krendering.KContainerRendering; -import de.cau.cs.kieler.klighd.krendering.KDecoratorPlacementData; import de.cau.cs.kieler.klighd.krendering.KEllipse; import de.cau.cs.kieler.klighd.krendering.KGridPlacement; import de.cau.cs.kieler.klighd.krendering.KPolygon; @@ -672,6 +671,47 @@ public KEllipse addTimerFigure(KNode node, TimerInstance timer) { return figure; } + /** Creates the rectangular node with text and ports. */ + public Pair addWatchdogFigureAndPorts(KNode node) { + final float size = 18; + _kNodeExtensions.setMinimalNodeSize(node, size, size); + KRectangle figure = _kRenderingExtensions.addRectangle(node); + _kRenderingExtensions.setBackground(figure, Colors.WHITE); + _linguaFrancaStyleExtensions.boldLineSelectionStyle(figure); + + // Add text to the watchdog figure + KText textToAdd = _kContainerRenderingExtensions.addText(figure, "W"); + _kRenderingExtensions.setFontSize(textToAdd, 8); + _linguaFrancaStyleExtensions.noSelectionStyle(textToAdd); + DiagramSyntheses.suppressSelectability(textToAdd); + _kRenderingExtensions.setPointPlacementData( + textToAdd, + _kRenderingExtensions.LEFT, + 0, + 0.5f, + _kRenderingExtensions.TOP, + (size * 0.15f), + 0.5f, + _kRenderingExtensions.H_CENTRAL, + _kRenderingExtensions.V_CENTRAL, + 0, + 0, + size, + size); + + // Add input port + KPort in = _kPortExtensions.createPort(); + node.getPorts().add(in); + in.setSize(0, 0); + DiagramSyntheses.setLayoutOption(in, CoreOptions.PORT_SIDE, PortSide.WEST); + + // Add output port + KPort out = _kPortExtensions.createPort(); + node.getPorts().add(out); + DiagramSyntheses.setLayoutOption(out, CoreOptions.PORT_SIDE, PortSide.EAST); + return new Pair(in, out); + } + /** Creates the visual representation of a startup trigger. */ public KEllipse addStartupFigure(KNode node) { _kNodeExtensions.setMinimalNodeSize(node, 18, 18); @@ -839,53 +879,6 @@ public KText addTextButton(KContainerRendering container, String text) { return textToAdd; } - /** Creates the triangular line decorator with text. */ - public KPolygon addActionDecorator(KPolyline line, String text) { - final float size = 18; - - // Create action decorator - KPolygon actionDecorator = _kContainerRenderingExtensions.addPolygon(line); - _kRenderingExtensions.setBackground(actionDecorator, Colors.WHITE); - List pointsToAdd = - List.of( - _kRenderingExtensions.createKPosition(LEFT, 0, 0.5f, TOP, 0, 0), - _kRenderingExtensions.createKPosition(RIGHT, 0, 0, BOTTOM, 0, 0), - _kRenderingExtensions.createKPosition(LEFT, 0, 0, BOTTOM, 0, 0)); - actionDecorator.getPoints().addAll(pointsToAdd); - - // Set placement data of the action decorator - KDecoratorPlacementData placementData = _kRenderingFactory.createKDecoratorPlacementData(); - placementData.setRelative(0.5f); - placementData.setAbsolute(-size / 2); - placementData.setWidth(size); - placementData.setHeight(size); - placementData.setYOffset(-size * 0.66f); - placementData.setRotateWithLine(true); - actionDecorator.setPlacementData(placementData); - - // Add text to the action decorator - KText textToAdd = _kContainerRenderingExtensions.addText(actionDecorator, text); - _kRenderingExtensions.setFontSize(textToAdd, 8); - _linguaFrancaStyleExtensions.noSelectionStyle(textToAdd); - DiagramSyntheses.suppressSelectability(textToAdd); - _kRenderingExtensions.setPointPlacementData( - textToAdd, - _kRenderingExtensions.LEFT, - 0, - 0.5f, - _kRenderingExtensions.TOP, - size * 0.15f, - 0.5f, - _kRenderingExtensions.H_CENTRAL, - _kRenderingExtensions.V_CENTRAL, - 0, - 0, - size, - size); - - return actionDecorator; - } - /** Creates the triangular action node with text and ports. */ public Pair addActionFigureAndPorts(KNode node, String text) { final float size = 18; diff --git a/core/src/main/java/org/lflang/generator/ReactionInstance.java b/core/src/main/java/org/lflang/generator/ReactionInstance.java index 36b0aae54a..029eb13f0a 100644 --- a/core/src/main/java/org/lflang/generator/ReactionInstance.java +++ b/core/src/main/java/org/lflang/generator/ReactionInstance.java @@ -40,6 +40,7 @@ import org.lflang.lf.TriggerRef; import org.lflang.lf.VarRef; import org.lflang.lf.Variable; +import org.lflang.lf.Watchdog; /** * Representation of a compile-time instance of a reaction. Like {@link ReactorInstance}, if one or @@ -108,6 +109,10 @@ public ReactionInstance(Reaction definition, ReactorInstance parent, int index) this.triggers.add(timerInstance); timerInstance.dependentReactions.add(this); this.sources.add(timerInstance); + } else if (variable instanceof Watchdog) { + var watchdogInstance = + parent.lookupWatchdogInstance((Watchdog) ((VarRef) trigger).getVariable()); + this.triggers.add(watchdogInstance); } } else if (trigger instanceof BuiltinTriggerRef) { var builtinTriggerInstance = parent.getOrCreateBuiltinTrigger((BuiltinTriggerRef) trigger); @@ -159,6 +164,9 @@ public ReactionInstance(Reaction definition, ReactorInstance parent, int index) var actionInstance = parent.lookupActionInstance((Action) variable); this.effects.add(actionInstance); actionInstance.dependsOnReactions.add(this); + } else if (variable instanceof Watchdog) { + var watchdogInstance = parent.lookupWatchdogInstance((Watchdog) variable); + this.effects.add(watchdogInstance); } else { // Effect is either a mode or an unresolved reference. // Do nothing, transitions will be set up by the ModeInstance. diff --git a/core/src/main/java/org/lflang/generator/ReactorInstance.java b/core/src/main/java/org/lflang/generator/ReactorInstance.java index 78f1312c97..39f7cd03da 100644 --- a/core/src/main/java/org/lflang/generator/ReactorInstance.java +++ b/core/src/main/java/org/lflang/generator/ReactorInstance.java @@ -693,6 +693,23 @@ public ModeInstance lookupModeInstance(Mode mode) { return null; } + /** + * Return the watchdog instance within this reactor instance corresponding to the specified + * watchdog reference. + * + * @param watchdog The watchdog as an AST node. + * @return The corresponding watchdog instance or null if the watchdog does not belong to this + * reactor. + */ + public WatchdogInstance lookupWatchdogInstance(Watchdog watchdog) { + for (WatchdogInstance watchdogInstance : watchdogs) { + if (watchdogInstance.getDefinition() == watchdog) { + return watchdogInstance; + } + } + return null; + } + /** Return a descriptive string. */ @Override public String toString() { @@ -877,6 +894,8 @@ public ReactorInstance( this.actions.add(new ActionInstance(actionDecl, this)); } + createWatchdogInstances(); + establishPortConnections(); // Create the reaction instances in this reactor instance. diff --git a/core/src/main/java/org/lflang/generator/WatchdogInstance.java b/core/src/main/java/org/lflang/generator/WatchdogInstance.java index ac51f389cb..2abc7c166f 100644 --- a/core/src/main/java/org/lflang/generator/WatchdogInstance.java +++ b/core/src/main/java/org/lflang/generator/WatchdogInstance.java @@ -17,10 +17,11 @@ * * @author{Benjamin Asch } */ -public class WatchdogInstance { +public class WatchdogInstance extends TriggerInstance { /** Create a new watchdog instance associated with the given reactor instance. */ public WatchdogInstance(Watchdog definition, ReactorInstance reactor) { + super(definition, reactor); if (definition.getTimeout() != null) { // Get the timeout value given in the watchdog declaration. this.timeout = reactor.getTimeValue(definition.getTimeout()); diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index 8fe0436ebf..4abce72f3f 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 8fe0436ebf8bffb53dfc3373c093c0a80e88a6a2 +Subproject commit 4abce72f3f056394c0486f4e4e159090b4022ff9