/** | |
******************************************************************************** | |
* Copyright (c) 2017-2020 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.sca2amalthea.ui.handlers; | |
import java.util.Timer; | |
import java.util.TimerTask; | |
import org.eclipse.app4mc.sca.logging.manager.Logmanager; | |
import org.eclipse.app4mc.sca2amalthea.llvm.headless.GenerateAmaltheaModelFromLLVM; | |
import org.eclipse.app4mc.sca2amalthea.ui.Activator; | |
import org.eclipse.app4mc.sca2amalthea.utils.UtilityForProcessHandling; | |
import org.eclipse.app4mc.sca2amalthea.utils.constants.SCA2AmaltheaConstants; | |
import org.eclipse.core.commands.AbstractHandler; | |
import org.eclipse.core.commands.ExecutionEvent; | |
import org.eclipse.core.commands.ExecutionException; | |
import org.eclipse.core.internal.resources.Resource; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.core.runtime.SubMonitor; | |
import org.eclipse.core.runtime.jobs.Job; | |
import org.eclipse.jface.dialogs.MessageDialog; | |
import org.eclipse.jface.preference.IPreferenceStore; | |
import org.eclipse.jface.viewers.ISelection; | |
import org.eclipse.jface.viewers.TreeSelection; | |
import org.eclipse.swt.widgets.Display; | |
import org.eclipse.swt.widgets.Shell; | |
import org.eclipse.ui.PlatformUI; | |
import org.eclipse.ui.handlers.HandlerUtil; | |
/** | |
*/ | |
@SuppressWarnings("restriction") | |
public class SCAToAmaltheaHandler extends AbstractHandler { | |
private String cFilePath = ""; | |
private String traverseASTPath = ""; | |
private String outPutPath = ""; | |
private String taskListFile = ""; | |
private String headerTextFilePath = ""; | |
private String buildCmdLogFilePath = ""; | |
private String lockfunctionFile = ""; | |
private boolean isStructMemberEnabled = false; | |
private volatile Boolean isGenerationDone = Boolean.FALSE; | |
private final Object mutex = new Object(); | |
private Job currentJob = null; | |
private boolean confirmation = true; | |
private Shell currentShell; | |
private static String infoMessage; | |
private static String confirmationMessage; | |
private static String title; | |
private static int timerDelay; | |
private static int timerPeriod; | |
/** | |
* Sets the timer delay for the timer | |
* | |
* @param timerDelay time delay in milliseconds. | |
*/ | |
public static void setTimerDelay(final int timerDelay) { | |
SCAToAmaltheaHandler.timerDelay = timerDelay; | |
} | |
/** | |
* Returns the timer delay that was set. | |
* | |
* @return timer delay. | |
*/ | |
public static int getTimerDelay() { | |
return SCAToAmaltheaHandler.timerDelay; | |
} | |
/** | |
* Sets the time period after which the timer task would be executed. | |
* | |
* @param timerPeriod time period in milliseonds. | |
*/ | |
public static void setTimerPeriod(final int timerPeriod) { | |
SCAToAmaltheaHandler.timerPeriod = timerPeriod; | |
} | |
/** | |
* Returns the time period that was set. | |
* | |
* @return timer period. | |
*/ | |
public static int getTimerPeriod() { | |
return SCAToAmaltheaHandler.timerPeriod; | |
} | |
@Override | |
public Object execute(final ExecutionEvent event) throws ExecutionException { | |
IPreferenceStore preferenceStore = org.eclipse.app4mc.sca2amalthea.utils.Activator.getDefault().getPreferenceStore(); | |
this.currentShell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); | |
if (event != null) { | |
ISelection selection = HandlerUtil.getCurrentSelection(event); | |
if (((TreeSelection) selection).getFirstElement() instanceof Resource) { | |
Resource resource = (Resource) ((TreeSelection) selection).getFirstElement(); | |
this.cFilePath = resource.getLocation().toString(); | |
this.isStructMemberEnabled = preferenceStore.getBoolean(SCA2AmaltheaConstants.ENABLE_STRUCT_MEMBER); | |
} | |
} | |
this.traverseASTPath = preferenceStore.getString(SCA2AmaltheaConstants.AST_PATH); | |
this.taskListFile = preferenceStore.getString(SCA2AmaltheaConstants.TASK_INFO); | |
this.headerTextFilePath = preferenceStore.getString(SCA2AmaltheaConstants.HDIR_LIST); | |
this.buildCmdLogFilePath = preferenceStore.getString(SCA2AmaltheaConstants.BLOG); | |
this.lockfunctionFile = preferenceStore.getString(SCA2AmaltheaConstants.LOCK_INFO); | |
if (preferenceStore.getBoolean(SCA2AmaltheaConstants.ENABLE_OPTIONAL_FIELDS)) { | |
this.outPutPath = preferenceStore.getString(SCA2AmaltheaConstants.OUTPUT_PATH); | |
} | |
else { | |
this.outPutPath = preferenceStore.getDefaultString(SCA2AmaltheaConstants.OUTPUT_PATH); | |
} | |
if(this.traverseASTPath.isEmpty()){ | |
MessageDialog.openError(this.currentShell, "Error Amalthea model generation", "Path to LLVM executable is not configured in sca2Amalthea preferences"); | |
} | |
else{ | |
if (UtilityForProcessHandling.getCurrentRunningProcess() == null) { | |
createAndExecuteJobForModelGeneration(); | |
} | |
else { | |
String information = "The generation of 2 models at the same time is not allowed"; | |
this.currentShell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); | |
MessageDialog.openInformation(this.currentShell, "Operation not allowed", information); | |
Logmanager.getInstance().log(information); | |
} | |
} | |
return null; | |
} | |
/** | |
* | |
*/ | |
private void createAndExecuteJobForModelGeneration() { | |
Job job = new Job("AMALTHEA model generation") { | |
@Override | |
public IStatus run(final IProgressMonitor monitor) { | |
final SubMonitor subMonitor = SubMonitor.convert(monitor, 600); | |
Thread progressMonitorObserverThread = null; | |
boolean isPopupToBeDisplayed = false; | |
try { | |
progressMonitorObserverThread = createInfiniteProgressMonitor(subMonitor); | |
progressMonitorObserverThread.start(); | |
GenerateAmaltheaModelFromLLVM generator = new GenerateAmaltheaModelFromLLVM( | |
SCAToAmaltheaHandler.this.cFilePath, SCAToAmaltheaHandler.this.outPutPath, | |
SCAToAmaltheaHandler.this.traverseASTPath, SCAToAmaltheaHandler.this.taskListFile, | |
SCAToAmaltheaHandler.this.headerTextFilePath, SCAToAmaltheaHandler.this.buildCmdLogFilePath, | |
SCAToAmaltheaHandler.this.lockfunctionFile, | |
SCAToAmaltheaHandler.this.isStructMemberEnabled); | |
setInfoMessage("The time required to generate the Amalthea model depends on the size of the PVER."); | |
setTitle("Information-Amalthea Model Generation"); | |
Display.getDefault().syncExec(new InfoPopUp()); | |
startTimer(); | |
int returncode = generator.run(); | |
synchronized (SCAToAmaltheaHandler.this.mutex) { | |
SCAToAmaltheaHandler.this.isGenerationDone = true; | |
} | |
// check the exit status of the process and show a information popup | |
if ((returncode != 0) || (UtilityForProcessHandling.getCurrentRunningProcess().exitValue() != 0)) { | |
if (SCAToAmaltheaHandler.this.confirmation) { | |
setInfoMessage("There was some error while parsing c files or when generating the AMALTHEA model."); | |
setTitle("Error"); | |
Display.getDefault().syncExec(new InfoPopUp()); | |
} | |
else { | |
SCAToAmaltheaHandler.this.confirmation = true; | |
} | |
UtilityForProcessHandling.setCurrentRunningProcess(null); | |
} | |
else { | |
isPopupToBeDisplayed = true; | |
} | |
} | |
catch (Exception e) { | |
Logmanager.getInstance().logException(this.getClass(), e, Activator.PLUGIN_ID); | |
if (progressMonitorObserverThread != null) { | |
waitForThread(progressMonitorObserverThread); | |
} | |
subMonitor.done(); | |
return Status.CANCEL_STATUS; | |
} | |
waitForThread(progressMonitorObserverThread); | |
subMonitor.done(); | |
if (isPopupToBeDisplayed) { | |
setInfoMessage("Amathea model generation completed"); | |
setTitle("Amathea model generated"); | |
Display.getDefault().syncExec(new InfoPopUp()); | |
} | |
return new Status(IStatus.OK, Activator.PLUGIN_ID, "AMALTHEA model generation completed"); | |
} | |
/** | |
* @param t | |
*/ | |
private void waitForThread(final Thread t) { | |
try { | |
if (t != null) { | |
t.join(); | |
} | |
} | |
catch (InterruptedException e) { | |
Logmanager.getInstance().log(e.getMessage()); | |
} | |
} | |
}; | |
this.currentJob = job; | |
job.schedule(); | |
} | |
/** | |
* @param subMonitor | |
*/ | |
private Thread createInfiniteProgressMonitor(final SubMonitor subMonitor) { | |
return new InfiniteProgressMonitorObserver(subMonitor); | |
} | |
class InfiniteProgressMonitorObserver extends Thread { | |
private String message = "Terminating AMALTHEA model generation job"; | |
private final SubMonitor subMonitor; | |
/** | |
* @param subMonitor monitor which will be observed | |
*/ | |
public InfiniteProgressMonitorObserver(final SubMonitor subMonitor) { | |
super(); | |
this.subMonitor = subMonitor; | |
} | |
@Override | |
public void run() { | |
this.message = runLogarithmicWaitOnProgressMonitor(); | |
terminateMonitorIn10Secondes(); | |
UtilityForProcessHandling.setCurrentRunningProcess(null); | |
UtilityForProcessHandling.setModelGenerationcancelled(false); | |
SCAToAmaltheaHandler.this.isGenerationDone = false; | |
} | |
/** | |
* @param subMonitor | |
* @param message | |
* @return | |
*/ | |
private String runLogarithmicWaitOnProgressMonitor() { | |
String messageToRenameJob = "Terminating AMALTHEA model generation job"; | |
boolean isProcessCompleted = false; | |
int advancementCounter = 0; | |
while (!isProcessCompleted) { | |
if (this.subMonitor.isCanceled()) { | |
synchronized (SCAToAmaltheaHandler.this.mutex) { | |
SCAToAmaltheaHandler.this.isGenerationDone = true; | |
} | |
Process p = UtilityForProcessHandling.getCurrentRunningProcess(); | |
p.destroyForcibly(); | |
UtilityForProcessHandling.setCurrentRunningProcess(null); | |
UtilityForProcessHandling.setModelGenerationcancelled(true); | |
messageToRenameJob = "Cancelling the model generation job"; | |
} | |
try { | |
this.subMonitor.worked(1); | |
advancementCounter++; | |
if ((advancementCounter % 300) == 0) { | |
advancementCounter = 1; | |
this.subMonitor.setWorkRemaining(600); | |
} | |
Thread.sleep(1000); | |
} | |
catch (InterruptedException e) { | |
Logmanager.getInstance().log(e.getMessage()); | |
} | |
synchronized (SCAToAmaltheaHandler.this.mutex) { | |
isProcessCompleted = SCAToAmaltheaHandler.this.isGenerationDone; | |
} | |
} | |
return messageToRenameJob; | |
} | |
/** | |
* @param subMonitor | |
* @param message | |
*/ | |
private void terminateMonitorIn10Secondes() { | |
int numberOfSecondUntilEnd = 10; | |
this.subMonitor.setWorkRemaining(numberOfSecondUntilEnd); | |
this.subMonitor.setTaskName(this.message); | |
SCAToAmaltheaHandler.this.currentJob.setName(this.message); | |
while (numberOfSecondUntilEnd > 0) { | |
try { | |
this.subMonitor.worked(1); | |
Thread.sleep(1000); | |
} | |
catch (InterruptedException e) { | |
Logmanager.getInstance().log(e.getMessage()); | |
} | |
--numberOfSecondUntilEnd; | |
} | |
} | |
} | |
/** | |
* Inner class to show a confirmation pop up. | |
* | |
*/ | |
class ConfirmationPopUp implements Runnable { | |
@Override | |
public void run() { | |
SCAToAmaltheaHandler.this.confirmation = | |
MessageDialog.openConfirm(SCAToAmaltheaHandler.this.currentShell, title, confirmationMessage); | |
} | |
} | |
/** | |
* Inner class to show a information pop up. | |
*/ | |
class InfoPopUp implements Runnable { | |
@Override | |
public void run() { | |
MessageDialog.openInformation(SCAToAmaltheaHandler.this.currentShell, title, infoMessage); | |
} | |
} | |
/** | |
* This method starts the timer.The timer is sceduled to run after every 15mins.Every time the timer runs it checks if | |
* the eclipse job is still alive. If so it pops up a confirmation dialog asking if the user wishes to continue. If | |
* the user wishes to continue the timer keeps running and keeps showing the popup after every 15 mins and if the user | |
* wishes to abort then it cancels the eclipse job, cancels the timer and kills the sca.exe process.If the eclipse job | |
* is not alive it cancels the timer. | |
*/ | |
private void startTimer() { | |
if (getTimerDelay() == 0) { | |
setTimerDelay(40 * 60 * 1000); | |
} | |
if (getTimerPeriod() == 0) { | |
setTimerPeriod(40 * 60 * 1000); | |
} | |
Job eclipseJob = Job.getJobManager().currentJob(); | |
Timer t = new Timer(); | |
t.scheduleAtFixedRate(new TimerTask() { | |
@Override | |
public void run() { | |
if ((eclipseJob.getThread() != null) && eclipseJob.getThread().isAlive()) { | |
setConfirmationMessage( | |
"The amalthea model generation job is still running.It might take some more time. Do you wish to continue? Press Ok to continue or cancel to abort"); | |
setTitle("Amalthea Job status"); | |
Display.getDefault().syncExec(new ConfirmationPopUp()); | |
if (!SCAToAmaltheaHandler.this.confirmation) { | |
t.cancel(); | |
eclipseJob.cancel(); | |
Process p = UtilityForProcessHandling.getCurrentRunningProcess(); | |
if (p != null) { | |
p.destroyForcibly(); | |
} | |
} | |
} | |
else { | |
t.cancel(); | |
} | |
} | |
}, getTimerDelay(), getTimerPeriod()); | |
} | |
private static void setInfoMessage(final String message) { | |
SCAToAmaltheaHandler.infoMessage = message; | |
} | |
private static void setTitle(final String message) { | |
SCAToAmaltheaHandler.title = message; | |
} | |
private static void setConfirmationMessage(final String message) { | |
SCAToAmaltheaHandler.confirmationMessage = message; | |
} | |
} |