use of org.lflang.TimeValue in project lingua-franca by lf-lang.
the class FedASTUtils method findMaxSTP.
/**
* Find the maximum STP offset for the given 'port'.
*
* An STP offset predicate can be nested in contained reactors in
* the federate.
* @param port The port to generate the STP list for.
* @param generator The GeneratorBase instance used to perform some target-specific actions
* @param reactor The top-level reactor (not the federate reactor)
* @return The maximum STP as a TimeValue
*/
private static TimeValue findMaxSTP(Variable port, FederateInstance instance, GeneratorBase generator, Reactor reactor) {
// Find a list of STP offsets (if any exists)
List<Value> STPList = new LinkedList<>();
// First, check if there are any connections to contained reactors that
// need to be handled
List<Connection> connectionsWithPort = ASTUtils.allConnections(reactor).stream().filter(c -> c.getLeftPorts().stream().anyMatch((VarRef v) -> v.getVariable().equals(port))).collect(Collectors.toList());
// Find the list of reactions that have the port as trigger or source
// (could be a variable name)
List<Reaction> reactionsWithPort = ASTUtils.allReactions(reactor).stream().filter(r -> {
// Check the triggers of reaction r first
return r.getTriggers().stream().anyMatch(t -> {
if (t instanceof VarRef) {
// Check if the variables match
return ((VarRef) t).getVariable() == port;
} else {
// Not a network port (startup or shutdown)
return false;
}
}) || // Then check the sources of reaction r
r.getSources().stream().anyMatch(s -> s.getVariable() == port);
}).collect(Collectors.toList());
// Find a list of STP offsets (if any exists)
if (generator.isFederatedAndDecentralized()) {
for (Reaction r : safe(reactionsWithPort)) {
if (!instance.contains(r)) {
continue;
}
// If not, assume it is zero
if (r.getStp() != null) {
if (r.getStp().getValue().getParameter() != null) {
List<Instantiation> instantList = new ArrayList<>();
instantList.add(instance.instantiation);
STPList.addAll(ASTUtils.initialValue(r.getStp().getValue().getParameter(), instantList));
} else {
STPList.add(r.getStp().getValue());
}
}
}
// Check the children for STPs as well
for (Connection c : safe(connectionsWithPort)) {
VarRef childPort = c.getRightPorts().get(0);
Reactor childReactor = (Reactor) childPort.getVariable().eContainer();
// Find the list of reactions that have the port as trigger or
// source (could be a variable name)
List<Reaction> childReactionsWithPort = ASTUtils.allReactions(childReactor).stream().filter(r -> r.getTriggers().stream().anyMatch(t -> {
if (t instanceof VarRef) {
// Check if the variables match
return ((VarRef) t).getVariable() == childPort.getVariable();
} else {
// Not a network port (startup or shutdown)
return false;
}
}) || r.getSources().stream().anyMatch(s -> s.getVariable() == childPort.getVariable())).collect(Collectors.toList());
for (Reaction r : safe(childReactionsWithPort)) {
if (!instance.contains(r)) {
continue;
}
// If not, assume it is zero
if (r.getStp() != null) {
if (r.getStp().getValue() instanceof Parameter) {
List<Instantiation> instantList = new ArrayList<>();
instantList.add(childPort.getContainer());
STPList.addAll(ASTUtils.initialValue(r.getStp().getValue().getParameter(), instantList));
} else {
STPList.add(r.getStp().getValue());
}
}
}
}
}
return STPList.stream().map(ASTUtils::getLiteralTimeValue).filter(Objects::nonNull).reduce(TimeValue.ZERO, TimeValue::max);
}
use of org.lflang.TimeValue 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);
}
use of org.lflang.TimeValue in project lingua-franca by lf-lang.
the class FederateInstance method findNearestPhysicalActionTrigger.
/**
* Find the nearest (shortest) path to a physical action trigger from this
* 'reaction' in terms of minimum delay.
*
* @param reaction The reaction to start with
* @return The minimum delay found to the nearest physical action and
* TimeValue.MAX_VALUE otherwise
*/
public TimeValue findNearestPhysicalActionTrigger(ReactionInstance reaction) {
TimeValue minDelay = TimeValue.MAX_VALUE;
for (TriggerInstance<? extends Variable> trigger : reaction.triggers) {
if (trigger.getDefinition() instanceof Action) {
Action action = (Action) trigger.getDefinition();
ActionInstance actionInstance = (ActionInstance) trigger;
if (action.getOrigin() == ActionOrigin.PHYSICAL) {
if (actionInstance.getMinDelay().isEarlierThan(minDelay)) {
minDelay = actionInstance.getMinDelay();
}
} else if (action.getOrigin() == ActionOrigin.LOGICAL) {
// Follow it upstream inside the reactor
for (ReactionInstance uReaction : actionInstance.getDependsOnReactions()) {
// Avoid a loop
if (!Objects.equal(uReaction, reaction)) {
TimeValue uMinDelay = actionInstance.getMinDelay().add(findNearestPhysicalActionTrigger(uReaction));
if (uMinDelay.isEarlierThan(minDelay)) {
minDelay = uMinDelay;
}
}
}
}
} else if (trigger.getDefinition() instanceof Output) {
// Outputs of contained reactions
PortInstance outputInstance = (PortInstance) trigger;
for (ReactionInstance uReaction : outputInstance.getDependsOnReactions()) {
TimeValue uMinDelay = findNearestPhysicalActionTrigger(uReaction);
if (uMinDelay.isEarlierThan(minDelay)) {
minDelay = uMinDelay;
}
}
}
}
return minDelay;
}
use of org.lflang.TimeValue in project lingua-franca by lf-lang.
the class CGeneratorExtension method getNetworkDelayLiteral.
/**
* Given a connection 'delay' predicate, return a string that represents the
* interval_t value of the additional delay that needs to be applied to the
* outgoing message.
*
* The returned additional delay in absence of after on network connection
* (i.e., if delay is passed as a null) is NEVER. This has a special
* meaning in C library functions that send network messages that carry
* timestamps (@see send_timed_message and send_port_absent_to_federate
* in lib/core/federate.c). In this case, the sender will send its current
* tag as the timestamp of the outgoing message without adding a microstep delay.
* If the user has assigned an after delay to the network connection (that
* can be zero) either as a time value (e.g., 200 msec) or as a literal
* (e.g., a parameter), that delay in nsec will be returned.
*
* @param delay
* @param generator
* @return
*/
public static String getNetworkDelayLiteral(Delay delay) {
String additionalDelayString = "NEVER";
if (delay != null) {
Parameter p = delay.getParameter();
TimeValue tv;
if (delay.getParameter() != null) {
// The parameter has to be parameter of the main reactor.
// And that value has to be a Time.
tv = ASTUtils.getDefaultAsTimeValue(p);
} else {
tv = ASTUtils.toTimeValue(delay.getTime());
}
additionalDelayString = Long.toString(tv.toNanoSeconds());
}
return additionalDelayString;
}
Aggregations