use of org.lflang.lf.Action in project lingua-franca by lf-lang.
the class FedASTUtils method makeCommunication.
/**
* Replace the specified connection with communication between federates.
* @param source The source port instance.
* @param destination The destination port instance.
* @param connection The connection.
* @param sourceFederate The source federate.
* @param leftBankIndex The left bank index or -1 if the left reactor is not in a bank.
* @param leftChannelIndex The left channel index or -1 if the left port is not a multiport.
* @param destinationFederate The destination federate.
* @param rightBankIndex The right bank index or -1 if the right reactor is not in a bank.
* @param rightChannelIndex The right channel index or -1 if the right port is not a multiport.
* @param generator The GeneratorBase instance used to perform some target-specific actions
* @param coordination One of CoordinationType.DECENTRALIZED or CoordinationType.CENTRALIZED.
*/
public static void makeCommunication(PortInstance source, PortInstance destination, Connection connection, FederateInstance sourceFederate, int leftBankIndex, int leftChannelIndex, FederateInstance destinationFederate, int rightBankIndex, int rightChannelIndex, GeneratorBase generator, CoordinationType coordination) {
// Get the serializer
var serializer = SupportedSerializers.NATIVE;
if (connection.getSerializer() != null) {
serializer = SupportedSerializers.valueOf(connection.getSerializer().getType().toUpperCase());
}
// Add it to the list of enabled serializers
generator.enabledSerializers.add(serializer);
// Add the sender reaction.
addNetworkSenderReaction(source, destination, connection, sourceFederate, leftBankIndex, leftChannelIndex, destinationFederate, generator, coordination, serializer);
if (!connection.isPhysical()) {
// The ID of the receiving port (rightPort) is the position
// of the networkAction (see below) in this list.
int receivingPortID = destinationFederate.networkMessageActions.size();
// Add the network output control reaction to the parent
FedASTUtils.addNetworkOutputControlReaction(source, sourceFederate, receivingPortID, leftBankIndex, leftChannelIndex, destinationFederate.id, generator, connection.getDelay());
// Add the network input control reaction to the parent
FedASTUtils.addNetworkInputControlReaction(source, destination, receivingPortID, rightBankIndex, destinationFederate, generator);
}
// Create the network action (@see createNetworkAction)
Action networkAction = createNetworkAction(connection, serializer, EcoreUtil.copy(source.getDefinition().getType()), generator.getNetworkBufferType());
// Keep track of this action in the destination federate.
destinationFederate.networkMessageActions.add(networkAction);
// Add the action definition to the parent reactor.
((Reactor) connection.eContainer()).getActions().add(networkAction);
// Add the network receiver reaction in the destinationFederate
addNetworkReceiverReaction(networkAction, source, destination, connection, sourceFederate, destinationFederate, rightBankIndex, rightChannelIndex, generator, coordination, serializer);
}
use of org.lflang.lf.Action in project lingua-franca by lf-lang.
the class FedASTUtils method createNetworkAction.
/**
* Create a "network action" in the reactor that contains the given
* connection and return it.
*
* The purpose of this action is to serve as a trigger for a "network
* input reaction" that is responsible for relaying messages to the
* port that is on the receiving side of the given connection. The
* connection is assumed to be between two reactors that reside in
* distinct federates. Hence, the container of the connection is
* assumed to be top-level.
*
* @param connection A connection between to federates.
* @param serializer The serializer used on the connection.
* @param type The type of the source port (indicating the type of
* data to be received).
* @param networkBufferType The type of the buffer used for network
* communication in the target (e.g., uint8_t* in C).
* @return The newly created action.
*/
private static Action createNetworkAction(Connection connection, SupportedSerializers serializer, Type type, String networkBufferType) {
Reactor top = (Reactor) connection.eContainer();
LfFactory factory = LfFactory.eINSTANCE;
Action action = factory.createAction();
// Name the newly created action; set its delay and type.
action.setName(ASTUtils.getUniqueIdentifier(top, "networkMessage"));
if (serializer == SupportedSerializers.NATIVE) {
action.setType(type);
} else {
Type action_type = factory.createType();
action_type.setId(networkBufferType);
action.setType(action_type);
}
// The connection is 'physical' if it uses the ~> notation.
if (connection.isPhysical()) {
action.setOrigin(ActionOrigin.PHYSICAL);
// the minDelay.
if (connection.getDelay() != null) {
action.setMinDelay(factory.createValue());
action.getMinDelay().setTime(connection.getDelay().getTime());
}
} else {
action.setOrigin(ActionOrigin.LOGICAL);
}
return action;
}
use of org.lflang.lf.Action in project lingua-franca by lf-lang.
the class FedASTUtils method addNetworkOutputControlReaction.
/**
* Add a network control reaction for a given output port 'source' to
* source's parent reactor. This reaction will send a port absent
* message if the status of the output port is absent.
*
* @note Used in federated execution
*
* @param source The output port of the source federate
* @param instance The federate instance is used to keep track of all
* network reactions and some relevant triggers
* @param receivingPortID The ID of the receiving port
* @param channelIndex The channel index of the sending port, if it is a multiport.
* @param bankIndex The bank index of the sending federate, if it is a bank.
* @param receivingFedID The ID of destination federate.
* @param generator The GeneratorBase instance used to perform some target-specific actions
* @param delay The delay value imposed on the connection using after
*/
private static void addNetworkOutputControlReaction(PortInstance source, FederateInstance instance, int receivingPortID, int bankIndex, int channelIndex, int receivingFedID, GeneratorBase generator, Delay delay) {
LfFactory factory = LfFactory.eINSTANCE;
Reaction reaction = factory.createReaction();
// Top-level reactor.
Reactor top = source.getParent().getParent().reactorDefinition;
// 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);
// Add the output from the contained reactor as a source to
// the reaction to preserve precedence order.
VarRef newPortRef = factory.createVarRef();
newPortRef.setContainer(source.getParent().getDefinition());
newPortRef.setVariable(source.getDefinition());
reaction.getSources().add(newPortRef);
// Check whether the action already has been created.
if (instance.networkOutputControlReactionsTrigger == null) {
// The port has not been created.
String triggerName = "outputControlReactionTrigger";
// Find the trigger definition in the reactor definition, which could have been
// generated for another federate instance if there are multiple instances
// of the same reactor that are each distinct federates.
Optional<Action> optTriggerInput = top.getActions().stream().filter(I -> I.getName().equals(triggerName)).findFirst();
if (optTriggerInput.isEmpty()) {
// If no trigger with the name "outputControlReactionTrigger" is
// already added to the reactor definition, we need to create it
// for the first time. The trigger is a logical action.
Action newTriggerForControlReactionVariable = factory.createAction();
newTriggerForControlReactionVariable.setName(triggerName);
newTriggerForControlReactionVariable.setOrigin(ActionOrigin.LOGICAL);
top.getActions().add(newTriggerForControlReactionVariable);
// Now that the variable is created, store it in the federate instance
instance.networkOutputControlReactionsTrigger = newTriggerForControlReactionVariable;
} else {
// If the "outputControlReactionTrigger" trigger is already
// there, we can re-use it for this new reaction since a single trigger
// will trigger
// all network output control reactions.
instance.networkOutputControlReactionsTrigger = optTriggerInput.get();
}
}
// Add the trigger for all output control reactions to the list of triggers
VarRef triggerRef = factory.createVarRef();
triggerRef.setVariable(instance.networkOutputControlReactionsTrigger);
reaction.getTriggers().add(triggerRef);
// Generate the code
reaction.setCode(factory.createCode());
reaction.getCode().setBody(generator.generateNetworkOutputControlReactionBody(newPortRef, receivingPortID, receivingFedID, bankIndex, channelIndex, delay));
// Make the reaction unordered w.r.t. other reactions in the top level.
generator.makeUnordered(reaction);
// Insert the newly generated reaction after the generated sender and
// receiver top-level reactions.
top.getReactions().add(reaction);
// Add the network output control reaction to the federate instance's list
// of network reactions
instance.networkReactions.add(reaction);
}
use of org.lflang.lf.Action in project lingua-franca by lf-lang.
the class ASTUtils method getDelayClass.
/**
* Return a synthesized AST node that represents the definition of a delay
* reactor. Depending on whether the target supports generics, either this
* method will synthesize a generic definition and keep returning it upon
* subsequent calls, or otherwise, it will synthesize a new definition for
* each new type it hasn't yet created a compatible delay reactor for.
* @param type The type the delay class must be compatible with.
* @param generator A code generator.
*/
private static Reactor getDelayClass(Type type, GeneratorBase generator) {
String className;
if (generator.getTargetTypes().supportsGenerics()) {
className = GeneratorBase.GEN_DELAY_CLASS_NAME;
} else {
String id = Integer.toHexString(InferredType.fromAST(type).toText().hashCode());
className = String.format("%s_%s", GeneratorBase.GEN_DELAY_CLASS_NAME, id);
}
// Only add class definition if it is not already there.
Reactor classDef = generator.findDelayClass(className);
if ((classDef != null)) {
return classDef;
}
Reactor delayClass = factory.createReactor();
Parameter delayParameter = factory.createParameter();
Action action = factory.createAction();
VarRef triggerRef = factory.createVarRef();
VarRef effectRef = factory.createVarRef();
Input input = factory.createInput();
Output output = factory.createOutput();
VarRef inRef = factory.createVarRef();
VarRef outRef = factory.createVarRef();
Reaction r1 = factory.createReaction();
Reaction r2 = factory.createReaction();
delayParameter.setName("delay");
delayParameter.setType(factory.createType());
delayParameter.getType().setId("time");
delayParameter.getType().setTime(true);
Time defaultTime = factory.createTime();
defaultTime.setUnit(null);
defaultTime.setInterval(0);
Value defaultValue = factory.createValue();
defaultValue.setTime(defaultTime);
delayParameter.getInit().add(defaultValue);
// Name the newly created action; set its delay and type.
action.setName("act");
action.setMinDelay(factory.createValue());
action.getMinDelay().setParameter(delayParameter);
action.setOrigin(ActionOrigin.LOGICAL);
if (generator.getTargetTypes().supportsGenerics()) {
action.setType(factory.createType());
action.getType().setId("T");
} else {
action.setType(EcoreUtil.copy(type));
}
input.setName("inp");
input.setType(EcoreUtil.copy(action.getType()));
output.setName("out");
output.setType(EcoreUtil.copy(action.getType()));
// Establish references to the involved ports.
inRef.setVariable(input);
outRef.setVariable(output);
// Establish references to the action.
triggerRef.setVariable(action);
effectRef.setVariable(action);
// Add the action to the reactor.
delayClass.setName(className);
delayClass.getActions().add(action);
// Configure the second reaction, which reads the input.
r1.getTriggers().add(inRef);
r1.getEffects().add(effectRef);
r1.setCode(factory.createCode());
r1.getCode().setBody(generator.generateDelayBody(action, inRef));
// Configure the first reaction, which produces the output.
r2.getTriggers().add(triggerRef);
r2.getEffects().add(outRef);
r2.setCode(factory.createCode());
r2.getCode().setBody(generator.generateForwardBody(action, outRef));
// Add the reactions to the newly created reactor class.
// These need to go in the opposite order in case
// a new input arrives at the same time the delayed
// output is delivered!
delayClass.getReactions().add(r2);
delayClass.getReactions().add(r1);
// Add a type parameter if the target supports it.
if (generator.getTargetTypes().supportsGenerics()) {
TypeParm parm = factory.createTypeParm();
parm.setLiteral(generator.generateDelayGeneric());
delayClass.getTypeParms().add(parm);
}
delayClass.getInputs().add(input);
delayClass.getOutputs().add(output);
delayClass.getParameters().add(delayParameter);
generator.addDelayClass(delayClass);
return delayClass;
}
use of org.lflang.lf.Action in project lingua-franca by lf-lang.
the class PythonReactionGenerator method generateCPythonInitializers.
/**
* Generate necessary Python-specific initialization code for <code>reaction<code> that belongs to reactor
* <code>decl<code>.
*
* @param reaction The reaction to generate Python-specific initialization for.
* @param decl The reactor to which <code>reaction<code> belongs to.
* @param pyObjectDescriptor For each port object created, a Python-specific descriptor will be added to this that
* then can be used as an argument to <code>Py_BuildValue<code>
* (@see <a href="https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue">docs.python.org/3/c-api</a>).
* @param pyObjects A "," delimited list of expressions that would be (or result in a creation of) a PyObject.
*/
private static String generateCPythonInitializers(Reaction reaction, ReactorDecl decl, List<String> pyObjects, ErrorReporter errorReporter) {
Set<Action> actionsAsTriggers = new LinkedHashSet<>();
Reactor reactor = ASTUtils.toDefinition(decl);
CodeBuilder code = new CodeBuilder();
// TODO: handle triggers
for (TriggerRef trigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) {
if (trigger instanceof VarRef) {
VarRef triggerAsVarRef = (VarRef) trigger;
code.pr(generateVariableToSendPythonReaction(triggerAsVarRef, actionsAsTriggers, decl, pyObjects));
}
}
if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
// NOTE: this does not include contained outputs.
for (Input input : reactor.getInputs()) {
PythonPortGenerator.generateInputVariablesToSendToPythonReaction(pyObjects, input, decl);
}
}
// Next add non-triggering inputs.
for (VarRef src : ASTUtils.convertToEmptyListIfNull(reaction.getSources())) {
code.pr(generateVariableToSendPythonReaction(src, actionsAsTriggers, decl, pyObjects));
}
// Next, handle effects
if (reaction.getEffects() != null) {
for (VarRef effect : reaction.getEffects()) {
if (effect.getVariable() instanceof Action) {
// If it has already appeared as trigger, do not redefine it.
if (!actionsAsTriggers.contains(effect.getVariable())) {
PythonPortGenerator.generateActionVariableToSendToPythonReaction(pyObjects, (Action) effect.getVariable(), decl);
}
} else {
if (effect.getVariable() instanceof Output) {
PythonPortGenerator.generateOutputVariablesToSendToPythonReaction(pyObjects, (Output) effect.getVariable());
} else if (effect.getVariable() instanceof Input) {
// It is the input of a contained reactor.
code.pr(PythonPortGenerator.generateVariablesForSendingToContainedReactors(pyObjects, effect.getContainer(), (Input) effect.getVariable()));
} else {
errorReporter.reportError(reaction, "In generateReaction(): " + effect.getVariable().getName() + " is neither an input nor an output.");
}
}
}
}
return code.toString();
}
Aggregations