Skip to content

Commit

Permalink
Add GUI and callbacks in simulate_fmu(). Keep start values as literal…
Browse files Browse the repository at this point in the history
…s. Add ScalarVariable.initial and set default values for causality, variability and initial. Add extras_require in setup.py.
  • Loading branch information
t-sommer committed Jan 12, 2018
1 parent df3ace8 commit 2789af2
Show file tree
Hide file tree
Showing 50 changed files with 3,117 additions and 108 deletions.
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,16 @@ install:
- conda info -a

# create the conda environment
- conda create -q -n test-env python=$PYTHON_VERSION
- conda create -q -n test-env -c anaconda python=$PYTHON_VERSION dask lxml matplotlib numpy pathlib pyqt pyqtgraph requests
- source activate test-env

# build the wheel
- python setup.py bdist_wheel --universal

# install the wheel and its dependencies
# install the wheel
- |
for f in dist/FMPy-*.whl; do
pip install $f
pip install $f --no-deps
done
script:
Expand Down
10 changes: 9 additions & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FMPy
====

Copyright (c) 2017 Dassault Systemes. All rights reserved.
Copyright (c) 2017-2018 Dassault Systemes. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Expand Down Expand Up @@ -31,6 +31,14 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


The icons and materials included in the project are released under the Creative
Commons Attribution 4.0 International Public License (CC BY 4.0) available on
https://creativecommons.org/licenses/by/4.0/.

Some icons are based on the free GLYPHICONS (https://glyphicons.com/) by Jan
Kovarik released under the Creative Commons Attribution 3.0 Unported (CC BY 3.0)
license available on https://creativecommons.org/licenses/by/3.0/.


SUNDIALS
========
Expand Down
6 changes: 3 additions & 3 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ install:
- conda config --set always_yes yes --set changeps1 no
- conda update -q conda
- conda info -a
- conda create -q -n test-env python=%PYTHON_VERSION%
- conda create -q -n test-env -c anaconda python=%PYTHON_VERSION% dask lxml matplotlib numpy pathlib pyqt pyqtgraph pywin32 requests
- activate test-env

# build the wheel
- python setup.py bdist_wheel --universal

# install the wheel and its dependencies
- for %%f in (dist\FMPy-*.whl) do pip install %%f
# install the wheel
- for %%f in (dist\FMPy-*.whl) do pip install %%f --no-deps

test_script:
- cd tests
Expand Down
14 changes: 9 additions & 5 deletions fmpy/command_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ def main():
import textwrap

description = """\
Simulate an FMU
Example:
> python -m fmpy.simulate Rectifier.fmu
Validate and simulate Functional Mock-up Units (FMUs)
Get information about an FMU:
fmpy info Rectifier.fmu
Simulate an FMU:
fmpy simulate Rectifier.fmu --show-plot
"""

parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
Expand Down
46 changes: 30 additions & 16 deletions fmpy/fmi1.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ def __str__(self):
self.nextEventTime)


def logger(component, instanceName, status, category, message):
if status == fmi1Warning:
print('[WARNING]', message)
elif status > fmi1Warning:
print('[ERROR]', message)
def printLogMessage(component, instanceName, status, category, message):
""" Print the FMU's log messages to the command line (works for both FMI 1.0 and 2.0) """

label = ['OK', 'WARNING', 'DISCARD', 'ERROR', 'FATAL', 'PENDING'][status]
print("[%s] %s" % (label, message))


def allocateMemory(nobj, size):
Expand All @@ -95,14 +95,6 @@ def stepFinished(componentEnvironment, status):
pass


callbacks = fmi1CallbackFunctions()
callbacks.logger = fmi1CallbackLoggerTYPE(logger)
callbacks.allocateMemory = fmi1CallbackAllocateMemoryTYPE(allocateMemory)
callbacks.freeMemory = fmi1CallbackFreeMemoryTYPE(freeMemory)
#callbacks.stepFinished = fmi1StepFinishedTYPE(stepFinished)
callbacks.stepFinished = None


class _FMU(object):
""" Base class for all FMUs """

Expand Down Expand Up @@ -131,6 +123,9 @@ def __init__(self, guid, modelIdentifier, unzipDirectory, instanceName, logFMICa

self.component = None

self.callbacks = None
" Reference to the callbacks struct (to save it from GC)"

def freeLibrary(self):
# unload the shared library
freeLibrary(self.dll._handle)
Expand Down Expand Up @@ -348,10 +343,19 @@ def __init__(self, **kwargs):
self._fmi1Function('FreeSlaveInstance', ['component'], [fmi1Component], None)

def instantiate(self, mimeType='application/x-fmu-sharedlibrary', timeout=0, visible=fmi1False,
interactive=fmi1False, functions=callbacks, loggingOn=fmi1False):
interactive=fmi1False, functions=None, loggingOn=fmi1False):

fmuLocation = pathlib.Path(self.unzipDirectory).as_uri()

if functions is None:
functions = fmi1CallbackFunctions()
functions.logger = fmi1CallbackLoggerTYPE(printLogMessage)
functions.allocateMemory = fmi1CallbackAllocateMemoryTYPE(allocateMemory)
functions.freeMemory = fmi1CallbackFreeMemoryTYPE(freeMemory)
functions.stepFinished = None

self.callbacks = functions

self.component = self.fmi1InstantiateSlave(self.instanceName.encode('UTF-8'),
self.guid.encode('UTF-8'),
fmuLocation.encode('UTF-8'),
Expand Down Expand Up @@ -444,10 +448,20 @@ def __init__(self, **kwargs):
[fmi1Component],
None)

def instantiate(self, functions=callbacks, loggingOn=fmi1False):
def instantiate(self, functions=None, loggingOn=fmi1False):

if functions is None:
functions = fmi1CallbackFunctions()
functions.logger = fmi1CallbackLoggerTYPE(logger)
functions.allocateMemory = fmi1CallbackAllocateMemoryTYPE(allocateMemory)
functions.freeMemory = fmi1CallbackFreeMemoryTYPE(freeMemory)
functions.stepFinished = None

self.callbacks = functions

self.component = self.fmi1InstantiateModel(self.instanceName.encode('UTF-8'),
self.guid.encode('UTF-8'),
functions,
self.callbacks,
loggingOn)

def setTime(self, time):
Expand Down
31 changes: 12 additions & 19 deletions fmpy/fmi2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pathlib
from ctypes import *
from . import free, calloc
from .fmi1 import _FMU
from .fmi1 import _FMU, printLogMessage


fmi2Component = c_void_p
Expand Down Expand Up @@ -45,15 +45,6 @@
fmi2Terminated = 3


def logger(componentEnvironment, instanceName, status, category, message):
if status == fmi2Warning:
print('[WARNING]', message)
elif status > fmi2Warning:
print('[ERROR]', message)
else:
print('[INFO]', message)


def allocateMemory(nobj, size):
return calloc(nobj, size)

Expand All @@ -74,13 +65,6 @@ class fmi2CallbackFunctions(Structure):
('stepFinished', fmi2StepFinishedTYPE),
('componentEnvironment', fmi2ComponentEnvironment)]

callbacks = fmi2CallbackFunctions()
callbacks.logger = fmi2CallbackLoggerTYPE(logger)
callbacks.allocateMemory = fmi2CallbackAllocateMemoryTYPE(allocateMemory)
#callbacks.stepFinished = fmi2StepFinishedTYPE(stepFinished)
callbacks.freeMemory = fmi2CallbackFreeMemoryTYPE(freeMemory)
#callbacks.componentEnvironment = None


class fmi2EventInfo(Structure):

Expand Down Expand Up @@ -207,18 +191,27 @@ def w(*args, **kwargs):

setattr(self, fname, w)

def instantiate(self, visible=False, loggingOn=False):
def instantiate(self, visible=False, callbacks=None, loggingOn=False):

kind = fmi2ModelExchange if isinstance(self, FMU2Model) else fmi2CoSimulation
resourceLocation = pathlib.Path(self.unzipDirectory, 'resources').as_uri()
visible = fmi2True if visible else fmi2False

if callbacks is None:
callbacks = fmi2CallbackFunctions()
callbacks.logger = fmi2CallbackLoggerTYPE(printLogMessage)
callbacks.allocateMemory = fmi2CallbackAllocateMemoryTYPE(allocateMemory)
callbacks.freeMemory = fmi2CallbackFreeMemoryTYPE(freeMemory)

self.callbacks = callbacks

loggingOn = fmi2True if loggingOn else fmi2False

self.component = self.fmi2Instantiate(self.instanceName.encode('utf-8'),
kind,
self.guid.encode('utf-8'),
resourceLocation.encode('utf-8'),
byref(callbacks),
byref(self.callbacks),
visible,
loggingOn)

Expand Down
25 changes: 25 additions & 0 deletions fmpy/gui/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
""" This package contains the graphical user interface based on PyQt5 """


def compile_resources():
""" Compile the .ui and .qrc files if they are available and newer than the compiled .py files """

import os

p = os.path.dirname(__file__)

mw_ui = os.path.join(p, 'forms', 'MainWindow.ui')
mw_py = os.path.join(p, 'generated', 'MainWindow.py')

if os.path.isfile(mw_ui):
if not os.path.isfile(mw_py) or os.path.getmtime(mw_ui) > os.path.getmtime(mw_py):
print("UIC'ing %s" % mw_ui)
os.system('pyuic5 %s -o %s --import-from .' % (mw_ui, mw_py))

icons_qrc = os.path.join(p, 'icons', 'icons.qrc')
icons_rc_py = os.path.join(p, 'generated', 'icons_rc.py')

if os.path.isfile(icons_qrc):
if not os.path.isfile(icons_rc_py) or os.path.getmtime(icons_qrc) > os.path.getmtime(icons_rc_py):
print("RCC'ing %s" % icons_qrc)
os.system('pyrcc5 %s -o %s' % (icons_qrc, icons_rc_py))
Loading

0 comments on commit 2789af2

Please sign in to comment.