Planimate as a DLL: Difference between revisions
No edit summary |
mNo edit summary |
||
Line 182: | Line 182: | ||
==== PLSI_ADVANCETOTIME ==== | ==== PLSI_ADVANCETOTIME ==== | ||
This returns the time (measured as for clock) that the model is advancing to. | This returns the time (measured as for clock) that the model is advancing to. If set and the time is in the future to the current simulation clock, the simulation will advance to the time if running. If paused, the advance will occur when the run is continued.<br> | ||
The engine must be in MD_PAUSED or MD_SIMULATE when using this otherwise it returns 0 and any setting is ignored.<br> | |||
==== PLSI_CURRENTPENDING ==== | ==== PLSI_CURRENTPENDING ==== | ||
Line 190: | Line 192: | ||
==== PLSI_ENGINESTATE ==== | ==== PLSI_ENGINESTATE ==== | ||
This returns the state of the simulation engine<br> | This returns the state of the simulation engine as defined in ePLMode enum:<br> | ||
<pre>enum ePLMode | |||
{ | |||
MD_OBJECT - = 0, // editing objects or user mode with engine stopped | |||
MD_FLOWEDIT, // flow or interaction edit mode (flow editor has submode) | |||
MD_PAINT, // editing paint object layer | |||
MD_SIMULATE, // engine started and model is running | |||
MD_PAUSED // engine started and model is paused | |||
}; | |||
</pre> | |||
==== PLSI_CURRENTFILEVERSION ==== | ==== PLSI_CURRENTFILEVERSION ==== | ||
Line 206: | Line 216: | ||
==== PLSI_DLLVERSION ==== | ==== PLSI_DLLVERSION ==== | ||
This returns the version of the DLL API. As of | This returns the version of the DLL API. As of December 2013 this is 4.<br> | ||
==== PLSI_PAUSEAFTERADVANCE<br> ==== | |||
When this is set, the run engine will pause after an advance-to-time instead of continuing with on screen animation. The run engine must be in MD_SIMULATE or MD_PAUSED state otherwise this returns 0 and setting is ignored.<br> | |||
<br> | |||
=== Broadcasts === | === Broadcasts === |
Revision as of 00:43, 26 December 2013
Planimate can convert itself into a DLL which can be embedded in other applications. This is achieved using the Planimate-as-a-DLL API (loadpl) which is made available to licensed Planimate users.
Planimate has been successfully incorporated into dotNET applications using the Planimate dotNET wrapper.
This page documents the use of the Planimate DLL and the win32 sample application loadpl.
Creating A DLL
A Planimate DLL can be created in various ways.
Converted Planimate EXE
A Planimate EXE can be make itself into a DLL by running it with the command line option /MAKEDLL. If the original EXE is called planimate.exe, this creates planimate.dll which can then be loaded by another application and commanded to load a model.
PBA Compiled To DLL
When compiling a Planimate Based Application, the Standalone EXE Creation dialog includes the option to "Make A DLL as well". This option requires an Open PBA Level 4 License and selection of the Unkeyed Standalone EXE Type.
Directly Compiled By InterDynamics
Planimate is not normally distributed as a DLL but this is a build option in its source project and may be useful for debugging.
Planimate API
The Planimate API is distributed as a Visual Studio project containing two targets which together form an application which demonstrates loading/running two instances of Planimate within a simple win32 container application.
Complete source to the two targets is provided.
pl5loader DLL
This implements a loader which manages the creation of separate Planimate instances, each running in a thread of its own. The thread management requires great care, this loader encapsulates the complexity into a simple C API which is defined in pl5loader.hpp.
LoadPL EXE
This is a simple Win32 C++ application which demonstrates using pl5loader to load/run two instances of Planimate.
The LoadPL.cpp sample includes demonstrations of:
- Start/stop/pause/close PL using the PL_Run() function
- Re-loading a model in a given PL instance (without unloading the instance)
- Re-loading an entire instance
- Switching between advance to time / animation (with ETA dialog)
- Setting data in a model
- Broadcasting to a model
- Broadcast callbacks from model
Due to the way LoadLibrary() operates, it is necessary that each Planimate instance be loaded from a separate DLL file, even though the DLLs may be identical. In this example demo.dll and demo2.dll are standard Planimate EXEs which have been converted into DLLs by running them with the /makedll command line option.
In your own application you should convert your version of Planimate (which most likely will be newer than the example in this API) to a DLL. This is best done using the "Make A DLL as well" option when creating a Planimate Based Application.
API File Summary
The Planimate API and loadPL sample has the following key files.
units.hpp |
These define data types, export function typedefs and enums used when calling Planimate. The exported function names are as for the typedefs without the preceeding 't', eg: tPL_LoadModel() is exported as PL_LoadModel(). |
pl5loader.cpp |
These build DLL pl5loader.dll which implements the multi-threaded management of multiple planimate dll instances. |
loadpl.cpp resource.h loadpl.rc stdafx.cpp |
These build EXE loadpl.exe which demonstrates loading two Planimate instances using the pl5loader.dll. |
demo.dll demo2.dll |
These are Planimate developer EXEs converted into DLLs using the /makedll command line option and are loaded by loadpl. |
demo.mdl demo.db |
The test model loaded by the loadpl sample application. |
matmult.dll matmult64.dll |
A planimate callable DLL which the demo.mdl test model uses to demonstrate how Planimate-as-a-DLL can call other DLLs. |
loadpl.vcproj |
Visual Studio 2008 project which builds both pl5loader.dll and loadpl.exe targets. 32 and 64 bit targets are supported. |
Usage walkthrough
This documents loading Planimate-as-a-DLL using the pl5loader DLL.
If you choose to load Planimate-as-a-DLL directly, you must take great care in the setup and managegment of the Planimate thread. Use Planimate5.cpp as a reference as any deviation from the procedure may lead to hard to replicate multi-threading bugs, race conditions, lock ups etc.
Pre-requisites
You need either a Planimate application converted to a DLL (Make A DLL as well in the PBA compiler) or a Planimate modeller EXE converted to a dLL (use the /MAKEDLL command line option).
Firstly, your code should #include pl5loader.hpp.
You will need a HWND (window handle) of the parent window into which Planimate will create a child window which completely fills the parent window. Planimate uses its own child window internally and this child window will have its message queue in Planimate's thread instead of the thread of the parent window.
Initialisation
You use LoadLibrary() to load the pl5loader DLL and bind to its functions (as in loadpl.cpp:InitPLLoader()). Call PLL_Init() with the path to the Planimate DLL, command line arguments ("/debugfile" enables Planimate to create the planimat.dbg file) and the parent window handle.
PLHANDLE pl = PLL_Init(dll_name,"/debugfile",hwnd);
This will return a PLHANDLE object which you use in further API calls. This is NOT a win32 HANDLE.
Before you work with this handle, you must call PLL_WaitRunning(handle,60000). This waits until the PL instance is ready (model data loaded for a PBA). You might do this after having initiated a number of PLL_Init() operations. the 60000 sets a timeout in ms in case something goes very wrong. Increase it for a slow to load PBA. If PLL_Init() fails, it returns NULL.
At this stage Planimate is loaded. If you passed a PBA DLL, it will have the model loaded but still stopped. If you specified an PL (editing) DLL, you will want to load a model. This requires calling Planimate directly and is achieved via the pl5loader DLL as follows:
((tPL_LoadModel*)PLL_GetProc(pl,ePL_LoadModel))(modelname,NULL);
This will be explained in a section below.
Suspend/Resume of Planimate
Since Planimate is running in its own thread, data corruption can occur if you read or write data which a running model may be simultaneously accessing, even if the model is written to anticipate such changes in the data. The pl5loader DLL provides functions PLL_SuspendThread() and PLL_ResumeThread() which are used to temporarily suspend Planimate from running the model. These should be wrapped around data access functions, as documented in planidll.hpp.
Calling Planimate API functions
The pl5loader DLL does not directly implement all the calls supported by Planimate itself (and defined in planidll.hpp). It does bind to all the functions and gives access to function pointers via the ePLProcs enum defined in planidll.hpp.
For example, reading a running model's run clock involves calling planimate function:
PL_GetSystemInfo(PLSI_CLOCK).
To do this, you first need the function pointer for PL_GetSystemInfo() for that loaded instance of Planimate (note the function pointers differ per loaded instance). You get this from the loader using PLL_GetProc() and the corresponding function type and enum defined in planidll.hpp:
tPL_GetSystemInfo * PL_GetSystemInfo = (tPL_GetSystemInfo*)PLL_GetProc(pl,ePL_GetSystemInfo);
Now you have the function pointer, you can call it:
double clock_time = PL_GetSystemInfo(PLSI_CLOCK);
In the loadpl sample, this two step process is often shortened into one line which gets the proc pointer typecasts it and calls it:
double clock_time = ((tPL_GetSystemInfo*)PLL_GetProc(pl,ePL_GetSystemInfo))(PLSI_CLOCK);
Run Control
The PL_Run() function enables control of the Planimate run engine including starting, pausing, stopping and closing the Planimate instance.
To get a newly loaded model running, call:
((tPL_Run*)PLL_GetProc(pl,ePL_Run))(PLRUNCMD_Run);
This will set the model running. The host application does not need to do anything else since Planimate runs in its own thread, keeps its window updated and accepts any input when its window has focus.
The only requirement is that the host application continue to process its own window events since its child Planimate window may send it messages and not processing them could cause a deadlock.
Terminating Planimate
The host application can use the loader to terminate and unload a Planimate instance:
PLL_Term(pl);
This gracefully stops the model, deletes it from memory, stops the thread and unloads the DLL from memory.
PL5Loader Exported Functions
The functions exported by the pl5loader DLL greatly simplify dealing with Planimate5 instances running in their own threads.
See pl5loader.hpp for function typedefs, comments and notes on the exported functions. See loadpl.cpp for use of LoadLibrary() and GetProcAddress() to bind to the functions.
Planimate DLL Exported Functions
Each Planimate DLL instance exports functions with types declared in planidll.hpp. If you are using the PL5Loader loader DLL (highly recommended) you do not need to directly initialise Planimate or manage the thread it runs in. You get access to the DLL functions using the loader's PLL_GetProc() function.
If you want to manage Planimate directly, you must follow the technique in pl5loader.cpp / planimate5.cpp, particularly the thread synchronisation.
Refer to planidll.hpp for function typedefs, comments and basic notes. Material here assumes familiarity with the planidll.hpp header.
PL_GetSystemInfo / PL_SetSystemInfo
These enable access to a number of attributes.
PLSI_CLOCK
This returns the model run clock during a simulation run. This is the number of seconds since the model's Run Start Date.
PLSI_ADVANCETOTIME
This returns the time (measured as for clock) that the model is advancing to. If set and the time is in the future to the current simulation clock, the simulation will advance to the time if running. If paused, the advance will occur when the run is continued.
The engine must be in MD_PAUSED or MD_SIMULATE when using this otherwise it returns 0 and any setting is ignored.
PLSI_CURRENTPENDING
This returns the number of events currently in the simulation Future Events List.
PLSI_ENGINESTATE
This returns the state of the simulation engine as defined in ePLMode enum:
enum ePLMode { MD_OBJECT - = 0, // editing objects or user mode with engine stopped MD_FLOWEDIT, // flow or interaction edit mode (flow editor has submode) MD_PAINT, // editing paint object layer MD_SIMULATE, // engine started and model is running MD_PAUSED // engine started and model is paused };
PLSI_CURRENTFILEVERSION
This returns the version of MDL files that the Planimate DLL will save
PLSI_OLDESTFILEVERSION
This returns the oldest file ersion that the Planimate DLL can load and run.
PLSI_LOADEDFILEVERSION
This returns the version of the loaded model, as appears next to "V" near the beginning of the MDL file.
PLSI_DLLVERSION
This returns the version of the DLL API. As of December 2013 this is 4.
PLSI_PAUSEAFTERADVANCE
When this is set, the run engine will pause after an advance-to-time instead of continuing with on screen animation. The run engine must be in MD_SIMULATE or MD_PAUSED state otherwise this returns 0 and setting is ignored.
Broadcasts
You can broadcast an item into a Planimate model using PL_SendBroadcast(). You can include tuple data using PL_SendBroadcastTuple().
You can send a silent broadcast (useful when Planimate is paused) which does not visibly continue the model run using PL_SendBroadcastBG() and PL_SendBroadcastTupleBC().
Planimate must be suspended (PL_SuspendThread()) before sending any broadcasts.
Broadcast Callbacks
You can arrange for Planimate to call back a function you provide using PL_RegisterBroadcastCallback(). This requires a broadcast handle which you can retrieve using PL_GetBroadcastName().
The callback function must conform to Win32 CALLBACK linkage and be declared match tPL_BroadcastCallback.
When using the PL5Loader, it is useful to pass the PLHANDLE as the userdata. This is passed back in the callback. Or you can pass your own management structure to enable you to track which Planimate instance has sent the broadcast, if necessary.
Data Access
The application using Planimate-as-a-DLL can access data objects (Attributes, LabelLists, Tables) which have been registered in the model's _Data_Objects label list. The PL_GetDataObjectName() function is useful if you know the name of the data object. This returns a PLDataObject handle.
Once you have a PLDataObject handle, you can use the appropriate functions for that object type, for example for an Attribute you would use PL_GetAttValue(). PL_SetAttValue() for numeric access and PL_GetAttText() and PL_SetAttText() for text formatted attributes.
Data formatting Services
Numerical data in Planimate including times is set/read as double values. You can access Planimate's data parsing/formatting routines using PL_StringToValue() and PL_ValueToString(). These take a format type parameter which are defined in the eTFUnit enum in file units.hpp.
You can query the display name of Planimate's different formats using PL_FormatName(). Note that these names could change in future versions of Planimate.