/******************************************************************************* * Copyright (c) 2010-2013, Embraer S.A., Budapest University of Technology and Economics * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marton Bur, Abel Hegedus, Akos Horvath - initial API and implementation *******************************************************************************/ package hu.bme.mit.massif.simulink.api; import hu.bme.mit.massif.communication.command.MatlabCommand; import hu.bme.mit.massif.communication.command.MatlabCommandFactory; import hu.bme.mit.massif.communication.datatype.CellMatlabData; import hu.bme.mit.massif.communication.datatype.Handle; import hu.bme.mit.massif.communication.datatype.IVisitableMatlabData; import hu.bme.mit.massif.communication.datatype.MatlabString; import hu.bme.mit.massif.communication.datatype.StructMatlabData; import hu.bme.mit.massif.simulink.Block; import hu.bme.mit.massif.simulink.BusCreator; import hu.bme.mit.massif.simulink.BusSelector; import hu.bme.mit.massif.simulink.BusSignalMapping; import hu.bme.mit.massif.simulink.Connection; import hu.bme.mit.massif.simulink.Enable; import hu.bme.mit.massif.simulink.From; import hu.bme.mit.massif.simulink.Goto; import hu.bme.mit.massif.simulink.GotoTagVisibility; import hu.bme.mit.massif.simulink.InPort; import hu.bme.mit.massif.simulink.MultiConnection; import hu.bme.mit.massif.simulink.OutPort; import hu.bme.mit.massif.simulink.Port; import hu.bme.mit.massif.simulink.Property; import hu.bme.mit.massif.simulink.SimulinkElement; import hu.bme.mit.massif.simulink.SimulinkModel; import hu.bme.mit.massif.simulink.SimulinkReference; import hu.bme.mit.massif.simulink.SingleConnection; import hu.bme.mit.massif.simulink.SubSystem; import hu.bme.mit.massif.simulink.Trigger; import hu.bme.mit.massif.simulink.api.exception.SimulinkApiException; import hu.bme.mit.massif.simulink.api.internal.PluginSimulinkAPILogger; import hu.bme.mit.massif.simulink.api.layout.BlockLayoutSpecification; import hu.bme.mit.massif.simulink.api.layout.DummyExporterLayoutProvider; import hu.bme.mit.massif.simulink.api.layout.IExporterLayoutProvider; import hu.bme.mit.massif.simulink.api.provider.block.SourceBlockProvider; import hu.bme.mit.massif.simulink.api.util.ISimulinkAPILogger; import hu.bme.mit.massif.simulink.api.util.Point; import hu.bme.mit.massif.simulink.api.util.bus.BusSignalMapper; import hu.bme.mit.massif.simulink.api.util.bus.BusSignalMappingPathFinder; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.util.BasicExtendedMetaData; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.util.ExtendedMetaData; import org.eclipse.emf.ecore.xmi.XMLResource; import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; import com.google.common.base.Strings; import com.google.common.collect.Sets; /** * This class provides functions to export and save Simulink models represented by EMF models */ public class Exporter { /** * The command evaluator instance used during the export process. It is used to send commands to Matlab and receive * the results. */ // private ICommandEvaluator commandEvaluator; private MatlabCommandFactory commandFactory; /** * The logger instance used during the export process. It does the logging. */ private ISimulinkAPILogger logger; /** * The layout provider used during the export process. It is responsible for determining the location and size of * each exported block. */ private IExporterLayoutProvider layoutTool; /** * A temporal cache to store Goto objects in a set. */ private Set<Goto> gotoCache; /** * A temporal cache to store GotoTagVisibility objects in a set. */ private Set<GotoTagVisibility> gotoTagVisibilityCache; /** * A temporal cache to store the FQNs of the already added blocks. */ private Set<String> addedBlockFQNs = new HashSet<String>(); private BusSignalMappingPathFinder pathFinder; /** * The constructor for the exporter. Creates the logger, layout provider and initializes caches. */ public Exporter() { logger = new PluginSimulinkAPILogger(); layoutTool = new DummyExporterLayoutProvider(); gotoCache = new HashSet<Goto>(); gotoTagVisibilityCache = new HashSet<GotoTagVisibility>(); } /** * Sets the layout provider. * * @param layoutTool * the new layout provider to be used by the exporter object */ public void setLayoutProvider(IExporterLayoutProvider layoutTool) { this.layoutTool = layoutTool; } /** * Loads the EMF representation of a Simulink model. * * @param fileNameWithoutExtension * the file to load specified by its name * @return the loaded SimulinkModel root EMF model object * @throws SimulinkApiException */ public SimulinkModel loadSimulinkModel(String fileNameWithoutExtension) throws SimulinkApiException { // Register a resource factory for the extension "simulink" Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE; Map<String, Object> m = reg.getExtensionToFactoryMap(); m.put("simulink", new XMIResourceFactoryImpl()); // Obtain a new resource set ResourceSet rs = new ResourceSetImpl(); final ExtendedMetaData extendedMetaData = new BasicExtendedMetaData(rs.getPackageRegistry()); rs.getLoadOptions().put(XMLResource.OPTION_EXTENDED_META_DATA, extendedMetaData); // The model will be saved to the root of the project for now under the // given name. // TODO save path may come from an extension or properties file String path = fileNameWithoutExtension + ".simulink"; // Load the resource Resource loadResource = rs.getResource(URI.createURI(path), true); Object resourceContent = loadResource.getContents().get(0); // Make a log entry if the root object isn't a SimulinkModel if (!(resourceContent instanceof SimulinkModel)) logger.error("The given resource contained no SimulinkModel as a root element!"); // Return the root EMF model object // Also throws an exception when invalid cast occurs return (SimulinkModel) resourceContent; } /** * Saves the exported Simuink model in Matlab. * * @param modelNameWithPath * the exported model's save path * @throws SimulinkApiException */ public void saveSimulinkModel(String modelNameWithPath, String fileExtension) throws SimulinkApiException { // If no command evaluator object is assigned yet, indicate it if (commandFactory == null) { logger.error("No Matlab command factory is set for the exporter. Was the model to be saved already exported?"); } /* * The exporter needs to navigate to the save directory first, because it is quite common, that the length of * the model absolute path exceeds the maximum number of characters accepted by the save_system command (exceeds * 63 characters) */ // Save the current directory String currentWorkdirectory = MatlabString.getMatlabStringData(commandFactory.cd().execute()); String[] savePathSegments = modelNameWithPath.split("\\\\"); String modelName = savePathSegments[savePathSegments.length - 1]; // Navigate to the save location for (int i = 0; i < savePathSegments.length - 1; i++) { String segment = savePathSegments[i]; MatlabCommand changeToSaveDir = commandFactory.cd().addParam(segment + "\\"); changeToSaveDir.execute(); } // Save the model MatlabCommand saveSystem = commandFactory.saveSystem().addParam(modelName).addParam(modelName + "." + fileExtension); saveSystem.execute(); // Navigate back to the original working directory String[] workDirSegments = currentWorkdirectory.split("\\\\"); for (int i = 0; i < workDirSegments.length; i++) { String segment = workDirSegments[i]; MatlabCommand changeToWorkDir = commandFactory.cd().addParam(segment + "\\"); changeToWorkDir.execute(); } } /** * Exports a given .simulink model to Simulink, but doesn't save it * * @param model * the EMF representation of the Simulink model to export * @param commandEvaluator * the command evaluator object to use for evaluating command in Matlab during export TODO this object * could be set in a setter, previously * @throws SimulinkApiException */ public void export(SimulinkModel model, MatlabCommandFactory commandFactory) throws SimulinkApiException { // Import time measurement begin long beginTime = System.currentTimeMillis(); // The given command factory is set for the exporter this.commandFactory = commandFactory; // The list of blocks on the top level EList<Block> topLevelBlocks = model.getContains(); // Create the model in Simulink String modelFQN = getFQN(model); // TODO do not close, open the model if able instead // Before the new system creation, close the possibly open model // MatlabCommand closeSystem = commandFactory.closeSystem().addParam(modelFQN); // closeSystem.execute(); boolean modelAlreadyExists = Handle.getHandleData(commandFactory.exist().addParam(modelFQN).execute()) > 0.5; if(modelAlreadyExists){ MatlabCommand loadSystem = commandFactory.loadSytem(); loadSystem.addParam(modelFQN).execute(); } else { MatlabCommand newSystem = commandFactory.newSytem(); newSystem.addParam(modelFQN); if (model.isLibrary()) { newSystem.addParam("Library"); } newSystem.execute(); } BusSignalMapper mapper = new BusSignalMapper(model.eResource().getResourceSet()); mapper.setLogger(logger); pathFinder = new BusSignalMappingPathFinder(mapper); // Start the export from the top level blocks // Also exports connections for each hierarchy level logger.debug("Exporting blocks..."); exportBlocks(topLevelBlocks); logger.debug("Block export finished"); // Some tasks can only be done after placing all model object to the exported model, so do these post processing // steps here postProcessModel(modelFQN); // End of import logger.debug("EXPORT TIME TOTAL: " + (System.currentTimeMillis() - beginTime) + "ms"); } /** * Gets the FQN for a given SimulinkElement * * @param element * the SimulinkElement object * @return */ private String getFQN(SimulinkElement element) { return element.getSimulinkRef().getFQN(); } /** * All the post processing tasks are done in this function. Post processing tasks can only be done after placing all * model elements to the model. * * @param modelFQN * the model's fully qualified name, to which the post process steps are need to be done */ private void postProcessModel(String modelFQN) { logger.debug("Post processing begins..."); setMissingGotoTags(); logger.debug("Post processing finished"); } /** * As a post process step, sets all missing goto tags for the exported elements in order to create the same * goto-from assignments as the EMF model contains. */ private void setMissingGotoTags() { for (GotoTagVisibility gotoTagVisibilityBlock : gotoTagVisibilityCache) { String gotoTagVisibilityFQN = getFQN(gotoTagVisibilityBlock); Goto gotoBlock = gotoTagVisibilityBlock.getGotoBlock(); String selectedGotoTag = gotoBlock.getGotoTag(); // When a gotoTagVisibility points to a processed goto block use the gotoTag of the goto block boolean gotoBlockIsAlreadyProcessedOnce = !gotoCache.contains(gotoBlock); if (gotoBlockIsAlreadyProcessedOnce) { MatlabCommand setGotoGotoTag = commandFactory.setParam().addParam(gotoTagVisibilityFQN).addParam("GotoTag").addParam(selectedGotoTag); setGotoGotoTag.execute(); break; } // When the gotoTag is empty/not set generate a new gotoTag if (Strings.isNullOrEmpty(selectedGotoTag)) { selectedGotoTag = generateGotoTag(); gotoBlock.setGotoTag(selectedGotoTag); } MatlabCommand setGotoTagVisibilityGotoTag = commandFactory.setParam().addParam(gotoTagVisibilityFQN).addParam("GotoTag").addParam(selectedGotoTag); setGotoTagVisibilityGotoTag.execute(); // Set it in the model, but do not save it gotoTagSet(gotoBlock, selectedGotoTag, true); gotoCache.remove(gotoBlock); } for (Goto gotoBlock : gotoCache) { // Test if the gotoTag is valid String selectedGotoTag = gotoBlock.getGotoTag(); if (Strings.isNullOrEmpty(selectedGotoTag)) { // When invalid generate one selectedGotoTag = generateGotoTag(); } // Set it in the model gotoTagSet(gotoBlock, selectedGotoTag, false); } } /** * Generates a unique and valid goto tag. * * @return the generated goto tag string */ private String generateGotoTag() { String generatedUUID = EcoreUtil.generateUUID(); return generatedUUID.replaceAll("[^a-zA-Z]", ""); } /** * Sets the given goto tag for the given goto block. * * @param gotoBlock * the goto block for which the tag should be set * @param gotoTag * the value of the tag * @param isScoped * indicates if the goto block is scoped */ private void gotoTagSet(Goto gotoBlock, String gotoTag, boolean isScoped) { String gotoFQN = getFQN(gotoBlock); String scopedOrLocal = null; if (gotoBlock.getTagVisibility() != null && !Strings.isNullOrEmpty(gotoBlock.getTagVisibility().name())) { // Whether there is a GotoTagVisibility block or not, the scoping can be set to any possible value scopedOrLocal = gotoBlock.getTagVisibility().name(); } else { // If the GotoTagVisibility scope is not set, it should be inferred from the existence of the tag visibility // block // knowing that global doesn't fulfill the criteria of well-formedness of the model scopedOrLocal = isScoped ? "scoped" : "local"; } // Set the parameter values in Matlab MatlabCommand setScope = commandFactory.setParam().addParam(gotoFQN).addParam("TagVisibility").addParam(scopedOrLocal); setScope.execute(); MatlabCommand setGotoTag = commandFactory.setParam().addParam(gotoFQN).addParam("GotoTag").addParam(gotoTag); setGotoTag.execute(); // Set the same tag for each assigned from block for (From from : gotoBlock.getFromBlocks()) { String fromFQN = getFQN(from); setGotoTag.setParam(0, new MatlabString(fromFQN)); setGotoTag.execute(); } } /** * Exports the blocks to the model contained in the given list. * * @param sameLevelBlocks * the list of EMF objects for which a Simulink block should be added to the model in Simulink * @throws SimulinkApiException */ private void exportBlocks(EList<Block> sameLevelBlocks) throws SimulinkApiException { // BlockFQN and block size map Map<Block, BlockLayoutSpecification> blocksOriginalSize = new HashMap<Block, BlockLayoutSpecification>(); // Create blocks for (Block block : sameLevelBlocks) { // Caching for post processing if (block instanceof GotoTagVisibility) { gotoTagVisibilityCache.add((GotoTagVisibility) block); } else if (block instanceof Goto) { gotoCache.add((Goto) block); } // TODO process the block IVisitableMatlabData newBlockHandle = null; String sourceBlockFQN = getRealSourceBlockFQN(block); String blockFQN = getFQN(block); // Making sure not to re-add blocks with the same name to the model by storing the already added blockFQNs if (addedBlockFQNs.contains(blockFQN)) { logger.error("The block '" + blockFQN + "' with sourceBlock '" + sourceBlockFQN + "' could not be added, because a block with the same name already exists in the model!"); continue; } addedBlockFQNs.add(blockFQN); // Adds a block to the model if able commandFactory.clearLastErrorMessage().execute(); MatlabCommand addBlock = commandFactory.addBlock().addParam(sourceBlockFQN).addParam(blockFQN); newBlockHandle = addBlock.execute(); MatlabCommand lastErrorMessage = commandFactory.getLastErrorMessage(); String lastErrorMessageString = MatlabString.getMatlabStringData(lastErrorMessage.execute()); if (!lastErrorMessageString.equals("")) { logger.error("The block '" + blockFQN + "' with sourceBlock '" + sourceBlockFQN + "' could not be added"); continue; } // Get original size of the block MatlabCommand getDefaultPosition = commandFactory.getParam().addParam(newBlockHandle).addParam("Position"); IVisitableMatlabData defaultPosition = getDefaultPosition.execute(); CellMatlabData defaultPositionData = (CellMatlabData) defaultPosition; BlockLayoutSpecification bls = new BlockLayoutSpecification(); bls.width = (Handle.getHandleData(defaultPositionData.getData(2)) - Handle .getHandleData(defaultPositionData.getData(0))); bls.height = (Handle.getHandleData(defaultPositionData.getData(3)) - Handle .getHandleData(defaultPositionData.getData(1))); // set temporary x,y coordinate bls.topLeft = new Point(0.0f, 0.0f); blocksOriginalSize.put(block, bls); // Try to load the source block library // DO NOT DELETE - TODO this might be necessary and useful // if (newBlockHandle == null) { // LoadSystem loadSystem = new LoadSystem(commandEvaluator); // loadSystem.addParam(block.getSourceBlockRef().getQualifier()); // loadSystem.execute(); // newBlockHandle = addBlock.execute(); // // If the add_block operation fails for the second time as well, throw an exception // if (newBlockHandle == null) { // logger.error("Failed to add block " + block.getSimulinkRef().getFQN() + "\nEscaped name: " + blockFQN); // } // } // ////////////////////////////////////////////////////////// // Block type specific processing part BEGIN // TODO find a more sophisticated solution (like the provider-adapter pattern applied in the Importer) if (block instanceof BusCreator) { busCreatorExtraActions(block); } else if (block instanceof BusSelector) { busSelectorExtraActions(block); } else if (block instanceof SubSystem) { subSystemExtraActions(block, newBlockHandle, blockFQN); } // Block type specific processing part END // ////////////////////////////////////////////////////////// // Set block dialog/mask parameters EList<Property> properties = block.getProperties(); for (Property property : properties) { commandFactory.setParam().addParam(getFQN(block)).addParam(property.getName()).addParam(property.getValue()).execute(); } } // When there is at least one block on the same level, check that only blocks are there on the same level will be present in the exported model String containerFQN = getContainerFQN(sameLevelBlocks); MatlabCommand findExistingBlocks = commandFactory.findSystem().addParam(containerFQN).addParam("SearchDepth").addParam(1.0).addParam("type").addParam("block"); List<IVisitableMatlabData> fqnsInTheModel = CellMatlabData.getCellMatlabDataData(findExistingBlocks.execute()); for (IVisitableMatlabData iVisitableMatlabData : fqnsInTheModel) { String fqn = MatlabString.getMatlabStringData(iVisitableMatlabData); boolean found = false; inner: for (Block block : sameLevelBlocks) { if(block.getSimulinkRef().getFQN().equals(fqn)){ found = true; break inner; } } if(!found && !fqn.equals(containerFQN)){ commandFactory.deleteBlock().addParam(fqn).execute(); } } // layout the complete level layoutBlocks(blocksOriginalSize); // create connections between the exported blocks exportLines(sameLevelBlocks); } private String getContainerFQN(EList<Block> sameLevelBlocks) { String containerFQN = null; if(sameLevelBlocks.size() > 0 ){ Block aBlock = sameLevelBlocks.get(0); EObject container = aBlock.eContainer(); if(container instanceof SimulinkModel){ containerFQN = ((SimulinkModel) container).getSimulinkRef().getFQN(); } else if (container instanceof SubSystem){ containerFQN = ((SubSystem) container).getSimulinkRef().getFQN(); } else { logger.error("Unknown parent encountered for: " + aBlock.getName()); } } return containerFQN; } private void layoutBlocks(Map<Block, BlockLayoutSpecification> blocksOriginalSize) { // set x,y coordinate of blocks layoutTool.updateSpecifications(blocksOriginalSize); for (Map.Entry<Block, BlockLayoutSpecification> entry : blocksOriginalSize.entrySet()) { Block block = entry.getKey(); BlockLayoutSpecification layoutSpecification = entry.getValue(); int topLeftX = (int) layoutSpecification.topLeft.x; int topLeftY = (int) layoutSpecification.topLeft.y; int width = (int) entry.getValue().width; int height = (int) entry.getValue().height; int bottomRightX = topLeftX + width; int bottomRightY = topLeftY + height; String corners = "[" + topLeftX + "," + topLeftY + "," + bottomRightX + "," + bottomRightY + "]"; commandFactory.setParam().addParam(getFQN(block)).addParam("Position").addParam(corners).execute(); logger.debug("Block position: " + getFQN(block) + " TopLeftX: " + topLeftX + " TopLeftY: " + topLeftY + " Width: " + width + " Height: " + height); } } /** * Gets the source block FQN. * * @param block * for which the source block FQN should be retreived * @return */ private String getRealSourceBlockFQN(Block block) { SimulinkReference sourceBlockRef = block.getSourceBlockRef(); String sourceBlockFQN = null; if (sourceBlockRef != null) sourceBlockFQN = block.getSourceBlockRef().getFQN(); if (Strings.isNullOrEmpty(sourceBlockFQN)) { sourceBlockFQN = SourceBlockProvider.INSTANCE.adapt(block); } return sourceBlockFQN; } /** * Specific actions to be taken in case of a bus creator * * @param block * the BusCreator block */ private void busCreatorExtraActions(Block block) { BusCreator busCreator = (BusCreator) block; int inportCount = busCreator.getInports().size(); commandFactory.setParam().addParam(getFQN(block)).addParam("Inputs").addParam(Integer.toString(inportCount)).execute(); } /** * Specific actions to be taken for a Subsystem * * @param block * the subsystem object * @param handle * the handle of the created block in Simulink * @param blockFQN * the FQN of the subsystem * @throws SimulinkApiException */ private void subSystemExtraActions(Block block, IVisitableMatlabData handle, String blockFQN) throws SimulinkApiException { if (isDefaultSubsystem(block)) { deleteDefaultContentsOfSubsystem(block, handle); } boolean notLinkedSubsystem = block.getSourceBlockRef().isDisabled(); if (notLinkedSubsystem) { // Naive implementation: build up the internal contents of a non linked subsystem from scratch MatlabCommand deleteContents = commandFactory.customCommand("Simulink.SubSystem.deleteContents", 0); deleteContents.addParam(blockFQN).execute(); commandFactory.setParam().addParam(blockFQN).addParam("LinkStatus").addParam("inactive").execute(); // Recursive call for the lower layer blocks, if there's any exportBlocks(((SubSystem) block).getSubBlocks()); } } /** * Specific actions to be taken in case of a bus selector * * @param block * the BusSelector block */ private void busSelectorExtraActions(Block block) throws SimulinkApiException { BusSelector busSelector = (BusSelector) block; EList<BusSignalMapping> portMappings = busSelector.getMappings(); String[] outputSignalNames = new String[portMappings.size()]; int portNumber = 0; for (BusSignalMapping busSignalMapping : portMappings) { OutPort mappedTo = busSignalMapping.getMappingTo(); String portNumberString = mappedTo.getName().substring(mappedTo.getName().lastIndexOf('.') + 1); if (busSelector.isOutputAsBus()) { portNumber++; } else { portNumber = Integer.parseInt(portNumberString); } try { String originalPath = busSignalMapping.getMappingPath(); String computedPath = originalPath; boolean incompleteButSetMapping = !Strings.isNullOrEmpty(originalPath) && busSignalMapping.isIncomplete(); if (incompleteButSetMapping) { logger.debug(String.format("Using stored mapping path for incomplete mapping [%s]", originalPath)); } else { computedPath = pathFinder.findMappingPath(busSignalMapping); busSignalMapping.setMappingPath(computedPath); logger.debug(String.format("Updating mapping path from [%s] to [%s]", originalPath, computedPath)); } outputSignalNames[portNumber - 1] = computedPath; } catch (IllegalArgumentException e) { throw new SimulinkApiException("Could not compute path mapping for " + busSignalMapping + "! Mapping is not set correctly.", e); } catch (IllegalStateException e) { throw new SimulinkApiException("Could not compute path mapping for " + busSignalMapping + "! Mapping is inconsistent with model.", e); } // If output as bus, increment port counter manually } String outputSignals = ""; for (int i = 0; i < outputSignalNames.length - 1; i++) { outputSignals = outputSignals.concat(outputSignalNames[i]) + ","; } outputSignals = outputSignals.concat(outputSignalNames[outputSignalNames.length - 1]); commandFactory.setParam().addParam(getFQN(block)).addParam("OutputSignals").addParam(outputSignals).execute(); } /** * Determines if a block is a basic built-in subsystem block * * @param block * is the model object to inspect * @return <li>true, if it is a default/inbuilt subsystem</li> <li>false, otherwise</li> */ private boolean isDefaultSubsystem(Block block) { String realSourceBlockFQN = getRealSourceBlockFQN(block); return realSourceBlockFQN.equalsIgnoreCase("simulink/Ports & Subsystems/Subsystem") || realSourceBlockFQN.equalsIgnoreCase("simulink/Ports & Subsystems/Atomic Subsystem"); } /** * Deletes the default blocks from a newly added default subsystem * * @param block * the EMF object representing the subsystem * @param handle * the handle of the exported block */ private void deleteDefaultContentsOfSubsystem(Block block, IVisitableMatlabData handle) { // Clear the default elements from the SubSystem block MatlabCommand findDefaultLines = commandFactory.findSystem().addParam(handle).addParam("FindAll").addParam("On").addParam("Type").addParam("Line"); IVisitableMatlabData lineToDelete = findDefaultLines.execute(); commandFactory.deleteLine().addParam(lineToDelete).execute(); commandFactory.deleteBlock().addParam(getFQN(block) + "/Out1").execute(); commandFactory.deleteBlock().addParam(getFQN(block) + "/In1").execute(); } /** * Exports the lines (connections) between blocks * * @param sameLevelBlocks * list of blocks for which lines should be created * @throws SimulinkApiException */ private void exportLines(EList<Block> sameLevelBlocks) { // Create lines on the same level for (Block block : sameLevelBlocks) { // For each block, organize the list of inports // Move the enable port to the end of the list, if exists (it might be followed by a trigger port later) EList<Port> ports = block.getPorts(); Enable enable = null; for (int i = 0; i < ports.size(); i++) { if (ports.get(i) instanceof Enable) { enable = (Enable) ports.get(i); ports.remove(i); break; } } if (enable != null) { ports.add(enable); } // Move the trigger port to the end of the list, if exists (this should be the very last) Trigger trigger = null; for (int i = 0; i < ports.size(); i++) { if (ports.get(i) instanceof Trigger) { trigger = (Trigger) ports.get(i); ports.remove(i); break; } } if (trigger != null) { ports.add(trigger); } } // Store the created line handles in order to be able to delete the rest in Simulink Set<Double> addedLineHandles = Sets.newHashSet(); for (Block block : sameLevelBlocks) { for (OutPort outPort : block.getOutports()) { Double addedLineHandle = null; Connection conn = outPort.getConnection(); if (conn == null) continue; if (conn instanceof SingleConnection) { SingleConnection sc = (SingleConnection) conn; addedLineHandle = createConnectionFromSingleConnection(sc, outPort); if(addedLineHandle != null){ addedLineHandles.add(addedLineHandle); } } else { // It is a multi connection (or null pointer) MultiConnection mc = (MultiConnection) conn; for (SingleConnection sc : mc.getConnections()) { addedLineHandle = createConnectionFromSingleConnection(sc, outPort); if(addedLineHandle != null){ addedLineHandles.add(addedLineHandle); } } } } } // Synchronize existing lines in the EMF model and in the exported Simulink model String containerFQN = getContainerFQN(sameLevelBlocks); MatlabCommand findLineHandles = commandFactory.findSystem().addParam(containerFQN).addParam("findall").addParam("on").addParam("SearchDepth").addParam(1.0).addParam("type").addParam("line"); IVisitableMatlabData allQueriedLineHandles = findLineHandles.execute(); Set<Handle> linesToDelete = Sets.newHashSet(); if(allQueriedLineHandles instanceof CellMatlabData){ List<IVisitableMatlabData> lineHandles = ((CellMatlabData) allQueriedLineHandles).getDatas(); for (IVisitableMatlabData iVisitableMatlabData : lineHandles) { // If the ImporterTmpResult doesn't contain the result of the previous command if(!(iVisitableMatlabData instanceof Handle)){ break; } double currentLineHandle = Handle.getHandleData(iVisitableMatlabData); boolean isContained = false; for (Double lh : addedLineHandles) { if(lh.doubleValue() == currentLineHandle){ isContained = true; break; } } if(!isContained){ // Add to delete list only when the children of the line is empty. // This extra constraint is needed, because we are exporting only 'single connections', so ignore the automatically created multi connections Handle lineToCheck = (Handle) iVisitableMatlabData; MatlabCommand getLineChildren = commandFactory.getParam().addParam(lineToCheck).addParam("LineChildren"); IVisitableMatlabData children = getLineChildren.execute(); // It exists in the model, has no children, but we did not exported it if (children instanceof CellMatlabData && ((CellMatlabData)children).getDatas().size() == 0){ linesToDelete.add(lineToCheck); } } } } else if (allQueriedLineHandles instanceof Handle){ if(!addedLineHandles.contains(allQueriedLineHandles)){ linesToDelete.add((Handle) allQueriedLineHandles); } } for (Handle handle : linesToDelete) { MatlabCommand deleteLine = commandFactory.deleteLine().addParam(handle); deleteLine.execute(); } } /** * Creates a line from a single connection * * @param singleConnection * the single connection from which the line should be created * @param outPort * the outport from which the line goes out * @throws SimulinkApiException */ private double prevdata; private Double createConnectionFromSingleConnection(SingleConnection singleConnection, OutPort outPort) { // FIXME sc.getFrom() didn't work!!! String fromBlockName = outPort.getContainer().getName(); // Get index of the port in the list of ports // Matlab indices start from 1 int indexOfOutportInMatlab = 1; EList<Port> outPortContainerPortList = outPort.getContainer().getPorts(); for (Port port : outPortContainerPortList) { if (port instanceof OutPort) { // Look for the outport involved in the connection if (port.equals(outPort)) { break; } else { // Only increment the counter, when outports are encountered in the list indexOfOutportInMatlab++; } } } int fromPortNumber = indexOfOutportInMatlab; String srcPort = fromBlockName + "/" + fromPortNumber; if(singleConnection.getTo() == null){ // If the connection is unconnected to a port, it won't be added to the Simulink model return null; } String toBlockName = singleConnection.getTo().getContainer().getName(); // Get index of the port in the list of inPorts, and increment it by 1, because Matlab indices start with 1 EList<Port> inPortContainerPortList = singleConnection.getTo().getContainer().getPorts(); InPort targetInPort = singleConnection.getTo(); int indexOfInportInMatlab = 1; for (Port port : inPortContainerPortList) { if (port instanceof InPort) { if (port.equals(targetInPort)) { break; } else { indexOfInportInMatlab++; } } } String toPortNumber = String.valueOf(indexOfInportInMatlab); // TODO optimize solution if(targetInPort instanceof Enable) toPortNumber = "Enable"; if(targetInPort instanceof Trigger) toPortNumber = "Trigger"; String dstPort = toBlockName + "/" + toPortNumber; // get the FQN of the current level / subsystem String system = outPort.getContainer().getSimulinkRef().getQualifier(); commandFactory.clearLastErrorMessage().execute(); MatlabCommand addLine = commandFactory.addLine().addParam(system).addParam(srcPort).addParam(dstPort).addParam("AutoRouting").addParam("on"); IVisitableMatlabData addedLineHandle = addLine.execute(); String lastErrorMsg = MatlabString.getMatlabStringData(commandFactory.getLastErrorMessage().execute()); if(!lastErrorMsg.equals("")) { // There was an error message in the returned variable instead of the desired handle // This also happens when there is already a connection between the selected elements, so try to get the line MatlabCommand getPortHandles = commandFactory.getParam().addParam(system + "/" + toBlockName).addParam("PortHandles"); IVisitableMatlabData execute = getPortHandles.execute(); IVisitableMatlabData inportData = StructMatlabData.getStructMatlabDataData(execute).get("Inport"); if(inportData instanceof Handle){ IVisitableMatlabData portHandle = inportData; addedLineHandle = commandFactory.getParam().addParam(portHandle).addParam("line").execute(); } else if(inportData instanceof CellMatlabData){ IVisitableMatlabData portHandle = ((CellMatlabData) inportData).getData(indexOfInportInMatlab-1); addedLineHandle = commandFactory.getParam().addParam(portHandle).addParam("line").execute(); } // TODO might be needed to check for null //return null; } if(Handle.asHandle(addedLineHandle).getData().equals(prevdata)){ // FIXME this is only a hotfix for State (out)ports addLine = commandFactory.addLine().addParam(system).addParam(fromBlockName+"/State").addParam(dstPort).addParam("AutoRouting").addParam("on"); addedLineHandle = addLine.execute(); } prevdata = Handle.asHandle(addedLineHandle).getData(); // IF the line name is not artificially created while importing, the exporter sets the name if (addedLineHandle != null && singleConnection.getLineName() != null) { MatlabCommand setLineName = commandFactory.setParam().addParam(addedLineHandle).addParam("Name").addParam(singleConnection.getLineName()); setLineName.execute(); } return addedLineHandle == null ? null : Handle.getHandleData(addedLineHandle); } }