ATTENTIONThis FlexSim Community Forum is read-only. Please post any new questions, ideas, or discussions to our new community (we call it Answers) at https://answers.flexsim.com/. Our new Question & Answer site brings a modern, mobile-friendly interface and more focus on getting answers quickly. There are a few differences between how our new Q&A community works vs. a classic, threaded-conversation-style forum like the one below, so be sure to read our Answers Best Practices. |
flexsim.com |
#1
|
||||
|
||||
DLL Linking - ProcessorProcessTime
Task:
The process time for a processor should be defined in an external C++ Function “ProcessorProcessTime”, where 0 < ProcessTime < MaxProcessTime. Also the name of the processor (instance, object) and the name of the involved item should be returned. The DLL-Linking Template is the following if you switch the node to DLL: /**DLL path: *//**/"../libraries/thedll.dll"/**/ /** \nFunction name:*//**/"dllfunction"/**/ 1. How to call the DLL-Function with the parameters “current”, “item” and “MaxProcessTime? An alternative way could be to call the DLL-Function with nodefunction() and a label and hand over the parameters. //Parameter „Process Time” als Flexscript treenode current = ownerobject(c); treenode item = parnode(1); /**DLL Function Call*/ // CALL FUNCTION FROM DLL OVER LABEL DLL_ProcessorProcessTime double MaxProcessTime = 77.0; double returnval = nodefunction(label(current, "DLL_ProcessorProcessTime"), tonum(item), MaxProcessTime); return returnval; //Label „DLL_ProcessorProcessTime” dll: "../libraries/thedll.dll" function: "ProcessorProcessTime" This is working and this is how I have solved this problem for myself. 2. Which references (Parameters) are between the nodefunction-interface and the FLEXSIMINTERFACE? In the flexsimdefs.h you can find for the FLEXSIMINTERFACE: code == 0 c != NULL theclass == c input == NULL output == NULL involved == NULL callpoint != NULL 3. For what can these parameters be used? How to use them? The code in the dll should be something like: visible double ProcessorProcessTime(FLEXSIMINTERFACE) { // FLEXSIMINTERFACE is defined by // #define FLEXSIMINTERFACE int code, linkedlist * c, // linkedlist * theclass, void * input, void * output, // linkedlist * iinvolved, _callpoint * callpoint // ACTUAL ARGUMENTS OF ProcessorProcessTime int icode = code; int iinst = (int)tonum(c); int iclass = (int)tonum(theclass); int iinput = (int)tonum((linkedlist*)input); int ioutput = (int)tonum((linkedlist*)output); int iinvolved = (int)tonum((linkedlist*)involved); int icallpoint = (int)tonum((linkedlist*)callpoint); // POINTER TO OBJECT treenode current = ownerobject(c); // POINTER TO ITEM via callpoint treenode item = parnode(1); // PARAMETER VALUE via callpoint int iMax = (int)parval(2); // RANDOM PROCESSING TIME int irand = 9999; while ( irand > iMax ) { irand = rand(); } dProcessTime = (double)irand; // DEFINE A MESSAGE ARRAY FROM TYPE CHAR char message[1000]; // ASSIGN MESSAGE sprintf(message, "ProcessorProcessTime: \nObject:%s \n Item: %s \nProcessTime: %f", getname(current).c_str(), getname(item).c_str(), dProcessTime); // STOP SIMULATION RUN stop(1); // REPAINT ALL WINDOWS repaintall(); // SCREEN MESSAGE WHERE USER CAN DECIDE TO GO OR TO STOP // SIMULATION RUN if(msg("DLL ProcessorProcessTime", message,0) == 1) {go(1);} else {stop(1);} // RPINT INFORMATION INTO OUTPUT CONSOLE pt("Time: "); pf(time()); pr(); pt(message); pr(); pr(); pd(icode); pr(); pd(iinst); pr(); pd(iclass); pr(); pd(iinput); pr(); pd(ioutput); pr(); pd(iinvolved); pr(); pd(icallpoint); pr(); pr(); return dProcessTime; } 4. Can this code work and what is to do to make it work? I am not a programmer and not sure if this goes in the right direction. I got these questions from a user and maybe some of you can give me some hints and help. Thanks in advance. tom the (A)tom
__________________
tom the (A)tom: "We have solved our problems ... now we have to fight the solutions." |
#2
|
|||
|
|||
Hi Tom,
I would use User Command to call the DLL_ProcessorProcessTime. In User Command you define a command called ProcessTime and in that you write the call to the DLL dll: "../libraries/thedll.dll" function: "ProcessorProcessTime" To call the ProcessTime you call it with: ProcessTime(tonum(current),tonum(item),MaxProcessT ime); In your C-code visible double ProcessorProcessTime(FLEXSIMINTERFACE) { // FLEXSIMINTERFACE is defined by // #define FLEXSIMINTERFACE int code, linkedlist * c, // linkedlist * theclass, void * input, void * output, // linkedlist * iinvolved, _callpoint * callpoint // ACTUAL ARGUMENTS OF ProcessorProcessTime // POINTER TO OBJECT fsnode* current = parnode(1); // POINTER TO ITEM via callpoint fsnode* item = parnode(2); // PARAMETER VALUE via callpoint int iMax = (int)parval(3); // RANDOM PROCESSING TIME int irand = 9999; while ( irand > iMax ) { irand = rand(); } dProcessTime = (double)irand; // DEFINE A MESSAGE ARRAY FROM TYPE CHAR char message[1000]; // ASSIGN MESSAGE sprintf(message, "ProcessorProcessTime: \nObject:%s \n Item: %s \nProcessTime: %f", getname(current).c_str(), getname(item).c_str(), dProcessTime); // STOP SIMULATION RUN stop(1); // REPAINT ALL WINDOWS repaintall(); // SCREEN MESSAGE WHERE USER CAN DECIDE TO GO OR TO STOP // SIMULATION RUN if(msg("DLL ProcessorProcessTime", message,0) == 1) {go(1);} else {stop(1);} // RPINT INFORMATION INTO OUTPUT CONSOLE pt("Time: "); pf(time()); pr(); pt(message); pr(); pr(); return dProcessTime; } Point 3 and 4 I hope Flexsim or someone else can explain. Lars-Olof |
#3
|
||||
|
||||
Tom,
From what I know the same information that is passed to the function calling the DLL is passed into the DLL. So, Lars nailed it. If you want to pass different information into the DLL than is passed normally into a "process time" function or some other function then you would want to use a User Command or Node Funciton to call the DLL and pass that function the information you want to use. Inside of the DLL you will want to use the parval and parnode commands to get to those variables. So if the function looked like this: myfunc(current, item, somedouble, centerobject(current, 1)); The DLL would look like this: fsnode *current = parnode(1); fsnode *item = parnode(2); fsnode *mydouble = parval(3); fsnode *cobj = parnode(4); This is how I've done it in the past, AJ or Anthony might have something better. Good Luck, Brandon
__________________
thats not normal. |
#4
|
||||
|
||||
Lars-Olof,
Brandon, I understand now that both of you will do it the same way I did it in my alternative way beside that you prefer to use a user command where I used a label. But it the same idea and same structure based on the fact that the parameters you use calling the user command or label are given further to the DLL. So in the DLL you can use these parameters. The question is if this is the only solution or is there a way directly to hand over parameters to the DLL. I am fine with the above solution, but a user asks if there is another way. If this is the only way than I agree with you to use a user command would be the easiest and readable solution. But why has the standard template (if you switch code to DLL) than no option to hand over parameters? Because there is no way to hand over parameters directly? Yes, I also hope that I get some more response (Point 3 and Point 4), because I like to understand the whole thing and I like to give the user a good and right reply. And he likes to understand this other stuff. Anyway, thanks guys for these first replies. tom the (A)tom
__________________
tom the (A)tom: "We have solved our problems ... now we have to fight the solutions." |
#5
|
||||
|
||||
Tom,
I've used DLLs in the last two or three projects I've done and in most cases I've been using them with the User Command method mentioned by Brandon and Lars-Olof. That's worked very well for me. As for those weird parameters (input, output, callpoint, etc) in the FLEXSIMINTERFACE, I've never worried about what they are for. I've never had to use them directly. I just get the parameters I need using the parval() and parnode() commands (like Brandon described). Those commands are created to retrieve data from the callpoint object as needed. There is no way to pass values directly to the DLL functions from the node that's toggled as DLL. The reason is that the DLL system is designed to treat the node as a sort of pass-through conduit to the actual function to call. Whatever parameters are passed to the node in the nodefunction() call will be passed straight through to the DLL (when using a User Command nodefunction() is still called behind the scenes). Basically, I think the trick is to write DLL functions as if they are nodefunctions, but you have the added bonus of being able to use C++ functionality. If there are values that need to be used in a function every time it's called (and if those values never change) they can be hard-coded into the function itself. This is sometimes a dangerous practice, but you can do it if you need to. |
#6
|
||||
|
||||
Tom,
On your point of using the callpoint, involved, etc., AJ is correct. Those parameters are only meant to be available so that you can use the standard access parameters that a nodefunction has: parval(), parnode(), c, i, etc. They are really meant to be "invisible" to you as the user, meaning you don't worry about them. You just call parval(), parnode(), etc. To your other point of getting direct access to a function in the dll, the only time that you would need to do this is if you are implementing C++ code that accessed functions in the dll. If your code is implemented in flexscript, then you can use the user command method (and you don't have to use tonum() because flexscript isn't strongly typed). However, if you're accessing the dll function from C++, you get issues in calling the user command because you have to pass all numbers, and therefore you have to do the tonum() thing. So right now, my answer to that is, don't use C++. Just implement your model code in flexscript, and everything works just as if your code is connecting directly to a dll function that takes parameters so-and-so. However, in some cases I realize that C++ is required, which presents the tonum() problem, where in order for it to work in C++, you have to add these extra tonum() things into your parameters of the call to the user command. For this problem I have found a solution that will directly connect Flexsim to non-FLEXSIMINTERFACE functions that you specify in the dll. I've got this working for the Rail API, but you have to change Flexsim4/program/config/flexsimexeB.txt to get the dll to be delay-loaded. We'll hopefully make this model-accessible in future releases. Right now I'm in the middle of an urgent project, so I don't have the time to document it all, but when I get the time, I'll put an article together. |