CompileDES  3.09
Executable-Code Generation from Synchronised libFAUDES Automata
cgp_iec61131st.cpp
Go to the documentation of this file.
1 
3 /*
4  FAU Discrete Event Systems Library (libFAUDES)
5 
6  Copyright (C) 2010, 2016 Thomas Moor
7 
8 */
9 
10 // my includes
11 #include "cgp_iec61131st.h"
12 
13 
14 /*
15 ******************************************************************
16 ******************************************************************
17 ******************************************************************
18 
19 Iec611311stGenerator implementation --- class maintenance
20 
21 ******************************************************************
22 ******************************************************************
23 ******************************************************************
24 */
25 
26 
27 // Register derived class
29 
30 
31 // Iec61131stCodeGenerator(void)
33  FD_DCG("Iec61131stCodeGenerator(" << this << ")::Iec61131stCodeGenerator()");
34 }
35 
36 
37 // Iec61131stCodeGenerator(void)
39  FD_DCG("Iec61131stCodeGenerator(" << this << ")::~Iec61131stCodeGenerator()");
40 }
41 
42 
43 // clear
45  FD_DCG("Iec61131stCodeGenerator::Clear()");
46  // call base
48  // my flavor of defaults
49  mPrefix="fcg_";
50  mWordType="BYTE";
51  mWordSize=8;
52  mIntegerType="INT";
53  mIntegerSize=16;
54  mIecDeclarePhysical="VAR_EXTERNAL";
55  mLiteralCyclic="";
56 }
57 
58 //DoReadTargetConfiguration(rTr)
60  FD_DCG("Iec61131stCodeGenerator::DoReadTargetConfiguration()");
61  // base
63  // iec option
64  Token token;
65  if(rTr.ExistsBegin("IecDeclarePhysical")) {
66  rTr.ReadBegin("IecDeclarePhysical",token);
67  mIecDeclarePhysical= token.AttributeStringValue("val");
68  rTr.ReadEnd("IecDeclarePhysical");
69  }
70  // iec option
71  if(rTr.ExistsBegin("IncludeCyclic"))
72  rTr.ReadVerbatim("IncludeCyclic",mLiteralCyclic);
73  if(rTr.ExistsBegin("IecTimeOperators")) {
74  rTr.ReadBegin("IecTimeOperators",token);
75  mHasIecTimeOperators= token.AttributeIntegerValue("val");
76  rTr.ReadEnd("IecTimeOperators");
77  }
78 }
79 
80 //DoWriteTargetConfiguration(rTw)
82  FD_DCG("Iec61131stCodeGenerator::DoWriteTargetConfiguration()");
83  // base
85  // code option
86  Token token;
87  token.SetEmpty("IecDeclarePhysical");
88  token.InsAttributeString("val",mIecDeclarePhysical);
89  rTw.Write(token);
90  // code option
91  if(mLiteralCyclic.size()>0)
92  rTw.WriteVerbatim("IncludeCyclic",mLiteralCyclic);
93  token.SetEmpty("IecTimeOperators");
94  token.InsAttributeBoolean("val",mHasIecTimeOperators);
95  rTw.Write(token);
96 }
97 
98 
99 
100 /*
101 ******************************************************************
102 ******************************************************************
103 ******************************************************************
104 
105 Iec611311stGenerator implementation --- code organisation
106 
107 ******************************************************************
108 ******************************************************************
109 ******************************************************************
110 */
111 
112 // DoCompile()
114  // call base
116  // my preferences
118  FCG_VERB1("Iec61131stCodeGenerator::Compile(): prefer compiled bitmasks over bit-address maths");
119  mBitAddressArithmetic = false;
120  }
121  // set array for state for returnvalue
123  FCG_VERB1("Iec61131stCodeGenerator::Compile(): using array for state as return value datatype");
124  mArrayForState = true;
125  }
126 }
127 
128 
129 // DoGenerate()
131  // cut and paste from base
132  mBitarrays.clear();
133  // doit: say hello
134  Comment("************************************************");
135  Comment("CodeGenerator: Target IEC 61131 Structured Text ");
136  Comment("************************************************");
137  LineFeed(1);
138  Comment(std::string("CompileDES ") + VersionString());
139  Comment(std::string("Configuration: ") + Name());
140  LineFeed(2+1);
141  // snippets
142  LiteralPrepend();
143  // doit: function block to host cyclic code
145  // doit: functions to host lookup tables
147  // snippets
148  LiteralAppend();
149  Comment("************************************************");
150  Comment("CodeGenerator: Generated Code Ends Here ");
151  Comment("************************************************");
152 }
153 
154 
155 
156 // code blocks: cyclic function block
158  Comment("************************************************");
159  Comment("* function block to host cyclic code *");
160  Comment("************************************************");
161  LineFeed(2);
162  Output() << "FUNCTION_BLOCK " << mPrefix <<"cyclic_fb";
163  LineFeed(2);
164  Output() << "VAR_INPUT";
165  LineFeed(2);
166  DeclareReset();
167  Output() << "END_VAR";
168  LineFeed(2);
169  Output() << "VAR_OUTPUT";
170  LineFeed(2);
171  DeclareStatus();
175  Output() << "END_VAR";
176  LineFeed(2);
177  Output() << "VAR";
178  LineFeed(2);
183  DeclareTimers();
184  DeclareAux();
187  DeclareSystime();
188  Output() << "END_VAR";
189  LineFeed(2);
190  if(CountImportSymbolicIo()>0) {
191  Output() << "VAR_EXTERNAL";
192  LineFeed(2);
194  Output() << "END_VAR";
195  LineFeed(2);
196  }
197  if(CountImportPhysicalIo()>0) {
199  LineFeed(2);
201  Output() << "END_VAR";
202  LineFeed(2);
203  }
204  LiteralCyclic();
206  DecrementTimers();
208  LineFeed(1+2);
209  Comment("************************************************");
210  Comment("* end of cyclic function block *");
211  LineFeed(1);
212  Output() << "END_FUNCTION_BLOCK";
213  LineFeed();
214  LineFeed(2+1);
215 }
216 
217 
218 // code blocks: function to host lookup tables
220  if(mEventNameLookup) {
221  Comment("************************************************");
222  Comment("* function to host the event name loopup table *");
223  Comment("************************************************");
224  LineFeed(2);
225  Output() << "FUNCTION " << mPrefix <<"event_lookup_f" << " : STRING";
226  LineFeed(2);
227  Output() << "VAR_INPUT";
228  LineFeed();
229  Output() << "IDX : " << mIntegerType <<";";
230  LineFeed();
231  Output() << "END_VAR";
232  LineFeed(2);
233  Output() << "VAR CONSTANT";
234  LineFeed();
236  Output() << "END_VAR";
237  LineFeed(2);
238  Output() << mPrefix <<"event_lookup_f" << " := " << mPrefix << "event_lookup[IDX]" << ";";
239  LineFeed(2);
240  Output() << "END_FUNCTION";
241  LineFeed();
242  LineFeed(2+1);
243  }
244  if(mStateNameLookup) {
245  Comment("************************************************");
246  Comment("* function to host state name loopup tables *");
247  Comment("************************************************");
248  LineFeed(2);
249  Output() << "FUNCTION " << mPrefix <<"state_lookup_f" << " : STRING";
250  LineFeed(2);
251  Output() << "VAR_INPUT";
252  LineFeed();
253  Output() << "GID : " << mIntegerType <<";";
254  LineFeed();
255  Output() << "IDX : " << mIntegerType <<";";
256  LineFeed();
257  Output() << "END_VAR";
258  LineFeed(2);
259  Output() << "VAR CONSTANT";
260  LineFeed();
262  Output() << "END_VAR";
263  LineFeed(2);
264  Output() << mPrefix << "state_lookup_f:='out_of_range';";
265  LineFeed();
266  Output() << "CASE GID OF";
267  LineFeed();
268  for(size_t gid=0; gid<Size(); ++gid) {
269  if(mHasStateNames[gid])
270  Output() << ToStringInteger(gid) << ": "
271  << mPrefix <<"state_lookup_f" << " := " << mPrefix << "state_lookup_" << ToStringInteger(gid) << "[IDX]" << ";";
272  else
273  Output() << ToStringInteger(gid) << ": "
274  << mPrefix <<"state_lookup_f" << " := CONCAT('s'," << mIntegerType << "_TO_STRING(IDX))" << ";";
275  LineFeed();
276  }
277  Output() << "END_CASE;";
278  LineFeed(2);
279  Output() << "END_FUNCTION";
280  LineFeed();
281  LineFeed(2+1);
282  }
283 }
284 
285 
286 // extend base reset by iec timers
288  FD_DCG("Iec61131stCodeGenerator(" << this << ")::StateReset()");
289  // call base
291  // have iec extras
292  UpdateSystime();
293 }
294 
295 
296 
297 // code blocks: extend base timer declaration
299  // call base
301  // extend
302  if(TimersBegin()!=TimersEnd()) {
303  Comment("elapsed time since last invokation");
304  VariableDeclare(TargetAddress(AA("time_elapsed")),"TIME");
305  LineFeed();
306  }
307 }
308 
309 
310 // code blocks: import interface variables
312  // bail out on unconfigured
313  if(mIecDeclarePhysical=="") return 0;
314  // figure count
315  int iocnt=0;
316  LineIterator lit=LinesBegin();
317  for(;lit!=LinesEnd();++lit) {
318  std::string lineaddr= lit->second.mAddress;
319  if(lineaddr.size()<1) continue; // paranoia
320  if(lineaddr.at(0)=='%') iocnt++;
321  }
323  for(;ait!=ActionAddressesEnd();++ait) {
324  if(!ait->second.mSetClr) continue;
325  std::string actaddr= ait->second.mAddress;
326  if(actaddr.size()<1) continue; // paranoia
327  if(actaddr.at(0)=='%') ++iocnt;
328  }
329  return iocnt;
330 }
331 
332 
334  Comment("import physical i/o addresses");
335  LineIterator lit=LinesBegin();
336  for(;lit!=LinesEnd();++lit) {
337  std::string lineaddr= lit->second.mAddress;
338  if(lineaddr.size()<1) continue; // paranoia
339  if(lineaddr.at(0)=='%') {
340  Output() << "AT " << lineaddr << " : BOOL;";
341  LineFeed();
342  }
343  }
345  for(;ait!=ActionAddressesEnd();++ait) {
346  if(!ait->second.mSetClr) continue;
347  std::string actaddr= ait->second.mAddress;
348  if(actaddr.size()<1) continue; // paranoia
349  if(actaddr.at(0)=='%') {
350  Output() << "AT " << actaddr << " : BOOL;";
351  LineFeed();
352  }
353  }
354  LineFeed(1);
355 }
356 
357 // code blocks: import interface variables
359  // figure count
360  int iocnt=0;
361  LineIterator lit=LinesBegin();
362  for(;lit!=LinesEnd();++lit) {
363  std::string lineaddr= lit->second.mAddress;
364  if(lineaddr.size()<1) continue; // paranoia
365  if(lineaddr.at(0)!='%') iocnt++;
366  }
368  for(;ait!=ActionAddressesEnd();++ait) {
369  if(!ait->second.mSetClr) continue;
370  std::string actaddr= ait->second.mAddress;
371  if(actaddr.size()<1) continue; // paranoia
372  if(actaddr.at(0)!='%') ++iocnt;
373  }
374  return iocnt;
375 }
376 
378  Comment("import i/o variables and addresses");
379  LineIterator lit=LinesBegin();
380  for(;lit!=LinesEnd();++lit) {
381  std::string lineaddr= lit->second.mAddress;
382  if(lineaddr.size()<1) continue; // paranoia
383  if(lineaddr.at(0)!='%') {
384  VariableDeclare(lineaddr,"BOOL");
385  LineFeed();
386  }
387  }
389  for(;ait!=ActionAddressesEnd();++ait) {
390  if(!ait->second.mSetClr) continue;
391  std::string actaddr= ait->second.mAddress;
392  if(actaddr.size()<1) continue; // paranoia
393  if(actaddr.at(0)!='%') {
394  VariableDeclare(actaddr,"BOOL");
395  LineFeed();
396  }
397  }
398  LineFeed(1);
399 }
400 
401 
402 // code blocks: literal prepend from configuration
404  if(mLiteralCyclic.size()==0) return;
405  Comment("************************************************");
406  Comment("* extra cyclic code from configuration *");
407  LineFeed();
408  Output() << mLiteralCyclic;
409  LineFeed(2);
410  Comment("* end of extra code from configuration *");
411  Comment("************************************************");
412  LineFeed(1+1);
413 }
414 
415 // generate code
417  if((mEventExecutionHook!="") || (mStateUpdateHook!=""))
418  Comment("call hooks");
419  if(mEventExecutionHook!="") {
420  std::string hook = mEventExecutionHook + "(" + TargetExpression(AA("exec_event")) + ");";
421  Output() << hook;
422  LineFeed(1);
423  }
424  if(mStateUpdateHook!="") {
425  std::string hook = mStateUpdateHook + "(" + TargetAddress(AA("parallel_state")) + ");";
426  Output() << hook;
427  LineFeed(1);
428  }
429  LineFeed(1);
430 }
431 
432 
433 // code blocks: organise time decrement as inline code snippet
435  if(TimersBegin()==TimersEnd()) return;
436  // timer update function
437  Comment("************************************************");
438  Comment("* update timer states *");
439  LineFeed(2);
440  Output() << "IF " << TargetAddress(AA("time_elapsed")) << " > " << TimeConstant(0) << " THEN";
441  LineFeed();
442  IndentInc();
443  TimerIterator tit= TimersBegin();
444  for(;tit!=TimersEnd();++tit) {
445  AA cnt("timer_" + tit->second.mAddress + "_cnt");
446  AA run("timer_" + tit->second.mAddress + "_run");
447  Output() << "IF " << TargetAddress(AA(run)) << " THEN";
448  IndentInc();
449  LineFeed();
450  Output() << "IF " << TargetAddress(cnt) << " > " << TargetAddress(AA("time_elapsed")) << " THEN";
451  IndentInc();
452  LineFeed();
454  Output() << TargetAddress(cnt) << " := " << TargetAddress(cnt) << " - " << TargetAddress(AA("time_elapsed")) << ";";
455  } else {
456  Output() << TargetAddress(cnt) << " := SUB_TIME(" << TargetAddress(cnt) << ", " << TargetAddress(AA("time_elapsed")) << ");";
457  }
458  IndentDec();
459  LineFeed();
460  Output() << "ELSE";
461  LineFeed();
462  IndentInc();
463  Output() << TargetAddress(cnt) << " := TIME#0s;";
464  IndentDec();
465  LineFeed();
466  Output() << "END_IF;";
467  IndentDec();
468  LineFeed();
469  Output() << "END_IF;";
470  LineFeed();
471  }
472  IndentDec();
473  Output() << "END_IF;";
474  LineFeed();
475  LineFeed(1+2);
476 
477 }
478 
479 // code blocks: mimique systime by iec timer
481  if(TimersBegin()==TimersEnd()) return;
482  Comment("iec timer to simulate system time");
483  VariableDeclare(TargetAddress(AA("systime_ton")),"TON");
484  VariableDeclare(TargetAddress(AA("systime_recent")),"TIME");
485  VariableDeclare(TargetAddress(AA("systime_now")),"TIME");
486  LineFeed();
487 }
488 
489 // code blocks: mimique systime by iec timer
491  if(TimersBegin()==TimersEnd()) return;
492  Comment("do reset/track systime");
493  Output() << "IF " << mPrefix << "exec_event = -1 " << " THEN" << std::endl;
494  Output() << " " << mPrefix << "systime_ton(IN:=false);" << std::endl;
495  Output() << " " << mPrefix << "systime_recent := TIME#0ms;" << std::endl;
496  Output() << "END_IF;" << std::endl;
497  Output() << mPrefix << "systime_ton(IN:=true, PT:=TIME#2h, ET=>" << mPrefix << "systime_now);" << std::endl;
499  Output() << mPrefix << "time_elapsed := " << mPrefix << "systime_now - " << mPrefix << "systime_recent;" << std::endl;
500  } else {
501  Output() << mPrefix << "time_elapsed := SUB_TIME(" << mPrefix << "systime_now," << mPrefix << "systime_recent);" << std::endl;
502  }
503  Output() << "IF " << mPrefix << "systime_now < TIME#1h THEN" << std::endl;
504  Output() << " " << mPrefix << "systime_recent := " << mPrefix << "systime_now;" << std::endl;
505  Output() << "ELSE" << std::endl;
506  Output() << " " << mPrefix << "systime_ton(IN:=false, PT:=TIME#2h);" << std::endl;
507  Output() << " " << mPrefix << "systime_ton(IN:=true, PT:=TIME#2h);" << std::endl;
508  Output() << " " << mPrefix << "systime_recent := TIME#0ms;" << std::endl;
509  Output() << "END_IF;" << std::endl;
510  LineFeed(1);
511 }
512 
513 
514 
515 /*
516 ******************************************************************
517 ******************************************************************
518 ******************************************************************
519 
520 Iec611311stGenerator implementation --- code primitives
521 
522 ******************************************************************
523 ******************************************************************
524 ******************************************************************
525 */
526 
527 
528 // generate code
529 void Iec61131stCodeGenerator::Comment(const std::string& text) {
530  // write comment
531  if(!mMuteComments) {
532  Output() << "(* " << text << " *)";
533  LineFeed();
534  }
535  // call base to record muted comments
537 }
538 
539 // declaration template
540 void Iec61131stCodeGenerator::VariableDeclare(const std::string& laddr, const std::string& ltype) {
541  if((ltype != mIntegerType) && (ltype != mWordType) && (ltype != "BOOL") && (ltype != "STRING") && (ltype != "TIME") && (ltype != "TON"))
542  FCG_ERR("Iec61131stCodeGenerator::VariableDeclare(): unsupported type [" << ltype << "]");
543  Output() << laddr << " : " << ltype << ";";
544  LineFeed();
545 }
546 
547 void Iec61131stCodeGenerator::VariableDeclare(const std::string& laddr, const std::string& ltype, const std::string& lval) {
548  if((ltype != mIntegerType) && (ltype != mWordType) && (ltype != "BOOL") && (ltype != "STRING") && (ltype != "TIME"))
549  FCG_ERR("Iec61131stCodeGenerator::VariableDeclare(): unsupported type [" << ltype << "]");
550  Output() << laddr << " : " << ltype << " := " << lval << ";";
551  LineFeed();
552 }
553 
554 // address transformation
555 std::string Iec61131stCodeGenerator::TargetAddress(const AA& address) {
556  std::string res;
557  // cosmetic: use uppercase plain names for input/output variables
558  if(address=="reset") res="RESET";
559  if(address=="status") res="STATUS";
560  if(mEventNameLookup) {
561  if(address=="recent_event") res="RECENT_EVENT";
562  if(address.find("pending_events")==0)
563  if(address!="pending_events_t")
564  res="PENDING_EVENTS"+address.substr(14);
565  if(address.find("enabled_events")==0)
566  if(address!="enabled_events_t")
567  res="ENABLED_EVENTS"+address.substr(14);
568  }
569  if(mStateNameLookup) {
570  if(address.find("parallel_state")==0)
571  if(address!="parallel_state_t")
572  res="PARALLEL_STATE"+address.substr(14);
573  }
574  // base case
575  if(res=="") res=mPrefix+address;
576  return res;
577 }
578 
579 // address transformation
581  return AX(TargetAddress(address));
582 }
583 
584 // generate code: integer
587 }
588 
589 // generate code: integer
590 void Iec61131stCodeGenerator::IntegerDeclare(const AA& address, int val){
592 }
593 
594 // generate code: integer
595 void Iec61131stCodeGenerator::IntegerAssign(const AA& address, int val){
596  Output() << TargetAddress(address) << " := " << IntegerConstant(val) << ";";
597  LineFeed();
598 }
599 
600 // generate code: integer
601 void Iec61131stCodeGenerator::IntegerAssign(const AA& address, const AX& expression){
602  Output() << TargetAddress(address) << " := " << expression << ";";
603  LineFeed();
604 }
605 
606 // generate code: integer
607 void Iec61131stCodeGenerator::IntegerIncrement(const AA& address, int val) {
608  if(val>0)
609  Output() << TargetAddress(address) << " := " << TargetAddress(address) << " + " << IntegerConstant(val) << ";";
610  else if(val<0)
611  Output() << TargetAddress(address) << " := " << TargetAddress(address) << " - " << IntegerConstant(-val) << ";";
612  if(val!=0)
613  LineFeed();
614 }
615 
616 // integer maths
618  return AX(expression + " / " + IntegerConstant(val));
619 }
620 
621 // integer maths
623  return AX(expression + " MOD " + IntegerConstant(val));
624 }
625 
626 // integer maths
628  return AX("SHL( IN:=" + WordConstant(1) + " , N:=" + expression + " )" );
629 }
630 
631 // integer maths
633  return true;
634 }
635 
636 // generate code: integer
638  std::string res(ToStringInteger(val));
639  if(mIntegerType == "INT") return AX(res);
640  if(mIntegerType == "DINT") return AX("DINT#" + res);
641  if(mIntegerType == "LINT") return AX("LINT#" + res);
642  FCG_ERR("Iec61131stCodeGenerator: unsupported integer data type");
643  return AX(res);
644 }
645 
646 // generate code: conditionals
648  return AX(TargetAddress(address) + " = " + IntegerConstant(val));
649 }
650 
651 // generate code: conditionals
652 CodePrimitives::AX Iec61131stCodeGenerator::IntegerIsEq(const AA& address, const AX& expression) {
653  return AX(TargetAddress(address) + " = " + expression);
654 }
655 
656 // generate code: conditionals
658  return AX(TargetAddress(address) + " <> " + IntegerConstant(val));
659 }
660 
661 // generate code: conditionals
663  return AX(TargetAddress(address) + " != " + expression);
664 }
665 
666 // generate code: conditionals
668  return AX(TargetAddress(address) + " > " + IntegerConstant(val));
669 }
670 
671 // generate code: conditionals
673  return AX(TargetAddress(address) + " < " + IntegerConstant(val));
674 }
675 
676 
677 
678 // generate code: word
681 }
682 
683 // generate code: word
686 }
687 
688 // generate code: word
690  Output() << TargetAddress(address) << " := " << WordConstant(val) << ";";
691  LineFeed();
692 }
693 
694 // generate code: word
695 void Iec61131stCodeGenerator::WordAssign(const AA& address, const AX& expression){
696  Output() << TargetAddress(address) << " := " << expression << ";";
697  LineFeed();
698 }
699 
700 
701 // generate code: word
702 void Iec61131stCodeGenerator::WordOr(const AA& address, word_t val) {
703  Output() << TargetAddress(address) << " := " << TargetExpression(address) << " OR " << WordConstant(val) << ";";
704  LineFeed();
705 }
706 
707 // generate code: word
708 void Iec61131stCodeGenerator::WordOr(const AA& address, const AX& expression) {
709  Output() << TargetAddress(address) << " := " << TargetExpression(address) << " OR " << expression << ";";
710  LineFeed();
711 }
712 
713 // generate code: word
714 void Iec61131stCodeGenerator::WordOr(const AA& address, const AA& op1, const AA& op2) {
715  Output() << TargetAddress(address) << " := " << TargetExpression(op1) << " OR " << TargetExpression(op2) << ";";
716  LineFeed();
717 }
718 
719 // generate code: word
720 void Iec61131stCodeGenerator::WordOr(const AA& address, const AA& op1, word_t op2) {
721  Output() << TargetAddress(address) << " := " << TargetExpression(op1) << " OR " << WordConstant(op2) << ";";
722  LineFeed();
723 }
724 
725 // generate code: word
726 void Iec61131stCodeGenerator::WordAnd(const AA& address, word_t val) {
727  Output() << TargetAddress(address) << " := " << TargetAddress(address) << " AND " << WordConstant(val) << ";";
728  LineFeed();
729 }
730 
731 // generate code: word
732 void Iec61131stCodeGenerator::WordAnd(const AA& address, const AX& expression) {
733  Output() << TargetAddress(address) << " := " << TargetAddress(address) << " AND " << expression << ";";
734  LineFeed();
735 }
736 
737 // generate code: word
738 void Iec61131stCodeGenerator::WordAnd(const AA& address, const AA& op1, const AA& op2) {
739  Output() << TargetAddress(address) << " := " << TargetAddress(op1) << " AND " << TargetAddress(op2) << ";";
740  LineFeed();
741 }
742 
743 // generate code: word
744 void Iec61131stCodeGenerator::WordAnd(const AA& address, const AA& op1, word_t op2) {
745  Output() << TargetAddress(address) << " := " << TargetExpression(op1) << " AND " << WordConstant(op2) << ";";
746  LineFeed();
747 }
748 
749 // generate code: word
750 void Iec61131stCodeGenerator::WordNand(const AA& address, const AX& expression) {
751  Output() << TargetAddress(address) << " := " << TargetAddress(address) << " AND (NOT " << expression << " );";
752  LineFeed();
753 }
754 
755 // generate code: conditionals
757  return AX("( " + TargetAddress(address) + " AND " + WordConstant(0x01<<idx) + " ) <> " + WordConstant(0));
758 }
759 
760 // generate code: conditionals
762  return AX("( " + TargetAddress(address) + " AND " + WordConstant(0x01<<idx) + " ) == " + WordConstant(0));
763 }
764 
767  return AX("( " + TargetAddress(address) + " AND " + WordConstant(mask) + " ) <> " + WordConstant(0));
768 }
769 
770 // generate code: conditionals
772  return AX(TargetAddress(address) + " = " + WordConstant(val));
773 }
774 
775 // generate code: conditionals
777  return AX("NOT ( " + TargetAddress(address) + " = " + WordConstant(val) + " )");
778 }
779 
780 // generate code: word
782  // have neet zero
783  if(val==0) return AX( mWordType + "#0");
784  // construct typed hex representation
785  std::stringstream sstr;
786  sstr << mWordType << "#16#" << std::setbase(16) << std::setfill('0');
787  if(mWordType == "BYTE") sstr << std::setw(2) << (val & 0xff);
788  else if(mWordType == "WORD") sstr << std::setw(4) << (val & 0xffff);
789  else if(mWordType == "DWORD") sstr << std::setw(8) << (val & 0xffffffff);
790  else if(mWordType == "LWORD") sstr << std::setw(16) << (val & 0xffffffffffffffff);
791  else FCG_ERR("Iec61131stCodeGenerator: unsupported word data type");
792  AX res(sstr.str());
793  return res;
794 }
795 
796 
797 // generate code: bool
799  VariableDeclare(TargetAddress(address),"BOOL");
800 }
801 
802 // generate code: bool
803 void Iec61131stCodeGenerator::BooleanDeclare(const AA& address, int val){
804  std::string valstr;
805  if(val) valstr="true"; else valstr="false";
806  VariableDeclare(TargetAddress(address),"BOOL",valstr);
807 }
808 
809 // generate code: bool
810 void Iec61131stCodeGenerator::BooleanAssign(const AA& address, int val){
811  std::string valstr;
812  if(val) valstr="true"; else valstr="false";
813  Output() << TargetAddress(address) << " := " << valstr << ";";
814  LineFeed();
815 }
816 
817 // generate code: bool
818 void Iec61131stCodeGenerator::BooleanAssign(const AA& address, const AX& expression){
819  Output() << TargetAddress(address) << " := " << expression << ";";
820  LineFeed();
821 }
822 
823 // generate code: bool
825  return AX(TargetAddress(op1) + " <> " + TargetAddress(op2));
826 }
827 
828 // generate code: bool
830  return AX(TargetAddress(op1) + " = " + TargetAddress(op2));
831 }
832 
833 
834 // const-int array (local iec only helper)
836  std::stringstream strstr;
837  int newline=20;
838  if(val.size()<25) newline=25;
839  strstr << "[ " ;
840  size_t vit=0;
841  while(true) {
842  if(vit==val.size()) break;
843  strstr << IntegerConstant(val[vit]);
844  ++vit;
845  if(vit==val.size()) break;
846  strstr << ", ";
847  newline--;
848  if(newline==0) {
849  strstr << std::endl << " ";
850  newline=20;
851  }
852  }
853  strstr << " ]";
854  return AX(strstr.str());
855 }
856 
857 // const-int-array
858 void Iec61131stCodeGenerator::CintarrayDeclare(const AA& address, const std::vector<int>& val) {
859  if(val.size()==0) {
860  FCG_ERR("Iec61131stCodeGenerator::Cintarray(): ignoring empty const vector");
861  return;
862  }
863  if(val.size()>= (1ULL << (mIntegerSize-1))) {
864  FCG_ERR("Iec61131stCodeGenerator::Cwordarray(): const vector exceeds address range");
865  return;
866  }
867  // declare with initialiser
868  Output() << TargetAddress(address) << " : ARRAY[0.." << val.size()-1 << "] OF " << mIntegerType << " := ";
869  LineFeed();
870  IndentInc();
871  Output() << IntarrayConstant(val) << ";";
872  LineFeed();
873  IndentDec();
874 }
875 
876 
877 // int array access by const
879  return AA(address + "[" + ToStringInteger(index) + "]");
880 };
881 
882 // int array access by expression
884  return AA(address + "[" + TargetAddress(indexaddr) + "]");
885 };
886 
887 // tell parent class about this feature
889  return true;
890 };
891 
892 
893 // const-word array
895  std::stringstream strstr;
896  int newline=8;
897  if(val.size()<11) newline=15;
898  strstr << "[ " ;
899  size_t vit=0;
900  while(true) {
901  if(vit==val.size()) break;
902  strstr << WordConstant(val[vit]);
903  ++vit;
904  if(vit==val.size()) break;
905  strstr << ", ";
906  newline--;
907  if(newline==0) {
908  strstr << std::endl << " ";
909  newline=8;
910  }
911  }
912  strstr << " ]";
913  return AX(strstr.str());
914 }
915 
916 // const-word-array
917 void Iec61131stCodeGenerator::CwordarrayDeclare(const AA& address, const std::vector<word_t>& val) {
918  if(val.size()==0) {
919  FCG_ERR("Iec61131stCodeGenerator::Cwordarray(): ignoring empty const vector");
920  return;
921  }
922  if(val.size()>= (1ULL << (mIntegerSize-1))) {
923  FCG_ERR("Iec61131stCodeGenerator::Cwordarray(): const vector exceeds addres range");
924  return;
925  }
926  // declare with initialiser
927  Output() << TargetAddress(address) << " : ARRAY[0.." << val.size()-1 << "] OF " << mWordType << " := ";
928  LineFeed();
929  IndentInc();
930  Output() << WordarrayConstant(val) << ";";
931  LineFeed();
932  IndentDec();
933 }
934 
935 
936 // word-array access by const
938  return AA(address + "[" + ToStringInteger(index) + "]");
939 };
940 
941 // word array access by expression
943  return AA(address + "[" + TargetAddress(indexaddr) + "]");
944 };
945 
946 // tell parent class about this feature
948  return true;
949 };
950 
951 // generate code: string constant
953  AX res;
954  res.append(1,'\'');
955  for(std::size_t i=0; i<val.length(); ++i) {
956  char c=val.at(i);
957  // terminate
958  if(c==0) break;
959  // quote quote
960  if(c=='\'') { res.append("$'"); continue; }
961  // quote dollar
962  if(c=='$') { res.append("$$"); continue; }
963  // printable ascii
964  if((c>=0x20) && (c<0x7f)) { res.append(1,c); continue; };
965  // error on other (alternative: use octal encoding to support utf8)
966  FCG_ERR("EmbeddedcCodeGenerator: non-printable ascii or other encoding unsupported [" << val <<"]");
967  }
968  res.append(1,'\'');
969  return res;
970 }
971 
972 // literal const-string array
974  std::stringstream strstr;
975  int newline;
976  if(val.size()<4) {
977  newline=4;
978  strstr << "[ ";
979  } else {
980  newline=1;
981  strstr << "[ " << std::endl;
982  }
983  size_t vit=0;
984  while(true) {
985  if(vit==val.size()) break;
986  strstr << StringConstant(val[vit]);
987  ++vit;
988  if(vit==val.size()) break;
989  strstr << ", ";
990  newline--;
991  if(newline==0) {
992  strstr << std::endl;
993  newline=1;
994  }
995  }
996  strstr << " ]";
997  return AX(strstr.str());
998 }
999 
1000 
1001 // const-string-array
1002 void Iec61131stCodeGenerator::CstrarrayDeclare(const AA& address, const std::vector<std::string>& val) {
1003  if(val.size()==0) {
1004  FCG_ERR("Iec61131stCodeGenerator::Cstrarray(): ignoring empty const vector");
1005  return;
1006  }
1007  if(val.size()>= (1ULL << (mIntegerSize-1))) {
1008  FCG_ERR("Iec61131stCodeGenerator::Cstrarray(): const vector exceeds addres range");
1009  return;
1010  }
1011  // figure string size
1012  size_t len=0;
1013  for(size_t i=0; i<val.size(); ++i)
1014  if(val[i].size()>len) len=val[i].size();
1015  // declare with initialiser
1016  Output() << TargetAddress(address) << " : ARRAY[0.." << val.size()-1 << "] OF STRING[" << ToStringInteger(len) <<"] := ";
1017  LineFeed();
1018  IndentInc();
1019  Output() << StrarrayConstant(val) << ";";
1020  LineFeed();
1021  IndentDec();
1022 }
1023 
1024 
1025 // string-array access by const
1027  return AA(address + "[" + ToStringInteger(index) + "]");
1028 };
1029 
1030 // string array access by expression
1032  return AA(address + "[" + TargetAddress(indexaddr) + "]");
1033 };
1034 
1035 // tell parent class about this feature
1037  return true;
1038 };
1039 
1040 // int-array
1041 void Iec61131stCodeGenerator::IntarrayDeclare(const AA& address, const std::vector<int>& val) {
1042  if(val.size()==0) {
1043  FCG_ERR("Iec61131stCodeGenerator::Intarray(): ignoring empty const vector");
1044  return;
1045  }
1046  if(val.size()>= (1ULL << (mIntegerSize-1))) {
1047  FCG_ERR("Iec61131stCodeGenerator::Intarray(): const vector exceeds addres range");
1048  return;
1049  }
1050  // declare with initialiser
1051  Output() << TargetAddress(address) << " : ARRAY[0.." << val.size()-1 << "] OF " << mIntegerType << " := ";
1052  LineFeed();
1053  IndentInc();
1054  Output() << IntarrayConstant(val) << ";";
1055  LineFeed();
1056  IndentDec();
1057 }
1058 
1059 
1060 // int-array
1061 void Iec61131stCodeGenerator::IntarrayDeclare(const AA& address, int len) {
1062  if(len==0) {
1063  FCG_ERR("Iec61131stCodeGenerator::Intarray(): ignoring empty const vector");
1064  return;
1065  }
1066  if(len>= (1LL << (mIntegerSize-1))) {
1067  FCG_ERR("Iec61131stCodeGenerator::Intarray(): const vector exceeds addres range");
1068  return;
1069  }
1070  Output() << TargetAddress(address) << " : ARRAY[0.." << len-1 << "] OF " << mIntegerType << ";";
1071  LineFeed();
1072 }
1073 
1074 // word-array access by const
1076  return AA(address + "[" + ToStringInteger(index) + "]");
1077 };
1078 
1079 // word array access by expression
1081  return AA(address + "[" + TargetAddress(indexaddr) + "]");
1082 };
1083 
1084 // tell parent class about this feature
1086  return true;
1087 };
1088 
1089 
1090 // word-array
1091 void Iec61131stCodeGenerator::WordarrayDeclare(const AA& address, const std::vector<word_t>& val) {
1092  if(val.size()==0) {
1093  FCG_ERR("Iec61131stCodeGenerator::Wordarray(): ignoring empty const vector");
1094  return;
1095  }
1096  if(val.size()>= (1ULL << (mIntegerSize-1))) {
1097  FCG_ERR("Iec61131stCodeGenerator::Wordarray(): const vector exceeds addres range");
1098  return;
1099  }
1100  // declare with initialiser
1101  Output() << TargetAddress(address) << " : ARRAY[0.." << val.size()-1 << "] OF " << mWordType << " := ";
1102  LineFeed();
1103  IndentInc();
1104  Output() << WordarrayConstant(val) << ";";
1105  LineFeed();
1106  IndentDec();
1107 }
1108 
1109 
1110 // word-array
1111 void Iec61131stCodeGenerator::WordarrayDeclare(const AA& address, int len) {
1112  if(len==0) {
1113  FCG_ERR("Iec61131stCodeGenerator::Wordarray(): ignoring empty const vector");
1114  return;
1115  }
1116  if(len>= (1LL << (mIntegerSize-1))) {
1117  FCG_ERR("Iec61131stCodeGenerator::Wordarray(): const vector exceeds addres range");
1118  return;
1119  }
1120  Output() << TargetAddress(address) << " : ARRAY[0.." << len-1 << "] OF " << mWordType << ";";
1121  LineFeed();
1122 }
1123 
1124 // word-array access by const
1126  return AA(address + "[" + ToStringInteger(index) + "]");
1127 };
1128 
1129 // word array access by expression
1131  return AA(address + "[" + TargetAddress(indexaddr) + "]");
1132 };
1133 
1134 // tell parent class about this feature
1136  return true;
1137 };
1138 
1139 
1140 
1141 // event set
1142 /*
1143 void Iec61131stCodeGenerator::EventSetDeclareImport(const AA& address) {
1144  // same as declare
1145  EventSetDeclare(address);
1146 }
1147 */
1148 
1149 // generate code: conditionals
1150 void Iec61131stCodeGenerator::IfTrue(const AX& expression) {
1151  Output() << "IF " << expression << " THEN";
1152  LineFeed();
1153  IndentInc();
1154 }
1155 
1156 // generate code: conditionals
1157 void Iec61131stCodeGenerator::IfFalse(const AX& expression) {
1158  Output() << "IF NOT " << expression << " THEN";
1159  LineFeed();
1160  IndentInc();
1161 }
1162 
1163 // generate code: conditionals
1164 void Iec61131stCodeGenerator::IfWord(const AX& expression) {
1165  Output() << "IF ( " << expression << " ) <> " << WordConstant(0) << " THEN";
1166  LineFeed();
1167  IndentInc();
1168 }
1169 
1170 // generate code: conditionals
1172  IndentDec();
1173  Output() << "ELSE";
1174  LineFeed();
1175  IndentInc();
1176 }
1177 
1178 // generate code: conditionals
1180  IndentDec();
1181  Output() << "ELSIF " << expression << " THEN";
1182  LineFeed();
1183  IndentInc();
1184 }
1185 
1186 // generate code: conditionals
1188  IndentDec();
1189  Output() << "END_IF;";
1190  LineFeed();
1191 }
1192 
1193 // switch control
1195  Output() << "CASE " << TargetAddress(address) << " OF";
1196  LineFeed();
1197 }
1198 
1199 // switch control
1200 void Iec61131stCodeGenerator::SwitchCase(const AA& address, int val){
1201  (void) address;
1202  Output() << IntegerConstant(val) << ":";
1203  LineFeed();
1204  IndentInc();
1205 }
1206 
1207 // switch control
1208 void Iec61131stCodeGenerator::SwitchCases(const AA& address, int from, int to){
1209  // bail out on empty
1210  if(from>to) {
1211  FCG_VERB0("CodeGenerator: WARNING: inconsistent empty range of switch-cases");
1212  IndentInc();
1213  return;
1214  }
1215  // simplify to singleton
1216  if(from==to) {
1217  SwitchCase(address, from);
1218  return;
1219  }
1220  // do range
1221  Output() << IntegerConstant(from) << " .. " << IntegerConstant(to) << ":";
1222  LineFeed();
1223  IndentInc();
1224 }
1225 
1226 // switch control
1227 void Iec61131stCodeGenerator::SwitchCases(const AA& address, const std::set< int >& vals){
1228  // bail out on empty
1229  if(vals.empty()) {
1230  FCG_VERB0("CodeGenerator: WARNING: inconsistent empty range of switch-cases");
1231  IndentInc();
1232  return;
1233  }
1234  // simplify to range
1235  int from=*vals.begin();
1236  int to= *(--vals.end());
1237  if(to+1-from== (int) vals.size()) {
1238  SwitchCases(address,from,to);
1239  return;
1240  }
1241  // do set
1242  std::set< int >::const_iterator vit=vals.begin();
1243  for(; vit!=vals.end(); ++ vit) {
1244  if(vit!=vals.begin()) Output() << ", ";
1245  Output() << IntegerConstant(*vit);
1246  }
1247  Output() << ":";
1248  LineFeed();
1249  IndentInc();
1250 }
1251 
1252 // switch control
1254  IndentDec();
1255 }
1256 
1257 // switch control
1259  Output() << "END_CASE;";
1260  LineFeed();
1261 }
1262 
1263 // switch control
1265  return true;
1266 }
1267 
1268 // loops
1270  Output() << "WHILE true DO";
1271  LineFeed();
1272  IndentInc();
1273 }
1274 
1275 // loops
1276 void Iec61131stCodeGenerator::LoopBreak(const AX& expression) {
1277  Output() << "IF " << expression << " THEN EXIT; END_IF;";
1278  LineFeed();
1279 }
1280 
1281 // loops
1283  IndentDec();
1284  Output() << "END_WHILE;";
1285  LineFeed();
1286 }
1287 
1288 // loops
1290  Output() << "RETURN;";
1291  LineFeed();
1292 }
1293 
1294 // timer
1295 void Iec61131stCodeGenerator::TimerDeclare(const AA& address, const std::string &val) {
1296  // todo: type check time literal
1297  (void) val;
1298  VariableDeclare(TargetAddress(AA("timer_"+address+"_cnt")),"TIME");
1299  VariableDeclare(TargetAddress(AA("timer_"+address+"_run")),"BOOL");
1300 }
1302  Output() << TargetAddress(AA("timer_"+address+"_run")) << " := true;";
1303  LineFeed();
1304 }
1306  Output() << TargetAddress(AA("timer_"+address+"_run")) << " := false;";
1307  LineFeed();
1308 }
1309 void Iec61131stCodeGenerator::TimerReset(const AA& address, const std::string& litval) {
1310  Output() << TargetAddress(AA("timer_"+address+"_cnt")) << " := " << litval << ";";
1311  LineFeed();
1312 }
1313 
1315  return AX(TargetAddress(AA("timer_"+address+"_run")) + " AND (" +
1316  TargetAddress(AA("timer_"+address+"_cnt")) + " <= " + TimeConstant(0) + ")");
1317 }
1318 
1320  std::string res("TIME#" + ToStringInteger(val) + "ms");
1321  return AX(res);
1322 }
1323 
1324 
1325 // output actions
1326 void Iec61131stCodeGenerator::RunActionSet(const std::string& address) {
1327  Output() << address << " := true;" ;
1328  LineFeed();
1329 }
1330 void Iec61131stCodeGenerator::RunActionClr(const std::string& address) {
1331  Output() << address << " := false;" ;
1332  LineFeed();
1333 }
1335  Output() << expression << ";";
1336  LineFeed();
1337 }
1338 
1339 Iec61131stCodeGenerator::IECVariableType Iec61131stCodeGenerator::CurrentVariableType(void) {
1340  return mCurrentVariableType;
1341 }
1342 
1343 void Iec61131stCodeGenerator::CurrentVariableType(const IECVariableType & type) {
1344  mCurrentVariableType = type;
1345 }
1346 
1347