/** | |
******************************************************************************** | |
* Copyright (c) 2018-2021 Robert Bosch GmbH and others. | |
* | |
* 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 | |
* | |
* Contributors: | |
* Robert Bosch GmbH - initial API and implementation | |
******************************************************************************** | |
*/ | |
package org.eclipse.app4mc.amalthea.converters090.impl; | |
import java.io.File; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import org.eclipse.app4mc.amalthea.converters.common.ServiceConstants; | |
import org.eclipse.app4mc.amalthea.converters.common.base.ICache; | |
import org.eclipse.app4mc.amalthea.converters.common.base.IConverter; | |
import org.eclipse.app4mc.amalthea.converters.common.converter.AbstractConverter; | |
import org.eclipse.app4mc.amalthea.converters.common.utils.AmaltheaNamespaceRegistry; | |
import org.eclipse.app4mc.amalthea.converters.common.utils.HelperUtil; | |
import org.eclipse.app4mc.amalthea.converters090.utils.HWCacheBuilder; | |
import org.eclipse.app4mc.amalthea.converters090.utils.HWTransformationCache; | |
import org.eclipse.app4mc.util.sessionlog.SessionLogger; | |
import org.jdom2.Attribute; | |
import org.jdom2.Document; | |
import org.jdom2.Element; | |
import org.osgi.service.component.annotations.Activate; | |
import org.osgi.service.component.annotations.Component; | |
import org.osgi.service.component.annotations.Reference; | |
/** | |
* This class is responsible for converting the HW Model elements from 0.8.3 to 0.9.0 version format of AMALTHEA model | |
* | |
* @author zmeer | |
* | |
*/ | |
@Component( | |
property = { | |
ServiceConstants.INPUT_MODEL_VERSION_PROPERTY + "=0.8.3", | |
ServiceConstants.OUTPUT_MODEL_VERSION_PROPERTY + "=0.9.0"}, | |
service = IConverter.class) | |
public class HwReferencesConverter extends AbstractConverter { | |
private static final String XSI = "xsi"; | |
private static final String HREF = "href"; | |
private static final String TYPE = "type"; | |
private static final String KEY = "key"; | |
private static final String VALUE = "value"; | |
private static final String MEMORY = "memory"; | |
private static final String MEMORIES = "memories"; | |
private static final String CORES = "cores"; | |
private static final String CORE_AFFINITY = "coreAffinity"; | |
private static final String CONSTRAINTS_MODEL = "constraintsModel"; | |
private static final String INSTRUCTIONS = "Instructions"; | |
private static final String AMLT_PREFIX = "amlt:/#"; | |
@Reference | |
SessionLogger logger; | |
private HWTransformationCache hwTransformationCache; | |
@Override | |
@Activate | |
protected void activate(Map<String, Object> properties) { | |
super.activate(properties); | |
} | |
/*- | |
* As in 0.9.0, there is a major restructuring of HW data model -> | |
* all model files HW data is transformed at once and its content is stored only inside a single HW model | |
*/ | |
@Override | |
public void convert(File targetFile, Map<File, Document> fileDocumentMapping, List<ICache> caches) { | |
logger.info("Migration from 0.8.3 to 0.9.0 : Executing HW references converter for model file : {0}", | |
targetFile.getName()); | |
/*-getting the cache object */ | |
hwTransformationCache = getHWTransformationCache(caches); | |
final Document root = fileDocumentMapping.get(targetFile); | |
if (root == null) { | |
return; | |
} | |
final Element rootElement = root.getRootElement(); | |
updateReferencesInModel(rootElement); | |
} | |
private void updateReferencesInModel(Element rootElement) { | |
migrateTargetMemory(rootElement); | |
migratePhysicalSectionConstraint(rootElement); | |
migrateMemoryMapping(rootElement); | |
migratePhysicalSectionMapping(rootElement); | |
migrateTargetCore(rootElement); | |
migrateEvents(rootElement); | |
migrateSchedulerAllocation(rootElement); | |
migrateTaskAllocation(rootElement); | |
migrateRunnableInstructionsEntry(rootElement); | |
migrateCPUPercentageRequirementLimit(rootElement); | |
} | |
private void migrateRunnableInstructionsEntry(Element rootElement) { | |
final StringBuilder xpathBuffer = new StringBuilder(); | |
xpathBuffer.append("./swModel/runnables//*[@xsi:type=\"am:RunnableInstructions\"]"); | |
xpathBuffer.append("|"); | |
xpathBuffer.append("./osModel/operatingSystems/taskSchedulers/computationItems[@xsi:type=\"am:RunnableInstructions\"]"); | |
xpathBuffer.append("|"); | |
xpathBuffer.append("./osModel/operatingSystems/interruptControllers/computationItems[@xsi:type=\"am:RunnableInstructions\"]"); | |
final List<Element> runnableInstructionsEntries = HelperUtil.getXpathResult( | |
rootElement, | |
xpathBuffer.toString(), | |
Element.class, | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
if (! runnableInstructionsEntries.isEmpty()) { | |
// in this case, HWFeatureCategory should be referenced in the newly created | |
// ExecutionNeed elements. | |
if (this.hwTransformationCache.getNewFeatureCategoriesMap().containsKey(INSTRUCTIONS) == false) { | |
checkAndCreateHWFeatureCategory(rootElement); | |
} | |
} | |
for (Element runnableInstruction : runnableInstructionsEntries) { | |
Element parentElementOfRunnableInstruction = runnableInstruction.getParentElement(); | |
String tagName = runnableInstruction.getName(); | |
int indexOfRunnableInstructions=parentElementOfRunnableInstruction.indexOf(runnableInstruction); | |
// runnableInstructions.detach(); //removing element from parent | |
Element executionNeedElement=new Element(tagName); | |
executionNeedElement.setAttribute(TYPE, "am:ExecutionNeed", AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
migrateValueOfRunnableInstructions(runnableInstruction, executionNeedElement,"default"); | |
List<Element> oldExtendedElements = runnableInstruction.getChildren("extended"); | |
for (Element oldExtendedElement : oldExtendedElements) { | |
Element newExtendedSubElement=new Element("extended"); | |
Map<String, String> coresMap = HelperUtil.getMultipleElementsNameandTypeFromAttributeOrChildeElement(KEY, oldExtendedElement); | |
for(String coreName:coresMap.keySet()) { | |
Element puReference=new Element(KEY); | |
puReference.setAttribute(HREF, AMLT_PREFIX + HelperUtil.encodeNameForReference(coreName) + "?type=ProcessingUnitDefinition"); | |
newExtendedSubElement.addContent(puReference); | |
} | |
migrateValueOfRunnableInstructions(oldExtendedElement, newExtendedSubElement, VALUE); | |
//Adding the extended elements to Execution element | |
executionNeedElement.addContent(newExtendedSubElement); | |
} | |
parentElementOfRunnableInstruction.addContent(indexOfRunnableInstructions,executionNeedElement); | |
runnableInstruction.detach(); | |
} | |
} | |
private void checkAndCreateHWFeatureCategory(Element rootElement) { | |
Element hwModelEleemnt = rootElement.getChild("hwModel"); | |
if(hwModelEleemnt==null) { | |
hwModelEleemnt=new Element("hwModel"); | |
rootElement.addContent(hwModelEleemnt); | |
} | |
Element featureCategoriesElement=new Element("featureCategories"); | |
featureCategoriesElement.setAttribute("name", INSTRUCTIONS); | |
featureCategoriesElement.setAttribute("featureType", "performance"); | |
hwModelEleemnt.addContent(featureCategoriesElement); | |
} | |
private void migrateValueOfRunnableInstructions(Element runnableInstruction, Element executionNeedElement, String valueTagName) { | |
Element oldDefaultElement = runnableInstruction.getChild(valueTagName); | |
if(oldDefaultElement!=null) { | |
String oldElement_defaultType = oldDefaultElement.getAttributeValue(TYPE, AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
if(oldElement_defaultType!=null) { | |
if(oldElement_defaultType.equals("am:InstructionsConstant")) { | |
String oldDefault_value = oldDefaultElement.getAttributeValue(VALUE); | |
if(oldDefault_value!=null) { | |
Element newDefaultSubElement=new Element(valueTagName); | |
newDefaultSubElement.setAttribute(KEY, INSTRUCTIONS); | |
Element newValueElement=new Element(VALUE); | |
newValueElement.setAttribute(TYPE, "am:NeedConstant", AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
newValueElement.setAttribute(VALUE, oldDefault_value); | |
newDefaultSubElement.addContent(newValueElement); | |
//Adding newly created default element here | |
executionNeedElement.addContent(newDefaultSubElement); | |
} | |
}else if(oldElement_defaultType.equals("am:InstructionsDeviation")) { | |
Element newDefaultSubElement=new Element(valueTagName); | |
if (valueTagName.equals("default")) { | |
newDefaultSubElement.setAttribute(KEY, INSTRUCTIONS); | |
} else { | |
/* | |
* Element newKeyElement=new Element("key"); | |
* | |
* newKeyElement.setAttribute("href", | |
* "amlt:/#Instructions?type=HwFeatureCategory"); | |
* | |
* newDefaultSubElement.addContent(newKeyElement); | |
*/ | |
newDefaultSubElement.setAttribute(new Attribute(KEY, INSTRUCTIONS)); | |
} | |
Element newValueElement=new Element(VALUE); | |
newValueElement.setAttribute(TYPE, "am:NeedDeviation", | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
Element oldDeviationElement = oldDefaultElement.getChild("deviation"); | |
if (oldDeviationElement != null) { | |
Element newDeviationElement = oldDeviationElement.clone(); | |
newDeviationElement.detach(); | |
newValueElement.addContent(newDeviationElement); | |
} | |
newDefaultSubElement.addContent(newValueElement); | |
//Adding newly created default element here | |
executionNeedElement.addContent(newDefaultSubElement); | |
} | |
} | |
} | |
} | |
private void migrateEvents(Element rootElement) { | |
Element eventModel = rootElement.getChild("eventModel"); | |
if (eventModel == null) return; | |
List<Element> events = eventModel.getChildren("events"); | |
for (Element event : events) { | |
Map<String, String> coresMap = HelperUtil.getMultipleElementsNameandTypeFromAttributeOrChildeElement("core", | |
event); | |
event.removeChildren("core"); | |
event.removeAttribute("core"); | |
for (String coreName : coresMap.keySet()) { | |
Element memoryElement = new Element("processingUnit"); | |
memoryElement.setAttribute(HREF, | |
AMLT_PREFIX + HelperUtil.encodeNameForReference(coreName) + "?type=ProcessingUnit"); | |
event.addContent(memoryElement); | |
} | |
} | |
} | |
private void migrateTaskAllocation(Element rootElement) { | |
Element mappingModel = rootElement.getChild("mappingModel"); | |
if (mappingModel == null) return; | |
List<Element> taskAllocations = mappingModel.getChildren("taskAllocation"); | |
for (Element taskAllocation : taskAllocations) { | |
Map<String, String> coresMap = HelperUtil | |
.getMultipleElementsNameandTypeFromAttributeOrChildeElement(CORE_AFFINITY, taskAllocation); | |
taskAllocation.removeChildren(CORE_AFFINITY); | |
taskAllocation.removeAttribute(CORE_AFFINITY); | |
for (String coreName : coresMap.keySet()) { | |
Element processingUnitElement = new Element("affinity"); | |
processingUnitElement.setAttribute(HREF, | |
AMLT_PREFIX + HelperUtil.encodeNameForReference(coreName) + "?type=ProcessingUnit"); | |
taskAllocation.addContent(processingUnitElement); | |
} | |
} | |
} | |
private void migrateSchedulerAllocation(Element rootElement) { | |
Element mappingModel = rootElement.getChild("mappingModel"); | |
if (mappingModel == null) return; | |
List<Element> schedulerAllocations = mappingModel.getChildren("schedulerAllocation"); | |
for (Element schedulerAllocation : schedulerAllocations) { | |
// Step 1: | |
Map<String, String> coresMap = HelperUtil | |
.getMultipleElementsNameandTypeFromAttributeOrChildeElement("responsibility", schedulerAllocation); | |
schedulerAllocation.removeChildren("responsibility"); | |
schedulerAllocation.removeAttribute("responsibility"); | |
for (String coreName : coresMap.keySet()) { | |
Element memoryElement = new Element("responsibility"); | |
// Info : In this case, CoreName is already encoded. Encoding again will cause | |
// problems w.r.t. association to its definition | |
memoryElement.setAttribute(HREF, AMLT_PREFIX + (coreName) + "?type=ProcessingUnit"); | |
schedulerAllocation.addContent(memoryElement); | |
} | |
// Step 2: modifying executingCore tag | |
coresMap = HelperUtil.getMultipleElementsNameandTypeFromAttributeOrChildeElement("executingCore", | |
schedulerAllocation); | |
schedulerAllocation.removeChildren("executingCore"); | |
schedulerAllocation.removeAttribute("executingCore"); | |
for (String coreName : coresMap.keySet()) { | |
Element memoryElement = new Element("executingPU"); | |
memoryElement.setAttribute(HREF, AMLT_PREFIX + (coreName) + "?type=ProcessingUnit"); | |
schedulerAllocation.addContent(memoryElement); | |
} | |
} | |
} | |
private void migratePhysicalSectionMapping(Element rootElement) { | |
Element mappingModel = rootElement.getChild("mappingModel"); | |
if (mappingModel == null) return; | |
List<Element> physicalSectionMappings = mappingModel.getChildren("physicalSectionMapping"); | |
for (Element physicalSectionMapping : physicalSectionMappings) { | |
Map<String, String> memoriesMap = HelperUtil | |
.getMultipleElementsNameandTypeFromAttributeOrChildeElement(MEMORY, physicalSectionMapping); | |
physicalSectionMapping.removeChildren(MEMORY); | |
physicalSectionMapping.removeAttribute(MEMORY); | |
for (String memoryName : memoriesMap.keySet()) { | |
// verify if the memory name is still transformed in 0.9.0 as Memory only .. as | |
// based on certain properties, it could also be transformed as a Cache and it | |
// should not be referred | |
if (hwTransformationCache.getNewMemoriesMap().containsKey(memoryName)) { | |
Element memoryElement = new Element(MEMORY); | |
memoryElement.setAttribute(HREF, AMLT_PREFIX + (memoryName) + "?type=Memory"); | |
physicalSectionMapping.addContent(memoryElement); | |
} else { | |
if (hwTransformationCache.getNewCachesMap().containsKey(memoryName)) { | |
logger.warn( | |
"In 0.8.3, Memory : \"{0}\" referred as a Target element in AffinityConstraint is no longer a valid Target element. \r\n -- As in 0.9.0 -> this Memory element is transformed to Cache ", | |
memoryName); | |
} | |
} | |
} | |
} | |
} | |
private void migrateMemoryMapping(Element rootElement) { | |
Element mappingModel = rootElement.getChild("mappingModel"); | |
if (mappingModel == null) return; | |
List<Element> memoryMappings = mappingModel.getChildren("memoryMapping"); | |
for (Element memroyMapping : memoryMappings) { | |
Map<String, String> memoriesMap = HelperUtil | |
.getMultipleElementsNameandTypeFromAttributeOrChildeElement(MEMORY, memroyMapping); | |
memroyMapping.removeChildren(MEMORY); | |
memroyMapping.removeAttribute(MEMORY); | |
for (String memoryName : memoriesMap.keySet()) { | |
// verify if the memory name is still transformed in 0.9.0 as Memory only .. as | |
// based on certain properties, it could also be transformed as a Cache and it | |
// should not be referred | |
if (hwTransformationCache.getNewMemoriesMap().containsKey(memoryName)) { | |
Element memoryElement = new Element(MEMORY); | |
memoryElement.setAttribute(HREF, AMLT_PREFIX + (memoryName) + "?type=Memory"); | |
memroyMapping.addContent(memoryElement); | |
} else { | |
if (hwTransformationCache.getNewCachesMap().containsKey(memoryName)) { | |
logger.warn( | |
"In 0.8.3, Memory : \"{0}\" referred as a Target element in AffinityConstraint is no longer a valid Target element. \r\n -- As in 0.9.0 -> this Memory element is transformed to Cache ", | |
memoryName); | |
} | |
} | |
} | |
} | |
} | |
private void migratePhysicalSectionConstraint(Element rootElement) { | |
Element constraintsModel = rootElement.getChild(CONSTRAINTS_MODEL); | |
if (constraintsModel == null) return; | |
List<Element> physicalSectionConstraints = constraintsModel.getChildren("physicalSectionConstraints"); | |
for (Element physicalSectionContraint : physicalSectionConstraints) { | |
Map<String, String> memoriesMap = HelperUtil | |
.getMultipleElementsNameandTypeFromAttributeOrChildeElement(MEMORIES, physicalSectionContraint); | |
physicalSectionContraint.removeChildren(MEMORIES); | |
physicalSectionContraint.removeAttribute(MEMORIES); | |
for (String memoryName : memoriesMap.keySet()) { | |
// verify if the memory name is still transformed in 0.9.0 as Memory only .. as | |
// based on certain properties, it could also be transformed as a Cache and it | |
// should not be referred | |
if (hwTransformationCache.getNewMemoriesMap().containsKey(memoryName)) { | |
Element memoryElement = new Element(MEMORIES); | |
memoryElement.setAttribute(HREF, AMLT_PREFIX + (memoryName) + "?type=Memory"); | |
physicalSectionContraint.addContent(memoryElement); | |
} else { | |
if (hwTransformationCache.getNewCachesMap().containsKey(memoryName)) { | |
logger.warn( | |
"In 0.8.3, Memory : \"{0}\" referred as a Target element in AffinityConstraint is no longer a valid Target element. \r\n -- As in 0.9.0 -> this Memory element is transformed to Cache ", | |
memoryName); | |
} | |
} | |
} | |
} | |
} | |
private void migrateCPUPercentageRequirementLimit(Element rootElement) { | |
Element constraintsModel = rootElement.getChild(CONSTRAINTS_MODEL); | |
if (constraintsModel == null) return; | |
List<Element> requirements = constraintsModel.getChildren("requirements"); | |
for (Element requirement : requirements) { | |
List<Element> limitElements = requirement.getChildren("limit"); | |
for (Element limitElement : limitElements) { | |
String limitElementType = limitElement.getAttributeValue(TYPE, | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
if (limitElementType != null && limitElementType.equals("am:CPUPercentageRequirementLimit")) { | |
Map<String, String> complexNodesMap = HelperUtil | |
.getMultipleElementsNameandTypeFromAttributeOrChildeElement("hardwareContext", | |
limitElement); | |
limitElement.removeChildren("hardwareContext"); | |
limitElement.removeAttribute("hardwareContext"); | |
for (Entry<String, String> entry : complexNodesMap.entrySet()) { | |
String complexNodeName = entry.getKey(); | |
String complexNodeType = entry.getValue(); | |
if (complexNodeType != null && complexNodeType.equals("Core")) { | |
Element puElement = new Element("hardwareContext"); | |
puElement.setAttribute(HREF, AMLT_PREFIX + complexNodeName + "?type=ProcessingUnit"); | |
limitElement.addContent(puElement); | |
} else { | |
logger.warn( | |
"As per 0.9.0 : Only ProcessingUnit element can be referred inside CPUPercentageRequirementLimit as a hardwareContext.\r\n Reference of : {0} of type : {1} is removed : as it is not valid as per 0.9.0", | |
complexNodeName, complexNodeType); | |
} | |
} | |
} | |
} | |
} | |
} | |
private void migrateTargetMemory(Element rootElement) { | |
Element constraintsModel = rootElement.getChild(CONSTRAINTS_MODEL); | |
if (constraintsModel == null) return; | |
List<Element> affinityConstraints = constraintsModel.getChildren("affinityConstraints"); | |
for (Element affinityConstraint : affinityConstraints) { | |
List<Element> targetMemories = affinityConstraint.getChildren("target"); | |
for (Element targetMemory : targetMemories) { | |
String elementType = targetMemory.getAttributeValue(TYPE, | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
if (elementType != null && elementType.equals("am:TargetMemory")) { | |
Map<String, String> memoriesMap = HelperUtil | |
.getMultipleElementsNameandTypeFromAttributeOrChildeElement(MEMORIES, targetMemory); | |
targetMemory.removeChildren(MEMORIES); | |
targetMemory.removeAttribute(MEMORIES); | |
for (String memoryName : memoriesMap.keySet()) { | |
// verify if the memory name is still transformed in 0.9.0 as Memory only .. as | |
// based on certain properties, it could also be transformed as a Cache and it | |
// should not be referred | |
if (hwTransformationCache.getNewMemoriesMap().containsKey(memoryName)) { | |
Element memoryElement = new Element(MEMORIES); | |
memoryElement.setAttribute(HREF, AMLT_PREFIX + (memoryName) + "?type=Memory"); | |
targetMemory.addContent(memoryElement); | |
} else { | |
if (hwTransformationCache.getNewCachesMap().containsKey(memoryName)) { | |
logger.warn( | |
"In 0.8.3, Memory : \"{0}\" referred as a Target element in AffinityConstraint is no longer a valid Target element. \r\n -- As in 0.9.0 -> this Memory element is transformed to Cache ", | |
memoryName); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
private void migrateTargetCore(Element rootElement) { | |
Element constraintsModel = rootElement.getChild(CONSTRAINTS_MODEL); | |
if (constraintsModel == null) return; | |
List<Element> affinityConstraints = constraintsModel.getChildren("affinityConstraints"); | |
for (Element affinityConstraint : affinityConstraints) { | |
List<Element> targetCores = affinityConstraint.getChildren("target"); | |
for (Element targetCore : targetCores) { | |
Map<String, String> coresMap = HelperUtil | |
.getMultipleElementsNameandTypeFromAttributeOrChildeElement(CORES, targetCore); | |
targetCore.removeChildren(CORES); | |
targetCore.removeAttribute(CORES); | |
for (String coreName : coresMap.keySet()) { | |
Element memoryElement = new Element(CORES); | |
memoryElement.setAttribute(HREF, AMLT_PREFIX + (coreName) + "?type=ProcessingUnit"); | |
targetCore.addContent(memoryElement); | |
} | |
} | |
} | |
} | |
/** | |
* This method is used to get the PeriodicStimulusCacheBuilder object | |
* | |
* @param caches The list of all caches. | |
* @return PeriodicStimulusCacheBuilder | |
*/ | |
private HWTransformationCache getHWTransformationCache(List<ICache> caches) { | |
if (caches == null) return null; | |
for (final ICache cache : caches) { | |
if (cache instanceof HWCacheBuilder) { | |
Map<File, Map<String, Object>> cacheMap = cache.getCacheMap(); | |
if (cacheMap != null && cacheMap.size() > 0) { | |
Map<String, Object> map = cacheMap.values().iterator().next(); | |
if (map != null) { | |
Object object = map.get("globalCache"); | |
return (HWTransformationCache) object; | |
} | |
} | |
} | |
} | |
return new HWTransformationCache(); | |
} | |
} |