use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.
the class LinguaFrancaSynthesis method createReactorNode.
private Collection<KNode> createReactorNode(ReactorInstance reactorInstance, boolean expandDefault, Table<ReactorInstance, PortInstance, KPort> inputPortsReg, Table<ReactorInstance, PortInstance, KPort> outputPortsReg, Map<ReactorInstance, KNode> allReactorNodes) {
Reactor reactor = reactorInstance.reactorDefinition;
KNode node = _kNodeExtensions.createNode();
allReactorNodes.put(reactorInstance, node);
associateWith(node, reactor);
_utilityExtensions.setID(node, reactorInstance.uniqueID());
// save to distinguish nodes associated with the same reactor
NamedInstanceUtil.linkInstance(node, reactorInstance);
List<KNode> nodes = new ArrayList<>();
nodes.add(node);
String label = createReactorLabel(reactorInstance);
if (reactorInstance.recursive) {
// Mark this node
node.setProperty(REACTOR_RECURSIVE_INSTANTIATION, true);
// Mark root
allReactorNodes.get(reactorInstance.root()).setProperty(REACTOR_RECURSIVE_INSTANTIATION, true);
}
if (reactor == null) {
_linguaFrancaShapeExtensions.addErrorMessage(node, TEXT_REACTOR_NULL, null);
} else if (reactorInstance.isMainOrFederated()) {
KRoundedRectangle figure = _linguaFrancaShapeExtensions.addMainReactorFigure(node, reactorInstance, label);
if (getObjectValue(REACTOR_PARAMETER_MODE) == ReactorParameterDisplayModes.TABLE && !reactorInstance.parameters.isEmpty()) {
KRectangle rectangle = _kContainerRenderingExtensions.addRectangle(figure);
_kRenderingExtensions.setInvisible(rectangle, true);
_kRenderingExtensions.to(_kRenderingExtensions.from(_kRenderingExtensions.setGridPlacementData(rectangle), _kRenderingExtensions.LEFT, 8, 0, _kRenderingExtensions.TOP, 0, 0), _kRenderingExtensions.RIGHT, 8, 0, _kRenderingExtensions.BOTTOM, 4, 0);
_kRenderingExtensions.setHorizontalAlignment(rectangle, HorizontalAlignment.LEFT);
addParameterList(rectangle, reactorInstance.parameters);
}
if (reactorInstance.recursive) {
nodes.add(addErrorComment(node, TEXT_ERROR_RECURSIVE));
_linguaFrancaStyleExtensions.errorStyle(figure);
} else {
_kContainerRenderingExtensions.addChildArea(figure);
node.getChildren().addAll(transformReactorNetwork(reactorInstance, new HashMap<>(), new HashMap<>(), allReactorNodes));
}
Iterables.addAll(nodes, createUserComments(reactor, node));
configureReactorNodeLayout(node);
// Additional layout adjustment for main node
setLayoutOption(node, CoreOptions.ALGORITHM, LayeredOptions.ALGORITHM_ID);
setLayoutOption(node, CoreOptions.DIRECTION, Direction.RIGHT);
setLayoutOption(node, CoreOptions.NODE_SIZE_CONSTRAINTS, EnumSet.of(SizeConstraint.MINIMUM_SIZE));
setLayoutOption(node, LayeredOptions.NODE_PLACEMENT_BK_FIXED_ALIGNMENT, FixedAlignment.BALANCED);
setLayoutOption(node, LayeredOptions.NODE_PLACEMENT_BK_EDGE_STRAIGHTENING, EdgeStraighteningStrategy.IMPROVE_STRAIGHTNESS);
setLayoutOption(node, LayeredOptions.SPACING_EDGE_NODE, LayeredOptions.SPACING_EDGE_NODE.getDefault() * 1.1f);
setLayoutOption(node, LayeredOptions.SPACING_EDGE_NODE_BETWEEN_LAYERS, LayeredOptions.SPACING_EDGE_NODE_BETWEEN_LAYERS.getDefault() * 1.1f);
if (!getBooleanValue(SHOW_HYPERLINKS)) {
setLayoutOption(node, CoreOptions.PADDING, new ElkPadding(-1, 6, 6, 6));
setLayoutOption(node, LayeredOptions.SPACING_COMPONENT_COMPONENT, LayeredOptions.SPACING_COMPONENT_COMPONENT.getDefault() * 0.5f);
}
} else {
ReactorInstance instance = reactorInstance;
// Expanded Rectangle
ReactorFigureComponents comps = _linguaFrancaShapeExtensions.addReactorFigure(node, reactorInstance, label);
comps.getOuter().setProperty(KlighdProperties.EXPANDED_RENDERING, true);
for (KRendering figure : comps.getFigures()) {
associateWith(figure, reactor);
_kRenderingExtensions.addDoubleClickAction(figure, MemorizingExpandCollapseAction.ID);
}
_reactorIcons.handleIcon(comps.getReactor(), reactor, false);
if (getBooleanValue(SHOW_HYPERLINKS)) {
// Collapse button
KText button = _linguaFrancaShapeExtensions.addTextButton(comps.getReactor(), TEXT_HIDE_ACTION);
_kRenderingExtensions.to(_kRenderingExtensions.from(_kRenderingExtensions.setGridPlacementData(button), _kRenderingExtensions.LEFT, 8, 0, _kRenderingExtensions.TOP, 0, 0), _kRenderingExtensions.RIGHT, 8, 0, _kRenderingExtensions.BOTTOM, 0, 0);
_kRenderingExtensions.addSingleClickAction(button, MemorizingExpandCollapseAction.ID);
_kRenderingExtensions.addDoubleClickAction(button, MemorizingExpandCollapseAction.ID);
}
if (getObjectValue(REACTOR_PARAMETER_MODE) == ReactorParameterDisplayModes.TABLE && !instance.parameters.isEmpty()) {
KRectangle rectangle = _kContainerRenderingExtensions.addRectangle(comps.getReactor());
_kRenderingExtensions.setInvisible(rectangle, true);
if (!getBooleanValue(SHOW_HYPERLINKS)) {
_kRenderingExtensions.to(_kRenderingExtensions.from(_kRenderingExtensions.setGridPlacementData(rectangle), _kRenderingExtensions.LEFT, 8, 0, _kRenderingExtensions.TOP, 0, 0), _kRenderingExtensions.RIGHT, 8, 0, _kRenderingExtensions.BOTTOM, 4, 0);
} else {
_kRenderingExtensions.to(_kRenderingExtensions.from(_kRenderingExtensions.setGridPlacementData(rectangle), _kRenderingExtensions.LEFT, 8, 0, _kRenderingExtensions.TOP, 4, 0), _kRenderingExtensions.RIGHT, 8, 0, _kRenderingExtensions.BOTTOM, 0, 0);
}
_kRenderingExtensions.setHorizontalAlignment(rectangle, HorizontalAlignment.LEFT);
addParameterList(rectangle, instance.parameters);
}
if (instance.recursive) {
comps.getFigures().forEach(_linguaFrancaStyleExtensions::errorStyle);
} else {
_kContainerRenderingExtensions.addChildArea(comps.getReactor());
}
// Collapse Rectangle
comps = _linguaFrancaShapeExtensions.addReactorFigure(node, reactorInstance, label);
comps.getOuter().setProperty(KlighdProperties.COLLAPSED_RENDERING, true);
for (KRendering figure : comps.getFigures()) {
associateWith(figure, reactor);
if (_utilityExtensions.hasContent(instance) && !instance.recursive) {
_kRenderingExtensions.addDoubleClickAction(figure, MemorizingExpandCollapseAction.ID);
}
}
_reactorIcons.handleIcon(comps.getReactor(), reactor, true);
if (getBooleanValue(SHOW_HYPERLINKS)) {
// Expand button
if (_utilityExtensions.hasContent(instance) && !instance.recursive) {
KText button = _linguaFrancaShapeExtensions.addTextButton(comps.getReactor(), TEXT_SHOW_ACTION);
_kRenderingExtensions.to(_kRenderingExtensions.from(_kRenderingExtensions.setGridPlacementData(button), _kRenderingExtensions.LEFT, 8, 0, _kRenderingExtensions.TOP, 0, 0), _kRenderingExtensions.RIGHT, 8, 0, _kRenderingExtensions.BOTTOM, 8, 0);
_kRenderingExtensions.addSingleClickAction(button, MemorizingExpandCollapseAction.ID);
_kRenderingExtensions.addDoubleClickAction(button, MemorizingExpandCollapseAction.ID);
}
}
if (instance.recursive) {
comps.getFigures().forEach(_linguaFrancaStyleExtensions::errorStyle);
}
// Create ports
Map<PortInstance, KPort> inputPorts = new HashMap<>();
Map<PortInstance, KPort> outputPorts = new HashMap<>();
for (PortInstance input : ListExtensions.reverseView(instance.inputs)) {
inputPorts.put(input, addIOPort(node, input, true, input.isMultiport(), reactorInstance.isBank()));
}
for (PortInstance output : instance.outputs) {
outputPorts.put(output, addIOPort(node, output, false, output.isMultiport(), reactorInstance.isBank()));
}
// Mark ports
inputPorts.values().forEach(it -> it.setProperty(REACTOR_INPUT, true));
outputPorts.values().forEach(it -> it.setProperty(REACTOR_OUTPUT, true));
// Add content
if (_utilityExtensions.hasContent(instance) && !instance.recursive) {
node.getChildren().addAll(transformReactorNetwork(instance, inputPorts, outputPorts, allReactorNodes));
}
// Pass port to given tables
if (!_utilityExtensions.isRoot(instance)) {
if (inputPortsReg != null) {
for (Map.Entry<PortInstance, KPort> entry : inputPorts.entrySet()) {
inputPortsReg.put(instance, entry.getKey(), entry.getValue());
}
}
if (outputPortsReg != null) {
for (Map.Entry<PortInstance, KPort> entry : outputPorts.entrySet()) {
outputPortsReg.put(instance, entry.getKey(), entry.getValue());
}
}
}
if (instance.recursive) {
setLayoutOption(node, KlighdProperties.EXPAND, false);
nodes.add(addErrorComment(node, TEXT_ERROR_RECURSIVE));
} else {
setLayoutOption(node, KlighdProperties.EXPAND, expandDefault);
// Interface Dependencies
_interfaceDependenciesVisualization.addInterfaceDependencies(node, expandDefault);
}
if (!_utilityExtensions.isRoot(instance)) {
// add the annotation now.
if (!getBooleanValue(SHOW_ALL_REACTORS)) {
Iterables.addAll(nodes, createUserComments(reactor, node));
}
} else {
Iterables.addAll(nodes, createUserComments(reactor, node));
}
configureReactorNodeLayout(node);
}
// Find and annotate cycles
if (getBooleanValue(CYCLE_DETECTION) && _utilityExtensions.isRoot(reactorInstance)) {
KNode errNode = detectAndAnnotateCycles(node, reactorInstance, allReactorNodes);
if (errNode != null) {
nodes.add(errNode);
}
}
return nodes;
}
use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.
the class PortInstanceTests method multiportDestination.
@Test
public void multiportDestination() throws Exception {
Reactor main = factory.createReactor();
ReactorInstance maini = new ReactorInstance(main, reporter);
ReactorInstance a = newReactor("A", maini);
ReactorInstance b = newReactor("B", maini);
b.setWidth(4);
PortInstance p = newOutputPort("p", a);
PortInstance q = newInputPort("q", b);
connect(p, 0, 1, q, 0, 4);
List<SendRange> sr = p.eventualDestinations();
// Destination has no reactions, so empty list is right.
Assertions.assertEquals("[]", sr.toString());
maini.clearCaches();
newReaction(q);
sr = p.eventualDestinations();
Assertions.assertEquals("[.A.p(0,1)->[.B.q(0,4)]]", sr.toString());
}
use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.
the class PortInstanceTests method newReactor.
protected ReactorInstance newReactor(String name, ReactorInstance container) {
Reactor r = factory.createReactor();
r.setName(name);
ReactorInstance ri = new ReactorInstance(r, container, reporter);
container.children.add(ri);
return ri;
}
use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.
the class LinguaFrancaDependencyAnalysisTest method cyclicDependency.
/**
* Check that circular dependencies between reactions are detected.
*/
@Test
public void cyclicDependency() throws Exception {
// Java 17:
// String testCase = """
// target C;
//
// reactor Clock {
// timer t(0, 10 msec);
// input x:int;
// output y:int;
// reaction(t) -> y {=
//
// =}
// reaction(x) -> y {=
//
// =}
// }
//
// reactor A {
// input x:int;
// output y:int;
// reaction(x) -> y {=
//
// =}
// }
//
// main reactor Loop {
// c = new Clock();
// a = new A();
// c.y -> a.x;
// a.y -> c.x;
// }
// """
// Java 11:
String testCase = String.join(System.getProperty("line.separator"), "target C;", "", "reactor Clock {", " timer t(0, 10 msec);", " input x:int;", " output y:int;", " reaction(t) -> y {=", " ", " =}", " reaction(x) -> y {=", " ", " =}", "}", "", "reactor A {", " input x:int;", " output y:int;", " reaction(x) -> y {=", " ", " =}", "}", "", "main reactor Loop {", " c = new Clock();", " a = new A();", " c.y -> a.x;", " a.y -> c.x;", "}");
Model model = parser.parse(testCase);
Assertions.assertNotNull(model);
Instantiation mainDef = null;
TreeIterator<EObject> it = model.eResource().getAllContents();
while (it.hasNext()) {
EObject obj = it.next();
if (!(obj instanceof Reactor)) {
continue;
}
Reactor reactor = (Reactor) obj;
if (reactor.isMain()) {
// Creating an definition for the main reactor because
// there isn't one.
mainDef = LfFactory.eINSTANCE.createInstantiation();
mainDef.setName(reactor.getName());
mainDef.setReactorClass(reactor);
}
}
ReactorInstance instance = new ReactorInstance(toDefinition(mainDef.getReactorClass()), new DefaultErrorReporter());
Assertions.assertFalse(instance.getCycles().isEmpty());
}
use of org.lflang.lf.Reactor in project lingua-franca by lf-lang.
the class ASTUtils method insertGeneratedDelays.
/**
* Find connections in the given resource that have a delay associated with them,
* and reroute them via a generated delay reactor.
* @param resource The AST.
* @param generator A code generator.
*/
public static void insertGeneratedDelays(Resource resource, GeneratorBase generator) {
// The resulting changes to the AST are performed _after_ iterating
// in order to avoid concurrent modification problems.
List<Connection> oldConnections = new ArrayList<>();
Map<EObject, List<Connection>> newConnections = new LinkedHashMap<>();
Map<EObject, List<Instantiation>> delayInstances = new LinkedHashMap<>();
Iterable<Reactor> containers = Iterables.filter(IteratorExtensions.toIterable(resource.getAllContents()), Reactor.class);
// Iterate over the connections in the tree.
for (Reactor container : containers) {
for (Connection connection : allConnections(container)) {
if (connection.getDelay() != null) {
EObject parent = connection.eContainer();
// Assume all the types are the same, so just use the first on the right.
Type type = ((Port) connection.getRightPorts().get(0).getVariable()).getType();
Reactor delayClass = getDelayClass(type, generator);
String generic = generator.getTargetTypes().supportsGenerics() ? generator.getTargetTypes().getTargetType(InferredType.fromAST(type)) : "";
Instantiation delayInstance = getDelayInstance(delayClass, connection, generic, !generator.generateAfterDelaysWithVariableWidth());
// Stage the new connections for insertion into the tree.
List<Connection> connections = convertToEmptyListIfNull(newConnections.get(parent));
connections.addAll(rerouteViaDelay(connection, delayInstance));
newConnections.put(parent, connections);
// Stage the original connection for deletion from the tree.
oldConnections.add(connection);
// Stage the newly created delay reactor instance for insertion
List<Instantiation> instances = convertToEmptyListIfNull(delayInstances.get(parent));
instances.add(delayInstance);
delayInstances.put(parent, instances);
}
}
}
// Remove old connections; insert new ones.
oldConnections.forEach(connection -> {
var container = connection.eContainer();
if (container instanceof Reactor) {
((Reactor) container).getConnections().remove(connection);
} else if (container instanceof Mode) {
((Mode) container).getConnections().remove(connection);
}
});
newConnections.forEach((container, connections) -> {
if (container instanceof Reactor) {
((Reactor) container).getConnections().addAll(connections);
} else if (container instanceof Mode) {
((Mode) container).getConnections().addAll(connections);
}
});
// Finally, insert the instances and, before doing so, assign them a unique name.
delayInstances.forEach((container, instantiations) -> instantiations.forEach(instantiation -> {
if (container instanceof Reactor) {
instantiation.setName(getUniqueIdentifier((Reactor) container, "delay"));
((Reactor) container).getInstantiations().add(instantiation);
} else if (container instanceof Mode) {
instantiation.setName(getUniqueIdentifier((Reactor) container.eContainer(), "delay"));
((Mode) container).getInstantiations().add(instantiation);
}
}));
}
Aggregations