Planimate calling dotNET

From Planimate Knowledge Base
Jump to navigation Jump to search

This describes Planimate's interface to the dotNET environment which gives access to a wide variety of modern UI elements, table editing and graphing libraries as well as libraries that easily interface to web APIs and databases.

PLCLR

PLCLR.DLL is a Planimate callable DLL which provides the interface to dotNET's Common Language Runtime. You'll see its name in all the calls to the API. In future versions the calls will be integrated and appear as internal Planimate routines.

PLCLR expects you to point it to dotNET DLLs (called Assemblies). All DLLs involved must be in the same folder as the MDL during editing and the PBA EXE for a PBA. In addition, users will need Visual Studio 2013 runtime libraries and dotNET 4.5 installed. These can be obtained from Microsoft. When shipping a PBA, an installer should check for these and install as required.

Creating Instances Of Classes

You start by creating an instance of a class written in C# (Windows Forms) or c# / XAML (WPF). The examples below assume you have access to the TestCLR model and WPFTest sample C# project.

To create an instance you need the DLL name and the class name including namespace, eg:

p.handle = PLCLR:New(“WPFTest.DLL”,”WPFTest.MainWindow”)

p.handle is a number that you'll need to work with the object. You can also use a table cell to store it.

For future maintenance, its good form to put the strings into portal text attributes, literal text is used here to make the example clearer.

You should test that p.handle is > 0 after a New. If its <= 0 an error occurred, in which case take away the negative (0 – p.handle) and you have the PLCLR error code.

When you've finished using an object you should dispose of it, though in some instances an object will persist the entire lifetime of the PBA so this is not necessary.

p.result = PLCLR:Del(p.handle)

PLCLR will recycle handle values for deleted objects.

Calling Methods

Lets say you c# class has a method:

public void SayHello()
{
 MessageBox.Show(“Hello”);
}

You can call a method as follows:

p.result = PLCLR:Call(p.handle,”SayHello”)

Note the result of the call is always the result of PLCLR's operation, return values of methods are not used. Instead, you can pass parameters by value or reference, eg:

public void ComplexCalculation(int value_in,out double value_out)
{
 value_out = value_in + 0.5;
}

In Planimate you use

p.result = PLCLR:Call(p.handle,"ComplexCalculation(p.value1,p.value2)

Since value_out is declared as an "out", Planimate will set p.value p.value2 to the value calculated in the method.

Properties

If in your c# class you have a public property with a getter/setter eg:

String username  {get; set;}

Then you can use PLCLR's Get and Set operations to access them. With p.name a portal attribute in text format, you can use:

p.name = PLCLR:get("username");

and

p.result = PLCLR:set("uername",p.name);

This also works for numbers, see the next section.

Data Types

PLCLR has an extensible translation system to map between Planimate's simple data types and c#'s types. It matches the type of data passed in a parameter from Planimate with the type in a method's parameter or class property and chooses a translation as follows.

Conversions work for inputs to a method and outputs ("ref" or "out" parameters in C#) unless otherwise noted.


Parameter/Property Type Translation
dotNET Planimate Notes
double value (portal,routine,item/system attribute), table cell
int,uint value,cell Nearest Integer used
bool value,cell Nearest Integer then 0=false, nonzero=true
string Text attribute or cell Not label but you can use string expression to convert any format to text
double[], int[], uint[], boolean[] t.table.Column() Conversions above applied per cell
double[], int[], uint[] t.tablep[] Just column one sent with less overhead than Column(). Conversions above applied per cell. Row number changes on return supported.
string[] t.table.Column() Column must be a text column, currently cannot return data.
double[][] t.table[] Entire table's raw numbers, as array of columns, ie: array of array of numbers
Dictionary<String,int> l.labellist Label list mapped so elements are index values, keyed by name
Dictionary<int,String> l.labellist Label list mapped so elements are strings, keyed by their index value
string[] l.labellist Label list names sent as a array of Strings. In order of index but the index numbers are lost. If returned, a new list is created indexed from one. The labels must not be repeated.

Dates And Times

Currently all Planimate date/times are sent as the number of seconds since the model run start date. A future Datetime interface may be added. The PLEngine module can assist with conversion.

Modal Dialog

A WPF Window can be made to "go modal" and show a modal dialog by calling ShowDialog(). Whilst "Call" could be used to call this directly, you cannot repeat use of a model dialog, you would need to create a New instance, set properties, Call ShowDialog (or a method you create which does so), read back any properties you want back from the dialog then Delete the instance.

PLCLR provides a Dialog() operation which reduces this all to one line in Planimate.

p.result = PLCLR:Dialog(ddllname,classname,... up to 8 optional parameters...)

Like New, this loads an assembly and creates an instance of a class. It then calls a function that you must provide in the class called DoDialog(), passing the parameters which can be by value as well as "ref" or "out". It then disposes of the instance.

The DoDialog in the WPFTest sample (file MainWindow.xaml.cs) demonstrates using the parameters to set up fields in a simple dialog and to pass back information.

The Dialog() operation also calls another function in the class, if present, SetOwner(). This is used to ensure the model dialog knows Planimate is its owning window. You should include the sample SetOwner() you'll find in WPFTest in your class.

Modeless Dialog

A WPF Window has a Show() method that causes it to be displayed without becoming modal. Planimate can call this to show the window and enable interaction with the controls on it. However its important that the popup know its owner and this can be done by calling SetOwner before Show().

p.result = PLCLR:SetOwner(p.handle)
p.result = PLCLR:Call(p.handle,"Show")

This requires the SetOwner() handler be present in the C# class, as described in modal dialog.

The window styles configured in the XAML and c# code control the title bar, resizing etc. The TestWPF sample demonstrates dynamically changing the style of the window to enable resizing and the titlebar.

Embedded Window

A modeless dialog can be embedded into a Planimate panel, becoming in effect a viewport on a dotNET class. Anchoring and resizing is fully supported, if the dotNET window includes a control that supports dynamic layout (eg: Grid) it will adapt to resizes.

In Planimate all that is required to put a popup into a Window paint object is to pass the paint object id to PLCLR's SetOwner:

p.result = PLCLR:SetOwner(p.handle,l._Paint_objects[MyWindow])

An additional function is required in the C# code, SetChild(). PLCLR calls this instead of SetOwner() when a paint object label is included. Refer to the WPFTest sample for its implementation. Please keep in mind this function may change with subsequent PLCLR releases.

Exceptions

PLCLR wraps all calls in an exception handler which displays a popup with the exception details and returns an error code in p.result, the objective being to prevent faulting Planimate. C# code can catch exceptions instead if different behaviour is desired. Care will be needed particularly with modeless dialogs.

Node DEBUG builds of PLCLR are set up not to trap all exceptions, if you are working with a debug build you likely have the source and can change this in PLCLR.cpp

PLCLR Error codes

The following error codes are returned in operations which return a "p.result". Note that "New" returns errors as negative numbers and succesful handles as positive numbers.


PLCLR Ertor Codes
Enum Value Meaning
PLDLLPROC_OK 0 Success. Note 0 never returned by New(), it returns > 0 for a handle or < 0 for an error code which is negative of the codes below.
PLDLLPROC_FILEERROR 1 Error opening DLL file (could be dotNET version not installed)
PLDLLPROC_CLASSERROR 2 Class not located in DLL - have you included the namespace?
PLDLLPROC_METHODERROR 3 Error locating named method in class
PLDLLPROC_PROPERTYERROR 4 Error locating named property in class
PLDLLPROC_HANDLEERROR 5 Error in passed instance handle (was it allocated with New() and subsequently Deleted()?
PLDLLPROC_OPIDERROR 6 PLDLL call failure - unrecognised operation (coding error in PLCLR)
PLDLLPROC_DATAERROR 7 Bad data/parameter (incompatible data type to what method expercts
PLDLLPROC_DLLERROR 8 Unexpected error in PLCLR
PLDLLPROC_EXCEPTION 9 Exception caught - see error popup for details

Callbacks

Beyond passing parameters, PLCLR will provide an API to C# virtually identical to the Planimate-As-A-DLL API, enabling managed code to call back Planimate to get/set data.

This will be particularly useful for modeless UIs.

TBD: merging PL Call DLL callbacks with PL as a DLL callbacks.