blob: 35c9650af2b8b5ccdd93d1e1eb751ac26d20c184 [file] [log] [blame]
/**
********************************************************************************
* 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();
}
}