Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ protected ISimulinkModelElement createInstanceInModel(String type)
@Override
protected void addToCache(String type, ISimulinkModelElement instance) throws EolModelElementTypeNotFoundException {
assert kindCache != null;
assert typeCache != null;

Object typeCacheKey = getCacheKeyForType(type);
typeCache.putIfPresent(typeCacheKey, instance);

for (String kind : getAllTypeNamesOf(instance)) {
Object kindCacheKey = getCacheKeyForType(kind);
kindCache.putIfPresent(kindCacheKey, instance);
Expand All @@ -173,17 +178,31 @@ protected void addToCache(String type, ISimulinkModelElement instance) throws Eo
@Override
protected void removeFromCache(ISimulinkModelElement instance) throws EolModelElementTypeNotFoundException {
assert kindCache != null;
assert typeCache != null;

final Object typeCacheKey = getCacheKeyForType(getTypeNameOf(instance));
typeCache.remove(typeCacheKey, instance);

for (String kind : getAllTypeNamesOf(instance)) {
final Object kindCacheKey = getCacheKeyForType(kind);
kindCache.remove(kindCacheKey, instance);
}
}

@Override
public void deleteElement(Object o) throws EolRuntimeException {
deleteElementInModel(o);
public void deleteElement(Object o) throws EolRuntimeException {
if (isCachingEnabled() && o instanceof ISimulinkModelElement) {
removeFromCache((ISimulinkModelElement) o);
// Subsystems may contain child blocks that also require to
// be removed from the caches
if (((ISimulinkModelElement) o).getType().equals("SubSystem")) {
try {
removeSimulinkSubsystemFromCaches((SimulinkBlock) o);
} catch (MatlabException e) {
throw new MatlabRuntimeException(e);
}
} else {
removeFromCache((ISimulinkModelElement) o);
}
String type = ((ISimulinkModelElement) o).getType();
for (List<String> specialType : deleteBlockMap) {
if (specialType.contains(type)) {
Expand All @@ -195,59 +214,84 @@ public void deleteElement(Object o) throws EolRuntimeException {
}
}
}
// The caches are stored as mappings to Simulink collections, which rely on
// the handle of a block to resolve and manage the elements of the collection,
// hence the deletion of the block from the Simulink model must happen AFTER
// the caches are updated
deleteElementInModel(o);
}

@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
try {
// When instantiating Subsystem blocks, these may create other blocks
// (e.g., inports and outports) as their children, which would need to
// be added to the caches as well
if (instance.getType().equals("SubSystem")) {
addSimulinkSubsystemToCaches((SimulinkBlock) instance);
} else {
addToCache(instance.getType(), instance);
}
}
if (createBlockMap.containsKey(type)) {
for (String equivalent : createBlockMap.get(type)) {
kindCache.replaceValues(equivalent, getAllOfTypeFromModel(equivalent)); // refresh for type
}
}
} catch (MatlabException e) {
throw new RuntimeException(e); }
}
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 {
try {
if (type.startsWith(STATEFLOW) && parameters.size() == 1) {
Object parentObject = parameters.toArray()[0];
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
}
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());
}
return instance;
} else {
throw new EolModelElementTypeNotFoundException(type, null, "invalid parameters");
}
} catch (EolRuntimeException e) {
throw new EolModelElementTypeNotFoundException(type, null, e.getMessage());
}
} else if (type.contains("/") && parameters.size() == 1) {
Object parentPath = parameters.toArray()[0];
try {
return new SimulinkBlock(this, engine, type, (String) parentPath);
} catch (MatlabRuntimeException e) {
throw new EolNotInstantiableModelElementTypeException(getSimulinkModelName(), type);
} else if (type.contains("/") && parameters.size() == 1) {
Object parentPath = parameters.toArray()[0];
ISimulinkModelElement instance = new SimulinkBlock(this, engine, type, (String) parentPath);
if (isCachingEnabled()) {
// When instantiating Subsystem blocks, these may create other blocks
// (e.g., inports and outports) as their children, which would need to
// be added to the caches as well
if (instance.getType().equals("SubSystem")) {
addSimulinkSubsystemToCaches((SimulinkBlock) instance);
} else {
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 (MatlabRuntimeException e) {
throw new EolNotInstantiableModelElementTypeException(getSimulinkModelName(), type);
} catch (EolRuntimeException | MatlabException e) {
throw new EolModelElementTypeNotFoundException(type, null, e.getMessage());
}
throw new EolModelElementTypeNotFoundException(type, null);
}

Expand All @@ -268,6 +312,44 @@ protected boolean deleteElementInModel(Object instance) throws EolRuntimeExcepti
protected Collection<ISimulinkModelElement> allContentsFromModel() {
return TypeHelper.getAll(this);
}

@Override
protected Collection<ISimulinkModelElement> getAllOfKindOrType(boolean isKind, String modelElementType) throws EolModelElementTypeNotFoundException {
Collection<ISimulinkModelElement> values = null;

// The code below is to prevent duplicate calls to getAllOf*FromModel.
// With multiple threads there could be a race condition, so the
// intent is to block the threads until the cache has been populated
// by a single thread, and the others can just pick up from the cache
// rather than recalculating.

// As caching is not currently supported for Simulink ports and lines, we
// always want to retrieve them from the model
if (isCachingEnabled()
&& (modelElementType.equals(Kind.BLOCK.getKind()) || Kind.BLOCK.equals(TypeHelper.getKind(modelElementType)))) {
final Multimap<Object, ISimulinkModelElement> cache = isKind ? kindCache : typeCache;
final Object key = getCacheKeyForType(modelElementType);

if ((values = cache.getMutable(key)) == null) synchronized (this) {
// Could've changed while we were waiting on the lock
if (!isConcurrent() || (values = cache.getMutable(key)) == null) {
values = wrap(isKind ?
getAllOfKindFromModel(modelElementType) :
getAllOfTypeFromModel(modelElementType)
);
cache.putAll(key, values, values == null);
}
}
}
else if (values == null) {
values = wrap(isKind ?
getAllOfKindFromModel(modelElementType) :
getAllOfTypeFromModel(modelElementType)
);
}

return wrapUnmodifiable(values);
}

@Override
protected Collection<ISimulinkModelElement> getAllOfTypeFromModel(String type)
Expand Down Expand Up @@ -479,4 +561,74 @@ public Collection<ISimulinkModelElement> findBlocks(Integer depth) throws Matlab
return SimulinkUtil.findBlocks(this, depth);
}

/**
* Updates the caches for a Simulink block when this is replaced by a
* new block. This needs to be called after the new block is created
* and before the old block is deleted to ensure the blocks can be
* resolved in the caches.
*
* @param oldHandle handle of the old block
* @param newHandle handle of the new block
* @throws EolModelElementTypeNotFoundException
* @throws MatlabRuntimeException
* @throws MatlabException
*/
public void updateCaches(Double oldHandle, Double newHandle) throws EolModelElementTypeNotFoundException, MatlabRuntimeException, MatlabException {
if(isCachingEnabled()) {
SimulinkBlock oldBlock = new SimulinkBlock(this, this.engine, oldHandle);
if (oldBlock.getType().equals("SubSystem")) {
removeSimulinkSubsystemFromCaches(oldBlock);
} else {
removeFromCache(oldBlock);
}

SimulinkBlock newBlock = new SimulinkBlock(this, this.engine, newHandle);
if (newBlock.getType().equals("SubSystem")) {
addSimulinkSubsystemToCaches(newBlock);
} else {
addToCache(newBlock.getType(), newBlock);
}
}
}

/**
* Adds a Subsystem block and all its child blocks to the caches.
*
* @param subSystem the Subsystem block to be cached
* @throws EolModelElementTypeNotFoundException
* @throws MatlabException if the block could not be queried for its children
*/
private void addSimulinkSubsystemToCaches(SimulinkBlock subSystem) throws EolModelElementTypeNotFoundException, MatlabException {
for (ISimulinkModelElement child : subSystem.getChildren()) {
if (!child.equals(subSystem)) {
if (child.getType().equals("SubSystem")) {
addSimulinkSubsystemToCaches((SimulinkBlock) child);
} else {
addToCache(child.getType(), child);
}
}
}
addToCache(subSystem.getType(), subSystem);
}

/**
* Removes a Subsystem block and all its child blocks from the caches.
*
* @param subSystem the Subsystem block to be removed form caches
* @throws EolModelElementTypeNotFoundException
* @throws MatlabException if the block could not be queried for its children
*/
private void removeSimulinkSubsystemFromCaches(SimulinkBlock subSystem) throws EolModelElementTypeNotFoundException, MatlabException {
for (ISimulinkModelElement child : subSystem.getChildren()) {
if (!child.equals(subSystem)) {
if (child.getType().equals("SubSystem")) {
removeSimulinkSubsystemFromCaches((SimulinkBlock) child);
} else {
removeFromCache(child);
}
}
}
removeFromCache(subSystem);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class SimulinkBlock extends SimulinkElement {
public SimulinkBlock(SimulinkModel model, MatlabEngine engine, String type, String destPath) throws MatlabRuntimeException {
super(model, engine, type, destPath);
}
public SimulinkBlock(SimulinkModel model, MatlabEngine engine, Double handle) throws MatlabRuntimeException {
super(model, engine, handle);
}
Expand All @@ -74,10 +74,14 @@ public void setParent(SimulinkBlock parent) {
String parentPath = parent == null ? ((SimulinkModel)model).getSimulinkModelName() : parent.getPath();
Double newHandle = (Double) engine.evalWithResult(ADD_BLOCK_MAKE_NAME_UNIQUE_ON, getPath(),
parentPath + "/" + name);
// Instruct the model that the caches need updating for this block
if (model instanceof SimulinkModel) {
((SimulinkModel) model).updateCaches(handle, newHandle);
}
engine.eval(HANDLE_DELETE_BLOCK_HANDLE, handle);
handle = newHandle;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Create a new block within an existing susbsystem
var subsys = new `simulink/Ports & Subsystems/Subsystem`;
subsys.name = "TestSubsystem";
var childSubsys = new `simulink/Ports & Subsystems/Subsystem`(subsys.path);

// There should now be 2 subsystems, 2 inports and
// 2 outports (which are automatically created when
// adding a new subsystem block)
assert(SubSystem.all.size() == 2);
assert(Inport.all.size() == 2);
assert(Outport.all.size() == 2);
assert(Block.all.size() == 6);

// Create another subsystem and add it to the root
var child2Subsys = new `simulink/Ports & Subsystems/Subsystem`();
child2Subsys.parent = subsys;

// There should still be 3 subsystems, 3 inports and 3 outports
assert(SubSystem.all.size() == 3);
assert(Inport.all.size() == 3);
assert(Outport.all.size() == 3);
assert(Block.all.size() == 9);

delete child2Subsys;
assert(SubSystem.all.size() == 2);
assert(Inport.all.size() == 2);
assert(Outport.all.size() == 2);
assert(Block.all.size() == 6);

// Deleting the top subsystem should delete the caches
// for all its (grand)children
delete subsys;
assert(Block.all.size() == 0);
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ public void testCachingSFChart() {
activeCache = true;
eolResourceFile = ROOT + "caching.eol";
}

@Test
public void testCachingSubsystems() {
activeCache = true;
eolResourceFile = ROOT + "cachingSubsystems.eol";
}

@Test
public void testParent() {
Expand Down
Loading