use of org.lflang.lf.Type in project lingua-franca by lf-lang.
the class FedASTUtils method addNetworkSenderReaction.
/**
* Add a network sender reaction for a given input port 'source' to
* source's parent reactor. This reaction will react to the 'source'
* and then send a message on the network destined for the destinationFederate.
*
* @note Used in federated execution
*
* @param source The source port instance.
* @param destination The destination port instance.
* @param connection The network 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 generator The GeneratorBase instance used to perform some target-specific actions
* @param coordination One of CoordinationType.DECENTRALIZED or CoordinationType.CENTRALIZED.
* @param serializer The serializer used on the connection
*/
private static void addNetworkSenderReaction(PortInstance source, PortInstance destination, Connection connection, FederateInstance sourceFederate, int leftBankIndex, int leftChannelIndex, FederateInstance destinationFederate, GeneratorBase generator, CoordinationType coordination, SupportedSerializers serializer) {
LfFactory factory = LfFactory.eINSTANCE;
// Assume all the types are the same, so just use the first on the right.
Type type = EcoreUtil.copy(source.getDefinition().getType());
VarRef sourceRef = factory.createVarRef();
VarRef destRef = factory.createVarRef();
Reactor parent = (Reactor) connection.eContainer();
Reaction networkSenderReaction = factory.createReaction();
// These reactions do not require any dependency relationship
// to other reactions in the container.
generator.makeUnordered(networkSenderReaction);
// 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(networkSenderReaction, leftBankIndex);
// The connection is 'physical' if it uses the ~> notation.
if (connection.isPhysical()) {
sourceFederate.outboundP2PConnections.add(destinationFederate);
} else {
// to make P2P connections
if (coordination == CoordinationType.DECENTRALIZED) {
sourceFederate.outboundP2PConnections.add(destinationFederate);
}
}
// Record this action in the right federate.
// The ID of the receiving port (rightPort) is the position
// of the action in this list.
int receivingPortID = destinationFederate.networkMessageActions.size();
// Establish references to the involved ports.
sourceRef.setContainer(source.getParent().getDefinition());
sourceRef.setVariable(source.getDefinition());
destRef.setContainer(destination.getParent().getDefinition());
destRef.setVariable(destination.getDefinition());
// Configure the sending reaction.
networkSenderReaction.getTriggers().add(sourceRef);
networkSenderReaction.setCode(factory.createCode());
networkSenderReaction.getCode().setBody(generator.generateNetworkSenderBody(sourceRef, destRef, receivingPortID, sourceFederate, leftBankIndex, leftChannelIndex, destinationFederate, InferredType.fromAST(type), connection.isPhysical(), connection.getDelay(), serializer));
// Add the sending reaction to the parent.
parent.getReactions().add(networkSenderReaction);
// Add the network sender reaction to the federate instance's list
// of network reactions
sourceFederate.networkReactions.add(networkSenderReaction);
}
use of org.lflang.lf.Type 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.Type 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);
}
}));
}
use of org.lflang.lf.Type in project lingua-franca by lf-lang.
the class LFValidator method checkConnection.
@Check(CheckType.FAST)
public void checkConnection(Connection connection) {
// Report if connection is part of a cycle.
Set<NamedInstance<?>> cycles = this.info.topologyCycles();
for (VarRef lp : connection.getLeftPorts()) {
for (VarRef rp : connection.getRightPorts()) {
boolean leftInCycle = false;
for (NamedInstance<?> it : cycles) {
if ((lp.getContainer() == null && it.getDefinition().equals(lp.getVariable())) || (it.getDefinition().equals(lp.getVariable()) && it.getParent().equals(lp.getContainer()))) {
leftInCycle = true;
break;
}
}
for (NamedInstance<?> it : cycles) {
if ((rp.getContainer() == null && it.getDefinition().equals(rp.getVariable())) || (it.getDefinition().equals(rp.getVariable()) && it.getParent().equals(rp.getContainer()))) {
if (leftInCycle) {
Reactor reactor = ASTUtils.getEnclosingReactor(connection);
String reactorName = reactor.getName();
error(String.format("Connection in reactor %s creates", reactorName) + String.format("a cyclic dependency between %s and %s.", toText(lp), toText(rp)), Literals.CONNECTION__DELAY);
}
}
}
}
}
// we leave type compatibility that language's compiler or interpreter.
if (isCBasedTarget()) {
Type type = (Type) null;
for (VarRef port : connection.getLeftPorts()) {
// error. Avoid a class cast exception.
if (port.getVariable() instanceof Port) {
if (type == null) {
type = ((Port) port.getVariable()).getType();
} else {
// method for AST types, so we have to manually check the types.
if (!sameType(type, ((Port) port.getVariable()).getType())) {
error("Types do not match.", Literals.CONNECTION__LEFT_PORTS);
}
}
}
}
for (VarRef port : connection.getRightPorts()) {
// error. Avoid a class cast exception.
if (port.getVariable() instanceof Port) {
if (type == null) {
type = ((Port) port.getVariable()).getType();
} else {
if (!sameType(type, type = ((Port) port.getVariable()).getType())) {
error("Types do not match.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
}
// Check whether the total width of the left side of the connection
// matches the total width of the right side. This cannot be determined
// here if the width is not given as a constant. In that case, it is up
// to the code generator to check it.
int leftWidth = 0;
for (VarRef port : connection.getLeftPorts()) {
// null args imply incomplete check.
int width = inferPortWidth(port, null, null);
if (width < 0 || leftWidth < 0) {
// Cannot determine the width of the left ports.
leftWidth = -1;
} else {
leftWidth += width;
}
}
int rightWidth = 0;
for (VarRef port : connection.getRightPorts()) {
// null args imply incomplete check.
int width = inferPortWidth(port, null, null);
if (width < 0 || rightWidth < 0) {
// Cannot determine the width of the left ports.
rightWidth = -1;
} else {
rightWidth += width;
}
}
if (leftWidth != -1 && rightWidth != -1 && leftWidth != rightWidth) {
if (connection.isIterated()) {
if (leftWidth == 0 || rightWidth % leftWidth != 0) {
// FIXME: The second argument should be Literals.CONNECTION, but
// stupidly, xtext will not accept that. There seems to be no way to
// report an error for the whole connection statement.
warning(String.format("Left width %s does not divide right width %s", leftWidth, rightWidth), Literals.CONNECTION__LEFT_PORTS);
}
} else {
// FIXME: The second argument should be Literals.CONNECTION, but
// stupidly, xtext will not accept that. There seems to be no way to
// report an error for the whole connection statement.
warning(String.format("Left width %s does not match right width %s", leftWidth, rightWidth), Literals.CONNECTION__LEFT_PORTS);
}
}
Reactor reactor = ASTUtils.getEnclosingReactor(connection);
// Make sure the right port is not already an effect of a reaction.
for (Reaction reaction : ASTUtils.allReactions(reactor)) {
for (VarRef effect : reaction.getEffects()) {
for (VarRef rightPort : connection.getRightPorts()) {
if (// Refers to the same variable
rightPort.getVariable().equals(effect.getVariable()) && // Refers to the same instance
rightPort.getContainer() == effect.getContainer() && (// Either is not part of a mode
reaction.eContainer() instanceof Reactor || connection.eContainer() instanceof Reactor || // Or they are in the same mode
connection.eContainer() == reaction.eContainer())) {
error("Cannot connect: Port named '" + effect.getVariable().getName() + "' is already effect of a reaction.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
// upstream connection.
for (Connection c : reactor.getConnections()) {
if (c != connection) {
for (VarRef thisRightPort : connection.getRightPorts()) {
for (VarRef thatRightPort : c.getRightPorts()) {
if (// Refers to the same variable
thisRightPort.getVariable().equals(thatRightPort.getVariable()) && // Refers to the same instance
thisRightPort.getContainer() == thatRightPort.getContainer() && (// Or either of the connections in not part of a mode
connection.eContainer() instanceof Reactor || c.eContainer() instanceof Reactor || // Or they are in the same mode
connection.eContainer() == c.eContainer())) {
error("Cannot connect: Port named '" + thisRightPort.getVariable().getName() + "' may only appear once on the right side of a connection.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
}
}
Aggregations