blob: 1a72c39f1b4732b9fb8e4a8f4f3b38ad07fac7f0 [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2016, 2020 fortiss GmbH, Johannes Kepler University, Linz,
* Primetals Technologies Austria GmbH
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Alois Zoitl - initial API and implementation and/or initial documentation
* - fixed coordinate system resolution conversion in in- and export
* - Changed XML parsing to Staxx cursor interface for improved
* parsing performance
* Bianca Wiesmayr - mux support
* Alois Zoitl - added error marker generation for missing fb types and
* connection error
********************************************************************************/
package org.eclipse.fordiac.ide.model.dataimport;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import javax.xml.stream.XMLStreamException;
import org.eclipse.core.resources.IMarker;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.fordiac.ide.model.CoordinateConverter;
import org.eclipse.fordiac.ide.model.LibraryElementTags;
import org.eclipse.fordiac.ide.model.Palette.FBTypePaletteEntry;
import org.eclipse.fordiac.ide.model.data.StructuredType;
import org.eclipse.fordiac.ide.model.dataimport.exceptions.TypeImportException;
import org.eclipse.fordiac.ide.model.helpers.FordiacMarkerHelper;
import org.eclipse.fordiac.ide.model.libraryElement.Attribute;
import org.eclipse.fordiac.ide.model.libraryElement.Connection;
import org.eclipse.fordiac.ide.model.libraryElement.ConnectionRoutingData;
import org.eclipse.fordiac.ide.model.libraryElement.Demultiplexer;
import org.eclipse.fordiac.ide.model.libraryElement.FB;
import org.eclipse.fordiac.ide.model.libraryElement.FBNetwork;
import org.eclipse.fordiac.ide.model.libraryElement.FBNetworkElement;
import org.eclipse.fordiac.ide.model.libraryElement.IInterfaceElement;
import org.eclipse.fordiac.ide.model.libraryElement.InterfaceList;
import org.eclipse.fordiac.ide.model.libraryElement.LibraryElement;
import org.eclipse.fordiac.ide.model.libraryElement.LibraryElementFactory;
import org.eclipse.fordiac.ide.model.libraryElement.LibraryElementPackage;
import org.eclipse.fordiac.ide.model.libraryElement.Multiplexer;
import org.eclipse.fordiac.ide.model.libraryElement.StructManipulator;
import org.eclipse.fordiac.ide.model.libraryElement.VarDeclaration;
class FBNetworkImporter extends CommonElementImporter {
private final FBNetwork fbNetwork;
// this is the interface list needed for checking connection to the containg
// types interface
private final InterfaceList interfaceList;
protected final Map<String, FBNetworkElement> fbNetworkElementMap = new HashMap<>();
public FBNetworkImporter(final CommonElementImporter importer) {
// so we need an empty interface list
// this is a type with no external interface (currently only application)
this(importer, LibraryElementFactory.eINSTANCE.createFBNetwork(),
LibraryElementFactory.eINSTANCE.createInterfaceList());
}
public FBNetworkImporter(final CommonElementImporter importer, final FBNetwork fbNetwork, final InterfaceList interfaceList) {
super(importer);
this.fbNetwork = fbNetwork;
this.interfaceList = interfaceList;
fbNetwork.getNetworkElements().forEach(element -> fbNetworkElementMap.put(element.getName(), element));
}
protected FBNetworkImporter(final CommonElementImporter importer, final FBNetwork fbNetwork) {
this(importer, fbNetwork, LibraryElementFactory.eINSTANCE.createInterfaceList());
}
public FBNetwork getFbNetwork() {
return fbNetwork;
}
public void parseFBNetwork(final String networkNodeName) throws TypeImportException, XMLStreamException {
processChildren(networkNodeName, this::handleFBNetworkChild);
}
protected boolean handleFBNetworkChild(final String name) throws XMLStreamException, TypeImportException {
switch (name) {
case LibraryElementTags.FB_ELEMENT:
parseFB();
break;
case LibraryElementTags.EVENT_CONNECTIONS_ELEMENT:
parseConnectionList(LibraryElementPackage.eINSTANCE.getEventConnection(), fbNetwork.getEventConnections(),
LibraryElementTags.EVENT_CONNECTIONS_ELEMENT);
break;
case LibraryElementTags.DATA_CONNECTIONS_ELEMENT:
parseConnectionList(LibraryElementPackage.eINSTANCE.getDataConnection(), fbNetwork.getDataConnections(),
LibraryElementTags.DATA_CONNECTIONS_ELEMENT);
break;
case LibraryElementTags.ADAPTERCONNECTIONS_ELEMENT:
parseConnectionList(LibraryElementPackage.eINSTANCE.getAdapterConnection(),
fbNetwork.getAdapterConnections(), LibraryElementTags.ADAPTERCONNECTIONS_ELEMENT);
break;
default:
return false;
}
return true;
}
protected void parseFB() throws TypeImportException, XMLStreamException {
FB fb = LibraryElementFactory.eINSTANCE.createFB();
readNameCommentAttributes(fb);
final String typeFbElement = getAttributeValue(LibraryElementTags.TYPE_ATTRIBUTE);
final FBTypePaletteEntry entry = getTypeEntry(typeFbElement);
if (null != entry) {
fb.setPaletteEntry(entry);
fb.setInterface(fb.getType().getInterfaceList().copy());
if ("STRUCT_MUX".equals(fb.getType().getName())) { //$NON-NLS-1$
final Multiplexer mux = LibraryElementFactory.eINSTANCE.createMultiplexer();
fb = convertFBtoMux(fb, mux);
} else if ("STRUCT_DEMUX".equals(fb.getType().getName())) { //$NON-NLS-1$
final Demultiplexer demux = LibraryElementFactory.eINSTANCE.createDemultiplexer();
fb = convertFBtoMux(fb, demux);
}
} else {
// add it to the fbnetwork so that the error marker can determine the location
fbNetwork.getNetworkElements().add(fb);
createErrorMarker(
MessageFormat.format("Type ({0}) could not be loaded for FB: {1}", typeFbElement, fb.getName()),
fb);
// as we don't have type information we create an empty interface list
fb.setInterface(LibraryElementFactory.eINSTANCE.createInterfaceList());
// TODO add attribute value for missing instance name and
// indicate that FB is missing for usage in outline views
}
getXandY(fb);
parseFBChildren(fb, LibraryElementTags.FB_ELEMENT);
fbNetwork.getNetworkElements().add(fb);
fbNetworkElementMap.put(fb.getName(), fb);
if (fb instanceof StructManipulator) {
final Attribute attr = fb.getAttribute("StructuredType"); //$NON-NLS-1$
final StructuredType structType = getTypeLibrary().getDataTypeLibrary().getStructuredType(attr.getValue());
((StructManipulator) fb).setStructTypeElementsAtInterface(structType);
}
}
private static FB convertFBtoMux(final FB fb, final StructManipulator mux) {
mux.setName(fb.getName());
mux.setComment(fb.getComment());
mux.setPosition(fb.getPosition());
mux.setPaletteEntry(fb.getPaletteEntry());
mux.setInterface(fb.getInterface());
return mux;
}
private FBTypePaletteEntry getTypeEntry(final String typeFbElement) {
if (null != typeFbElement) {
return getPalette().getFBTypeEntry(typeFbElement);
}
return null;
}
protected void parseFBChildren(final FBNetworkElement block, final String parentNodeName)
throws TypeImportException, XMLStreamException {
processChildren(parentNodeName, name -> {
switch (name) {
case LibraryElementTags.PARAMETER_ELEMENT:
final VarDeclaration parameter = parseParameter();
final VarDeclaration vInput = getVarNamed(block.getInterface(), parameter.getName(), true);
if (null != vInput) {
vInput.setValue(parameter.getValue());
}
return true;
case LibraryElementTags.ATTRIBUTE_ELEMENT:
parseGenericAttributeNode(block);
proceedToEndElementNamed(LibraryElementTags.ATTRIBUTE_ELEMENT);
return true;
default:
return false;
}
});
}
protected <T extends Connection> void parseConnectionList(final EClass conType, final EList<T> connectionlist,
final String parentNodeName) throws XMLStreamException, TypeImportException {
processChildren(parentNodeName, name -> {
final T connection = parseConnection(conType);
if (null != connection) {
connectionlist.add(connection);
}
proceedToEndElementNamed(LibraryElementTags.CONNECTION_ELEMENT);
return true;
});
}
private <T extends Connection> T parseConnection(final EClass conType) {
@SuppressWarnings("unchecked")
final
T connection = (T) LibraryElementFactory.eINSTANCE.create(conType);
connection.setResTypeConnection(false);
final String destinationElement = getAttributeValue(LibraryElementTags.DESTINATION_ATTRIBUTE);
final String sourceElement = getAttributeValue(LibraryElementTags.SOURCE_ATTRIBUTE);
if (null != destinationElement) {
final IInterfaceElement destination = getConnectionEndPoint(destinationElement, conType, true);
if (null != destination) {
connection.setDestination(destination);
} else {
// TODO model refactoring - this connection is missing an end point. Add a dummy connection points so
// that the connection can be handled in the according FBNetorwk editor
createConnectionErrorMarker("Connection destination not found: " + destinationElement, getFbNetwork(),
sourceElement, destinationElement);
return null;
}
} else {
createConnectionErrorMarker("Connection destination missing!", getFbNetwork(), sourceElement,
destinationElement);
}
if (null != sourceElement) {
final IInterfaceElement source = getConnectionEndPoint(sourceElement, conType, false);
if (null != source) {
connection.setSource(source);
} else {
createConnectionErrorMarker("Connection source not found: " + sourceElement, getFbNetwork(),
sourceElement, destinationElement);
return null;
}
} else {
createConnectionErrorMarker("Connection source missing!", getFbNetwork(), sourceElement,
destinationElement);
}
final String commentElement = getAttributeValue(LibraryElementTags.COMMENT_ATTRIBUTE);
if (null != commentElement) {
connection.setComment(commentElement);
}
parseConnectionRouting(connection);
return connection;
}
private void createConnectionErrorMarker(final String message, final FBNetwork fbNetwork,
final String sourceIdentifier, final String destinationIdentifier) {
final Map<String, String> attrs = new HashMap<>();
attrs.put(IMarker.MESSAGE, message);
// use a dummy connection to get target identifier
FordiacMarkerHelper.addTargetIdentifier(LibraryElementFactory.eINSTANCE.createDataConnection(), attrs);
final String location = FordiacMarkerHelper.getLocation(fbNetwork) + "." + sourceIdentifier + " -> " //$NON-NLS-1$
+ destinationIdentifier;
attrs.put(IMarker.LOCATION, location);
createErrorMarker(attrs);
}
private void parseConnectionRouting(final Connection connection) {
final ConnectionRoutingData routingData = LibraryElementFactory.eINSTANCE.createConnectionRoutingData();
final String dx1Element = getAttributeValue(LibraryElementTags.DX1_ATTRIBUTE);
if (null != dx1Element) {
routingData.setDx1(parseConnectionValue(dx1Element));
}
final String dx2Element = getAttributeValue(LibraryElementTags.DX2_ATTRIBUTE);
if (null != dx2Element) {
routingData.setDx2(parseConnectionValue(dx2Element));
}
final String dyElement = getAttributeValue(LibraryElementTags.DY_ATTRIBUTE);
if (null != dyElement) {
routingData.setDy(parseConnectionValue(dyElement));
}
connection.setRoutingData(routingData);
}
private IInterfaceElement getConnectionEndPoint(final String path, final EClass conType, final boolean isInput) {
final String[] split = path.split("\\."); //$NON-NLS-1$
if (1 == split.length) {
return getContainingInterfaceElement(path, conType, isInput);
}
if (split.length >= 2) {
final FBNetworkElement element = findFBNetworkElement(split[0]);
if (null != element) {
return getInterfaceElement(element.getInterface(), path.substring(split[0].length() + 1), conType,
isInput);
}
}
return null;
}
/**
* Check if the element that contains the fbnetwork has an interface element
* with the given name. this is needed for subapps, cfbs, devices and resources
*/
protected IInterfaceElement getContainingInterfaceElement(final String interfaceElement, final EClass conType,
final boolean isInput) {
return getInterfaceElement(interfaceList, interfaceElement, conType, !isInput); // for connections to the
// interface inputs are the
// outputs of the FB
}
private static IInterfaceElement getInterfaceElement(final InterfaceList il, final String interfaceElement, final EClass conType,
final boolean isInput) {
final EList<? extends IInterfaceElement> ieList = getInterfaceElementList(il, conType, isInput);
for (final IInterfaceElement ie : ieList) {
if (ie.getName().equals(interfaceElement)) {
return ie;
}
}
return null;
}
private static EList<? extends IInterfaceElement> getInterfaceElementList(final InterfaceList il, final EClass conType,
final boolean isInput) {
if (isInput) {
if (LibraryElementPackage.eINSTANCE.getEventConnection() == conType) {
return il.getEventInputs();
}
if (LibraryElementPackage.eINSTANCE.getDataConnection() == conType) {
return il.getInputVars();
}
if (LibraryElementPackage.eINSTANCE.getAdapterConnection().equals(conType)) {
return il.getSockets();
}
} else {
if (LibraryElementPackage.eINSTANCE.getEventConnection() == conType) {
return il.getEventOutputs();
}
if (LibraryElementPackage.eINSTANCE.getDataConnection() == conType) {
return il.getOutputVars();
}
if (LibraryElementPackage.eINSTANCE.getAdapterConnection().equals(conType)) {
return il.getPlugs();
}
}
return null;
}
protected FBNetworkElement findFBNetworkElement(final String fbName) {
return fbNetworkElementMap.get(fbName);
}
protected static VarDeclaration getVarNamed(final InterfaceList interfaceList, final String varName, final boolean input) {
VarDeclaration retVal;
boolean hasType = true;
if (interfaceList.eContainer() instanceof FB) {
// only if it is an FB check if it is typed
hasType = (null != ((FB) interfaceList.eContainer()).getPaletteEntry());
}
if (hasType) {
// we have a typed FB
retVal = interfaceList.getVariable(varName);
if ((null != retVal) && (retVal.isIsInput() != input)) {
retVal = null;
}
} else {
// if we couldn't load the type create the interface entry
retVal = createVarDecl(interfaceList, varName, input);
}
return retVal;
}
private static VarDeclaration createVarDecl(final InterfaceList interfaceList, final String varName, final boolean input) {
final VarDeclaration var = LibraryElementFactory.eINSTANCE.createVarDeclaration();
var.setName(varName);
var.setIsInput(input);
if (input) {
interfaceList.getInputVars().add(var);
} else {
interfaceList.getOutputVars().add(var);
}
return var;
}
/**
* returns an valid dx, dy integer value
*
* @param value
* @return if value is valid the converted int of that otherwise 0
*/
private static int parseConnectionValue(final String value) {
try {
return CoordinateConverter.INSTANCE.convertFrom1499XML(value);
} catch (final NumberFormatException ex) {
return 0;
}
}
@Override
protected LibraryElement createRootModelElement() {
// Nothing to be done for FBNetworks
return null;
}
@Override
protected String getStartElementName() {
// Nothing to be done for FBNetworks
return null;
}
@Override
protected IChildHandler getBaseChildrenHandler() {
// Nothing to be done for FBNetworks
return null;
}
}