home *** CD-ROM | disk | FTP | other *** search
/ AI Game Programming Wisdom / AIGameProgrammingWisdom.iso / SourceCode / 02 Useful Techniques / 08 Zarozinski / src / ffllapi / FuzzyVariableBase.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-11-29  |  32.3 KB  |  1,459 lines

  1. //
  2. // File:    FuzzyVariableBase.cpp
  3. //
  4. // Purpose:    This file contains ancestor code for the FuzzyVariable classes
  5. //
  6. // Copyright ⌐ 1999-2001 Louder Than A Bomb! Software
  7. //
  8. // This file is part of the FFLL (Free Fuzzy Logic Library) project (http://ffll.sourceforge.net)
  9. // It is released under the BSD license, see http://ffll.sourceforge.net/license.txt for the full text.
  10. // 
  11.  
  12. #include "FuzzyVariableBase.h"
  13. #include "FuzzySetBase.h"
  14. #include "FuzzyModelBase.h"
  15. #include "MemberFuncBase.h"
  16.  
  17. #include <fstream>
  18.  
  19. #ifdef _DEBUG
  20. #undef THIS_FILE
  21. static char THIS_FILE[] = __FILE__;
  22. #endif
  23.  
  24. // init static variables to a reasonable value. We add one to each array count to
  25. // account for the fact that we start counting at zero.
  26. // For example if we only had 5 elements in the array and the variable range was 0 to 5,
  27. // we'd expect each element to be 1 'x' value, but then the 'x' value at values[4] (the
  28. // fifth element) would be 4 and we EXPECT 5.The values array would look like:
  29. // 
  30. //        values[0] = 0
  31. //        values[1] = 1
  32. //        values[2] = 2
  33. //        values[3] = 3
  34. //        values[4] = 4
  35. //
  36. // if each "step" was 1. So we need to add 1 element to the array to get the results we want.
  37.  
  38.  
  39. FFLL_API ValuesArrCountType FuzzyVariableBase::x_array_count = 201;
  40. FFLL_API ValuesArrCountType FuzzyVariableBase::x_array_max_idx = FuzzyVariableBase::x_array_count - 1;
  41. FFLL_API DOMType FuzzyVariableBase::dom_array_count = 101;
  42. FFLL_API DOMType FuzzyVariableBase::dom_array_max_idx = FuzzyVariableBase::dom_array_count - 1;
  43.  
  44.  
  45. //
  46. // Function: FuzzyVariableBase()
  47. // 
  48. // Purpose:    default constructor 
  49. // 
  50. // Arguments:
  51. //
  52. //        FuzzyModelBase* _parent - model that this variable is part of
  53. // 
  54. // Returns:
  55. //
  56. //        nothing
  57. // 
  58. // Author:    Michael Zarozinski    
  59. // Date:    6/10/99
  60. // 
  61. // Modification History
  62. // Author        Date        Modification
  63. // ------        ----        ------------
  64. //
  65. //
  66.  
  67. FuzzyVariableBase::FuzzyVariableBase(FuzzyModelBase* _parent) :  FFLLBase(_parent) 
  68. {
  69.      num_of_sets = 0;
  70.     rule_index = 0;
  71.      index = -1;
  72.  
  73.     sets = NULL; 
  74.  
  75.     // set left/right shoulder
  76.     left_x = 0;
  77.     right_x = FuzzyVariableBase::x_array_max_idx;
  78.  
  79.     set_id(L""); // init identifier
  80.  
  81.     idx_multiplier = 0; // it should NEVER be 0 so by init'ing to 0 we'll know if there's a problem sooner
  82.  
  83. } // end FuzzyVariableBase::FuzzyVariableBase()
  84.  
  85. //
  86. // Function: ~FuzzyVariableBase()
  87. // 
  88. // Purpose:    default destructor 
  89. // 
  90. // Arguments:
  91. //
  92. //        none
  93. // 
  94. // Returns:
  95. //
  96. //        nothing
  97. // 
  98. // Author:    Michael Zarozinski    
  99. // Date:    6/10/99
  100. // 
  101. // Modification History
  102. // Author        Date        Modification
  103. // ------        ----        ------------
  104. //
  105. //
  106. FuzzyVariableBase::~FuzzyVariableBase()
  107. {
  108.  
  109.     delete_all_sets();
  110.  
  111. } // end FuzzyVariableBase::~FuzzyVariableBase()
  112.  
  113.  
  114.  
  115.  
  116.  
  117. //
  118. // Function:    set_ramp()
  119. // 
  120. // Purpose:        Change the ramp status of one of the variable's sets.
  121. //                A ramp is a membership function that has the 'y' axis
  122. //                as one of it's sides. For example a normal trapezoid memberhip
  123. //                function would look like this:
  124. //                                       ___
  125. //              Normal Trapezoid:       /   \
  126. //                                       ___
  127. //              Left Ramp Trapezoid:    |   \ 
  128. //                                       ___
  129. //              Right Ramp Trapezoid:   /   |  
  130. //
  131. // Arguments:
  132. //
  133. //        int left_right_ind        -    indicates if we're making a left (1) or right (0) ramp
  134. //        int hi_lo_ind            -    indicates if we're creating (1) or removing (0) ramp
  135. //        int set_idx                -    index of the set to change the ramp for
  136. //
  137. // Returns:
  138. //
  139. //        
  140. //
  141. // Author:    Michael Zarozinski
  142. // Date:    7/99
  143. // 
  144. // Modification History
  145. // Author    Date        Modification
  146. // ------    ----        ------------
  147. //
  148. //
  149.  
  150. void FuzzyVariableBase::set_ramp(int left_right_ind, int hi_lo_ind, int set_idx )
  151. {
  152.      sets[set_idx]->set_ramp(hi_lo_ind, left_right_ind);
  153.  
  154. } // end FuzzyVariableBase::set_ramp()
  155.  
  156.  
  157.  
  158.  
  159. //
  160. // Function:    is_set_id_unique
  161. // 
  162. // Purpose:        Checks if the set name passed in is unique for this variable
  163. //
  164. // Arguments:
  165. //
  166. //        const wchar_t*    set_id    - set name to check
  167. //        int                set_idx    - index for the set the id belongs to
  168. //
  169. // Returns:
  170. //
  171. //        true - id is unique
  172. //        false - id is NOT unique
  173. //
  174. // Author:    Michael Zarozinski
  175. // Date:    6/00
  176. // 
  177. // Modification History
  178. // Author    Date        Modification
  179. // ------    ----        ------------
  180. //
  181. //
  182. bool FuzzyVariableBase::is_set_id_unique(const wchar_t* set_id, int set_idx) const
  183. {
  184.      FuzzySetBase*    set;    // set we're checking
  185.  
  186.     // go through all the OTHER sets and make sure there is no match
  187.  
  188.     for (int i = 0; i < get_num_of_sets(); i++)
  189.         {
  190.         if (i == set_idx)
  191.             continue;    // skip this set
  192.  
  193.         set = get_set(i);  
  194.  
  195.         assert(set != NULL);
  196.  
  197.         // case insensitive comparison
  198.         if (wcsicmp(set->get_id(), set_id) == 0)
  199.             {
  200.             set_msg_text(ERR_SET_NON_UNIQUE_ID);  
  201.             return false;
  202.             }
  203.  
  204.         } // end loop through sets
  205.  
  206.     // if we got here the id IS unique...
  207.     return true;
  208.  
  209. } // end  is_set_id_unique()
  210.  
  211. //
  212. // Function:    set_left_x()
  213. // 
  214. // Purpose:        Set the left value for this variable. Note that the left value IS
  215. //                allowed to be greater than the right value. The only restriction is
  216. //                that they can NOT be the same.
  217. //
  218. // Arguments:
  219. //
  220. //        RealType value - value to set left_x to
  221. //
  222. // Returns:
  223. //
  224. //        0 - set value 
  225. //        -1 - problem setting value 
  226. //
  227. // Author:    Michael Zarozinski
  228. // Date:    7/99
  229. // 
  230. // Modification History
  231. // Author    Date        Modification
  232. // ------    ----        ------------
  233. //
  234. //
  235.  
  236. int FuzzyVariableBase::set_left_x(RealType value)
  237. {
  238.  
  239.     // make sure value != right_x
  240.     if (value == right_x)
  241.         {
  242.         set_msg_text(ERR_SAME_LEFT_RIGHT_VALS);
  243.         return -1;
  244.         }
  245.  
  246.     left_x = value; 
  247.  
  248.     // re-calc multiplier cuz it's affected by this
  249.     calc_idx_multiplier();
  250.  
  251.     return 0;
  252.  
  253. } // end FuzzyVariableBase::set_left_x()
  254.  
  255. //
  256. // Function:    set_right_x()
  257. // 
  258. // Purpose:        Set the right value for this variable. Note that the left value IS
  259. //                allowed to be greater than the right value. The only restriction is
  260. //                that they can NOT be the same.
  261. //
  262. // Arguments:
  263. //
  264. //        RealType value - value to set right_x to
  265. //
  266. // Returns:
  267. //
  268. //        0 - set value 
  269. //        -1 - problem setting value 
  270. //
  271. // Author:    Michael Zarozinski
  272. // Date:    7/99
  273. // 
  274. // Modification History
  275. // Author    Date        Modification
  276. // ------    ----        ------------
  277. //
  278. //
  279. int FuzzyVariableBase::set_right_x(RealType value)
  280. {
  281.     // make sure avlue != left_x
  282.     if (value == left_x)
  283.         {
  284.         set_msg_text(ERR_SAME_LEFT_RIGHT_VALS);
  285.         return -1;
  286.         }
  287.  
  288.     right_x = value; 
  289.  
  290.     // re-calc multiplier cuz it's affected by this
  291.     calc_idx_multiplier();
  292.  
  293.     return 0;
  294.  
  295. } // end FuzzyVariableBase::set_right_x()
  296.  
  297. //
  298. // Function:    calc_idx_multiplier()
  299. // 
  300. // Purpose:        Calculate the index multiplier value for this variable.
  301. //                NOTE: there is not 'set' for the multiplier - it's always calculated
  302. //
  303. // Arguments:
  304. //
  305. //        none
  306. //
  307. // Returns:
  308. //
  309. //        void
  310. //
  311. // Author:    Michael Zarozinski
  312. // Date:    8/99
  313. // 
  314. // Modification History
  315. // Author    Date        Modification
  316. // ------    ----        ------------
  317. //
  318. //
  319. // 
  320. void FuzzyVariableBase::calc_idx_multiplier()
  321.      idx_multiplier =(right_x - left_x) /((RealType)( FuzzyVariableBase::x_array_max_idx));
  322.     
  323. }; // end FuzzyVariableBase::calc_idx_multiplier()
  324.  
  325.  
  326.  
  327. //
  328. // Function:    delete_set()
  329. // 
  330. // Purpose:        Deletes a set from the variable and adjusts the sets[]
  331. //                array appropriately.
  332. //
  333. // Arguments:
  334. //
  335. //        int _set_idx - index of the set to delete
  336. //
  337. // Returns:
  338. //
  339. //        0 - success
  340. //        non-zero - failure
  341. //
  342. // Author:    Michael Zarozinski
  343. // Date:    8/99
  344. // 
  345. // Modification History
  346. // Author    Date        Modification
  347. // ------    ----        ------------
  348. //
  349. //
  350. int FuzzyVariableBase::delete_set(int _set_idx)
  351. {
  352.     // if we don't have any sets, just return
  353.     if (sets == NULL)
  354.         return 0; // nothing to do.
  355.  
  356.     FuzzySetBase**    tmp_sets; // temp array to hold sets
  357.     
  358.     // if we're deleting the last set, don't alloc any memory
  359.     if (num_of_sets - 1 == 0)
  360.         tmp_sets = NULL;
  361.     else
  362.         tmp_sets = new FuzzySetBase*[num_of_sets - 1];    // create temp array
  363.  
  364.     // copy the old memory to the temp mem...
  365.     int j = 0;
  366.     for (int i = 0; i < num_of_sets; i++)
  367.         {
  368.         // don't copy the set we're deleting
  369.         if (i == _set_idx)
  370.             {
  371.             // free the memory for the set we're deleting
  372.             delete sets[i] ;
  373.             sets[i] = NULL;
  374.             }
  375.         else
  376.             {
  377.             tmp_sets[j] = (FuzzySetBase*)sets[i];
  378.             tmp_sets[j]->set_index(j);
  379.             // set the index
  380.             j++;
  381.             }
  382.         
  383.         } // end loop through sets
  384.  
  385.     // decrement the set count
  386.     num_of_sets--;
  387.  
  388.     // free old memory...
  389.     delete[] sets;
  390.  
  391.     // assign the new array of sets to the sets[] member variable
  392.      sets = tmp_sets;
  393.     
  394.     return 0; 
  395.  
  396. } // end FuzzyVariableBase::delete_set()
  397.  
  398.  
  399. //
  400. // Function: delete_all_sets()
  401. // 
  402. // Purpose:    clean up the memory allocated for sets
  403. // 
  404. // Arguments:
  405. //
  406. //        none
  407. // 
  408. // Returns:
  409. //
  410. //        void
  411. // 
  412. // Author:    Michael Zarozinski    
  413. // Date:    6/10/99
  414. // 
  415. // Modification History
  416. // Author        Date        Modification
  417. // ------        ----        ------------
  418. //
  419. //
  420.  
  421. void FuzzyVariableBase::delete_all_sets()
  422. {
  423.      // if we don't have any sets, just return
  424.     if (sets == NULL)
  425.         return; // nothing to do.
  426.  
  427.     // loop through all the sets and free memory
  428.     for (int i = 0; i < num_of_sets; i++)
  429.         {
  430.         delete sets[i] ;
  431.         sets[i] = NULL;
  432.         } // end loop through sets
  433.  
  434.     num_of_sets = 0;
  435.  
  436.     // free old memory...
  437.     delete[] sets;
  438.      sets = NULL;
  439.  
  440. } // end FuzzyVariableBase::delete_all_sets()
  441.  
  442.  
  443. //
  444. // Function:    set_id()
  445. // 
  446. // Purpose:        Set the identifier for a variable or set. This version
  447. //                takes an ASCII string, converts it to wide characters and
  448. //                calls the wide character version of this function
  449. //
  450. // Arguments:
  451. //
  452. //        const char*    _id        -    identifier to set
  453. //        int            set_idx    -    index of the set to change the id for, if -1 (default), change
  454. //                                the id for the variable
  455. //
  456. // Returns:
  457. //    
  458. //        0 - success
  459. //        non-zero - non-unique id    
  460. //
  461. // Author:    Michael Zarozinski
  462. // Date:    8/99
  463. // 
  464. // Modification History
  465. // Author    Date        Modification
  466. // ------    ----        ------------
  467. //
  468. //
  469. int FuzzyVariableBase::set_id(const char* _id, int set_idx /* = -1 */)
  470. {
  471.     wchar_t* wc = new wchar_t[strlen(_id) + 1];
  472.  
  473.     // convert from char to wchar_t
  474.     swprintf(wc, L"%S", _id);
  475.  
  476.     int ret_val = set_id(wc, set_idx);  
  477.  
  478.     delete[] wc;
  479.  
  480.     return ret_val;
  481.      
  482. } // end FuzzyVariableBase::set_id()
  483.  
  484. //
  485. // Function:    set_id()
  486. // 
  487. // Purpose:        Set the identifier for a variable or set. If no set index
  488. //                is passed in set the variable's id.
  489. //                This function will only set the identifier if it's unique
  490. //
  491. // Arguments:
  492. //
  493. //        const wchar_t*    _id        -    identifier to set
  494. //        int                set_idx    -    index of the set to change the id for, if -1 (default), change
  495. //                                    the id for the variable
  496. //
  497. // Returns:
  498. //    
  499. //        0 - success
  500. //        non-zero - non-unique id    
  501. //
  502. // Author:    Michael Zarozinski
  503. // Date:    8/99
  504. // 
  505. // Modification History
  506. // Author    Date        Modification
  507. // ------    ----        ------------
  508. //
  509. //
  510. int FuzzyVariableBase::set_id(const wchar_t* _id, int set_idx /* = -1 */)
  511. {
  512.     // if a set_idx is passed in, set the name for that set
  513.     if (set_idx >= 0)
  514.         {
  515.         int ret_val = sets[set_idx]->set_id(_id);
  516.  
  517.         if (ret_val)
  518.             {
  519.             // read the error from the set and set it for 'this'
  520.             set_msg_text(sets[set_idx]->get_msg_text());
  521.              return ret_val;  
  522.             }
  523.         }
  524.  
  525.      if (_id == NULL || wcslen(_id) == 0)
  526.         {
  527.         id = L"";
  528.         return 0;
  529.         }
  530.  
  531.      FuzzyModelBase* par = get_parent();
  532.  
  533.     // make sure our id is UNIQUE within the rule block
  534.       if (par->is_var_id_unique(_id, get_index()) == false)
  535.         {
  536.         // read the error from the parent and set it for 'this'
  537.         set_msg_text(par->get_msg_text());
  538.          return -1;  
  539.         }
  540.  
  541.     // set the identifier
  542.      id = _id;
  543.  
  544.     return 0;
  545.  
  546. }; // end FuzzyVariableBase::set_id()
  547.  
  548. //
  549. // Function:    add_set()
  550. // 
  551. // Purpose:        Insert a set into this variable.  NOTE: that a COPY of the
  552. //                set passed in is inserted into the var so the caller must
  553. //                free the object if it dynamically allocated it.
  554. //
  555. // Arguments:
  556. //
  557. //        const FuzzySetBase* _new_set - set to insert a copy of
  558. //
  559. // Returns:
  560. //
  561. //        0 - success
  562. //        non-zero - failure
  563. //
  564. // Author:    Michael Zarozinski
  565. // Date:    5/00
  566. // 
  567. // Modification History
  568. // Author    Date        Modification
  569. // ------    ----        ------------
  570. //
  571. //
  572. int FuzzyVariableBase::add_set(const FuzzySetBase* _new_set)
  573.  
  574.     // allocate temp memory which is the current number of sets PLUS 1
  575.     FuzzySetBase**    tmp_sets = new FuzzySetBase*[num_of_sets + 1];
  576.  
  577.     // copy the old memory to the temp mem...
  578.     for (int i = 0; i < num_of_sets; i++)
  579.         {
  580.          tmp_sets[i] = (FuzzySetBase*)sets[i];
  581.         }
  582.  
  583.      tmp_sets[num_of_sets] =  new_set(); // insert empty set 
  584.  
  585.     tmp_sets[num_of_sets]->copy(*_new_set); // copy the set we passed in
  586.  
  587.     // ensure that the set id is unique... if the user changed the id we
  588.     // performed a check PRIOR to calling this, so it'll be unique, if
  589.     // we get there through cut/copy/paste or something else we 
  590.     // may need to change the name...
  591.  
  592.     std::wstring tmp_name; // temp name
  593.           
  594.     std::wstring set_name = tmp_sets[num_of_sets]->get_id();
  595.  
  596.      while (!(is_set_id_unique(set_name.data(),  get_num_of_sets())))
  597.         {
  598.         // save the name
  599.         tmp_name = set_name;
  600.  
  601.         // just keep adding "Copy of" until we get a unique id
  602.         set_name = load_string(STR_COPY_OF);
  603.         set_name += L" " + tmp_name;
  604.  
  605.         // clear the message text...
  606.         set_msg_text();
  607.  
  608.         } // end wile set is NOT unique
  609.    
  610.     tmp_sets[num_of_sets]->set_id(set_name.data());
  611.  
  612.      // set the set index...
  613.     tmp_sets[num_of_sets]->set_index(num_of_sets);
  614.  
  615.     tmp_sets[num_of_sets]->calc();
  616.  
  617.     num_of_sets++;    // increment the number of sets
  618.  
  619.     // free old memory...
  620.     delete[] sets;
  621.  
  622.     // copy new mem to old
  623.     sets = tmp_sets;
  624.   
  625.     return 0;
  626.  
  627. } // end FuzzyVariableBase::add_set()
  628.  
  629.  
  630. //
  631. // Function:    set_x_array_count()
  632. // 
  633. // Purpose:        Set the value for x_array_count. This also sets the
  634. //                x_array_max_idx value appropriately. Note: this is a static
  635. //                function so it can't call non-static member funcs which
  636. //                means the caller needs to make sure the idx_multiplier
  637. //                variables are re-calculated.
  638. //
  639. // Arguments:
  640. //
  641. //        int _count - value to set x_array_count to.
  642. //
  643. // Returns:
  644. //
  645. //        0 - success
  646. //        -1 - invalid value passed in, set x_array_count to max allowed by datatype
  647. //
  648. // Author:    Michael Zarozinski
  649. // Date:    5/01
  650. // 
  651. // Modification History
  652. // Author    Date        Modification
  653. // ------    ----        ------------
  654. //
  655. //
  656.  
  657. int FuzzyVariableBase::set_x_array_count(int _count)
  658. {
  659.     // if they're trying to set over max, set to max and return -1
  660.     if (_count > USHRT_MAX)
  661.         {
  662.         x_array_count = USHRT_MAX;
  663.         x_array_max_idx = x_array_count - 1;
  664.         return -1;
  665.         }
  666.     
  667.     x_array_count = _count;
  668.     x_array_max_idx = x_array_count - 1;
  669.  
  670.     return 0;
  671.  
  672. }; // end FuzzyVariableBase::set_x_array_count()
  673.  
  674.  
  675. //
  676. // Function:    set_dom_array_count()
  677. // 
  678. // Purpose:        Set the value for dom_array_count. This also sets the
  679. //                dom_array_max_idx value appropriately.
  680. //
  681. // Arguments:
  682. //
  683. //        int _count - value to set dom_array_count to.
  684. //
  685. // Returns:
  686. //
  687. //        0 - success
  688. //        -1 - invalid value passed in, set dom_array_count to max allowed by datatype
  689. //
  690. // Author:    Michael Zarozinski
  691. // Date:    5/01
  692. // 
  693. // Modification History
  694. // Author    Date        Modification
  695. // ------    ----        ------------
  696. //
  697. //
  698.  
  699. int FuzzyVariableBase::set_dom_array_count(int _count)
  700. {
  701.     // if they're trying to set over max, set to max and return -1
  702.     if (_count > UCHAR_MAX)
  703.         {
  704.         dom_array_count = UCHAR_MAX;
  705.         dom_array_max_idx = x_array_count - 1;
  706.         return -1;
  707.         }
  708.     
  709.     dom_array_count = _count;
  710.     dom_array_max_idx = dom_array_count - 1;
  711.  
  712.     return 0;    
  713.  
  714. }; // end FuzzyVariableBase::set_dom_array_count()
  715.  
  716.  
  717. //
  718. // Function:    save_var_to_fcl_file()
  719. // 
  720. // Purpose:        Write out the FCL (Fuzzy Control Language) for this variable. This is written
  721. //                inside a FUNCTION_BLOCK section and has the format (as defined by the
  722. //                IEC 61131-7 International Standard):
  723. //
  724. //                    VAR_INPUT Input parameter declaration
  725. //                        variable_name: data_type; (* RANGE *)
  726. //                        ....
  727. //                    END_VAR
  728. //
  729. //                NOTE: we augment the standard by writing out the variable's range in a comment
  730. //                after the semi-colon. Without this we won't know the left/right values for the
  731. //                varaible when a FCL file is read in.
  732. //
  733. // Arguments:
  734. //
  735. //        std::ofstream& file_contents - STL stream to write out to a file
  736. //
  737. /// Returns:
  738. //
  739. //        void
  740. //
  741. // Author:    Michael Zarozinski
  742. // Date:    9/01
  743. // 
  744. // Modification History
  745. // Author    Date        Modification
  746. // ------    ----        ------------
  747. //
  748. //
  749. void FuzzyVariableBase::save_var_to_fcl_file(std::ofstream& file_contents)
  750. {
  751.     // get the ascii version of the id...
  752.     char* aid = convert_to_ascii(get_id());
  753.      
  754.     // store the ORIGINAL version BEFORE we replace any spaces so we can report on it
  755.     std::string tmp = aid;
  756.  
  757.     delete[] aid;
  758.  
  759.     // now get the version with spaces replaced by underscores
  760.     aid = convert_to_ascii(get_id(), '_');
  761.  
  762.     // NOTE: the IEC 61131-7 does not specify a way to set the range of a varaible, so 
  763.     // we write them out in a range comment
  764.  
  765.     file_contents << "\t" << aid <<  "\tREAL; (* RANGE(" << get_left_x() << " .. " << get_right_x() << ") *) ";
  766.  
  767.     delete[] aid;
  768.  
  769.     FuzzyModelBase::validate_fcl_identifier(file_contents, tmp);
  770.  
  771.     file_contents << "\n";
  772.  
  773. } // end FuzzyVariableBase::save_var_to_fcl_file()
  774.  
  775.  
  776. //
  777. // Function:    save_sets_to_fcl_file()
  778. // 
  779. // Purpose:        Write out the FCL (Fuzzy Control Language) for the sets in this variable
  780. //                as defined by the IEC 61131-7 International Standard:
  781. //
  782. //                    FUZZIFY variable_name
  783. //                        TERM term_name:= membership_function;
  784. //                        ....
  785. //                    END_FUZZIFY
  786. //
  787. // Arguments:
  788. //
  789. //        std::ofstream& file_contents - STL stream to write out to a file
  790. //
  791. /// Returns:
  792. //
  793. //        void
  794. //
  795. // Author:    Michael Zarozinski
  796. // Date:    9/01
  797. // 
  798. // Modification History
  799. // Author    Date        Modification
  800. // ------    ----        ------------
  801. //
  802. //
  803. void FuzzyVariableBase::save_sets_to_fcl_file(std::ofstream& file_contents)
  804. {
  805.      FuzzySetBase*    set;    // set we're dealing with
  806.  
  807.      // get the ascii version of the id and replace any spaces
  808.     // with an underscore...
  809.     char* aid = convert_to_ascii(get_id(), '_');
  810.  
  811.     file_contents << "FUZZIFY " << aid << "\n";
  812.  
  813.     delete[] aid;
  814.  
  815.     // loop through the sets
  816.     for (int i = 0; i < num_of_sets; i++)
  817.         {
  818.         set = get_set(i);
  819.  
  820.         set->save_to_fcl_file(file_contents);
  821.  
  822.         char* aid_set = convert_to_ascii(set->get_id());
  823.  
  824.         FuzzyModelBase::validate_fcl_identifier(file_contents, aid_set);
  825.  
  826.         delete[] aid_set;
  827.  
  828.         file_contents << "\n";
  829.  
  830.         } // end loop through sets
  831.  
  832.     file_contents << "END_FUZZIFY\n\n";
  833.  
  834. } // end FuzzyVariableBase::save_sets_to_file()
  835.  
  836. //
  837. // Function:    load_sets_from_fcl_file()
  838. // 
  839. // Purpose:        Read in an FCL file and create the sets that are associated
  840. //                with this variable. 
  841. //                NOTE: This is a brain-dead parser, it can be made much more robust.
  842. //
  843. // Arguments:
  844. //
  845. //        std::ifstream& file_contents - FCL file in stream form
  846. //
  847. // Returns:
  848. //
  849. //        0 - success
  850. //        non-zero - failure
  851. //
  852. // Author:    Michael Zarozinski
  853. // Date:    9/01
  854. // 
  855. // Modification History
  856. // Author    Date        Modification
  857. // ------    ----        ------------
  858. //
  859. // 
  860. int FuzzyVariableBase::load_sets_from_fcl_file(std::ifstream& file_contents)
  861. {
  862.       file_contents.seekg(0);    // go to start of file
  863.  
  864.     // get the ascii version of this variable's id...
  865.     char* aid = convert_to_ascii(get_id());
  866.  
  867.     // search for keyword FUZZIFY followed by id of this var...
  868.     std::string stoken, var_name;
  869.     bool found = false;
  870.  
  871.     while (!found)
  872.         {
  873.  
  874.         // read until we get to the start of the variable declaration
  875.         do
  876.             {
  877.             file_contents >> stoken;
  878.  
  879.             // check for EOF
  880.             if (file_contents.eof())
  881.                 {
  882.                 set_msg_text(ERR_EOF_READING_SETS);
  883.                  return -1; 
  884.                 }
  885.  
  886.             } while (strcmp(stoken.data(), "FUZZIFY") != 0 );
  887.         
  888.         // if we got here we have a FUZZIFY block... check if it's the right var...
  889.         file_contents >> var_name;
  890.  
  891.          if (strcmp(var_name.data(), aid) == 0)
  892.              found = true;
  893.  
  894.         }; // end while !found
  895.  
  896.     delete[] aid;
  897.  
  898.     // we found "FUZZIFY <var name>", now loop until we find "END_FUZZIFY" reading
  899.     // the sets which are in the format:
  900.     // 
  901.     //            TERM term_name:= membership_function;
  902.     //
  903.     // Where membership_function ::= ( point i), ( point j), ...
  904.     // and point is either a single value (if singleton) or a list of x/y values (x, y)
  905.     // and the values MAY have a decimal point.
  906.  
  907.     // read line-by-line to get the sets...
  908.  
  909.     char line[300];
  910.      RealType x_point[7], y_point[7];    // x/y points for the set - NOTE: we assume a max of 7 points
  911.  
  912.     char* set_name;
  913.     char* token;
  914.             
  915.     // inlude ":=" in tokens so we skip the assignment operator
  916.     char seps[]   = " ,()\t\n:=";
  917.  
  918.     while(1)
  919.         {
  920.         // read in the next line
  921.         file_contents.getline(line, 300);
  922.  
  923.         // check for EOF
  924.         if (file_contents.eof())
  925.             {
  926.             set_msg_text(ERR_EOF_READING_SETS);
  927.              return -1; 
  928.             }
  929.  
  930.         token = strtok(line, seps);
  931.  
  932.         // if token is null... blank line
  933.         if (token == NULL)
  934.             continue;
  935.  
  936.         if (strcmp(token, "END_FUZZIFY") == 0)
  937.             break;    // we're done
  938.  
  939.         // validate this is the "term" keyword
  940.         if (stricmp(token, "term") != 0)
  941.             {
  942.             set_msg_text(ERR_INVALID_FILE_FORMAT); // could be a bit more "robuts" in our error reporting!
  943.             return -1;
  944.             }
  945.         
  946.         // get the set name
  947.         set_name = strtok(NULL, seps);
  948.  
  949.         token = strtok(NULL, seps);
  950.  
  951.         int num_of_points = 0;
  952.  
  953.         // handle special case of singletons.  They'll be of the form "<value>;"
  954.         // read points until we reach the semicolon
  955.  
  956.         if (token[strlen(token) - 1] == ';')
  957.             {
  958.  
  959.             // replace semi with null so we can convert
  960.             token[strlen(token) - 1] = '\0';
  961.             x_point[num_of_points] = atof(token);
  962.             // set 'y' value to max
  963.              y_point[num_of_points]  = FuzzyVariableBase::get_dom_array_max_idx();
  964.  
  965.             num_of_points = 1;
  966.             }
  967.         else
  968.             {
  969.             // not a singleton, points are of the form "(<value> , <value>)"
  970.  
  971.             while (token[0] != ';')  
  972.                 {
  973.  
  974.                 x_point[num_of_points] = atof(token);
  975.  
  976.                 token = strtok(NULL, seps);
  977.  
  978.                 if (token[0] == ';') 
  979.                     {
  980.                     if (num_of_points != 0)
  981.                         {
  982.                         // ERROR - invalid format
  983.                         set_msg_text(ERR_INVALID_FILE_FORMAT); // could be a bit more "robuts" in our error reporting!
  984.                         return -1;
  985.                         }
  986.  
  987.                     // else singleton which is OK
  988.  
  989.                      y_point[num_of_points]  = 0; 
  990.                     num_of_points++;
  991.                     continue;
  992.                     } // end if ';' found
  993.  
  994.                  y_point[num_of_points]  = atof(token);
  995.  
  996.                 num_of_points++;
  997.  
  998.                 token = strtok(NULL, seps);
  999.                 } // end while still reading values
  1000.  
  1001.             } // end if not singleton
  1002.  
  1003.         // set the membership func dependent on the number of datapoints....
  1004.         int type;    // type of new member func
  1005.         switch(num_of_points)
  1006.             {
  1007.              case 1:
  1008.                  type = MemberFuncBase::TYPE::SINGLETON;
  1009.                 break;
  1010.             case 3:
  1011.                 type = MemberFuncBase::TYPE::TRIANGLE;
  1012.                 break;
  1013.             case 4:
  1014.                 type = MemberFuncBase::TYPE::TRAPEZOID;
  1015.                 break;
  1016.             case 7:
  1017.                 type = MemberFuncBase::TYPE::S_CURVE;
  1018.                 break;
  1019.             default:
  1020.                 return -1; // error
  1021.  
  1022.             } // end switch on num_of_points
  1023.  
  1024.         // create the set, note we put fake values for width and stuff cuz that'll get
  1025.         // set when we set the points
  1026.  
  1027.         wchar_t* wset_name = convert_to_wide_char(set_name);
  1028.  
  1029.          FuzzySetBase* set = new_set(wset_name, 0, this, num_of_sets, 0, type);
  1030.  
  1031.         delete[] wset_name;
  1032.  
  1033.         if (set == NULL)
  1034.             return -1; // error is in msg_text
  1035.  
  1036.         // set up the nodes...
  1037.  
  1038.            // loop points. Note we skip sanity checks set_node() uses
  1039.          for (int i = 0; i < num_of_points  ; i++)
  1040.             {
  1041.             // convert values to indexes...
  1042.             int x = convert_value_to_idx(x_point[i]);
  1043.             int y = y_point[i] * FuzzyVariableBase::get_dom_array_max_idx();
  1044.  
  1045.             set->set_node(i, x, y, false); // false indicates skip sanity checks
  1046.  
  1047.             } // end loop through points
  1048.  
  1049.         // need to call ModelBase version to set up the the rule array
  1050.         (get_parent())->add_set(index, set);
  1051.         
  1052.         delete set;
  1053.  
  1054.         }; // end while(1) loop
  1055.  
  1056.     return 0;
  1057.  
  1058. } // end FuzzyVariableBase::load_sets_from_file()
  1059.  
  1060.  
  1061. //
  1062. // Function: calc()
  1063. // 
  1064. // Purpose:    This function calculates the 'y' values for all the sets
  1065. //            associated with this variable
  1066. // 
  1067. // Arguments:
  1068. //
  1069. //        int set_idx -    index of the set to calculate for. If -1 (default value)
  1070. //                        do ALL the sets
  1071. //
  1072. // Returns:
  1073. //
  1074. //        void
  1075. // 
  1076. // Author:    Michael Zarozinski    
  1077. // Date:    1/21/99
  1078. // 
  1079. // Modification History
  1080. // Author        Date        Modification
  1081. // ------        ----        ------------
  1082. //
  1083. //
  1084.  
  1085. void FuzzyVariableBase::calc(int set_idx /* = -1 */)
  1086. {
  1087.     if (set_idx >= 0)
  1088.         {
  1089.         sets[set_idx]->calc();
  1090.         }
  1091.     else
  1092.         {
  1093.          for (int i = 0; i < num_of_sets; i++)
  1094.              sets[i]->calc();
  1095.         }
  1096.  
  1097. } // end FuzzyVariableBase::calc(void)
  1098.  
  1099.  
  1100.  
  1101. //
  1102. // Function: convert_idx_to_value()
  1103. // 
  1104. // Purpose:    This function converts an index (into the values[] array) to 
  1105. //            an 'x' value between left_x and right_x for this variable
  1106. // 
  1107. // Arguments:
  1108. //
  1109. //        int idx -    index to convert to a value
  1110. //
  1111. // Returns:
  1112. //
  1113. //        RealType - x value for the index passed in
  1114. // 
  1115. // Author:    Michael Zarozinski    
  1116. // Date:    1/99
  1117. // 
  1118. // Modification History
  1119. // Author        Date        Modification
  1120. // ------        ----        ------------
  1121. // Michael Z    10/23/01    Added sanity checks
  1122. //
  1123. //
  1124.  
  1125. RealType FuzzyVariableBase::convert_idx_to_value(int idx) const
  1126. {
  1127.     assert(idx >= 0);
  1128.     assert(idx <= FuzzyVariableBase::get_x_array_count());
  1129.  
  1130.     // make sure index is within range
  1131.     if (idx < 0)
  1132.         idx = 0;
  1133.     if (idx > FuzzyVariableBase::get_x_array_max_idx())
  1134.         idx = FuzzyVariableBase::get_x_array_max_idx();
  1135.  
  1136.     return left_x + (idx * get_idx_multiplier());
  1137. };
  1138.  
  1139. //
  1140. // Function: convert_value_to_idx()
  1141. // 
  1142. // Purpose:    This function converts the 'x' value passed in to
  1143. //            an index (into the values[] array) 
  1144. // 
  1145. // Arguments:
  1146. //
  1147. //        RealType - x value to convert
  1148. //
  1149. // Returns:
  1150. //
  1151. //        ValuesArrCountType idx -    index for the value
  1152. // 
  1153. // Author:    Michael Zarozinski    
  1154. // Date:    1/99
  1155. // 
  1156. // Modification History
  1157. // Author        Date        Modification
  1158. // ------        ----        ------------
  1159. // Michael Z    10/23/01    Added sanity checks
  1160. //
  1161. //
  1162.  
  1163. ValuesArrCountType FuzzyVariableBase::convert_value_to_idx(RealType value) const
  1164. {
  1165.     // add .5 so any floating point values get rounded correctly when converting to int
  1166.     int idx = ((value -  get_left_x()) / get_idx_multiplier()) + .5;
  1167.  
  1168.     // make sure index is within range
  1169.     if (idx < 0)
  1170.         idx = 0;
  1171.     if (idx > FuzzyVariableBase::get_x_array_max_idx())
  1172.         idx = FuzzyVariableBase::get_x_array_max_idx();
  1173.  
  1174.     return idx;
  1175. };
  1176.  
  1177.  
  1178. //
  1179. // Function: init()
  1180. // 
  1181. // Purpose:    Perform common variable initialization
  1182. // 
  1183. // Arguments:
  1184. //
  1185. //        const wchar_t*    _id                    -    identifier for the variable
  1186. //        RealType        _left_x                -    left x value
  1187. //        RealType        _right_x            -    right x value
  1188. //        bool            create_unique_id    -    if true, alter the identifier (if needed) to make it unique) default = true
  1189. //
  1190. // Returns:
  1191. //
  1192. //        0 - success
  1193. //        non-zero - failure
  1194. // 
  1195. // Author:    Michael Zarozinski    
  1196. // Date:    1/99
  1197. // 
  1198. // Modification History
  1199. // Author        Date        Modification
  1200. // ------        ----        ------------
  1201. //
  1202. //
  1203.         
  1204. int FuzzyVariableBase::init(const wchar_t* _id, RealType _left_x, RealType _right_x, bool create_unique_id /* = true */)
  1205. {
  1206.     if (set_left_x(_left_x))
  1207.         return -1;
  1208.  
  1209.     if (set_right_x(_right_x))
  1210.         return -1;
  1211.  
  1212.     if (init(_id, create_unique_id)) // virtual func to set in/out var appropriately
  1213.         return -1;
  1214.  
  1215.     calc();
  1216.  
  1217.     return 0;
  1218.  
  1219. } // end FuzzyVariableBase::init()
  1220.  
  1221.  
  1222. //
  1223. // Function: init()
  1224. // 
  1225. // Purpose:    Initialize variable identifier and make it unique if applicable
  1226. // 
  1227. // Arguments:
  1228. //
  1229. //        const wchar_t*    _id                    -    identifier for the variable
  1230. //        bool            create_unique_id    -    if true, alter the identifier (if needed) to make it unique) default = true
  1231. //
  1232. // Returns:
  1233. //
  1234. //        0 - success
  1235. //        non-zero - failure
  1236. // 
  1237. // Author:    Michael Zarozinski    
  1238. // Date:    1/99
  1239. // 
  1240. // Modification History
  1241. // Author        Date        Modification
  1242. // ------        ----        ------------
  1243. //
  1244. //
  1245.         
  1246. int FuzzyVariableBase::init(const wchar_t* _id, bool create_unique_id /* = true */)
  1247. {
  1248.     int name_len;    // length of the name
  1249.  
  1250.     if (_id == NULL)
  1251.         name_len = 30;
  1252.     else
  1253.         name_len = wcslen(_id) + 5;
  1254.  
  1255.     wchar_t* tmp_name = new wchar_t[name_len]; // temp var to hold var name
  1256.  
  1257.     // if no name is passed in name it "Variable 0"
  1258.      if (_id == NULL)
  1259.         swprintf(tmp_name, L"%s 1", load_string(STR_VARIABLE));  
  1260.      else
  1261.         swprintf(tmp_name, L"%s", _id );  
  1262.      
  1263.      // set the name.  NOTE: this set_id() performs a check to ensure that the variable name is UNIQUE
  1264.     // for this model.  
  1265.  
  1266.     int ret_value; // return value
  1267.  
  1268.     if (!create_unique_id)
  1269.         {
  1270.         ret_value = set_id(tmp_name);
  1271.         }
  1272.     else
  1273.         {
  1274.         ret_value = 0;
  1275.  
  1276.          int counter = 1; // value to add to identifier to make it unique
  1277.  
  1278.         while (set_id(tmp_name))
  1279.             {
  1280.             // rename variable...
  1281.             if (_id == NULL)
  1282.                 swprintf(tmp_name, L"%s %d", load_string(STR_VARIABLE), counter  );  
  1283.              else
  1284.                 swprintf(tmp_name, L"%s %d", _id, counter );  
  1285.  
  1286.             counter++;
  1287.  
  1288.             // clear message text (should say "var not unique" right now)
  1289.             set_msg_text();
  1290.             
  1291.             } // end while !unique id
  1292.  
  1293.         }// end if rename var if not unique
  1294.  
  1295.     // free memory
  1296.     delete[] tmp_name;
  1297.  
  1298.     return ret_value;
  1299.  
  1300. } // end FuzzyVariableBase::init()
  1301.  
  1302.  
  1303. /////////////////////////////////////////////////////////////////////
  1304. ////////// Trivial Functions That Don't Require Headers /////////////
  1305. /////////////////////////////////////////////////////////////////////
  1306.         
  1307. void FuzzyVariableBase::move_node(int set_idx, int  idx, _point pt)
  1308. {
  1309.      sets[set_idx]->move_node( idx, pt);
  1310. };
  1311.  
  1312. void FuzzyVariableBase::move_node(int set_idx, int  idx, int x, int y)
  1313. {
  1314.       sets[set_idx]->move_node( idx, x, y);
  1315. };
  1316.         
  1317. FuzzySetBase* FuzzyVariableBase::get_set(int idx) const
  1318. {
  1319.     return((idx < num_of_sets)? sets[idx] : NULL);
  1320. }; 
  1321.         
  1322. int FuzzyVariableBase::get_num_of_sets(void) const 
  1323. {
  1324.     return(num_of_sets);
  1325. };
  1326.         
  1327. FFLL_INLINE short FuzzyVariableBase::get_index() const
  1328. {
  1329.     return index;
  1330. };
  1331. FFLL_INLINE void FuzzyVariableBase::set_index(int idx)
  1332.     index = static_cast<short>(idx);
  1333. }; 
  1334.          
  1335. FuzzyModelBase* FuzzyVariableBase::get_parent(void) const
  1336. {
  1337.     return static_cast< FuzzyModelBase*>(FFLLBase::get_parent());
  1338. };
  1339.  
  1340.  
  1341. const wchar_t* FuzzyVariableBase::get_id(int set_idx /* = -1 */) const
  1342. {
  1343.     if (set_idx == -1)
  1344.         return((id == L"") ? NULL : id.data()); 
  1345.     else
  1346.         {
  1347.         FuzzySetBase* set = get_set(set_idx);
  1348.         return set->get_id(); 
  1349.         }
  1350.  
  1351. };  
  1352.  
  1353.  
  1354. int FuzzyVariableBase::get_rule_index(int _set_idx /* = -1 */) const
  1355. {
  1356.     if (_set_idx == -1)
  1357.         return rule_index;
  1358.  
  1359.     return sets[_set_idx]->get_rule_index();
  1360. };  
  1361.  
  1362. DOMType FuzzyVariableBase::get_dom(int set_idx, int x_position /* = -1 */) const
  1363. {
  1364.     if (sets)
  1365.         return sets[set_idx]->get_dom(x_position);  
  1366.     else
  1367.         return 0;
  1368. };
  1369.         
  1370. void FuzzyVariableBase::set_rule_index(int _rule_index, int set_idx /* = -1 */)
  1371. {
  1372.     if (set_idx == -1)
  1373.         rule_index = _rule_index;
  1374.     else
  1375.         {
  1376.         FuzzySetBase* set = get_set(set_idx);
  1377.         set->set_rule_index(_rule_index);
  1378.         } // end if set rule_index for set
  1379.  
  1380. };  
  1381.  
  1382. std::string FuzzyVariableBase::get_model_name() const
  1383. {
  1384.      return get_parent()->get_model_name();
  1385. };
  1386.  
  1387. RealType FuzzyVariableBase::get_idx_multiplier() const
  1388. {
  1389.     return idx_multiplier;
  1390. };
  1391.  
  1392. void FuzzyVariableBase::move(int set_idx, int node_idx, int new_x)
  1393. {
  1394.     // need to calc the 'x' delta...
  1395.     int x_delta = new_x - sets[set_idx]->get_node_x(node_idx);
  1396.  
  1397.     sets[set_idx]->move(x_delta);
  1398. };
  1399.  
  1400. bool FuzzyVariableBase::is_output() const 
  1401. {
  1402.     return false;
  1403. }; 
  1404.         
  1405. const char* FuzzyVariableBase::get_fcl_block_start() 
  1406.     return "VAR_INPUT"; 
  1407. }         
  1408. const char* FuzzyVariableBase::get_fcl_block_end() 
  1409.     return "END_VAR"; 
  1410.  
  1411. RealType FuzzyVariableBase::get_left_x() const 
  1412. {    
  1413.     return left_x;
  1414. }
  1415. RealType FuzzyVariableBase::get_right_x() const 
  1416. {    
  1417.     return right_x;
  1418.  
  1419. ValuesArrCountType FuzzyVariableBase::get_x_array_count() 
  1420. {    
  1421.     return x_array_count;
  1422. };
  1423. ValuesArrCountType FuzzyVariableBase::get_x_array_max_idx()
  1424. {    
  1425.     return x_array_max_idx;
  1426. };
  1427.  
  1428. DOMType FuzzyVariableBase::get_dom_array_count()
  1429. {    
  1430.     return dom_array_count;
  1431. };
  1432. DOMType FuzzyVariableBase::get_dom_array_max_idx()
  1433. {    
  1434.     return     dom_array_max_idx;
  1435. };
  1436.  
  1437.  
  1438. FuzzySetBase* FuzzyVariableBase::new_set()
  1439. {
  1440.     return new FuzzySetBase(this);
  1441. };
  1442.  
  1443. FuzzySetBase* FuzzyVariableBase::new_set(wchar_t* n, int mid_pt_x, FuzzyVariableBase* par, short index, int set_width, int type)
  1444. {
  1445.     FuzzySetBase* tmp = new_set(); // virtual function
  1446.     
  1447.     tmp->init(n, mid_pt_x, index, set_width, type);
  1448.  
  1449.     return tmp;
  1450.  
  1451. };  
  1452.