/** | |
******************************************************************************** | |
* 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.converters093.impl; | |
import java.io.File; | |
import java.util.ArrayList; | |
import java.util.Iterator; | |
import java.util.LinkedHashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import java.util.Set; | |
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.converters.common.utils.ModelVersion; | |
import org.eclipse.app4mc.amalthea.converters093.utils.HWCacheBuilder; | |
import org.eclipse.app4mc.amalthea.converters093.utils.HelperUtils_092_093; | |
import org.eclipse.app4mc.amalthea.converters093.utils.PUDefinition_IPCData; | |
import org.eclipse.app4mc.util.sessionlog.SessionLogger; | |
import org.jdom2.Attribute; | |
import org.jdom2.Document; | |
import org.jdom2.Element; | |
import org.jdom2.Parent; | |
import org.osgi.service.component.annotations.Activate; | |
import org.osgi.service.component.annotations.Component; | |
import org.osgi.service.component.annotations.Reference; | |
@Component( | |
property = { | |
ServiceConstants.INPUT_MODEL_VERSION_PROPERTY + "=0.9.2", | |
ServiceConstants.OUTPUT_MODEL_VERSION_PROPERTY + "=0.9.3" | |
}, | |
service = IConverter.class) | |
public class SwConverter extends AbstractConverter { | |
private static final String AM = "am"; | |
private static final String XSI = "xsi"; | |
private static final String TYPE = "type"; | |
private static final String KEY = "key"; | |
private static final String VALUE = "value"; | |
@Reference | |
SessionLogger logger; | |
private PUDefinition_IPCData cache; | |
@Override | |
@Activate | |
protected void activate(Map<String, Object> properties) { | |
super.activate(properties); | |
} | |
@Override | |
public void convert(File targetFile, Map<File, Document> fileDocumentMapping, List<ICache> caches) { | |
logger.info( | |
"Migration from 0.9.2 to 0.9.3 : Executing Sw converter for model file : {0}", targetFile.getName()); | |
this.cache = getHWCache(caches); | |
if (this.cache == null) { | |
throw new IllegalStateException("PUDefinition_IPCData is not built and Object of it is not available in Converters"); | |
} | |
final Document root = fileDocumentMapping.get(targetFile); | |
if (root == null) { | |
return; | |
} | |
final Element rootElement = root.getRootElement(); | |
update_ExecutionNeed(rootElement); | |
update_ChunkProcessingInstructions(rootElement); | |
update_activations(rootElement); | |
} | |
private void update_activations(Element rootElement) { | |
final StringBuilder xpathBuffer = new StringBuilder(); | |
xpathBuffer.append("./swModel/activations"); | |
final List<Element> activationElements = HelperUtil.getXpathResult( | |
rootElement, | |
xpathBuffer.toString(), | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._093, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
for (Element activationElement : activationElements) { | |
String activationType=activationElement.getAttributeValue(TYPE, AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
if(activationType!=null) { | |
if(activationType.equals("am:VariableRateActivation")) { | |
Element occurrencesPerStep_double_deviationElement = activationElement.getChild("occurrencesPerStep"); | |
if(occurrencesPerStep_double_deviationElement!=null) { | |
Element migratedElement = HelperUtils_092_093.migrateDeviationElementContainingDoubleValue(occurrencesPerStep_double_deviationElement, "occurrencesPerStep", logger); | |
int indexOf = activationElement.indexOf(occurrencesPerStep_double_deviationElement); | |
activationElement.removeContent(occurrencesPerStep_double_deviationElement); | |
if(migratedElement!=null) { | |
//adding migrated element to the activation element | |
activationElement.addContent(indexOf, migratedElement); | |
} | |
} | |
} else if(activationType.equals("am:SporadicActivation")) { | |
Element activationDeviation_Time_deviationElement = activationElement.getChild("activationDeviation"); | |
if(activationDeviation_Time_deviationElement!=null) { | |
Element migratedElement = HelperUtils_092_093.migrateDeviationElementContainingTimeValue(activationDeviation_Time_deviationElement, "activationDeviation", logger); | |
int indexOf = activationElement.indexOf(activationDeviation_Time_deviationElement); | |
activationElement.removeContent(activationDeviation_Time_deviationElement); | |
if (migratedElement!=null) { | |
//adding migrated element to the activation element | |
activationElement.addContent(indexOf, migratedElement); | |
migratedElement.setName("occurrence"); | |
} | |
} | |
} | |
} | |
} | |
} | |
private void update_ChunkProcessingInstructions(Element rootElement) { | |
final StringBuilder xpathBuffer = new StringBuilder(); | |
/*- | |
* As per the change in 0.9.3, chunkProcessingInstructions (of 0.9.2) inside TransmissionPolicy class, is changed to chunkProcessingTicks | |
* | |
*/ | |
xpathBuffer.append("./swModel/runnables//*[@xsi:type=\"am:LabelAccess\" or @xsi:type=\"am:ChannelSend\" or @xsi:type=\"am:ChannelReceive\"]/transmissionPolicy"); | |
xpathBuffer.append("|"); | |
xpathBuffer.append("./osModel/operatingSystems/taskSchedulers/computationItems[@xsi:type=\"am:LabelAccess\"]/transmissionPolicy"); | |
xpathBuffer.append("|"); | |
xpathBuffer.append("./osModel/operatingSystems/interruptControllers/computationItems[@xsi:type=\"am:LabelAccess\"]/transmissionPolicy"); | |
final List<Element> transmissionPolicyElements = HelperUtil.getXpathResult( | |
rootElement, | |
xpathBuffer.toString(), | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._093, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
for (Element transmissionPolicyElement : transmissionPolicyElements) { | |
Attribute attribute = transmissionPolicyElement.getAttribute("chunkProcessingInstructions"); | |
if(attribute!=null) { | |
attribute.setName("chunkProcessingTicks"); | |
} | |
} | |
} | |
private void update_ExecutionNeed(final Element rootElement) { | |
final StringBuilder xpathBuffer = new StringBuilder(); | |
/*- | |
* As per the change in 0.9.3, ExecutionNeed (of 0.9.2) with default and extended entries having key as Instructions, is converted to ExecutionTicks | |
* | |
* xpathBuffer.append("./swModel/sections/size"); | |
* xpathBuffer.append("|"); | |
*/ | |
xpathBuffer.append("./swModel/runnables//*[@xsi:type=\"am:ExecutionNeed\"]"); | |
xpathBuffer.append("|"); | |
xpathBuffer | |
.append("./osModel/operatingSystems/taskSchedulers/computationItems[@xsi:type=\"am:ExecutionNeed\"]"); | |
xpathBuffer.append("|"); | |
xpathBuffer.append( | |
"./osModel/operatingSystems/interruptControllers/computationItems[@xsi:type=\"am:ExecutionNeed\"]"); | |
final List<Element> executionNeeds = HelperUtil.getXpathResult( | |
rootElement, | |
xpathBuffer.toString(), | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._093, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
for (final Element executionNeedElement : executionNeeds) { | |
String containerElementName = null; | |
String containerType = ""; | |
Element parentElement = HelperUtil.getParentElementOfName(executionNeedElement, "runnables", | |
"taskSchedulers", "interruptControllers"); | |
if (parentElement != null) { | |
containerElementName = parentElement.getAttributeValue("name"); | |
if (parentElement.getName().equals("runnables")) { | |
containerType = "Runnable"; | |
} else if (parentElement.getName().equals("taskSchedulers")) { | |
containerType = "TaskScheduler"; | |
} else if (parentElement.getName().equals("interruptControllers")) { | |
containerType = "InterruptController"; | |
} | |
} | |
String nodeName = executionNeedElement.getName(); | |
/*- | |
* <default key="Instructions"> | |
<value xsi:type="am:NeedConstant" value="45"/> | |
</default> | |
<default key="FLOP"> | |
<value xsi:type="am:NeedDeviation"> | |
<deviation> | |
<lowerBound xsi:type="am:LongObject" value="0"/> | |
<upperBound xsi:type="am:LongObject" value="0"/> | |
<distribution xsi:type="am:BetaDistribution" alpha="0.0" beta="0.0"/> | |
</deviation> | |
</value> | |
</default> | |
*/ | |
List<Element> defaultElements = executionNeedElement.getChildren("default"); | |
Map<String, Element> defaultElementsMap = new LinkedHashMap<>(); | |
for (Element defaultElement : defaultElements) { | |
String key = defaultElement.getAttributeValue(KEY); | |
if (key != null) { | |
defaultElementsMap.put(key, defaultElement); | |
} | |
} | |
Element newExecutionTicksElement = null; | |
Element newExecutionNeedsElement = null; | |
Set<Entry<String, Element>> entrySet = defaultElementsMap.entrySet(); | |
Iterator<Entry<String, Element>> iterator = entrySet.iterator(); | |
while(iterator.hasNext()) { | |
Entry<String, Element> next = iterator.next(); | |
String key = next.getKey(); | |
if (key.equals("Instructions")) { | |
Element defaultInstructionsElement = defaultElementsMap.get("Instructions"); | |
/*- ------ExecutionTicks element --------------*/ | |
newExecutionTicksElement = new Element(nodeName); | |
Attribute typeAttribute = new Attribute(TYPE, "am:Ticks", AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
newExecutionTicksElement.getAttributes().add(typeAttribute); | |
/*- | |
* <default key="Instructions"> | |
<value xsi:type="am:NeedConstant" value="20"/> | |
</default> | |
*/ | |
Element valueElement = defaultInstructionsElement.getChild(VALUE); | |
Element default_executionTicksElement = createTicksElementFromNeed(valueElement, "default", 1.0); | |
if (default_executionTicksElement != null) { | |
// Adding default ExecutionTicks element | |
newExecutionTicksElement.addContent(default_executionTicksElement); | |
} | |
} else { | |
if (containerType.equals("TaskScheduler") || containerType.equals("InterruptController")) { | |
logger.warn("In {0} : {1}, ExecutionNeed element's NeedEntry (having HwFeatureCategory : {2})-> can not be migrated. As this is not supported in 0.9.3. \nIn {3} element only NeedEntry elements having HwFeatureCategory Instructions are migrated to Ticks", | |
containerType, | |
containerElementName, | |
key, | |
containerType); | |
continue; | |
} | |
Element defaultExecutionNeedElement = defaultElementsMap.get(key); | |
if (newExecutionNeedsElement == null) { | |
newExecutionNeedsElement = new Element(nodeName); | |
Attribute typeAttribute = new Attribute(TYPE, "am:ExecutionNeed", | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
newExecutionNeedsElement.getAttributes().add(typeAttribute); | |
} | |
/*- | |
* <default key="Instructions"> | |
<value xsi:type="am:NeedConstant" value="20"/> | |
</default> | |
*/ | |
Element valueElement = defaultExecutionNeedElement.getChild(VALUE); | |
if (valueElement != null) { | |
Element newNeedElementValue = createTicksElementFromNeed(valueElement, VALUE,1.0); | |
if (newNeedElementValue != null) { | |
Element needsElement = new Element("needs"); | |
needsElement.setAttribute(new Attribute(KEY, key)); | |
needsElement.addContent(newNeedElementValue); | |
// adding needs element to ExecutionNeed element | |
newExecutionNeedsElement.addContent(needsElement); | |
} | |
} | |
} | |
} | |
// for (String key : defaultElementsMap.keySet()) {} | |
/*------------------------------ Handling ExtendedElements ---------------------------*/ | |
List<Element> extendedElements = executionNeedElement.getChildren("extended"); | |
for (Element extendedElement : extendedElements) { | |
Double ipcValue=getIPCValue(extendedElement); | |
final List<Element> valueElements = HelperUtil.getXpathResult( | |
extendedElement, | |
"./value[@key=\"Instructions\"]", | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._093, AM)); | |
if (!valueElements.isEmpty()) { | |
Element extended_executionTicksElement = new Element("extended"); | |
HelperUtil.copyElement_Attribute_or_Element(extendedElement, extended_executionTicksElement, | |
KEY); | |
/*- | |
* <extended key="puDef?type=ProcessingUnitDefinition"> | |
<value key="Instructions"> | |
<value xsi:type="am:NeedConstant" value="10"/> | |
</value> | |
</extended> | |
* | |
*/ | |
Element valueElement = valueElements.get(0); | |
Element valueValueElement = valueElement.getChild(VALUE); | |
Element value_executionTicksElement = createTicksElementFromNeed(valueValueElement, VALUE,ipcValue); | |
if (value_executionTicksElement != null) { | |
/*- adding value to the Extended ticks element */ | |
extended_executionTicksElement.addContent(value_executionTicksElement); | |
// Adding extended ExecutionTicks element | |
if (newExecutionTicksElement == null) { | |
newExecutionTicksElement = new Element(nodeName); | |
Attribute typeAttribute = new Attribute(TYPE, "am:Ticks", | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
newExecutionTicksElement.getAttributes().add(typeAttribute); | |
} | |
newExecutionTicksElement.addContent(extended_executionTicksElement); | |
} | |
} | |
} | |
List<Element> newRunnableItemElementsList=new ArrayList<>(); | |
if (newExecutionTicksElement != null) { | |
newRunnableItemElementsList.add(newExecutionTicksElement); | |
} | |
if (newExecutionNeedsElement != null) { | |
newRunnableItemElementsList.add(newExecutionNeedsElement); | |
} | |
Parent parent = executionNeedElement.getParent(); | |
int indexOf = parent.indexOf(executionNeedElement); | |
if (!newRunnableItemElementsList.isEmpty()) { | |
parent.addContent(indexOf, newRunnableItemElementsList); | |
} | |
final List<Element> skippedValuesExtended = HelperUtil.getXpathResult( | |
executionNeedElement, | |
"./extended/value[not(@key=\"Instructions\")]", | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._093, AM)); | |
final List<Element> skippedValuesDefault = HelperUtil.getXpathResult( | |
executionNeedElement, | |
"./default[not(@key=\"Instructions\")]", | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._093, AM)); | |
StringBuilder extendedElementsbuffer = new StringBuilder(); | |
for (Element elementKey : skippedValuesExtended) { | |
String attributeValue = elementKey.getAttributeValue(KEY); | |
extendedElementsbuffer.append(attributeValue); | |
extendedElementsbuffer.append(","); | |
} | |
StringBuilder defaultElementsbuffer = new StringBuilder(); | |
for (Element elementKey : skippedValuesDefault) { | |
String attributeValue = elementKey.getAttributeValue(KEY); | |
defaultElementsbuffer.append(attributeValue); | |
defaultElementsbuffer.append(","); | |
} | |
if (extendedElementsbuffer.length() > 0) { | |
logger.info("In {0} : \"{1}, \" Entries of ExecutionNeed (extended elements) with following keys can not be migrated : {2} -> (only supported \"HwFeatureCategory\" as key is : \"Instructions\")", | |
containerType, | |
containerElementName, | |
extendedElementsbuffer.toString().substring(0, extendedElementsbuffer.length() - 1)); | |
} | |
if ((defaultElementsbuffer.length() > 0)&&(containerType.equals("TaskScheduler")||containerType.equals("InterruptController"))) { | |
logger.info("In {0} : \"{1}, \" Entries of ExecutionNeed (default elements) with following keys can not be migrated : {2} ->(only supported \"HwFeatureCategory\" as key is : \"Instructions\")", | |
containerType, | |
containerElementName, | |
defaultElementsbuffer.toString().substring(0, defaultElementsbuffer.length() - 1)); | |
} | |
/*- Removing ExecutionNeeds element as the data of it is migrated */ | |
parent.removeContent(executionNeedElement); | |
} | |
} | |
private Double getIPCValue(Element extendedElement) { | |
String puDefinition = HelperUtil.getSingleElementNameFromAttributeOrChildeElement(KEY, extendedElement); | |
if (puDefinition != null) { | |
String ipcFeatureElement = this.cache.getPuDefinitionIPCFeatureMap().get(puDefinition); | |
if (ipcFeatureElement != null) { | |
Double ipcValue = this.cache.getIpcFeatureValueMap().get(ipcFeatureElement); | |
if (ipcValue != null) { | |
return ipcValue; | |
} | |
} | |
} | |
return 1d; | |
} | |
private Element createTicksElementFromNeed( Element valueElement, String newElementName, double ipcValue) { | |
if(valueElement !=null) { | |
String valueType = valueElement.getAttributeValue(TYPE, AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
/*- it can be either NeedConstant or NeedDeviation */ | |
if(valueType !=null && valueType.equals("am:NeedConstant")) { | |
Element tc_executionTicksElement=new Element(newElementName); | |
Attribute default_executionTicksElement_TypeAttribute=new Attribute(TYPE, "am:DiscreteValueConstant", | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
tc_executionTicksElement.getAttributes().add(default_executionTicksElement_TypeAttribute); | |
String valueValue = valueElement.getAttributeValue(VALUE); | |
if(valueValue!=null) { | |
//TODO: divide with value of Instructions | |
tc_executionTicksElement.getAttributes().add(new Attribute(VALUE, HelperUtils_092_093.getValueAfterApplyingIPC(valueValue, ipcValue, logger))); | |
} | |
return tc_executionTicksElement; | |
} else if(valueType !=null && valueType.equals("am:NeedDeviation")) { | |
Element deviationElement = valueElement.getChild("deviation"); | |
return HelperUtils_092_093.migrateDeviationElementContainingLongValue(deviationElement, newElementName, ipcValue, logger); | |
} | |
} | |
return null; | |
} | |
/** | |
* This method is used to get the HW model cache data | |
* | |
* @param caches The list of all caches. | |
* @return PUDefinition_IPCData | |
*/ | |
private PUDefinition_IPCData getHWCache(List<ICache> caches) { | |
if (caches != null) { | |
for (final ICache c : caches) { | |
if (c instanceof HWCacheBuilder) { | |
Map<File, Map<String, Object>> cacheMap = c.getCacheMap(); | |
if (cacheMap != null && cacheMap.size() > 0) { | |
Map<String, Object> map = cacheMap.values().iterator().next(); | |
if (map != null) { | |
Object object = map.get("globalCache"); | |
return (PUDefinition_IPCData) object; | |
} | |
} | |
} | |
} | |
} | |
return null; | |
} | |
} |