Planimate as a DLL

From Planimate Knowledge Base
Revision as of 16:54, 27 March 2013 by Rick (talk | contribs)
Jump to navigation Jump to search

Planimate can convert itself into a DLL which can be embedded in other applications, includding dotNET applications using the Planimate dotNET wrapper.

This page documents the use of the Planimate DLL and the win32 sample application.

Creating A DLL

A Planimate DLL can be created in various ways.

Converted Developmen 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

Planimate is not normally distributed as a DLL but this is a build option in its source project and may be useful for debugging.

LoadPL Demo

LoadPL is an application which demonstrates loading 2 Planimate instances (running in their own threads) into the one Win32 application, then commanding them independently from the menu bar, including run/psuse/stop, advance to time/animate and reading data, sending broadcasts and getting callbacks from Planimate generated broadcasts.

LoadPL contains two targets

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
  • 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

API File Summary

The Planimate API and loadPL sample has the following key files.

units.hpp
planidll.hpp

These define data types, export function typedefs and enums used when calling Planimate.

pl5loader.cpp
pl5loader.hpp
planimate5.hpp
planimate5.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.

planidll.hpp

One instance of the pl5loader DLL manages loading and accessing multiple planimate instances. Once the pl5loader.dll is loaded and functions are bound, loading a PL is a matter of

PLHANDLE pl = PLL_Init("PLDLL.dll","",parent_hwnd);

// wait for it to be loaded and running
PLL_WaitRunning(pl);

To call a PL function as defined in planidll.hpp you need to retrieve its pointer and cast it to its typdef. PLL_GetProc() returns a pointer for a given instance. For C, the casting takes the form:

// ((tFunctionType*)PLL_GetProc(plhandle,eFunctionEnum))(function-parameters);

tFunctionType and eFunctionEnum are declared in planidll.hpp and must match.

For example, calling Run to get the model going:

((tPL_Run*)PLL_GetProc(PL,ePL_Run))(PLRUNCMD_Run);

As noted in planidll.hpp, data access functions need to be protected by calls to PLL_SuspendThread() / PLL_ResumeThread().


pl5loader exports

These are functions exported in pl5loader.hpp for the pl5loaderd dll which encapsulates much of the complexity in managing Planimate in its own thread.

planidll exports

These are functions exported in planidll.hpp for a Planimate EXE converted to a DLL. The PL5loader takes care of the loading/unloading of the DLL but you directly call other functions here to read and write to model data structures.

Loading the DLL

Typically you will load the DLL using LoadLibrary() followed by GetProcAddress to bind to the member functions. This is demontrated in the LoadPL SDK demo application and will not be covered in detail here.

Since an EXE converted to a DLL does not have a regular DllMain(), you need to invoke initialisation functions explicitly as follows. This example is for a simplistic single threaded use of the DLL.

  • LoadLibrary() and GetProcAddress() for the functions
  • PL_SetInstance() to set the instance handle
  • PL_Init() to initialise planimate. This can be passed command line arguments to load a model. It also needs a window handle for a window into which Planimate will create a child window for its output.
  • PL_GetSystemInfo() / PL_SetSystemInfo() to configure special options
  • PL_GetBroadcastName() to lookup a broadcast handle
  • PL_SendBroadcast() to send a broadcast (via its handle) to the model
  • PL_Term() when you are finished with the model to deallocate it
  • FreeLibrary() the DLL instance

PL_GetSystemInfo / PL_SetSystemInfo

These enable access to a number of attributes.

PLSI_CLOCK = 0

This read only attribute returns the model run clock during a simulation run. This is the number of seconds since the model's Run Start Date.

PLSI_ADVANCETOTIME = 1

This read only attribute returns the time (measured as for clock) that the model is advancing to. Valid if an advance to time is active.

PLSI_CURRENTPENDING = 2

This returns the number of events currently in the simulation Future Events List.

PLSI_ENGINESTATE = 3

This returns the state of the simulation engine

PLSI_CURRENTFILEVERSION

This returns the version of MDL files that the Planimate DLL will save

PLSI_OLDESTFILEVERSION = 5

This returns the oldest file ersion that the Planimate DLL can load and run.

PLSI_LOADEDFILEVERSION = 6

This returns the version of the loaded model, as appears next to "V" near the beginning of the MDL file.

PLSI_DLLVERSION = 7

This returns the version of the DLL API. As of January 2013 this is 2.

PLSI_BATCH = 8

This is a read/write attribute. When 1 it indicates that Planimate is running in "batch" mode where no screen updates or animation delays are performed and no windows event dispatching occurs.

This property can be set on and off however If the "/BATCH" command line option was used to initialise Planimate, Planimate will remain in batch mode.

A model in batch mode must not pop up dialogs or panels. The s.Batch system attribute can be used by the modeller to test this state.

It will be desireable to hide the parent window used to initialise Planimate in batch mode as it will not be updated or redrawn.

PLSI_MESSAGELOCK = 9

As a standalone application, when Planimate is running a model, the main thread is in a loop in which Planimate processes events, performs display updates, performs timing delays (to keep animation at a consistent speed) and also dispatches windows events to keep the process responsive to paint messages and user interaction with the running model.

When embedded as a DLL, it is possible to run the model in its own thread whilst the main thread continues independently. In such cases it is important that the Planimate thread does not dispatch windows messages as well as the main thread.

Setting this value to 1 will prevent the simulation loop from dispatching windows events.