SimpleDataType
Description
The SimpleDataType class is at the heart of all FlexSim objects. Any node with any kind of object data is ultimately associated with an instance of SimpleDataType. It is also the parent class of the CouplingDataType.
SimpleDataType objects can hold multiple variables as a single node in the tree. They do not, however, respond to any of the FlexSim events (such as stop, reset, or start) or to any user events, like clicking. This allows them to have very low memory requirements. They are generally used as part of a larger object to hold data that can be saved in the tree.
treenode holder
The only member variable defined by SimpleDataType is holder, a pointer to the node in the tree that references this object. It is used by every child class for this purpose.
Direct Child Classes
Class | Description |
---|---|
ObjectDataType | The parent of FlexsimObject (the core of all library objects) |
CouplingDataType | Provides node to node pointer capability. |
FlexSimEvent | The base class for event list event that FlexSim can use. |
Implementation
Virtual Methods
Method | Description |
---|---|
void bind() | Binds the variables of the class so that they are visible within FlexSim and so that they can be saved. (Required) |
void bindEvents() | Binds the events of the class. See Bound Events for more information. |
void bindStatistics() | Binds the statistics of the class. See Bound Statistics for more information. |
const char* getClassFactory() | Returns the name of the class to FlexSim so that objects can be instantiated properly.(Required) |
char* toString(int verbose) | Returns the string that is displayed within FlexSim. (Optional) |
bind()
The bind() method is called at various times. Its behavior depends on the current bind mode. bind() will be fired with different bind modes depending on what FlexSim needs to do. While not essential to implementing a proper bind() method, you can explicitly get the bind mode within your bind() method with getBindMode(). The different bind modes are as follows:
- SDT_BIND_ON_LOAD - When the node's state is loaded from the tree (model load, DLL reconnect, etc.)
- SDT_BIND_ON_SAVE - When the node is saved to the tree (model save, DLL disconnect, etc.)
- SDT_BIND_ON_CREATE - When a new instance of the SDT is created using nodeaddsimpledata() (not available in FlexScript)
- SDT_BIND_ON_DISPLAY - When the node is displayed in the tree
- SDT_BIND_SET_VALUE - As part of the application command setsdtvalue, allowing you to set the value of a class member through flexscript.
- SDT_BIND_GET_VALUE - As part of the application command getsdtvalue, allowing you to set the value of a class member through flexscript.
Save/Load - When the node is being saved, bind() creates subnodes for each variable in the object; the resulting structure is then saved. When the node is loaded, bind() takes the values from the saved structure and stores them in the object. A separate part of the loading process deletes the structure nodes.
The following images show how this works. They involve a Dot class, which extends the SimpleDataType class. The Dot class has the variables x, y, z, and width. Note that the variables x, y, and z are bound as "persistent" subnodes, meaning the node named "x" will persist as a subnode even after the load phase. Also, persistent subnodes are "bound" to the value in the class member, so that if you change the value of the node, the class member's value will change automatically.
The code for the Dot class is in the Example section of this document. The following image shows how an instance of Dot looks in the tree. Note that all its values are displayed, and that x is a subnode.
This image shows the saved structure of the same object. Each variable that was not previously a node is now a node in the sdt::attributetree.
When the node is loaded, the values are taken out of the object's subnodes and are used to re-create the object. In this way, the state of the object can be saved at any time.
nodeaddsimpledata() - This is a function available within the module source code. It is usually called as follows:
nodeaddsimpledata(someTreenode, new SimpleDataTypeDerivative, 1)
This call sets the data pointer on some tree node to a new instance of SimpleDataTypeDerivative. The third parameter can be a 1 or a 0. If 1, then FlexSim will call bind() with the SDT_BIND_ON_CREATE mode. This mode will make sure that any needed persistent nodes are asserted as subnodes (like the x node in the Dot example). If you do not use any persistent subnodes in you SDT class, then you do not need to bind on create.
Display - The default implementation of SimpleDataType::toString() calls the bind() method, which returns the value strings to display in the tree. More about the toString() method will be discussed later.
getsdtvalue( theSDTNode, "memberName") / setsdtvalue( theSDTNode, "memberName", value ) - What these commands do is call the bind() method with the bind mode as SDT_BIND_MODE_GET_VALUE and SDT_BIND_MODE_SET_VALUE respectively. Each member binding in the bind() method then simply checks if the name of the member matches the desired name, and if so, gets/sets the value of that member. Note also that these functions are undo-aware, meaning if you call it within the context of an undo, then that change will be part of the undo record and will be undone if the user chooses to undo (you can use the SimpleDataType::setValue() method directly from within the dll if you want your change to be undo-able).
In case you are still using a version 7 beta please check with Flexsim because these functions are only available as applicationcommands.
Binding Variables
Classes inheriting SimpleDataType must use the bind() function to bind their member variables; otherwise, their variable values will not be saved when FlexSim is saved. There are different ways of binding a variable depending on its type, as shown in the table below.
Bind Function | Data Type | Example |
---|---|---|
bindDouble(double varName, int asSubNode) | double | bindDouble(x, 1) - binds a double x as a subnode of the class. |
bindNumber(Type varName) | float, int, all other numeric primitives | bindNumber(y) - binds a numeric primitive y to the class. |
bindSubNode(treenode varName, int dataType) | treenode | bindSubNode(myTreenode, 0) - binds a subnode with no data |
bindVariant(Variant varName) | Variant | bindVariant(myVariant) - binds a variant |
bindNodePtr(treenode varName) | treenode | bindNodePtr(myTreenode) |
bindNodeRef(NodeRef varName, int asSubNode) | NodeRef | bindNodeRef(myNodeRef) |
bindObjPtr(SimpleDataType* varName) | SimpleDataType* | bindObjPtr(myObjPtr) |
bindObjRef(ObjRef varName, int asSubNode) | ObjRef | bindObjRef(myObjRef) |
bindString(std::string varName) | std::string | bindString(myStr) |
bindByteBlock(ByteBlock varName, int asSubNode) | ByteBlock | bindByteBlock(bb, 1) - binds a ByteBlock as a subnode. In the tree, this is a node with text data. |
bindStlContainer(std::container varName | vector, stack, deque (std) | bindStlContainer(myVectorOfDoubles) |
bindStlSet(std::set varName) | set, multiset, unordered_set (std) | bindStlSet(mySet) |
bindStlMap(std::map varName) | map, multimap, unordered_map (std) | bindStlMap(myMap) |
bindCallback(funcName, ClassType) | funcName is the name of the function to bind. The ClassType is the class name of the current class. | bindCallback(functionName, MyClass) |
Note: The bind() functions that deal with STL containers require that the containers be composed of primitive types.
Binding Callbacks
Sometimes, you may want to make a function available on a custom SimpleDataType. If you made a class like the one shown
below, you could then use the FlexScript
function_s(talkerNode, "sayHello")
to call the sayHello function on that object.
class Talker : public SimpleDataType { public: void sayHello() { pt("hello"); pr(); } Variant sayHello(FLEXSIMINTERFACE) { sayHello(); } virtual void bind() override { SimpleDataType::bind(); bindCallback(sayHello, Talker); } };
getClassFactory()
This method is a virtual member of SimpleDataType which your sub-class must implement in order to properly tell FlexSim what the class is when saving the tree. This enables FlexSim to later read the class factory when loading the tree, and subsequently make the correct call into createsdtderivative() so that you can properly instantiate the class when loading from the tree.
toString()
The toString() method returns the string that is displayed in the FlexSim tree window. The default string returns the result of the bind() function call, but this function can be overwritten, allowing the data to be formatted in any way.
There are two cases that toString() should cover. The first case occurs when the node is not selected, but it is displayed. The default implementation returns a one-line string. The other case occurs when the node is selected, and the full viewing area is dedicated to the node. The default implmentation for this case simply displays each variable and its value on individual lines.
To determine which string should be returned, use the "verbose" parameter. If it is 1, return the full-view string. If it is 0, return the one-line string.
Links
Hotlinks and coldlinks can be used to get and set the variables of a SimpleDataType class. There are several requirements in order to do this:
- The link path must point to the SimpleDataType node (../..>path/to/node/TheSDT).
- The coldlink must have a subnode called sdtvalue with the SDT variable name as text data.
- The SDT variable must be a bound variable, i.e. bind() has been used to bind this variable.
Assuming the above conditions are met, a coldlink node might look something like this:
Example
As a basic example, let's use the Dot class. It doesn't need more than its position and size information. The header file for the Dot class is below:
#pragma once #include "FlexsimDefs.h" class Dot : public SimpleDataType { public: double x, y, z; float width; Dot() : x(0), y(0), z(0), width(0) {} Dot(double x, double y, double z, float w) : x(x), y(y), z(z), width(w) {} virtual const char* getClassFactory() override { return "ModuleName::Dot"; } virtual void bind() override { SimpleDataType::bind(); bindDouble(x, 1); bindDouble(y, 1); bindDouble(z, 1); bindNumber(width); } };
The figure below shows what an instance of a Dot looks like in the tree. Notice how the variables x, y, and z are actually subnodes, since that's how they were bound. The width is listed in text only.
Making this Example
- Read this document to create a DLL module.
- Add a header to the DLL project called Dot.h
- Copy the Dot class shown above into Dot.h
- Change
"ModuleName::Dot"
to reflect the name of your module. - In your module's main .cpp file, add the following lines to
createsdtderivative(char* classname)
:if (strcmp(classname, "Dot") == 0) return new Dot();
- If FlexSim is running, disconnect all DLLs.
- Build the module DLL.
- Start FlexSim, or reconnect the DLLs.
- Create a blank node anywhere in the tree.
- Give it a name and a subnode.
- Name the subnode "sdt::attributetree".
- Give the subnode this text data: [moduleName]::Dot
- Designate the node as so().
- Run this command as a script:
nodeadddata(so(), DATATYPE_SIMPLE);
This should make the node a SimpleDataType object. - Copy the node using "Ctrl-c".
- Paste it to another blank node using "Ctrl-v". This should create an instance of your Dot class.
- Add a subnode to the pasted node.
- Copy it again. This will cause the data bound as subnodes to appear.