3.2.1. Predicates |
Token
and Data Types
Capacity
Access
Modes
Initial
Marking
Predicate
Properties
Predicates are data containers for dynamic net elements called tokens. The number and type of tokens to be incorporated as well as the mode of token access are defined by the predicates.
3.2.1.1. Token and Data Types |
A predicate can only incorporate tokens of the same data type. Therefore the token type is defined as a property of the predicate. The type of predicate without any token attribution is called place. Such predicates contain tokens which need not differ from one another. Therefore they are called black tokens. Their syntactic designation of a black token is expressed by the $ symbol.
The usable types in Poses++ orientate themselves by the data types of the "C" language (char, short, int, long with the modifiers signed and unsigned, float, double). Most types known from this language can be used with the exception of any pointer types such as char*. Chains of characters are described in Poses++ by the predefined type string (implemented by the C++-class Pos_String in posstr.h), time values by the predefined type time (implemented by the C++-class Pos_Time in postime.h). Enumerations can be defined C-like by enum, and structured types by struct. The typedef keyword is also C-like available in Poses++. The predifined data type string offers the + operator to concat two strings and the == operator to compare two strings with a boolean result TRUE if equal.
typedef string firstname; // tokentype for predicate P3 typedef int numbers; // tokentype for predikate P4 enum ColorOfLight { RED, YELLOW, GREEN, REDYELLOW }; // tokentype for predicate P5 struct personnel { // tokentype for predicate P6 string name; float bonus; struct { int adge; enum {MALE, FEMALE} kind; }; }; struct TypeCollection { char c; signed char sc; unsigned char uc; short int shi; signed short int sshi; unsigned short int ushi; int i; signed int si; unsigned int ui; long l; signed long sl; unsigned long ul; float f; double d; string s; time t; enum Enum { E1=1, E2, E3 } e; struct Struct { char a,b,c; personnel person; } abc_person; numbers n; };
The binary representation of all data types depents from the hardware and the operating system your simulation server is running on.
3.2.1.2. Capacity |
The capacity, which is a property of predicates, defines the maximum number of tokens a predicate can incorporate simultaneously. The standard capacity is infinite which means for Poses++ exactly 2^32 - 1 = 4294967295. The lowest permissible capacity is 1.
All modelling elements in Poses++ are based on module types. Thats why the following source code examples use the module syntax.
// valid definitions for predicate capacities #define MAX 4294967295 #define MIN 1 // capacity as adjustable parameter module CapacityExamples { public: const unsigned long Parameter = MAX; private: // a type declaration for a place with maximal "Parameter" tokens: typedef place<Parameter> PlaceType; // a type declaration for a place with maximal one token: typedef place<MIN> FlagType; place<Parameter > P1; // a place with in maximum "Parameter" black tokens PlaceType P2; // capacity from data type "PlaceType" place<P1.capacity> P3; // capacity identical to P1.capacity place P4; // a place with infinite capacity (in practice: 2^32) FlagType P5; // a place with 1 token capacity /* the following source would produce an error because expressions are not allowed to define the capacity of a predicate: place<MAX-MIN> P6; // Error: ... ">" expected */ // to avoid this error you can specify: const unsigned long CAP = MAX - MIN; place<CAP> P6; };
3.2.1.3. Access Modes |
The access mode of a predicate defines how the tokens are to be filled into a predicate, and in what sequence they are searched. The following access modes are available:
BUFFER
|
This is the basic and simplest token access mode. The internally implemented mechanism is the same like for FIFORAM. During the search process, tokens may be found which are not in the first position of the searching sequence. This automatically results in a principle which gives preference to the oldest tokens of the predicate. The buffer access mode is the fastest mode and so mostly the best choice. |
---|---|
RAM
|
The tokens are filled at random within the predicate. During the search process in order to meet the fire rule for a transition the order of tokens is not changed. In every search process the token order is different which corresponds with the random access principle. |
FIFO
|
Those tokens which were filled first into the predicate are the first ones in searching sequence. Their positions in the token order do not change. The tokens in the following positions can only be removed after the first one has been removed. This is the first-in-first-out principle. |
LIFO
|
Those tokens which were filled last into the predicate are the first ones in the searching sequence. Their positions in the token order do not change. The tokens in the following positions can only be removed after the first one has been removed. This is the last-in-first-out principle. |
FIFORAM
|
The FIFO access mode is applied. During the search process, tokens may be found which are not in the first position of the searching sequence. This automatically results in a principle which gives preference to the oldest tokens of the predicate. The fiforam access mode is identical to the buffer access mode. |
LIFORAM
|
The LIFO access mode is applied. During the search process, tokens may be found which are not in the first position of the searching sequence. This results in a principle which gives preference to the youngest tokens of the predicate. |
Which of the above mentioned access modes is selected depends on the problem you want to solve. If you believe it doesn't matter select buffer at first. Predicates of the place type automatically apply the RAM mode because they contain black tokens which cannot be differed from each other and, therefore, it does not matter whether a token is older or younger than others.
module AccessModeExamples { // a common predicate with token type int and capacity=5: buffer <int,5> BufferInt; // a random access predicate with token type int and capacity=3: ram <int,3> RamInt; fifo <int,3> FifoInt; lifo <int,3> LifoInt; fiforam <int,3> FifoRamInt; liforam <int,3> LifoRamInt; place < 3> Place; // without capacity specifications: buffer <int > RamIntUnlimited; place PlaceUnlimited; };
3.2.1.4. Initial Marking |
A model is not only described by predicates, transitions and arcs. The behaviour of the net the model is based upon is also defined by its initial marking i.e., the initial token constellation within the predicates:
Poses++ makes available constant arc expressions to define a initial marking for a predicate.
module InitialMarkingExample { enum Color { // definition of token type "Color": RED, YELLOW, GREEN, BLUE }; struct Person { string Name; unsigned Age; }; place <25 > Counter << card(4),$; // 4 black tokens initialized fiforam<string> Names << "Otto" << "Anna" << "Paul"; // 3 string tokens initialized ram <Color> Colors << BLUE << RED << GREEN; // 3 colors initialized lifo <Person> Persons << {"Andrae",35} << {"Bert" ,36}; };
Another way to parametrize a model with the necessary start situation you have using command files. There you can spezify a command like PutToken PredicateName TokenValue. Different to the C-like syntax conventions in Poses++ source code the token value have to specified TCL-conform:
PutToken InitialMarkingExample.persons {"Andrae" 35}
PutToken InitialMarkingExample.persons {"Bert" 36}
The entries in such a command file will be sent to the simulation server typically after loading the model and the server will execute this commands which results in the same data situation like reached via initial marking expressions.
3.2.1.5. Predicate Properties |
Every predicate instance has the predefined properties capacity, card, reserve, name and path. The writable property capacity specifies the amount of tokens the predicate can hold at maximum (see also: Capacity). The read only property card on one hand specifies the actual count of token in that predicate and the property reserve specifies the amount of tokens the predicate is able to get. The context is not every time capacity = card + reserve because future events for this predicate or output arcs of the same transition can occupy buffer space not belonging to card or to reserve. These three properties are based on the data type unsigned long int.
module PrecicatePropertyExample { place<10> P; trans Clear { // clear all tokens in one fire step P >> card(P.card),$; action { P.capacity = P.capacity + 1; } } trans Fill { // fill the free place in one fire step P << card(P.capacity), $; } trans FillRest { // fill the rest of possible tokens P << card(P.reserve), $; } };
The property name specifies the real instance name of this module instance and path the real full instance name including all parent names of that module instance. Both non writable properties are based on the data type string. These both properties are identical to the properties of module instances (see also: Module Properties).
|