IEC language compiler, was Don't despair

M

Thread Starter

Mario de Sousa

Johan Bengtsson wrote: > I might be able to do a quick and dirty one (at least from IL if that could help), but I can not start during the next two months. The graphical languages takes more time since they require some kind of editor to be made. < Hi Johan, Yes, we were not considering the graphical languages for the moment. We might later be able to use an existing editor later on, but we can worry about that later... Are you sure you can't start a little earlier? You don't really have to do much, just so we feel somebody is assuming control and taking responsibility seriously. It's that I feel we will have to add a few features to the underlying LPLC libraries to support these languages, and having somebody at least _thinking_ about it is already a lot of help. David (Campbell), I am assuming that you are no longer interested / are un-able to continue with this code? Cheers, Mario. _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
C

Campbell, David (Ex AS17)

Mario, > David (Campbell), I am assuming that you are no longer interested / > are un-able to continue with this code? How about "out of email contact due to work commitments". I will be looking at the IEC compiler shortly (*again*) due to debugging problems we are having with an internal version (which generates FORTRAN code). When I stopped on the code, there was an issue of matching variable names to hardware references. This had not been resolved (from memory), this is a fairly fundamental problem which creates problems declaring variables. Back to the problem which draws me back to IEC FB coding (and similar stuff). Please remeber that I am dealing with engineers rather than computer science people on a daily basis so usability comes before elegant solutions: Problem: ================== Simulated IEC logic is compiled to executable code and operates on a memory array. This is extremely difficult to debug without the development tools (to pause execution and examine registers/variables). Proposed solution: ================== The vendor IEC logic can be broken into sections where values are taken from the memory map, calculations executed and results stored. All calculation elements are connected together. Each section is called a "ladder" (possibly a bad use of terminology but the best available term). I will be rebuilding my internal IEC translator (an early kludge for a specific problem, certain people on the list will know that my bison/flex was very primative 6 months ago) to bring it in line with the formal IEC specifications with supporting those sections of the IEC standard that are used. Other areas of the complete IEC standard can be added at a latter date. I find that program development works better from a prototyping rather than trying to throw everything in at one hit (the IEC standard is HUGE, 60+KB of raw bison script). The translator will generate not only the executable logic but an emulation sub-routine to predict the state of a "ladder" segment by examining the memory array of the executing logic (via an appropriate comunications protocol). The idea is to have a GUI that can "peer" into the working system, collect the inputs for a section of the logic, predict/calculate the outputs for each operation (eg: AND, OR, NOT gates) and display them to the user. This will involve a dynamic auto-routing program to build the "live" wiring diagram. The above description is only about half of what is needed (naturally there is a search tool by alias index [memory location] and a point & click for tracing of logic signals). Part of the work will involve a Win32 application... *sigh* David Campbell PS: I don't appear to have missed much with only 90 email in my LinuxPLC basket. _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
M

Mario de Sousa

Hi David! Welcome back!!! "Campbell, David (Ex AS17)" wrote: > > Mario, > > > David (Campbell), I am assuming that you are no longer interested / > > are un-able to continue with this code? > > How about "out of email contact due to work commitments". I will be > looking at the IEC compiler shortly (*again*) due to debugging problems > we are having with an internal version (which generates FORTRAN code). Yep. I can almost hear you sigh from here, but I guess you can almost see me smile. I guess this means good news for the PuffinPLC? > When I stopped on the code, there was an issue of matching variable names > to hardware references. This had not been resolved (from memory), this > is a fairly fundamental problem which creates problems declaring variables. Yes. This has not yet been resolved. I had proposed the PuffinPLC support arrays of plc_pt_t (plc points), but I don't know how this will affect the API. In the meantime I dropped off-line due to the birth of my first child, and when I got back Jiri seemed to have disapeared. He has re-surfaced since then, so I hope we can get back to it once again. I have been working mostly on the dsp module (i.e. digital filtering, PID loops, etc...), but at a much less slower rate due to other easily guessed time resource drains. > Back to the problem which draws me back to IEC FB coding (and similar > stuff). Please remeber that I am dealing with engineers rather than > computer science people on a daily basis so usabilty comes before > elegant solutions: > > Problem: > ================== > Simulated IEC logic is compiled to executable code and operates > on a memory array. This is extremely difficult to debug without the > development tools (to pause execution and examine registers/variables). > > Proposed solution: > ================== > The vendor IEC logic can be broken into sections where values are taken > from the memory map, calculations executed and results stored. > All calculation elements are connected together. Each section is called a > "ladder" (possibly a bad use of terminology but the best available term). > I think we can leave this out for the PuffinPLC, at least for the moment. I can envision the PuffinPLC compiled IEC code storing every result in the memory map as soon as it is produced, so the state of the memory map can be determined at any time. I am hoping that in the future we will have a general purpose aplication that will enable the user to see in real-time the state of the PuffinPLC point map. What we need then is a method of knowing which point of the IEC code is currently being executed. How about we maintain a call stack. Whenever a certain function is called that function pushes its unique identifier onto the stack, and when it leaves, it pops the same identifier. Lie this we can know at any time the state of the call stack. This is just the first thought that crossed my mind. Maybe we can do it better than that. If we do a direct mapping of IEC (ST) functions onto C functions, maybe we can get to the C call stack, just like gdb? Maybe I am mis-understanding your problem? > (...) > > Other areas of the complete IEC standard can be added at a latter > date. (...) Sure thing. > The translator will generate not only the executable logic but > an emulation sub-routine to predict the state of a "ladder" segment > by examining the memory array of the executing logic (via an > appropriate comunications protocol). Hang on. Maybe some light is slowly dawning on me. Do you mean to say they want to be able to know exactly what line of IEC code is currently being executed? Sorry for the stupid questions and suggestions... > The idea is to have a GUI that can "peer" into the working system, > collect the inputs for a section of the logic, predict/calculate the > outputs for each operation (eg: AND, OR, NOT gates) and display > them to the user. This will involve a dynamic auto-routing program > to build the "live" wiring diagram. > > (...) > > Part of the work will involve a Win32 application... *sigh* Have you considered using wxWindows (http://www.wxwindows.org/)? I have never used it myself, so I don't know how good it is, but it sure seems interesting to be able to write source code for a GUI that can be compiled onto several graphical packages and use those graphical package's widgets. > PS: I don't appear to have missed much with only 90 email in > my LinuxPLC basket. To be honest, you really haven't missed much. It has been slow going lately. Cheers, Mario. -- ---------------------------------------------------------------------------- Mario J. R. de Sousa [email protected] ---------------------------------------------------------------------------- The box said it requires Windows 95 or better, so I installed Linux _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
C

Campbell, David (Ex AS17)

> Hi David! > > Welcome back!!! *nod* > Yep. I can almost hear you sigh from here, but I guess you can almost > see me smile. I guess this means good news for the PuffinPLC? I can imagine that. As soon as we have something working with IEC ST then hopefully the remaining blanks will be filled in quickly (by other people :). (*snip*) > > Problem: > > ================== > > Simulated IEC logic is compiled to executable code and operates > > on a memory array. This is extremely difficult to debug without the > > development tools (to pause execution and examine registers/variables). > > > > Proposed solution: > > ================== > > The vendor IEC logic can be broken into sections where values are taken > > from the memory map, calculations executed and results stored. > > All calculation elements are connected together. Each section is called a > > "ladder" (possibly a bad use of terminology but the best available term). > > I think we can leave this out for the PuffinPLC, at least for the > moment. I can envision the PuffinPLC compiled IEC code storing every > result in the memory map as soon as it is produced, so the state of the > memory map can be determined at any time. I am hoping that in the future > we will have a general purpose aplication that will enable the user to > see in real-time the state of the PuffinPLC point map. With the FORTRAN solution I currently have that is indeed the case (as FORTRAN always passes pointers to variables rather than values for function call arguments). In C this is still possible but requires a little creative coding (either heavy use of "#define" or declare all variables as pointers and use them as "var" => "(*var)". The engineers who use the translated logic are chemical engineers (the project is a chemical process plant simulator and the logic being simulated is safety interlock logic). When I started my engineering degree about 10 years ago, the only programming courses we did was 6 months in Pascal and 3 months of FORTRAN. The course structure changed shortly afterwards to focus on C/C++. With such limited background in programming, most chemical engineers are reluctant or just plain ignorant of debugging tools (never developed a program complicated enough to require a debugging tool). > What we need then is a method of knowing which point of the > IEC code is currently being executed. How about we maintain > a call stack. Whenever a certain function is called that function > pushes it's unique identifier onto the stack, and when it leaves, > it pops the same identifier. I am not concerned about a fatal crash situation. I am concerned about where the customer supplied logic is incorrect. While attempting to start the chemical process unit (I am starting to slip into chemical engineering terminology, please forgive me) we run into a situation where we can not start a pump or open a valve due to the interlock logic. What is needed is some method to trace the logic to determine what condition is preventing the operation (eg: low level protection on a pump) or is the constructed logic incorrect. My existing translator already has a function call stack system, although this is used to map local variables to unused PLC memory address space. This allows the state of the logic to be saved by copying the entire memory array to file (this includes internal variables for time delays, etc). > Lie this we can know at any time the state of the call stack. > This is just the first thought that crossed my mind. Maybe we can > do it better than that. If we do a direct mapping of IEC (ST) > functions onto C functions, maybe we can get to the C call stack, > just like gdb? This already happens, each IEC ST function is translated to a FORTRAN subroutine with a fixed prefix to avoid namespace pollution. Tracing the function execution in a debugger is relatively painless. > Maybe I am mis-understanding your problem? No. I was deliberately holding back information about who my customer (internal customer but still a customer). Although I will probably never use PuffinPLC as a PLC replacement there are certain aspects I can contribute and areas where I can collect some benifit. >> The translator will generate not only the executable logic but >> an emulation sub-routine to predict the state of a "ladder" segment >> by examining the memory array of the executing logic (via an >> appropriate comunications protocol). > > Hang on. Maybe some light is slowly dawning on me. > Do you mean to say they want to be able to know exactly what line of > IEC code is currently being executed? > > Sorry for the stupid questions and sugestions... In engineering there is no such this as a stupid question, just someone has not provided you with enough information (which happens so frequently for an engineer it becomes a sad fact of life). When attempting to debug a chemical process simulator there is different levels of view. A) Examining each connection to an element (an inlet to an AND gate) This is extremely slow and tedious. B) Examining each element and all its connections (inlet 3 of this AND gate is low). The following is a display from the simulator showing this for an AND gate (please note that the chemical process simulator uses a point-to-point connection system, hence it can not "mailbox" values to a PLC memory location. This prevents certain PLC "features" from operating correctly like an OR gate wrapped on itself as a latch). ======================================================================== Tag: AND Module: 412402350 Type: AND gate +----------+ 412401860 , 1 | | 412602310 , 1 HIGH>-------------|1:L 1:L|-----------------> LOW 412402334 , 1 | | HIGH>-------------|2:L | 412402336 , 1 | | LOW>-------------|3:L | +----------+ ======================================================================== To trace the logic the engineer would need to type "F,412402336" to look at the source of inlet 3 (outlet 1 of module 412402336). C) Examining a group of elements showing the state of all the elements inlets and outlets. (eg: a page of logic showing the current state) I am attempting to jump from debugging at level "A" to debugging at level "C". >> The idea is to have a GUI that can "peer" into the working system, >> Part of the work will involve a Win32 application... *sigh* > > Have you considered using wxWindows (http://www.wxwindows.org/)? I have > never used it myself, so I don't know how good it is, but it sure seems > interesting to be able to write source code for a GUI that can be > compiled onto several graphical packages and use those graphical > package's widgets. No. I have bookmarked the page for future reference, but I would rather get something coded first and then port it later (unless I hit a problem with the existing APIs which will take several days to work around). David Campbell PS: I hope I haven't frightened anyone by going into the messy details about my end user. _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
Hi, I have had a good look over Davids lex/yacc code and all of the IEC languages in the spec. It became obvious looking at his stubs, and then reviewing the IEC 61131 specification that the 61131 language is using instanciated functions. You might want to consider converting the code to C++ instead of C, this will then handle variable access, and other similar issues. Take the code below as an example. It can almost be directly converted from the IEC code. Once run through a C++ compiler the code can be optimized, and debugged reasonably with a normal debugger if there are major problems. It also takes care of some of those annoying 'features' of the IEC standard, such as arbitrary argument passing. FUNCTION_BLOCK AVERAGE VAR_INPUT RUN : BOOL ; (* 1 = run, 0 = reset *) XIN : REAL ; (* Input variable *) N : INT ; (* 0 <= N < 128 or manufacturer- *) END_VAR (* specified maximum value *) VAR_OUTPUT XOUT : REAL ; END_VAR (* Averaged output *) VAR SUM : REAL := 0.0; (* Running sum *) FIFO : DELAY ; (* N-Element FIFO *) END_VAR SUM := SUM - FIFO.XOUT ; FIFO (RUN := RUN , XIN := XIN, N := N) ; SUM := SUM + FIFO.XOUT ; IF RUN THEN XOUT := SUM/N ; ELSE SUM := N*XIN ; XOUT := XIN ; END_IF ; END_FUNCTION_BLOCK ------ It would then become, class AVERAGE { public: boolean RUN; double XIN; int N; public: // the IEC compiler should check for input/output conflicts double XOUT; protected: double SUM = 0.0; delay FIFO; // Another class public: void step(){ SUM = SUM - FIFO.XOUT; FIFO.RUN = RUN; // This makes dealing with those random arguments easy FIFO.XIN = XIN; FIFO.N = N; FIFO.step(); SUM = SUM + FIFO.XOUT; if(RUN){ XOUT = SUM/N;} else { SUM = N*XIN; XOUT = XIN; } } } --------- Now the function block delay FUNCTION_BLOCK DELAY (* N-sample delay *) VAR_INPUT RUN : BOOL ; (* 1 = run, 0 = reset *) XIN : REAL ; N : INT (* 0 <= N < 128 or manufacturer- *) END_VAR (* specified maximum value *) VAR_OUTPUT XOUT : REAL; END_VAR (* Delayed output *) VAR X : ARRAY [0..127] (* N-Element queue *) OF REAL; (* with FIFO discipline *) I, IXIN, IXOUT : INT := 0; END_VAR IF RUN THEN IXIN := MOD(IXIN + 1, 128) ; X[IXIN] := XIN ; IXOUT := MOD(IXOUT + 1, 128) ; XOUT := X[IXOUT]; ELSE XOUT := XIN ; IXIN := N ; IXOUT := 0; FOR I := 0 TO N DO X := XIN; END_FOR; END_IF ; END_FUNCTION_BLOCK ------- and the C++ equivalent int mod(int a, int b){ return a % b;} // a function in a global library class DELAY { public: boolean RUN; double XIN; int N; public: double XOUT; protected: double X[128]; int XIN_start = 0; int I = 0, IXIN = 0, IXOUT = 0; void step(){ if(RUN){ IXIN = mod(IXIN + 1, 128); X[IXIN - XIN_start] = XIN; IXOUT = mod(IXOUT + 1, 128); XOUT = X[IXOUT - XIN_start]; } else { XOUT = XIN; IXIN = N; IXOUT = 0; for(i = 0; i <= N; i++){ X[I - XIN_start] = XIN; } } } } _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
C

Campbell, David (Ex AS17)

Hugh Jack wrote: > I have had a good look over Davids lex/yacc code and all of the IEC > languages in the spec. It became obvious looking at his stubs, and then > reviewing the IEC 61131 specification that the 61131 language is using > instanciated functions. > > You might want to consider converting the code to C++ instead of C, > this will then handle variable access, and other similar issues. (*snip*) I had to think about this for a couple of minutes. I agree that implementing IEC would be easier in C++ than C, especially for the argument list (this was causing no end of grief with respect to "named" subroutine arguments, default values, argument order, etc...). I see that Jack used a standard entry point to execute the routine [ "obj->Step(void)" ]. This might need to be extended to include other entry points such as "obj->Save()" and "obj->Load()" to dump and restore the object data to an off-line media (eg: disk file). For most installations this could be redundant code, except if the translated code was wrapped with exeception detection code (eg: something did a div-by-zero or similar) then the IEC code could be "core dumped" for further analysis. David Campbell PS: I still think that function arguments should be passed on the stack rather than via object variables. _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
Hi, >I had to think about this for a couple of minutes. I agree that >implementing IEC would be easier in C++ than C, especially for the >argument list (this was causing no end of grief with respect to "named" >subroutine arguments, default values, argument order, etc...). When I looked through your code I could tell you were getting stuck on dealing with the variables. The advantage of this method is that it requires very little reorganization, and you leave the complexity of variables, etc to the C++ compiler. (I could use Curt's analogy of standing on a giants shoulders). Basically you can traverse the tree and print out code sections. >I see that Jack used a standard entry point to execute the routine >[ "obj->Step(void)" ]. This might need to be extended to include other >entry points such as "obj->Save()" and "obj->Load()" to dump and >restore the object data to an off-line media (eg: disk file). I would suggest using constructors and destructors instead, and let the system do the calling. >For most installations this could be redundant code, except if >the translated code was wrapped with exeception detection code >(eg: something did a div-by-zero or similar) then the IEC code >could be "core dumped" for further analysis. A global library could always be included that can trap error signals, such as divide by zero. >PS: I still think that function arguments should be passed on >the stack rather than via object variables. From a traditional perspective this feels like the way to go, but without a guaranteed order, and fixed number of variables, pulling the values back off the stack becomes complex and time consuming at the subroutine. To pass only a partial list you would also need to pass their names also, or use dummy placeholders for the values not filled in. You would also need to sort them before pushing them on the stack - yuck. ----- Also, if you recall the standard also has a more traditional call with the arguments listed in order, this could be done with an overloaded function. An expanded example. class DELAY { public: boolean RUN; double XIN; int N; public: double XOUT; protected: double X[128]; int XIN_start = 0; int I = 0, IXIN = 0, IXOUT = 0; public: DELAY(){ //stuff to initialize the class here } ~DELAY(){ // stuff to shut down the class here } void step(boolean _RUN, double _XIN, int _N){ RUN = _RUN; XIN = _XIN; N = _N; step(); } void step(){ if(RUN){ IXIN = mod(IXIN + 1, 128); X[IXIN - XIN_start] = XIN; IXOUT = mod(IXOUT + 1, 128); XOUT = X[IXOUT - XIN_start]; } else { XOUT = XIN; IXIN = N; IXOUT = 0; for(i = 0; i <= N; i++){ X[I - XIN_start] = XIN; } } } } _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
C

Campbell, David (Ex AS17)

Hugh Jack wrote: >> I see that Jack used a standard entry point to execute the routine >> [ "obj->Step(void)" ]. This might need to be extended to include other >> entry points such as "obj->Save()" and "obj->Load()" to dump and >> restore the object data to an off-line media (eg: disk file). > > I would suggest using constructors and destructors instead, and let the > system do the calling. I have my own reasons for needing seperate save & restore routines but these are minor details (the constructors and destructors could simply be a call to the appropriate routine). >> PS: I still think that function arguments should be passed on >> the stack rather than via object variables. (*snip*) > Also, if you recall the standard also has a more traditional > call with the arguments listed in order, this could be done with an > overloaded function. Agreed. After digging through the C++ documentation I found that the overload matching method was based on argument type and no option for named arguments (I deal with several different programing languages at once so I tend to get confused on the minor semantics). Once I have beaten the IT dept here into submission (I currently only have email access to puffinplc.org due to a bad local route) I will upload the current IEC ST stuff (I have managed to get rid of the last 8 shift/reduce conflicts). Finally, does anyone have a suggested method of dealing with the TIME data types. These data types have greater resolution than the time_t variable (eg: millisec -> centuries). I am currently looking at a "long long" data type (actually since we are using C++, we might as well wrap this as a class with associated operators). David Campbell _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
Campbell, David (Ex AS17): > Finally, does anyone have a suggested method of dealing > with the TIME data types. These data types have greater > resolution than the time_t variable (eg: millisec -> centuries). > I am currently looking at a "long long" data type (actually > since we are using C++, we might as well wrap this as > a class with associated operators). The standard type is struct timeval, I think, with the fields: long tv_sec; /* seconds since Jan. 1, 1970 */ long tv_usec; /* and microseconds */ See gettimeofday(3). This would seem to have the year 2038 problem, though; apparently some systems will fail with EOVERFLOW after 2038, but I don't have my linux box handy to check. In any case, though, a lot of things will fail on unix in 2038, and with any luck there'll be a general solution we'll be able to use (like time_t and the above structure being redefined). Jiri <[email protected]> _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
C

Campbell, David (Ex AS17)

Jiri wrote: > Campbell, David (Ex AS17): >> Finally, does anyone have a suggested method of dealing >> with the TIME data types. These data types have greater >> resolution than the time_t variable (eg: millisec -> centuries). >> I am currently looking at a "long long" data type (actually >> since we are using C++, we might as well wrap this as >> a class with associated operators). > > The standard type is struct timeval, I think, with the fields: > long tv_sec; /* seconds since Jan. 1, 1970 */ > long tv_usec; /* and microseconds */ Thanks for the suggestion. After reviewing the storage requirements I have found the following: sizeof(timeval) = 8 bytes sizeof(double) = 8 bytes Using only the mantisa bits (52 for double) yields the following: 2^52 => 10^15.65 (active bits in double) 2^31 => 10^9.33 (active bits in long) 2^21 => 10^6.32 (bits in double avail for sub-second) Therefore a double will be accurate to: 0.2 usec @ Today (19-Feb-2001) 0.4 usec @ 03:14, 19-Jan-2038 (Unix rollover) 1.0 usec @ 23:53, 17-Sep-2112 2.0 usec @ 23:47, 05-Jun-2255 5.0 usec @ 23:28, 26-Jul-2683 10.0 usec @ 22:57, 17-Feb-3397 I hope this provides sufficient accuracy for most applications in the "near" future ;-) This assumes that 0.0 equals the Unix epoc of 1-Jan-1970. Using a double greatly simplifies the maths involved (providing you don't have a buggy pentium). David Campbell _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
C

Campbell, David (Ex AS17)

> Hugh Jack wrote: > You might want to consider converting the code to C++ instead of C Jack, Attached is the current output from my private copy of the IEC ST translator (currently I don't have direct access to PuffinPLC.org or otherwise I would upload the latest code, the local network router is sending packets for 12.x.x.x to some obscure corner of our company). The output is the result of translating AnnexF/derivative_st.txt, please note that the output consists of a header file (IEC_derivative.h) and the main code body (IEC_derivative.cpp). Comments anyone before I go off and attack another example file? David Campbell PS: The IEC_BASE class contains all the implicit functions such as type conversion and "specials" such as bit rotation (I said rotate, not shift!!). The IEC_BASE.h also contains the data type definitions. PPS: The code is intentionally un-indented, that is where the GNU indent program comes in :) //////////////////////////////////////// // Header section // //////////////////////////////////////// #ifndef _IEC_derivative #define _IEC_derivative // Dependent class info #include "IEC_BASE.h" class IEC_derivative : IEC_BASE { public: // Input vars bool RUN; R32 XIN; IEC_TIME CYCLE; // Output vars R32 XOUT; ///////////////////////////////////// // Entry for named argument list ///////////////////////////////////// void Step(void); ///////////////////////////////////// // Sequenced argument list ///////////////////////////////////// void Step( bool _RUN, R32 _XIN, IEC_TIME _CYCLE) { this->RUN = _RUN; this->XIN = _XIN; this->CYCLE = _CYCLE; this->Step(); } private: R32 X1; R32 X2; R32 X3; } #endif /* _IEC_derivative */ //////////////////////////////////////// // Code section // //////////////////////////////////////// void IEC_derivative::Step() { if (RUN) { XOUT=(((3.000000*(XIN-X3))+X1-X2)/(10.000000*IEC_BASE::time_to_real(CYCLE))) ; X3=X2; X2=X1; X1=XIN; } else { XOUT=0.000000; X1=XIN; X2=XIN; X3=XIN; } } _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
Hi, >Attached is the current output from my private copy of the IEC ST translator (currently I don't have direct access to PuffinPLC.org or otherwise I would upload the latest code, the local network router is sending packets for 12.x.x.x to some obscure corner of our company).< I saw the code Mario uploaded for you, but I haven't had a look at it yet. But, you could always try logging into the router youself and try to fix the routing table - try user admin, password 123456 ;{ Sounds like the administrator set up a company subnet as class A instead of a few smaller ones to save a few minutes setting up the routing tables. I pasted in the ST file for the benefit of other readers, and I have inserted comments below. FUNCTION_BLOCK DERIVATIVE VAR_INPUT RUN : BOOL ; (* 0 = reset *) XIN : REAL ; (* Input to be differentiated *) CYCLE : TIME ; (* Sampling period *) END_VAR VAR_OUTPUT XOUT : REAL ; (* Differentiated output *) END_VAR VAR X1, X2, X3 : REAL ; END_VAR IF RUN THEN XOUT := (3.0 * (XIN - X3) + X1 - X2) / (10.0 * TIME_TO_REAL(CYCLE)) ; X3 := X2 ; X2 := X1 ; X1 := XIN ; ELSE XOUT := 0.0; X1 := XIN ; X2 := XIN ; X3 := XIN ; END_IF ; END_FUNCTION_BLOCK //////////////////////////////////////// // Header section // //////////////////////////////////////// #ifndef _IEC_derivative #define _IEC_derivative // Dependent class info #include "IEC_BASE.h" -- I could also see an argument for making the base class -- normal non-class functions. class IEC_derivative : IEC_BASE { public: // Input vars -- For the data types I would make the names more complex -- so that they don't get mixed up with normal variables. -- for example 'bool' becomes '__iec_bool' -- 'R32' becomes '__iec_R32' bool RUN; R32 XIN; IEC_TIME CYCLE; // Output vars R32 XOUT; ///////////////////////////////////// // Entry for named argument list ///////////////////////////////////// void Step(void); -- I would also include a function that allows a normal -- argument list here eg. void Step(bool RUN, R32 XIN, IEC_TIME CYCLE); -- and then define it below, as in my previous example ///////////////////////////////////// // Sequenced argument list ///////////////////////////////////// void Step( bool _RUN, R32 _XIN, IEC_TIME _CYCLE) { this->RUN = _RUN; this->XIN = _XIN; this->CYCLE = _CYCLE; this->Step(); } private: R32 X1; R32 X2; R32 X3; } #endif /* _IEC_derivative */ //////////////////////////////////////// // Code section // //////////////////////////////////////// void IEC_derivative::Step() { if (RUN) { -- If the base functions, such as 'time_to_real' are defined as global -- functions, it will simplify the line below, and reduce the overall size -- of the program. XOUT=(((3.000000*(XIN-X3))+X1-X2)/(10.000000*IEC_BASE::time_to_real(CYCLE))) ; X3=X2; X2=X1; X1=XIN; } else { XOUT=0.000000; X1=XIN; X2=XIN; X3=XIN; } } You might want to try implementing a normal function with a return value now. Take for example the function definition below. It is not much different from a FUNCTION_BLOCK conversion. FUNCTION WEIGH : WORD (* BCD encoded *) VAR_INPUT (* "EN" input is used to indicate "scale ready" *) weigh_command : BOOL; gross_weight : WORD ; (* BCD encoded *) tare_weight : INT ; END_VAR (* Function Body *) IF weigh_command THEN WEIGH := INT_TO_BCD (BCD_TO_INT(gross_weight) - tare_weight); END_IF ; END_FUNCTION (* Implicit "ENO" *) --- Normal functions are easier to convert, just define them as normal functions. --- I left the normal IEC data types here. WORD WEIGH(BOOL weigh_command, WORD gross_weight, INT tare_weight){ WORD WEIGH; IF(weight_command){ WEIGH = INT_TO_BCD(BCD_TO_INT(gross_weight) - tare_weight); } return WEIGH; }
 
Campbell, David (Ex AS17): > The output is the result of translating AnnexF/derivative_st.txt, please > note that the output consists of a header file (IEC_derivative.h) and the > main code body (IEC_derivative.cpp). > Comments anyone before I go off and attack another example file? You might put in #line directives, but that's extra and can be done any time. (Somebody just needs to go look up how to keep track of line numbers in flex/bison, there's a standard method for doing it.) > PPS: The code is intentionally un-indented, that is where the GNU indent > program comes in :) It doesn't matter, anyway; it wouldn't be much work putting it in, but I don't think there's any point. Jiri -- Jiri Baum <[email protected]> Q: Why did the chicken cross the Moebius Strip? A: To get to the other... um... er... --r.h.f.r _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
C

Campbell, David (Ex AS17)

Hugh Jack wrote: (*snip*) > -- I could also see an argument for making the base class > > -- normal non-class functions. > The IEC_BASE class is merely a container for implicit IEC functions. A valid comment. > > > class IEC_derivative : IEC_BASE { > > public: > > // Input vars > bool RUN; > R32 XIN; > IEC_TIME CYCLE; > -- For the data types I would make the names more complex > > -- so that they don't get mixed up with normal variables. > > -- for example 'bool' becomes '__iec_bool' > > -- 'R32' becomes '__iec_R32' > Again a valid point (also makes them more "readable" to someone familiar with IEC ST but not C++), this is a simple matter of a quick search replace: SINT => __iec_sint (char) INT => __iec_int (short) DINT => __iec_dint (long) LINT => __iec_lint (long long) etc, etc, etc... > ///////////////////////////////////// > > // Entry for named argument list > > ///////////////////////////////////// > > void Step(void); > > -- I would also include a function that allows a normal > > -- argument list here eg. void Step(bool RUN, R32 XIN, IEC_TIME CYCLE); > > -- and then define it below, as in my previous example > If I understand this correctly, you would prefer that the class definition have no embedded functions. The body of all functions should be in a seperate file. The advantages is that the overall code size would be smaller however you loose some opportunities for optimisation (the overload conversion routine borders on an inline function). > void IEC_derivative::Step() > > { > > if (RUN) { > > -- If the base functions, such as 'time_to_real' are defined as global > > -- functions, it will simplify the line below, and reduce the overall > size > > -- of the program. > Since the class is derived from IEC_BASE, there is no need for the preamble ("IEC_BASE::") on the implicit routine (I have modified the generation code to remove the preamble). > You might want to try implementing a normal function with a return > value now. Take for example the function definition below. It is not > much different from a FUNCTION_BLOCK conversion. I'm working on it, I'm working on it!!! Biggest problem at the moment is that the translator does not recognise the BCD_TO_INT and INT_TO_BCD routines (to be precise, the return value type checking fails...) > -- Normal functions are easier to convert, just define them as > -- normal functions. > I'll see what I can do, I have seen some "named argument" functions where some of the arguments are left at their default values. From the code I have seen, functions should be call independent (eg: not having any persistent internal variables) David Campbell _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
Hi, David wrote: > > > class IEC_derivative : IEC_BASE { > > > public: > > > // Input vars > > bool RUN; > > R32 XIN; > > IEC_TIME CYCLE; > > -- For the data types I would make the names more complex > > > -- so that they don't get mixed up with normal variables. > > > -- for example 'bool' becomes '__iec_bool' > > > -- 'R32' becomes '__iec_R32' > > > Again a valid point (also makes them more "readable" to someone familiar > with > IEC ST but not C++), this is a simple matter of a quick search replace: > > SINT => __iec_sint (char) > INT => __iec_int (short) > DINT => __iec_dint (long) > LINT => __iec_lint (long long) > etc, etc, etc... ......I thought about this after and realized that the IEC syntax already guarantees that variables and functions shouldn't be the IEC data types, so why not just define the data types the way we want them. #define SINT char #define INT short etc.... > > ///////////////////////////////////// > > > // Entry for named argument list > > > ///////////////////////////////////// > > > void Step(void); > > > > -- I would also include a function that allows a normal > > > -- argument list here eg. void Step(bool RUN, R32 XIN, IEC_TIME CYCLE); > > > -- and then define it below, as in my previous example > > > If I understand this correctly, you would prefer that the > class definition have no embedded functions. The body of all > functions should be in a seperate file. The advantages is > that the overall code size would be smaller however you > loose some opportunities for optimisation (the overload > conversion routine borders on an inline function). To clarify - I want the Step function to be overloaded, in this case with void Step() and void Step(bool RUN, R32 XIN, IEC_TIME CYCLE) The body of the function can be stored separately in another file, I have just been writing them in a single class to keep them looking compact. The reason that we need two functions is that in the standard you could call the function the following ways bool RUN; R32 XIN; IEC_TIME CYCLE; Step(RUN, XIN, CYCLE); // This uses Step(bool RUN, R32 XIN, IEC_TIME CYCLE) Step(RUN := RUN); // This uses Step() Step(RUN := RUN, XIN := XIN); // This uses Step() Step(XIN := XIN, RUN := RUN); // This uses Step() Step(RUN := RUN, CYCLE := CYCLE); // This uses Step() Step(CYCLE := CYCLE, XIN := XIN, RUN := RUN); // This uses Step() Step(CYCLE := CYCLE); // This uses Step() Etc.... > > You might want to try implementing a normal function with a return > > value now. Take for example the function definition below. It is not > > much different from a FUNCTION_BLOCK conversion. > > I'm working on it, I'm working on it!!! > > Biggest problem at the moment is that the translator does not > recognise the BCD_TO_INT and INT_TO_BCD routines (to be precise, > the return value type checking fails...) For now you could leave the type checking to the C++ compiler, and just spit out what you can (if bison/flex will let you). > > -- Normal functions are easier to convert, just define them as > > -- normal functions. > > > I'll see what I can do, I have seen some "named argument" functions > where some of the arguments are left at their default values. > >From the code I have seen, functions should be call independent > (eg: not having any persistent internal variables) I'm sure the IEC guys thought the named arguments idea was a good one at the time but it sure leads to some potential programming conflicts and execution bottlenecks. I think they do specify only a single return value, so maybe functions could be defined as classes, and instantiated (declared) once globally, or you could do it like this, WEIGHT temp = new WEIGH(); result = temp.Step(arg1, arg2, arg3); delete temp; Hope these comments help, Hugh _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
H

Harald Albrecht

> SINT => __iec_sint (char) > INT => __iec_int (short) > DINT => __iec_dint (long) > LINT => __iec_lint (long long) > etc, etc, etc... Please watch the double leading underscores -- they do not mix well with C++ compilers. In general, identifiers beginning with two underscores are reserved for compiler-internal purposes. I learned that quite some time ago the hard way when the RPC headers on RedHat/Alpha 64bit were broken. They declared their own internal datatypes but the C++ compiler misinterpreted them to be overloaded operators etc. and did not accept them in function signatures (the error messages from g++ were funny though). Just my .02e Harald -- Harald Albrecht Chair of Process Control Engineering RWTH Aachen University of Technology Turmstrasse 46, D-52064 Aachen, Germany Tel.: +49 241 80-7703, Fax: +49 241 8888-238 _______________________________________________ LinuxPLC mailing list [email protected] http://linuxplc.org/mailman/listinfo/linuxplc
 
Top