use of org.lflang.lf.Action in project lingua-franca by lf-lang.
the class PythonReactionGenerator method generatePythonReactionParametersAndInitializations.
/**
* Generate parameters and their respective initialization code for a reaction function
* The initialization code is put at the beginning of the reaction before user code
* @param parameters The parameters used for function definition
* @param inits The initialization code for those paramters
* @param decl Reactor declaration
* @param reaction The reaction to be used to generate parameters for
*/
public static void generatePythonReactionParametersAndInitializations(List<String> parameters, CodeBuilder inits, ReactorDecl decl, Reaction reaction) {
Reactor reactor = ASTUtils.toDefinition(decl);
LinkedHashSet<String> generatedParams = new LinkedHashSet<>();
// Handle triggers
for (TriggerRef trigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) {
if (!(trigger instanceof VarRef)) {
continue;
}
VarRef triggerAsVarRef = (VarRef) trigger;
if (triggerAsVarRef.getVariable() instanceof Port) {
if (triggerAsVarRef.getVariable() instanceof Input) {
if (((Input) triggerAsVarRef.getVariable()).isMutable()) {
generatedParams.add("mutable_" + triggerAsVarRef.getVariable().getName() + "");
// Create a deep copy
if (ASTUtils.isMultiport((Input) triggerAsVarRef.getVariable())) {
inits.pr(triggerAsVarRef.getVariable().getName() + " = [Make() for i in range(len(mutable_" + triggerAsVarRef.getVariable().getName() + "))]");
inits.pr("for i in range(len(mutable_" + triggerAsVarRef.getVariable().getName() + ")):");
inits.pr(" " + triggerAsVarRef.getVariable().getName() + "[i].value = copy.deepcopy(mutable_" + triggerAsVarRef.getVariable().getName() + "[i].value)");
} else {
inits.pr(triggerAsVarRef.getVariable().getName() + " = Make()");
inits.pr(triggerAsVarRef.getVariable().getName() + ".value = copy.deepcopy(mutable_" + triggerAsVarRef.getVariable().getName() + ".value)");
}
} else {
generatedParams.add(triggerAsVarRef.getVariable().getName());
}
} else {
// Handle contained reactors' ports
generatedParams.add(triggerAsVarRef.getContainer().getName() + "_" + triggerAsVarRef.getVariable().getName());
inits.pr(PythonPortGenerator.generatePythonPortVariableInReaction(triggerAsVarRef));
}
} else if (triggerAsVarRef.getVariable() instanceof Action) {
generatedParams.add(triggerAsVarRef.getVariable().getName());
}
}
// Handle non-triggering inputs
if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
for (Input input : ASTUtils.convertToEmptyListIfNull(reactor.getInputs())) {
generatedParams.add(input.getName());
if (input.isMutable()) {
// Create a deep copy
inits.pr(input.getName() + " = copy.deepcopy(" + input.getName() + ")");
}
}
}
for (VarRef src : ASTUtils.convertToEmptyListIfNull(reaction.getSources())) {
if (src.getVariable() instanceof Output) {
// Output of a contained reactor
generatedParams.add(src.getContainer().getName() + "_" + src.getVariable().getName());
inits.pr(PythonPortGenerator.generatePythonPortVariableInReaction(src));
} else {
generatedParams.add(src.getVariable().getName());
if (src.getVariable() instanceof Input) {
if (((Input) src.getVariable()).isMutable()) {
// Create a deep copy
inits.pr(src.getVariable().getName() + " = copy.deepcopy(" + src.getVariable().getName() + ")");
}
}
}
}
// Handle effects
for (VarRef effect : ASTUtils.convertToEmptyListIfNull(reaction.getEffects())) {
if (effect.getVariable() instanceof Input) {
generatedParams.add(effect.getContainer().getName() + "_" + effect.getVariable().getName());
inits.pr(PythonPortGenerator.generatePythonPortVariableInReaction(effect));
} else {
generatedParams.add(effect.getVariable().getName());
if (effect.getVariable() instanceof Port) {
if (ASTUtils.isMultiport((Port) effect.getVariable())) {
// Handle multiports
}
}
}
}
for (String s : generatedParams) {
parameters.add(s);
}
}
use of org.lflang.lf.Action in project lingua-franca by lf-lang.
the class GeneratorUtils method accommodatePhysicalActionsIfPresent.
/**
* Look for physical actions in 'resource'.
* If appropriate, set keepalive to true in
* {@code targetConfig}.
* This is a helper function for setTargetConfig. It
* should not be used elsewhere.
*/
public static void accommodatePhysicalActionsIfPresent(List<Resource> resources, boolean setsKeepAliveOptionAutomatically, TargetConfig targetConfig, ErrorReporter errorReporter) {
if (!setsKeepAliveOptionAutomatically) {
return;
}
for (Resource resource : resources) {
for (Action action : findAll(resource, Action.class)) {
if (action.getOrigin() == ActionOrigin.PHYSICAL && // Check if the user has explicitly set keepalive to false
!targetConfig.setByUser.contains(TargetProperty.KEEPALIVE) && !targetConfig.keepalive) {
// If not, set it to true
targetConfig.keepalive = true;
errorReporter.reportWarning(action, String.format("Setting %s to true because of the physical action %s.", TargetProperty.KEEPALIVE.getDisplayName(), action.getName()));
return;
}
}
}
}
use of org.lflang.lf.Action in project lingua-franca by lf-lang.
the class LFValidator method checkTargetProperties.
/**
* Check for consistency of the target properties, which are
* defined as KeyValuePairs.
*
* @param targetProperties The target properties defined
* in the current Lingua Franca program.
*/
@Check(CheckType.EXPENSIVE)
public void checkTargetProperties(KeyValuePairs targetProperties) {
EList<KeyValuePair> fastTargetProperties = new BasicEList<>(targetProperties.getPairs());
fastTargetProperties.removeIf(pair -> TargetProperty.forName(pair.getName()) != TargetProperty.FAST);
KeyValuePair fastTargetProperty = fastTargetProperties.size() > 0 ? fastTargetProperties.get(0) : null;
if (fastTargetProperty != null) {
// Check for federated
for (Reactor reactor : info.model.getReactors()) {
// Check to see if the program has a federated reactor
if (reactor.isFederated()) {
error("The fast target property is incompatible with federated programs.", fastTargetProperty, Literals.KEY_VALUE_PAIR__NAME);
break;
}
}
// Check for physical actions
for (Reactor reactor : info.model.getReactors()) {
// Check to see if the program has a physical action in a reactor
for (Action action : reactor.getActions()) {
if (action.getOrigin().equals(ActionOrigin.PHYSICAL)) {
error("The fast target property is incompatible with physical actions.", fastTargetProperty, Literals.KEY_VALUE_PAIR__NAME);
break;
}
}
}
}
EList<KeyValuePair> clockSyncTargetProperties = new BasicEList<>(targetProperties.getPairs());
// Check to see if clock-sync is defined
clockSyncTargetProperties.removeIf(pair -> TargetProperty.forName(pair.getName()) != TargetProperty.CLOCK_SYNC);
KeyValuePair clockSyncTargetProperty = clockSyncTargetProperties.size() > 0 ? clockSyncTargetProperties.get(0) : null;
if (clockSyncTargetProperty != null) {
boolean federatedExists = false;
for (Reactor reactor : info.model.getReactors()) {
if (reactor.isFederated()) {
federatedExists = true;
}
}
if (!federatedExists) {
warning("The clock-sync target property is incompatible with non-federated programs.", clockSyncTargetProperty, Literals.KEY_VALUE_PAIR__NAME);
}
}
EList<KeyValuePair> schedulerTargetProperties = new BasicEList<>(targetProperties.getPairs());
schedulerTargetProperties.removeIf(pair -> TargetProperty.forName(pair.getName()) != TargetProperty.SCHEDULER);
KeyValuePair schedulerTargetProperty = schedulerTargetProperties.size() > 0 ? schedulerTargetProperties.get(0) : null;
if (schedulerTargetProperty != null) {
String schedulerName = schedulerTargetProperty.getValue().getId();
if (!TargetProperty.SchedulerOption.valueOf(schedulerName).prioritizesDeadline()) {
// Check if a deadline is assigned to any reaction
if (info.model.getReactors().stream().filter(reactor -> {
// has a deadline handler.
return ASTUtils.allReactions(reactor).stream().filter(reaction -> {
return reaction.getDeadline() != null;
}).count() > 0;
}).count() > 0) {
warning("This program contains deadlines, but the chosen " + schedulerName + " scheduler does not prioritize reaction execution " + "based on deadlines. This might result in a sub-optimal " + "scheduling.", schedulerTargetProperty, Literals.KEY_VALUE_PAIR__VALUE);
}
}
}
}
use of org.lflang.lf.Action in project lingua-franca by lf-lang.
the class ModeDiagrams method handleModes.
public void handleModes(List<KNode> nodes, ReactorInstance reactor) {
if (!reactor.modes.isEmpty()) {
var modeNodes = new LinkedHashMap<ModeInstance, KNode>();
var modeDefinitionMap = new LinkedHashMap<Mode, ModeInstance>();
for (ModeInstance mode : reactor.modes) {
var node = _kNodeExtensions.createNode();
associateWith(node, mode.getDefinition());
NamedInstanceUtil.linkInstance(node, mode);
_utilityExtensions.setID(node, mode.uniqueID());
modeNodes.put(mode, node);
modeDefinitionMap.put(mode.getDefinition(), mode);
if (mode.isInitial()) {
DiagramSyntheses.setLayoutOption(node, LayeredOptions.LAYERING_LAYER_CONSTRAINT, LayerConstraint.FIRST);
}
DiagramSyntheses.setLayoutOption(node, LayeredOptions.CROSSING_MINIMIZATION_SEMI_INTERACTIVE, true);
var expansionState = MemorizingExpandCollapseAction.getExpansionState(mode);
DiagramSyntheses.setLayoutOption(node, KlighdProperties.EXPAND, expansionState != null ? expansionState : !this.getBooleanValue(INITIALLY_COLLAPSE_MODES));
// Expanded Rectangle
var expandFigure = addModeFigure(node, mode, true);
expandFigure.setProperty(KlighdProperties.EXPANDED_RENDERING, true);
_kRenderingExtensions.addDoubleClickAction(expandFigure, MemorizingExpandCollapseAction.ID);
if (getBooleanValue(LinguaFrancaSynthesis.SHOW_HYPERLINKS)) {
// Collapse button
KText textButton = _linguaFrancaShapeExtensions.addTextButton(expandFigure, LinguaFrancaSynthesis.TEXT_HIDE_ACTION);
_kRenderingExtensions.to(_kRenderingExtensions.from(_kRenderingExtensions.setGridPlacementData(textButton), _kRenderingExtensions.LEFT, 8, 0, _kRenderingExtensions.TOP, 0, 0), _kRenderingExtensions.RIGHT, 8, 0, _kRenderingExtensions.BOTTOM, 0, 0);
_kRenderingExtensions.addSingleClickAction(textButton, MemorizingExpandCollapseAction.ID);
_kRenderingExtensions.addDoubleClickAction(textButton, MemorizingExpandCollapseAction.ID);
}
_kContainerRenderingExtensions.addChildArea(expandFigure);
// Collapse Rectangle
var collapseFigure = addModeFigure(node, mode, false);
collapseFigure.setProperty(KlighdProperties.COLLAPSED_RENDERING, true);
if (this.hasContent(mode)) {
_kRenderingExtensions.addDoubleClickAction(collapseFigure, MemorizingExpandCollapseAction.ID);
if (getBooleanValue(LinguaFrancaSynthesis.SHOW_HYPERLINKS)) {
// Expand button
KText textButton = _linguaFrancaShapeExtensions.addTextButton(collapseFigure, LinguaFrancaSynthesis.TEXT_SHOW_ACTION);
_kRenderingExtensions.to(_kRenderingExtensions.from(_kRenderingExtensions.setGridPlacementData(textButton), _kRenderingExtensions.LEFT, 8, 0, _kRenderingExtensions.TOP, 0, 0), _kRenderingExtensions.RIGHT, 8, 0, _kRenderingExtensions.BOTTOM, 8, 0);
_kRenderingExtensions.addSingleClickAction(textButton, MemorizingExpandCollapseAction.ID);
_kRenderingExtensions.addDoubleClickAction(textButton, MemorizingExpandCollapseAction.ID);
}
}
}
var modeChildren = HashMultimap.<ModeInstance, KNode>create();
var nodeModes = new HashMap<KNode, ModeInstance>();
for (var node : nodes) {
var instance = NamedInstanceUtil.getLinkedInstance(node);
if (instance == null && node.getProperty(CoreOptions.COMMENT_BOX)) {
var firstEdge = IterableExtensions.<KEdge>head(node.getOutgoingEdges());
if (firstEdge != null && firstEdge.getTarget() != null) {
instance = NamedInstanceUtil.getLinkedInstance(firstEdge.getTarget());
}
}
if (instance != null) {
var mode = instance.getMode(true);
modeChildren.put(mode, node);
nodeModes.put(node, mode);
} else {
modeChildren.put(null, node);
}
}
var modeContainer = _kNodeExtensions.createNode();
modeContainer.getChildren().addAll(modeNodes.values());
var fig = addModeContainerFigure(modeContainer);
_kRenderingExtensions.addDoubleClickAction(fig, MemorizingExpandCollapseAction.ID);
if (modeChildren.get(null).isEmpty()) {
_kRenderingExtensions.setInvisible(fig, true);
DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.PADDING, new ElkPadding());
}
DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.NODE_SIZE_CONSTRAINTS, SizeConstraint.minimumSizeWithPorts());
DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.EDGE_ROUTING, EdgeRouting.SPLINES);
DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.DIRECTION, Direction.DOWN);
DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_ORDER);
var modeContainerPorts = new HashMap<KPort, KPort>();
for (var mode : ListExtensions.reverseView(reactor.modes)) {
var modeNode = modeNodes.get(mode);
var edges = new HashSet<KEdge>();
// add children
for (var child : modeChildren.get(mode)) {
nodes.remove(child);
modeNode.getChildren().add(child);
edges.addAll(child.getIncomingEdges());
edges.addAll(child.getOutgoingEdges());
}
// add transitions
var representedTargets = new HashSet<Pair<ModeInstance, ModeInstance.ModeTransitionType>>();
for (var transition : ListExtensions.reverseView(mode.transitions)) {
if (!representedTargets.contains(new Pair<ModeInstance, ModeInstance.ModeTransitionType>(transition.target, transition.type))) {
var edge = _kEdgeExtensions.createEdge();
edge.setSource(modeNode);
edge.setTarget(modeNodes.get(transition.target));
addTransitionFigure(edge, transition);
if (getBooleanValue(SHOW_TRANSITION_LABELS)) {
associateWith(edge, transition.getDefinition());
} else {
// Bundle similar transitions
representedTargets.add(new Pair<ModeInstance, ModeInstance.ModeTransitionType>(transition.target, transition.type));
}
}
}
// handle cross hierarchy edges
var portCopies = new HashMap<KPort, KPort>();
for (var edge : edges) {
if (!edge.getProperty(CoreOptions.NO_LAYOUT)) {
var sourceNodeMode = nodeModes.get(edge.getSource());
if (sourceNodeMode == null) {
sourceNodeMode = nodeModes.get(edge.getSource().getParent());
}
var sourceIsInMode = sourceNodeMode != null;
var targetNodeMode = nodeModes.get(edge.getTarget());
if (targetNodeMode == null) {
targetNodeMode = nodeModes.get(edge.getTarget().getParent());
}
var targetIsInMode = targetNodeMode != null;
if (!sourceIsInMode || !targetIsInMode) {
var node = sourceIsInMode ? edge.getTarget() : edge.getSource();
var port = sourceIsInMode ? edge.getTargetPort() : edge.getSourcePort();
var isLocal = modeChildren.get(null).contains(node);
if (isLocal) {
// Add port to mode container
if (modeContainerPorts.containsKey(port)) {
node = modeContainer;
port = modeContainerPorts.get(port);
} else {
var containerPort = _kPortExtensions.createPort();
modeContainerPorts.put(port, containerPort);
modeContainer.getPorts().add(containerPort);
_kPortExtensions.setPortSize(containerPort, 8, 4);
KRectangle rect = _kRenderingExtensions.addRectangle(containerPort);
_kRenderingExtensions.setBackground(rect, Colors.BLACK);
DiagramSyntheses.setLayoutOption(containerPort, CoreOptions.PORT_BORDER_OFFSET, -4.0);
DiagramSyntheses.setLayoutOption(containerPort, CoreOptions.PORT_SIDE, sourceIsInMode ? PortSide.EAST : PortSide.WEST);
var source = _utilityExtensions.sourceElement(node);
var label = "";
if (source instanceof Action) {
label = ((Action) source).getName();
} else if (source instanceof Timer) {
label = ((Timer) source).getName();
}
_kLabelExtensions.addOutsidePortLabel(containerPort, label, 8);
// new connection
var copy = EcoreUtil.copy(edge);
if (sourceIsInMode) {
copy.setSource(modeContainer);
copy.setSourcePort(containerPort);
copy.setTarget(edge.getTarget());
} else {
copy.setTarget(modeContainer);
copy.setTargetPort(containerPort);
copy.setSource(edge.getSource());
}
node = modeContainer;
port = containerPort;
}
}
// Duplicate port
if (!portCopies.containsKey(port)) {
var copy = EcoreUtil.copy(port);
portCopies.put(port, copy);
var dummyNode = _kNodeExtensions.createNode();
var newID = mode.uniqueID() + "_";
if (!port.getLabels().isEmpty()) {
newID += IterableExtensions.head(port.getLabels()).getText();
}
_utilityExtensions.setID(dummyNode, newID);
_kRenderingExtensions.addInvisibleContainerRendering(dummyNode);
dummyNode.getPorts().add(copy);
DiagramSyntheses.setLayoutOption(dummyNode, LayeredOptions.LAYERING_LAYER_CONSTRAINT, port.getProperty(CoreOptions.PORT_SIDE) == PortSide.WEST ? LayerConstraint.FIRST : LayerConstraint.LAST);
modeNode.getChildren().add(dummyNode);
}
var newPort = portCopies.get(port);
if (sourceIsInMode) {
edge.setTarget(newPort.getNode());
edge.setTargetPort(newPort);
} else {
edge.setSource(newPort.getNode());
edge.setSourcePort(newPort);
}
}
}
}
}
nodes.add(modeContainer);
}
}
use of org.lflang.lf.Action in project lingua-franca by lf-lang.
the class FedASTUtils method addNetworkInputControlReaction.
/**
* Add a network control reaction for a given input port 'destination' to
* destination's parent reactor. This reaction will block for
* any valid logical time until it is known whether the trigger for the
* action corresponding to the given port is present or absent.
*
* @note Used in federated execution
*
* @param source The output port of the source federate reactor.
* Added as a trigger to the network control reaction to preserve the
* overall dependency structure of the program across federates.
* @param destination The input port of the destination federate reactor.
* @param recevingPortID The ID of the receiving port
* @param bankIndex The bank index of the receiving federate, or -1 if not in a bank.
* @param instance The federate instance is used to keep track of all
* network input ports globally
* @param generator The GeneratorBase instance used to perform some target-specific actions
*/
private static void addNetworkInputControlReaction(PortInstance source, PortInstance destination, int recevingPortID, int bankIndex, FederateInstance instance, GeneratorBase generator) {
LfFactory factory = LfFactory.eINSTANCE;
Reaction reaction = factory.createReaction();
VarRef sourceRef = factory.createVarRef();
VarRef destRef = factory.createVarRef();
// If the sender or receiver is in a bank of reactors, then we want
// these reactions to appear only in the federate whose bank ID matches.
generator.setReactionBankIndex(reaction, bankIndex);
// Create a new action that will be used to trigger the
// input control reactions.
Action newTriggerForControlReactionInput = factory.createAction();
newTriggerForControlReactionInput.setOrigin(ActionOrigin.LOGICAL);
// Set the container and variable according to the network port
destRef.setContainer(destination.getParent().getDefinition());
destRef.setVariable(destination.getDefinition());
sourceRef.setContainer(source.getParent().getDefinition());
sourceRef.setVariable(source.getDefinition());
Reactor top = destination.getParent().getParent().reactorDefinition;
newTriggerForControlReactionInput.setName(ASTUtils.getUniqueIdentifier(top, "inputControlReactionTrigger"));
// Add the newly created Action to the action list of the federated reactor.
top.getActions().add(newTriggerForControlReactionInput);
// Create the trigger for the reaction
VarRef newTriggerForControlReaction = factory.createVarRef();
newTriggerForControlReaction.setVariable(newTriggerForControlReactionInput);
// Add the appropriate triggers to the list of triggers of the reaction
reaction.getTriggers().add(newTriggerForControlReaction);
// Add the original output port of the source federate
// as a trigger to keep the overall dependency structure.
// This is useful when assigning levels.
reaction.getTriggers().add(sourceRef);
// Add this trigger to the list of disconnected network reaction triggers
instance.remoteNetworkReactionTriggers.add(sourceRef);
// Add the destination port as an effect of the reaction
reaction.getEffects().add(destRef);
// Generate code for the network input control reaction
reaction.setCode(factory.createCode());
TimeValue maxSTP = findMaxSTP(destination.getDefinition(), instance, generator, destination.getParent().reactorDefinition);
reaction.getCode().setBody(generator.generateNetworkInputControlReactionBody(recevingPortID, maxSTP));
generator.makeUnordered(reaction);
// Insert the reaction
top.getReactions().add(reaction);
// Add the trigger for this reaction to the list of triggers, used to actually
// trigger the reaction at the beginning of each logical time.
instance.networkInputControlReactionsTriggers.add(newTriggerForControlReactionInput);
// Add the network input control reaction to the federate instance's list
// of network reactions
instance.networkReactions.add(reaction);
}
Aggregations