#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''
Copyright (C) 2011-2015 German Aerospace Center DLR
(Deutsches Zentrum fuer Luft- und Raumfahrt e.V.),
Institute of System Dynamics and Control
Copyright (C) 2014-2018 ESI ITI GmbH
All rights reserved.

This file is part of PySimulator.

PySimulator is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PySimulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with PySimulator. If not, see www.gnu.org/licenses.
'''

'''
***************************
This Simulator plugin can load Modelica models (assumed SimulationX is installed),
simulate them by SimulationX and save the results.
***************************
'''

import csv
import locale
import os
import re
import string
import time
import types

import pythoncom
import win32com.client

from ...SimulationResult.SimulationXCsv import SimulationXCsv
from .. import SimulatorBase
import _winreg as winreg
from SimXEnums import *


iconImage = 'simulatorSimulationX.ico'
modelExtension = ['mo', 'ism', 'isx']

def closeSimulatorPlugin():
	pass

def prepareSimulationList(fileName, name, config):
	pass

def getNewModel(modelName=None, modelFileName=None, config=None):
	return Model(modelName, modelFileName, config)

def _isNumeric(s):
	'''  Check if a string value can be successfully converted to a double value
	'''
	if not s:
		# Empty string
		return True
	else:
		try:
			float(s)
			return True
		except ValueError:
			return False
		except TypeError:
			return False

class Model(SimulatorBase.Model):

	def __init__(self, modelName, modelFileName, config):
		SimulatorBase.Model.__init__(self, modelName, modelFileName, config)
		self.modelType = 'SimulationX'

		sim = None
		self._doc = None

		try:
			if not config['Plugins']['SimulationX'].has_key('version'):
				config['Plugins']['SimulationX']['version'] = 'Iti.Simx39'
				config.write()
			dispatch = config['Plugins']['SimulationX']['version']
			if dispatch == 'Iti.Simx36':
				sub_key = r'Software\ITI GmbH\SimulationX 3.6\Modelica'
			elif dispatch == 'Iti.Simx37':
				sub_key = r'Software\ITI GmbH\SimulationX 3.7\Modelica'
			elif dispatch == 'Iti.Simx38':
				sub_key = r'Software\ESI Group\SimulationX 3.8\Modelica'
			elif dispatch == 'Iti.Simx39':
				sub_key = r'Software\ESI Group\SimulationX 3.9\Modelica'
			elif dispatch == 'ESI.SimulationX40':
				sub_key = r'Software\ESI Group\SimulationX 4.0\Modelica'
			else:
				sub_key = r'Software\ITI GmbH\SimulationX 3.5\Modelica'
			# Make sure Modelica models can be simulated
			try:
				key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, sub_key, 0, winreg.KEY_ALL_ACCESS)
			except WindowsError:
				key = winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, sub_key, 0, winreg.KEY_ALL_ACCESS)
			winreg.SetValueEx(key, 'AutoCreateSimModel', 0, winreg.REG_DWORD, 1)
			winreg.CloseKey(key)

			pythoncom.CoInitialize()

			# A dummy object to get result properties:
			self.integrationResults = SimulationXCsv.Results('')
			self.integrationSettings.resultFileExtension = 'csvx'

			if dispatch == 'Iti.Simx38' or dispatch == 'Iti.Simx39' or dispatch == 'ESI.SimulationX40':
				self._availableIntegrationAlgorithms = ['BDF (Byte code)', 'BDF (C code)', 'MEBDF (Byte code)', 'MEBDF (C code)', 'CVODE (C code)', 'Fixed Step (C code)']
				self._solverByName = dict([('BDF (Byte code)', 'MultiStepMethod2'), ('BDF (C code)', 'BDFCompiled'), ('MEBDF (Byte code)', 'MEBDFDAE'), ('MEBDF (C code)', 'MEBDFCompiled'), ('CVODE (C code)', 'CVODE'), ('Fixed Step (C code)', 'FixStep')])
				self._IntegrationAlgorithmHasFixedStepSize = [False, False, False, False, False, True]
				self._IntegrationAlgorithmCanProvideStepSizeResults = [False, False, False, False, False, True]
			else:
				self._availableIntegrationAlgorithms = ['BDF (Byte code)', 'MEBDF (Byte code)', 'CVODE (C code)', 'Fixed Step (C code)']
				self._solverByName = dict([('BDF (Byte code)', 'MultiStepMethod2'), ('MEBDF (Byte code)', 'MEBDFDAE'), ('CVODE (C code)', 'CVODE'), ('Fixed Step (C code)', 'FixStep')])
				self._IntegrationAlgorithmHasFixedStepSize = [False, False, False, True]
				self._IntegrationAlgorithmCanProvideStepSizeResults = [False, False, False, True]

			self.integrationSettings.algorithmName = self._availableIntegrationAlgorithms[0]
			self.simulationStopRequest = False

			# Open SimulationX
			try:
				sim = win32com.client.GetActiveObject(dispatch)
			except:
				sim = win32com.client.Dispatch(dispatch)

			# Show SimulationX window
			sim.Visible = True

			# Wait till SimulationX is initialized
			if sim.InitState == simUninitialized:
				while sim.InitState != simInitBase:
					time.sleep(0.1)

			# SimulationX in non-interactive mode
			sim.Interactive = False

			# Load libraries
			if sim.InitState == simInitBase and (dispatch == 'Iti.Simx3' or dispatch == 'Iti.Simx36' or dispatch == 'Iti.Simx37'):
				sim.InitSimEnvironment()

			self.modelType += ' ' + sim.Version

			if len(modelFileName) == 1:
				strMsg = 'PySimulator: Load model'

				split = unicode.rsplit(modelFileName[0], '.', 1)
				if len(split) > 1:
					suffix = split[1]
				else:
					suffix = ''

				if suffix in ['ism', 'isx']:
					# Try to load as file
					try:
						# Write tracing marker message to output window in SimulationX
						sim.Trace(SimTraceMsgLocationFile, SimTraceMsgTypeInfo, strMsg, '')
						self._doc = sim.Documents.Open(modelFileName[0])
					except win32com.client.pywintypes.com_error:
						self._doc = None
						print 'SimulationX: COM Error.'

				elif suffix == 'mo':
					# Try to load as library
					try:
						try:
							libraryFileName = modelFileName[0].replace('/', '\\')
							sim.LoadLibrary(libraryFileName)
						except:
							pass
						# Write tracing marker message to output window in SimulationX
						sim.Trace(SimTraceMsgLocationFile, SimTraceMsgTypeInfo, strMsg, '')
						self._doc = sim.Documents.Open(modelName)
					except win32com.client.pywintypes.com_error:
						self._doc = None
						print 'SimulationX: COM Error.'

				if suffix in modelExtension:
					# Read complete tracing to string
					strTracing = sim.GetTraceMessages(SimTraceMsgLocationFile)
					# Find last occurrence of tracing marker message
					pos = strTracing.rfind(strMsg)
					if pos >= 0:
						strTracing = strTracing[pos + len(strMsg):].strip()
						if len(strTracing):
							print strTracing
							self._doc.Close(False)
							self._doc = None

				if not type(self._doc) is types.NoneType:
					self._marshalled_doc = pythoncom.CreateStreamOnHGlobal()
					pythoncom.CoMarshalInterface(self._marshalled_doc, pythoncom.IID_IDispatch, self._doc._oleobj_, pythoncom.MSHCTX_INPROC)
					self._marshalled_doc.Seek(0, pythoncom.STREAM_SEEK_SET)
				else:
					self._marshalled_doc = None
					print 'SimulationX: Load error.'

		except win32com.client.pywintypes.com_error as error:
			print 'SimulationX: COM error.'
		except:
			print 'SimulationX: Unhandled exception.'
			import traceback
			print traceback.format_exc()

		finally:
			try:
				if not type(sim) is types.NoneType:
					# SimulationX in interactive mode
					sim.Interactive = True
			except:
				pass

	def __exit__(self, _type, _value, _traceback):
		pythoncom.CoUninitialize()

	def close(self):
		''' Close a Modelica/SimulationX model
		'''
		sim = None
		try:
			doc = win32com.client.Dispatch(pythoncom.CoUnmarshalInterface(self._marshalled_doc, pythoncom.IID_IDispatch))
			self._marshalled_doc.Seek(0, pythoncom.STREAM_SEEK_SET)
			if not type(doc) is types.NoneType:
				sim = doc.Application
				sim.Interactive = False
				doc.Close(False)
				doc = None
				pythoncom.CoReleaseMarshalData(self._marshalled_doc)
				self._marshalled_doc = None
		except win32com.client.pywintypes.com_error:
			print 'SimulationX: COM error.'
		except:
			print 'SimulationX: Unhandled exception.'
			import traceback
			print traceback.format_exc()
		finally:
			SimulatorBase.Model.close(self)
			if not type(sim) is types.NoneType:
				sim.Interactive = True
				sim = None

		if hasattr(self, 'variableTree'):
			del self.variableTree
		if hasattr(self, '_availableIntegrationAlgorithms'):
			del self._availableIntegrationAlgorithms
		if hasattr(self, '_solverByName'):
			del self._solverByName
		if hasattr(self, '_IntegrationAlgorithmHasFixedStepSize'):
			del self._IntegrationAlgorithmHasFixedStepSize
		if hasattr(self, '_IntegrationAlgorithmCanProvideStepSizeResults'):
			del self._IntegrationAlgorithmCanProvideStepSizeResults

	def setVariableTree(self):
		''' Generate variable tree
		'''
		sim = None
		try:
			if not type(self._doc) is types.NoneType:
				sim = self._doc.Application
				sim.Interactive = False
				self._fillTree(self._doc, self._doc)
		except win32com.client.pywintypes.com_error:
			print 'SimulationX: COM error.'
		finally:
			if not type(sim) is types.NoneType:
				sim.Interactive = True

	def _fillTree(self, pObject, doc):
		''' Scan a SimulationX entity object for all child parameters and results
		'''
		for pBaseEntity in pObject.BaseEntities:
			self._fillTree(pBaseEntity, doc)

		for pChild in pObject.Children:
			if pChild.Kind == simType:
				continue
			if pChild.GetProperty(simIsBaseClass) or (pChild.GetProperty(simIsHidden) and not pChild.GetProperty(simIsFinal)) or pChild.GetProperty(simIsProtected) or pChild.GetProperty(simIsForCompat) or pChild.GetProperty(simIsAttribute):
				continue
			childIsASimVariable = pChild.IsA(simVariable)
			childIsASimParameter = pChild.IsA(simParameter)
			if ((childIsASimParameter or pChild.IsA(simGeneralParameter)) and not childIsASimVariable) or (childIsASimParameter and pChild.GetProperty(simIsInput) and childIsASimVariable):
				# Parameter
				self._fillTreeParam(pObject, doc, pChild)
			elif childIsASimVariable:
				# Result
				self._fillTreeResult(pObject, doc, pChild)
			if not pChild.GetProperty(simIsInner) and pChild.GetProperty(simIsOuter):
				continue
			childEntityClass = pChild.Class
			if childEntityClass == simSimObject or childEntityClass == simSimBlock or childEntityClass == simConservConnection or childEntityClass == simFluidConnection or childEntityClass == simModelicaPin:
				self._fillTree(pChild, doc)

	def _fillTreeParam(self, pObject, doc, pChild):
		''' Scan a SimulationX parameter object
		'''
		docIdentDot = doc.Ident + '.'
		dim = pChild.Execute('GetDimension', [])[0]
		if dim == '':
			# Scalar dimension
			childTypeIdent = pChild.Type.Ident
			if not childTypeIdent == 'BuiltIn.BaseModel.ProtKind' and not childTypeIdent == 'StateSelect':
				# childRelIdent = pChild.GetRelIdent(doc)
				childRelIdent = pChild.Ident
				if childRelIdent.startswith(docIdentDot):
					childRelIdent = childRelIdent[len(docIdentDot):]
				if (not pChild.Parent == doc and not pObject.Name.find('_base') == 0) or (not childRelIdent == 'iSim' and not childRelIdent == 'tStart' and not childRelIdent == 'tStop' and not childRelIdent == 'userInitFile' and not childRelIdent == 'termCond' and not childRelIdent == 'traceOn' and not childRelIdent == 'protOn'):
					childValue = pChild.Value
					childValueEdit = not pChild.GetProperty(simIsFinal)
					childUnit = pChild.Unit
					if childUnit == '-':
						childUnit = None
					childVariability = 'fixed'
					childVariableAttr = ''
					childComment = pChild.comment
					if childComment != '':
						childVariableAttr += 'Description:' + chr(9) + childComment + '\n'
					childVariableAttr += 'Causality:' + chr(9) + 'parameter' + '\n'
					childVariableAttr += 'Variability:' + chr(9) + childVariability  # + '\n'
					if pChild.IsA(simEnumeration):
						childVariableAttr += '\nEnumeration:' + chr(9)
						for a in pChild.Alternatives:
							childVariableAttr += str(a.Value) + '=' + a.Name + ', '
						childVariableAttr = childVariableAttr.rstrip(', ')
					self.variableTree.variable[childRelIdent] = SimulatorBase.TreeVariable(self.structureVariableName(childRelIdent), childValue, childValueEdit, childUnit, childVariability, childVariableAttr)
		elif _isNumeric(dim):
			# Fixed vector dimension
			childTypeIdent = pChild.Type.Ident
			if not childTypeIdent == 'BuiltIn.BaseModel.ProtKind' and not childTypeIdent == 'StateSelect':
				# childRelIdent = pChild.GetRelIdent(doc)
				childRelIdent = pChild.Ident
				if childRelIdent.startswith(docIdentDot):
					childRelIdent = childRelIdent[len(docIdentDot):]
				if (not pChild.Parent == doc and not pObject.Name.find('_base') == 0) or (not childRelIdent == 'iSim' and not childRelIdent == 'tStart' and not childRelIdent == 'tStop'):
					dim = int(dim)
					childValue = pChild.Value
					childValue = re.sub('[\{\}\[\] ]', '', childValue)
					childValue = childValue.replace(';', ',')
					childValueList = childValue.split(',')
					if len(childValueList) == dim:
						childValueEdit = True
						childUnit = pChild.Unit
						if childUnit == '-':
							childUnit = None
						childVariability = 'fixed'
						childVariableAttr = ''
						childComment = pChild.Comment
						if childComment != '':
							childVariableAttr += 'Description:' + chr(9) + childComment + '\n'
						childVariableAttr += 'Causality:' + chr(9) + 'parameter' + '\n'
						childVariableAttr += 'Variability:' + chr(9) + childVariability  # + '\n'
						for i in range(1, dim + 1):
							if _isNumeric(childValueList[i - 1]):
								self.variableTree.variable[childRelIdent + '[' + str(i) + ']'] = SimulatorBase.TreeVariable(self.structureVariableName(childRelIdent + '[' + str(i) + ']'), childValueList[i - 1], childValueEdit, childUnit, childVariability, childVariableAttr)

	def _fillTreeResult(self, pObject, doc, pChild):
		''' Scan a SimulationX result object
		'''
		docIdentDot = doc.Ident + '.'
		dim = pChild.Execute('GetDimension', [])[0]
		if dim == '':
			# Scalar dimension
			# childRelIdent = pChild.GetRelIdent(doc)
			childRelIdent = pChild.Ident
			if childRelIdent.startswith(docIdentDot):
				childRelIdent = childRelIdent[len(docIdentDot):]
			if (not pChild.Parent == doc and not pObject.Name.find('_base') == 0) or (not childRelIdent == 't' and not childRelIdent == 'dt' and not childRelIdent == 'solverInfo' and not childRelIdent == 'lambdaHomotopy' and not childRelIdent == 'lambdaSteadyState'):
				childValue = None
				childValueEdit = False
				childUnit = pChild.Unit
				if childUnit == '-':
					childUnit = None
				if (pChild.GetProperty(simIsDiscrete)):
					childVariability = 'discrete'
				else:
					childVariability = 'continuous'
				childVariableAttr = ''
				childComment = pChild.Comment
				if childComment != '' :
					childVariableAttr += 'Description:' + chr(9) + childComment + '\n'
				childVariableAttr += 'Causality:' + chr(9) + 'state' + '\n'
				childVariableAttr += 'Variability:' + chr(9) + childVariability  # + '\n'
				self.variableTree.variable[childRelIdent] = SimulatorBase.TreeVariable(self.structureVariableName(childRelIdent), childValue, childValueEdit, childUnit, childVariability, childVariableAttr)
		elif _isNumeric(dim):
			# Fixed vector dimension
			# childRelIdent = pChild.GetRelIdent(doc)
			childRelIdent = pChild.Ident
			if childRelIdent.startswith(docIdentDot):
				childRelIdent = childRelIdent[len(docIdentDot):]
			if (not pChild.Parent == doc and not pObject.Name.find('_base') == 0) or (not childRelIdent == 't' and not childRelIdent == 'dt' and not childRelIdent == 'solverInfo' and not childRelIdent == 'lambdaHomotopy'):
				dim = int(dim)
				childValue = None
				childValueEdit = False
				childUnit = pChild.Unit
				if childUnit == '-':
					childUnit = None
				if (pChild.GetProperty(simIsDiscrete)):
					childVariability = 'discrete'
				else:
					childVariability = 'continuous'
				childVariableAttr = ''
				childComment = pChild.Comment
				if childComment != '':
					childVariableAttr += 'Description:' + chr(9) + childComment + '\n'
				childVariableAttr += 'Causality:' + chr(9) + 'state' + '\n'
				childVariableAttr += 'Variability:' + chr(9) + childVariability  # + '\n'
				for i in range(1, dim + 1):
					self.variableTree.variable[childRelIdent + '[' + str(i) + ']'] = SimulatorBase.TreeVariable(self.structureVariableName(childRelIdent + '[' + str(i) + ']'), childValue, childValueEdit, childUnit, childVariability, childVariableAttr)

	def getReachedSimulationTime(self):
		''' Read the current simulation time during a simulation
		'''
		sim = None
		rTime = None
		try:
			if not type(self._doc) is types.NoneType:
				sim = self._doc.Application
				sim.Interactive = False
				if self._doc.SolutionState > simReady:
					rTime = self._doc.Lookup('t').LastValue
		except:
			rTime = None
		finally:
			if not type(sim) is types.NoneType:
				sim.Interactive = True

		return rTime

	def simulate(self):
		''' Run a simulation of a Modelica/SimulationX model
		'''
		sim = None
		try:
			doc = win32com.client.Dispatch(pythoncom.CoUnmarshalInterface(self._marshalled_doc, pythoncom.IID_IDispatch))
			self._marshalled_doc.Seek(0, pythoncom.STREAM_SEEK_SET)
			if not type(doc) is types.NoneType:
				sim = doc.Application
				sim.Interactive = False
				self._simulate_sync(doc)
		except win32com.client.pywintypes.com_error:
			print 'SimulationX: COM error.'
			raise(SimulatorBase.Stopping)
		except:
			print 'SimulationX: Unhandled exception.'
			import traceback
			print traceback.format_exc()
			raise(SimulatorBase.Stopping)
		finally:
			if not type(sim) is types.NoneType:
				sim.Interactive = True

	def _simulate_sync(self, doc):
		simulation = self.integrationSettings

		# Integration settings
		doc.Lookup('tStart').Value = simulation.startTime
		doc.Lookup('tStop').Value = simulation.stopTime
		doc.Lookup('relTol').Value = simulation.errorToleranceRel
		if simulation.errorToleranceAbs is None:
			if not self.config['Plugins']['SimulationX'].has_key('absTol'):
				absTol = simulation.errorToleranceRel
			else:
				absTol = self.config['Plugins']['SimulationX']['absTol']
			doc.Lookup('absTol').Value = absTol
		else:
			doc.Lookup('absTol').Value = simulation.errorToleranceAbs

		ialg = self._availableIntegrationAlgorithms.index(simulation.algorithmName)
		if self._IntegrationAlgorithmHasFixedStepSize[ialg]:
			doc.Lookup('dtMin').Value = simulation.fixedStepSize
		else:
			if not self.config['Plugins']['SimulationX'].has_key('dtMin'):
				dtMin = '1e-010'
			else:
				dtMin = self.config['Plugins']['SimulationX']['dtMin']
			doc.Lookup('dtMin').Value = dtMin
		if simulation.gridPointsMode == 'NumberOf':
			if simulation.gridPoints > 1:
				dtProtMin = (simulation.stopTime - simulation.startTime) / (simulation.gridPoints - 1)
				protKind = 0  # = 'BaseModel.ProtKind.EquidistantTimeSteps'
			else:
				dtProtMin = (simulation.stopTime - simulation.startTime) / 500
				protKind = 0  # = 'BaseModel.ProtKind.EquidistantTimeSteps'
		elif simulation.gridPointsMode == 'Width':
			dtProtMin = simulation.gridWidth
			protKind = 0  # = 'BaseModel.ProtKind.EquidistantTimeSteps'
		elif simulation.gridPointsMode == 'Integrator':
			dtProtMin = 'dtDetect'
			protKind = 3  # = 'BaseModel.ProtKind.MinTimeStepsPrePostEvents'
		doc.Lookup('dtProtMin').Value = dtProtMin
		doc.Lookup('protKind').Value = protKind
		try:
			doc.SolverByName = self._solverByName[simulation.algorithmName]
		except KeyError:
			pass

		for name, newValue in self.changedStartValue.iteritems():
			i = name.find('[')
			if i >= 0 and name.endswith(']'):
				value = doc.Lookup(name[0:i]).Value
				n = name[i:]
				n = re.sub('[\[\]]', '', n)
				if _isNumeric(n):
					n = int(n)
					value = re.sub('[\{\}\[\] ]', '', value)
					value = value.replace(';', ',')
					valueList = value.split(',')
					valueList[n - 1] = newValue
					doc.Lookup(name[0:i]).Value = '{' + ','.join(valueList) + '}'
			else:
				doc.Lookup(name).Value = newValue

		# Build variable tree if empty, e.g. if simulate is called by the Testing plugin
		if not bool(self.variableTree.variable):
			self._fillTree(doc, doc)
			treeWasEmpty = True
		else:
			treeWasEmpty = False

		# Log all parameters and variables
		paramName = list()
		paramUnit = list()
		paramValue = list()
		for name, item in self.variableTree.variable.iteritems():
			pChild = doc.Lookup(name)
			childIsASimVariable = pChild.IsA(simVariable)
			childIsASimParameter = pChild.IsA(simParameter)
			if childIsASimVariable:
				# Result
				try:
					pChild.Protocol = True
				except:
					try:
						pChild.Parent.Results(pChild.Name).Protocol = True
					except:
						pass
			if ((childIsASimParameter or pChild.IsA(simGeneralParameter)) and not childIsASimVariable) or (childIsASimParameter and pChild.GetProperty(simIsInput) and childIsASimVariable):
				# Parameter
				childRelIdent = re.sub(r'\[.*?\]', '', name)
				childUnit = pChild.Unit.encode(locale.getpreferredencoding())
				childValue = pChild.Value
				dim = pChild.Execute('GetDimension', [])[0]
				if dim == '':
					# Scalar dimension
					if not childRelIdent in paramName:
						paramName.append(childRelIdent)
						paramUnit.append(childUnit)
						if childValue == '':
							childValue = 0
						if not _isNumeric(childValue):
							childValue = pChild.Eval()
							if type(childValue) == types.BooleanType:
								if childValue:
									childValue = '1'
								else:
									childValue = '0'
							else:
								childValue = '{0}'.format(childValue)
						paramValue.append(childValue)
				elif _isNumeric(dim):
					# Fixed vector dimension
					dim = int(dim)
					childValue = re.sub('[\{\}\[\] ]', '', childValue)
					childValue = childValue.replace(';', ',')
					childValueList = childValue.split(',')
					if len(childValueList) == dim:
						for i in range(1, dim + 1):
							if _isNumeric(childValueList[i - 1]):
								childCompName = childRelIdent + '[' + str(i) + ']'
								if not childCompName in paramName:
									paramName.append(childCompName)
									paramUnit.append(childUnit)
									childValue = childValueList[i - 1]
									if childValue == '':
										childValue = 0
									paramValue.append(childValue)

		# Start simulation
		doc.Reset()
		time.sleep(0.01)
		doc.Start()

		# Wait till simulation is finished
		while doc.SolutionState < simStopped:
			if self.simulationStopRequest:
				doc.Stop()
				self.simulationStopRequest = False
				raise(SimulatorBase.Stopping)
			time.sleep(0.1)

		# Integration is finished
		if doc.SolutionState == simStopped:
			# Save results in CSV file
			resultFileName = os.path.abspath(simulation.resultFileName).replace('\\', '/')
			ver = self.config['Plugins']['SimulationX']['version']
			canExportDisplayUnit = True
			if ver == 'Iti.Simx36':
				sub_key = r'Software\ITI GmbH\SimulationX 3.6\DataFilter'
			elif ver == 'Iti.Simx37':
				sub_key = r'Software\ITI GmbH\SimulationX 3.7\DataFilter'
			elif ver == 'Iti.Simx38':
				sub_key = r'Software\ESI Group\SimulationX 3.8\DataFilter'
			elif ver == 'Iti.Simx39':
				sub_key = r'Software\ESI Group\SimulationX 3.9\DataFilter'
			elif ver == 'ESI.SimulationX40':
				sub_key = r'Software\ESI Group\SimulationX 4.0\DataFilter'
			else:
				sub_key = r'Software\ITI GmbH\SimulationX 3.5\DataFilter'
				canExportDisplayUnit = False

			try:
				key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, sub_key, 0, winreg.KEY_ALL_ACCESS)
			except WindowsError:
				key = winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, sub_key, 0, winreg.KEY_ALL_ACCESS)
			try:
				frt = winreg.QueryValueEx(key, 'Format')
			except WindowsError:
				frt = (u'%.15lg', winreg.REG_SZ)
			try:
				dec = winreg.QueryValueEx(key, 'Dec')
			except WindowsError:
				dec = (u'.', winreg.REG_SZ)
			try:
				sep = winreg.QueryValueEx(key, 'Separator')
			except WindowsError:
				sep = (u'\t', winreg.REG_SZ)
			try:
				adT = winreg.QueryValueEx(key, 'AddTableName')
			except WindowsError:
				adT = (0, winreg.REG_DWORD)
			try:
				adN = winreg.QueryValueEx(key, 'AddColumnNames')
			except WindowsError:
				adN = (1, winreg.REG_DWORD)
			try:
				adU = winreg.QueryValueEx(key, 'AddColumnUnits')
			except WindowsError:
				adU = (0, winreg.REG_DWORD)
			winreg.SetValueEx(key, 'Format', 0, winreg.REG_SZ, '%.17lg')
			winreg.SetValueEx(key, 'Dec', 0, winreg.REG_SZ, '.')
			winreg.SetValueEx(key, 'Separator', 0, winreg.REG_SZ, ';')
			winreg.SetValueEx(key, 'AddTableName', 0, winreg.REG_DWORD, 0)
			winreg.SetValueEx(key, 'AddColumnNames', 0, winreg.REG_DWORD, 2)
			winreg.SetValueEx(key, 'AddColumnUnits', 0, winreg.REG_DWORD, 1)
			winreg.FlushKey(key)
			if canExportDisplayUnit:
				doc.StoreAllResultsAsText(resultFileName, False)  # Export in displayUnit
			else:
				doc.StoreAllResultsAsText(resultFileName)  # Export in SI-Unit
			winreg.SetValueEx(key, 'Format', 0, winreg.REG_SZ, frt[0])
			winreg.SetValueEx(key, 'Dec', 0, winreg.REG_SZ, dec[0])
			winreg.SetValueEx(key, 'Separator', 0, winreg.REG_SZ, sep[0])
			winreg.SetValueEx(key, 'AddTableName', 0, winreg.REG_DWORD, adT[0])
			winreg.SetValueEx(key, 'AddColumnNames', 0, winreg.REG_DWORD, adN[0])
			winreg.SetValueEx(key, 'AddColumnUnits', 0, winreg.REG_DWORD, adU[0])
			winreg.CloseKey(key)

			# Save parameters in CSV file
			if len(paramName) > 0:
				with open(resultFileName + 'p', 'wb') as csvfile:
					csvwriter = csv.writer(csvfile, delimiter=';')
					csvwriter.writerow(paramName)
					csvwriter.writerow(paramUnit)
					csvwriter.writerow(paramValue)

			self.integrationStatistics.reachedTime = simulation.stopTime
			self.integrationStatistics.nGridPoints = len(doc.Lookup('t').ProtValues)
		elif doc.SolutionState == simFailed:
			print('SimulationX: Simulation error.')

		if treeWasEmpty:
			self.variableTree.variable.clear()

	def getAvailableIntegrationAlgorithms(self):
		''' Returns a list of strings with available integration algorithms
		'''
		return self._availableIntegrationAlgorithms

	def getIntegrationAlgorithmHasFixedStepSize(self, algorithmName):
		''' Returns True or False dependent on the fact,
			if the integration algorithm given by the string algorithmName
			has a fixed step size or not (if not it has a variable step size).
		'''
		return self._IntegrationAlgorithmHasFixedStepSize[self._availableIntegrationAlgorithms.index(algorithmName)]

	def getIntegrationAlgorithmCanProvideStepSizeResults(self, algorithmName):
		''' Returns True or False dependent on the fact,
			if the integration algorithm given by the string algorithmName
			can provide result points at every integration step.
		'''
		return self._IntegrationAlgorithmCanProvideStepSizeResults[self._availableIntegrationAlgorithms.index(algorithmName)]