/********************************************************************* | |
* Copyright (c) 2008 The University of York. | |
* | |
* This program and the accompanying materials are made | |
* available under the terms of the Eclipse Public License 2.0 | |
* which is available at https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
**********************************************************************/ | |
package org.eclipse.epsilon.emc.simulink.model; | |
import java.io.File; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collection; | |
import java.util.List; | |
import java.util.concurrent.Future; | |
import org.eclipse.epsilon.common.util.Multimap; | |
import org.eclipse.epsilon.common.util.StringProperties; | |
import org.eclipse.epsilon.emc.simulink.engine.MatlabEngine; | |
import org.eclipse.epsilon.emc.simulink.engine.MatlabEnginePool; | |
import org.eclipse.epsilon.emc.simulink.exception.MatlabException; | |
import org.eclipse.epsilon.emc.simulink.exception.MatlabRuntimeException; | |
import org.eclipse.epsilon.emc.simulink.introspection.java.SimulinkPropertyGetter; | |
import org.eclipse.epsilon.emc.simulink.introspection.java.SimulinkPropertySetter; | |
import org.eclipse.epsilon.emc.simulink.model.TypeHelper.Kind; | |
import org.eclipse.epsilon.emc.simulink.model.element.ISimulinkModelElement; | |
import org.eclipse.epsilon.emc.simulink.model.element.SimulinkBlock; | |
import org.eclipse.epsilon.emc.simulink.model.element.StateflowBlock; | |
import org.eclipse.epsilon.emc.simulink.operations.contributors.ModelOperationContributor; | |
import org.eclipse.epsilon.emc.simulink.util.MatlabEngineUtil; | |
import org.eclipse.epsilon.emc.simulink.util.SimulinkUtil; | |
import org.eclipse.epsilon.eol.exceptions.EolInternalException; | |
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException; | |
import org.eclipse.epsilon.eol.exceptions.models.EolEnumerationValueNotFoundException; | |
import org.eclipse.epsilon.eol.exceptions.models.EolModelElementTypeNotFoundException; | |
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException; | |
import org.eclipse.epsilon.eol.exceptions.models.EolNotInstantiableModelElementTypeException; | |
import org.eclipse.epsilon.eol.execute.introspection.IPropertyGetter; | |
import org.eclipse.epsilon.eol.execute.introspection.IPropertySetter; | |
import org.eclipse.epsilon.eol.execute.operations.contributors.IOperationContributorProvider; | |
import org.eclipse.epsilon.eol.execute.operations.contributors.OperationContributor; | |
import org.eclipse.epsilon.eol.models.CachedModel; | |
import org.eclipse.epsilon.eol.models.IRelativePathResolver; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
public class SimulinkModel extends CachedModel<ISimulinkModelElement> implements IOperationContributorProvider { | |
/** CONSTANTS */ | |
public static final String PROPERTY_FILE = "file"; | |
public static final String PROPERTY_LIBRARY_PATH = "library_path"; | |
public static final String PROPERTY_ENGINE_JAR_PATH = "engine_jar_path"; | |
public static final String PROPERTY_SHOW_IN_MATLAB_EDITOR = "hidden_editor"; | |
public static final String PROPERTY_FOLLOW_LINKS = "follow_links"; | |
public static final String PROPERTY_WORKING_DIR = "working_dir"; | |
public static final String BLOCK = "Block"; | |
public static final String SIMULINK = "Simulink"; | |
public static final String STATEFLOW = "Stateflow"; | |
public static final String PWD = "cd '?';"; | |
public static final String GET_PARAM = "get_param('?', 'Handle');"; | |
public static final String LOAD_SYSTEM = "load_system('?')"; | |
public static final String OPEN_SYSTEM = "open_system('?')"; | |
public static final String NEW_SYSTEM = "new_system('?', 'Model');"; | |
public static final String SAVE_SYSTEM = "save_system('?', '?');"; | |
private static final Logger LOGGER = LoggerFactory.getLogger(SimulinkModel.class); | |
// | |
private static final Multimap<String, String> createBlockMap = new Multimap<>(); | |
// | |
private static final ArrayList<ArrayList<String>> deleteBlockMap = new ArrayList<>(); | |
static { | |
createBlockMap.put("sflib/Chart", "Stateflow.Chart"); | |
ArrayList<String> chart = new ArrayList<>(); | |
chart.add("SubSystem"); | |
chart.add("Stateflow.Chart"); | |
deleteBlockMap.add(chart); | |
} | |
/** FIELDS */ | |
protected File file = null; | |
protected SimulinkPropertyGetter propertyGetter; | |
protected SimulinkPropertySetter propertySetter; | |
protected ModelOperationContributor simulinkOperationContributor; | |
protected File workingDir = null; | |
protected String libraryPath; | |
protected String engineJarPath; | |
protected MatlabEngine engine; | |
protected boolean showInMatlabEditor = false; | |
protected boolean followLinks = true; | |
protected double handle = -1; | |
@Override | |
protected void loadModel() throws EolModelLoadingException { | |
try { | |
engine = MatlabEnginePool.getInstance(libraryPath, engineJarPath).getMatlabEngine(); | |
simulinkOperationContributor = new ModelOperationContributor(engine); | |
if ((workingDir != null && workingDir.exists())) { | |
try { | |
engine.eval(PWD, workingDir); | |
} catch (Exception ex) { | |
LOGGER.info(ex.getMessage()); | |
} | |
} | |
if (readOnLoad) { | |
String cmd = showInMatlabEditor ? OPEN_SYSTEM : LOAD_SYSTEM; | |
try{ | |
engine.eval(cmd, file.getAbsolutePath()); | |
} catch (Exception e) { | |
try { | |
engine.eval(NEW_SYSTEM, getSimulinkModelName()); | |
} catch (Exception ex) { | |
// Ignore; system already exists | |
} | |
} | |
} else { | |
try { | |
engine.eval(NEW_SYSTEM, getSimulinkModelName()); | |
} catch (Exception ex) { | |
// Ignore; system already exists | |
} | |
} | |
this.handle = (Double) engine.evalWithResult(GET_PARAM, getSimulinkModelName()); | |
} catch (Exception e) { | |
throw new EolModelLoadingException(e, this); | |
} | |
} | |
@Override | |
protected void disposeModel() { | |
try { | |
MatlabEnginePool.getInstance(libraryPath, engineJarPath).release(engine); | |
} catch (MatlabRuntimeException e) { | |
} | |
} | |
@Override | |
protected ISimulinkModelElement createInstanceInModel(String type) | |
throws EolModelElementTypeNotFoundException, EolNotInstantiableModelElementTypeException { | |
if (type.contains("/")) { | |
try { | |
return new SimulinkBlock(this, engine, type); | |
} catch (MatlabRuntimeException e) { | |
throw new EolNotInstantiableModelElementTypeException(getSimulinkModelName(), type); | |
} | |
} else if (type.startsWith(STATEFLOW + ".")) { | |
try { | |
return new StateflowBlock(this, engine, type); | |
} catch (MatlabException e) { | |
throw new EolNotInstantiableModelElementTypeException(getSimulinkModelName(), type); | |
} | |
} else { | |
throw new EolModelElementTypeNotFoundException(type, null); | |
} | |
} | |
@Override | |
protected void addToCache(String type, ISimulinkModelElement instance) throws EolModelElementTypeNotFoundException { | |
for (String kind : getAllTypeNamesOf(instance)) { | |
Object kindCacheKey = getCacheKeyForType(kind); | |
kindCache.putIfPresent(kindCacheKey, instance); | |
} | |
} | |
@Override | |
protected void removeFromCache(ISimulinkModelElement instance) throws EolModelElementTypeNotFoundException { | |
for (String kind : getAllTypeNamesOf(instance)) { | |
final Object kindCacheKey = getCacheKeyForType(kind); | |
kindCache.remove(kindCacheKey, instance); | |
} | |
} | |
@Override | |
public void deleteElement(Object o) throws EolRuntimeException { | |
deleteElementInModel(o); | |
if (isCachingEnabled()) { | |
if (o instanceof ISimulinkModelElement) { | |
removeFromCache((ISimulinkModelElement) o); | |
String type = ((ISimulinkModelElement) o).getType(); | |
for (List<String> specialType : deleteBlockMap) { | |
if (specialType.contains(type)) { | |
for (String equivalent : specialType) { | |
if (!equivalent.equals(type)) { | |
kindCache.replaceValues(equivalent, getAllOfTypeFromModel(equivalent)); // refresh for type | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
@Override | |
public ISimulinkModelElement createInstance(String type) | |
throws EolModelElementTypeNotFoundException, EolNotInstantiableModelElementTypeException { | |
ISimulinkModelElement instance = createInstanceInModel(type); | |
if (isCachingEnabled()) { | |
addToCache(instance.getType(), instance); | |
if (createBlockMap.containsKey(type)){ | |
for (String equivalent : createBlockMap.get(type)){ | |
kindCache.replaceValues(equivalent, getAllOfTypeFromModel(equivalent)); // refresh for type | |
} | |
} | |
} | |
return instance; | |
} | |
@Override | |
public Object createInstance(String type, Collection<Object> parameters) | |
throws EolModelElementTypeNotFoundException, EolNotInstantiableModelElementTypeException { | |
if (type.startsWith(STATEFLOW) && parameters.size() == 1) { | |
Object parentObject = parameters.toArray()[0]; | |
try { | |
if (parentObject instanceof StateflowBlock) { | |
try { | |
StateflowBlock instance = new StateflowBlock(this, engine, type, (StateflowBlock) parentObject); | |
if (isCachingEnabled()) { | |
addToCache(instance.getType(), instance); | |
if (createBlockMap.containsKey(type)){ | |
for (String equivalent : createBlockMap.get(type)){ | |
kindCache.replaceValues(equivalent, getAllOfTypeFromModel(equivalent)); // refresh for type | |
} | |
} | |
} | |
return instance; | |
} catch (MatlabException e) { | |
throw new EolModelElementTypeNotFoundException(type, null, e.getMessage()); | |
} | |
} else { | |
throw new EolModelElementTypeNotFoundException(type, null, "invalid parameters"); | |
} | |
} catch (EolRuntimeException e) { | |
throw new EolModelElementTypeNotFoundException(type, null, e.getMessage()); | |
} | |
} | |
throw new EolModelElementTypeNotFoundException(type, null); | |
} | |
@Override | |
protected boolean deleteElementInModel(Object instance) throws EolRuntimeException { | |
try { | |
if (instance instanceof ISimulinkModelElement) | |
return ((ISimulinkModelElement) instance).deleteElementInModel(); | |
return false; | |
} catch (Exception e) { | |
throw new EolInternalException(e); | |
} | |
} | |
@Override | |
protected Object getCacheKeyForType(String type) throws EolModelElementTypeNotFoundException { | |
return type; | |
} | |
// COLLECTORS | |
@Override | |
protected Collection<String> getAllTypeNamesOf(Object instance) { | |
if (instance instanceof ISimulinkModelElement) { | |
return ((ISimulinkModelElement) instance).getAllTypeNamesOf(); | |
} else { | |
return Arrays.asList(getTypeNameOf(instance)); | |
} | |
} | |
@Override | |
protected Collection<ISimulinkModelElement> allContentsFromModel() { | |
return TypeHelper.getAll(engine, this); | |
} | |
@Override // FIXME | |
protected Collection<ISimulinkModelElement> getAllOfTypeFromModel(String type) | |
throws EolModelElementTypeNotFoundException { | |
return TypeHelper.getAllOfType(this, engine, type); | |
} | |
@Override | |
protected Collection<ISimulinkModelElement> getAllOfKindFromModel(String kind) | |
throws EolModelElementTypeNotFoundException { | |
try { | |
try { | |
return Kind.get(kind).getAll(this); | |
} catch (Exception e) { | |
return getAllOfTypeFromModel(kind); | |
} | |
} catch (Exception e) { | |
throw new EolModelElementTypeNotFoundException(null, null); | |
} | |
} | |
public static void main(String[] args) throws Exception { | |
File tmpFile = File.createTempFile("foo", ".slx"); | |
SimulinkModel model = new SimulinkModel(); | |
model.setName("M"); | |
model.setFile(tmpFile); | |
model.setWorkingDir(null); | |
model.setReadOnLoad(false); | |
model.setStoredOnDisposal(false); | |
model.setShowInMatlabEditor(true); | |
model.setFollowLinks(false); | |
//model.setEngineJarPath(MatlabEngineFilesEnum.ENGINE_JAR.path()); | |
//model.setLibraryPath(MatlabEngineFilesEnum.LIBRARY_PATH.path()); | |
model.load(); | |
System.out.println(model.getAllOfType("Gain")); | |
} | |
public void load(StringProperties properties, IRelativePathResolver resolver) throws EolModelLoadingException { | |
super.load(properties, resolver); | |
String filePath = properties.getProperty(SimulinkModel.PROPERTY_FILE); | |
String workingDirPath = properties.getProperty(SimulinkModel.PROPERTY_WORKING_DIR); | |
if (properties.hasProperty(SimulinkModel.PROPERTY_LIBRARY_PATH)) | |
libraryPath = properties.getProperty(SimulinkModel.PROPERTY_LIBRARY_PATH); | |
if (properties.hasProperty(SimulinkModel.PROPERTY_ENGINE_JAR_PATH)) | |
engineJarPath = properties.getProperty(SimulinkModel.PROPERTY_ENGINE_JAR_PATH); | |
if (properties.hasProperty(SimulinkModel.PROPERTY_SHOW_IN_MATLAB_EDITOR)) | |
showInMatlabEditor = properties.getBooleanProperty(SimulinkModel.PROPERTY_SHOW_IN_MATLAB_EDITOR, false); | |
if (properties.hasProperty(SimulinkModel.PROPERTY_FOLLOW_LINKS)) | |
followLinks = properties.getBooleanProperty(SimulinkModel.PROPERTY_FOLLOW_LINKS, true); | |
if (filePath != null && filePath.trim().length() > 0) | |
file = new File(resolver.resolve(filePath)); | |
if (workingDirPath != null && workingDirPath.trim().length() > 0) { | |
workingDir = new File(resolver.resolve(filePath)); | |
} | |
load(); | |
} | |
public void simulate() throws InterruptedException { | |
String name = getFile().getName().substring(0, getFile().getName().lastIndexOf(".")); | |
Future<Void> fSim; | |
try { | |
fSim = engine.evalAsync("simout = sim('" + name + "', []);"); | |
while (!fSim.isDone()) { | |
Thread.sleep(1000); | |
} | |
} catch (MatlabException e) { | |
e.printStackTrace(); | |
} | |
} | |
@Override | |
public boolean hasType(String type) { | |
return true; // FIXME No validation? | |
} | |
@Override | |
public String getTypeNameOf(Object instance) { | |
if (instance instanceof ISimulinkModelElement) { | |
return ((ISimulinkModelElement) instance).getType(); | |
} | |
return instance.getClass().getSimpleName().replace(SIMULINK, ""); | |
} | |
@Override | |
public Object getElementById(String id) { | |
return null; | |
} | |
@Override | |
public void setElementId(Object instance, String newId) { | |
} | |
@Override | |
public String getElementId(Object instance) { | |
try { | |
return (String) propertyGetter.invoke(instance, "id"); | |
} catch (EolRuntimeException e) { | |
return ""; | |
} | |
} | |
@Override | |
public boolean owns(Object instance) { | |
if (instance == null) { | |
return false; | |
} | |
return ((instance instanceof ISimulinkModelElement) | |
&& ((ISimulinkModelElement) instance).getOwningModel() == this ) | |
|| (instance instanceof SimulinkModel) | |
|| (instance.getClass().getCanonicalName().startsWith("org.eclipse.epsilon.emc.simulink.types")); | |
} | |
@Override | |
public boolean store(String location) { | |
try { | |
engine.eval(SAVE_SYSTEM, getSimulinkModelName(), location); | |
return true; | |
} catch (Exception e) { | |
return false; | |
} | |
} | |
@Override | |
public boolean store() { | |
store(file.getAbsolutePath()); | |
return true; | |
} | |
@Override | |
public Object getEnumerationValue(String enumeration, String label) throws EolEnumerationValueNotFoundException { | |
throw new UnsupportedOperationException(); | |
} | |
@Override | |
public boolean isInstantiable(String type) { | |
return hasType(type); | |
} | |
public String getSimulinkModelName() { | |
String name = file.getName(); | |
int pos = name.lastIndexOf("."); | |
if (pos > 0) { | |
name = name.substring(0, pos); | |
} | |
return name; | |
} | |
@Override | |
public IPropertySetter getPropertySetter() { | |
if (propertySetter == null) { | |
propertySetter = new SimulinkPropertySetter(engine); | |
} | |
return propertySetter; | |
} | |
@Override | |
public IPropertyGetter getPropertyGetter() { | |
if (propertyGetter == null) { | |
propertyGetter = new SimulinkPropertyGetter(); | |
} | |
return propertyGetter; | |
} | |
@Override | |
public OperationContributor getOperationContributor() { | |
return simulinkOperationContributor; | |
} | |
public File getFile() { | |
return file; | |
} | |
public void setFile(File file) { | |
this.file = file; | |
} | |
public MatlabEngine getEngine() { | |
return engine; | |
} | |
public Double getHandle() { | |
return handle; | |
} | |
public String getLibraryPath() { | |
return libraryPath; | |
} | |
public void setLibraryPath(String libraryPath) { | |
this.libraryPath = libraryPath; | |
} | |
public String getEngineJarPath() { | |
return engineJarPath; | |
} | |
public void setEngineJarPath(String engineJarPath) { | |
this.engineJarPath = engineJarPath; | |
} | |
public boolean isShowInMatlabEditor() { | |
return showInMatlabEditor; | |
} | |
public File getWorkingDir() { | |
return workingDir; | |
} | |
public void setWorkingDir(File workingDir) { | |
this.workingDir = workingDir; | |
} | |
/** | |
* If true, the model will be shown in the MATLAB Editor. | |
* If the model is already loaded, it will not open it again. | |
* If false, the model will not be open in the MATLAB editor, | |
* but won't close an already open model | |
*/ | |
public void setShowInMatlabEditor(boolean openMatlabEditor) { | |
this.showInMatlabEditor = openMatlabEditor; | |
} | |
public boolean isFollowLinks() { | |
return followLinks; | |
} | |
/** | |
* If true, adds the 'Follow_Link' parameter to the 'find_system' method in MATLAB | |
*/ | |
public void setFollowLinks(boolean followLinks) { | |
this.followLinks = followLinks; | |
} | |
public Object parseMatlabEngineVariable(String variableName) throws MatlabException { | |
return MatlabEngineUtil.parseMatlabEngineVariable(engine, variableName); | |
} | |
public void statement(String statement) throws EolRuntimeException { | |
try{ | |
engine.eval(statement); | |
} catch (MatlabException e) { | |
throw new EolRuntimeException(e.getMessage()); | |
} | |
} | |
public Object statementWithResult(String statement) throws EolRuntimeException { | |
try{ | |
return engine.evalWithResult(statement); | |
} catch (MatlabException e) { | |
throw new EolRuntimeException(e.getMessage()); | |
} | |
} | |
public Object getWorkspaceVariable(String value) { | |
try { | |
return MatlabEngineUtil.parseMatlabEngineVariable(engine,value); | |
} catch (MatlabException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public Collection<ISimulinkModelElement> getChildren() throws MatlabException { | |
return SimulinkUtil.findBlocks(this,1); | |
} | |
public Collection<ISimulinkModelElement> findBlocks() throws MatlabException{ | |
return SimulinkUtil.findBlocks(this,1); | |
} | |
public Collection<ISimulinkModelElement> findBlocks(Integer depth) throws MatlabException { | |
return SimulinkUtil.findBlocks(this,depth); | |
} | |
} |