PNController.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2010, LABUST, UNIZG-FER
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the LABUST nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *
00034  *  Author: Dula Nad
00035  *  Created: 30.10.2013.
00036  *********************************************************************/
00037 #include <labust/control/PNController.hpp>
00038 #include <boost/graph/graphviz.hpp>
00039 #include <boost/graph/visitors.hpp>
00040 #include <boost/graph/breadth_first_search.hpp>
00041 
00042 #include <queue>
00043 #include <list>
00044 #include <algorithm>
00045 #include <sstream>
00046 
00047 using namespace labust::control;
00048 
00049 PNController::PNController():
00050                                                         pnum(6),
00051                                                         tnum(0),
00052                                                         marking(pnum)
00053 {
00054         //Add the basic vertices.
00055         std::string dofs[]={"X","Y","Z","K","M","N"};
00056         //Initial marking
00057         for (int i=0; i<6;++i)
00058         {
00059                 placeMap[i]=dofs[i];
00060                 nameMap[dofs[i]].place_num = i;
00061                 marking(i) = 1;
00062         }
00063 }
00064 
00065 void PNController::addToGraph(const navcon_msgs::RegisterControllerRequest& info)
00066 {
00067         PlaceInfo& newcon=nameMap[info.name];
00068         newcon.place_num = pnum++;
00069 
00070         marking.conservativeResize(pnum);
00071         marking(newcon.place_num) = 0;
00072         newcon.enable_t = tnum++;
00073         newcon.disable_t = tnum++;
00074 
00075         //Debug info
00076         placeMap[newcon.place_num] = info.name;
00077         transitionMap[newcon.enable_t] = info.name+"_enable";
00078         transitionMap[newcon.disable_t] = info.name+"_disable";
00079 
00080         Dm.conservativeResize(pnum, tnum);
00081         Dp.conservativeResize(pnum, tnum);
00082         Dm.row(newcon.place_num) = Eigen::VectorXi::Zero(tnum);
00083         Dp.row(newcon.place_num) = Eigen::VectorXi::Zero(tnum);
00084         Dm.col(newcon.enable_t)= Eigen::VectorXi::Zero(pnum);
00085         Dp.col(newcon.enable_t)= Eigen::VectorXi::Zero(pnum);
00086         Dm.col(newcon.disable_t)= Eigen::VectorXi::Zero(pnum);
00087         Dp.col(newcon.disable_t)= Eigen::VectorXi::Zero(pnum);
00088 
00089         //Add local connection
00090         Dm(newcon.place_num, newcon.disable_t) = 1;
00091         Dp(newcon.place_num, newcon.enable_t) = 1;
00092         //Add basic dependencies.
00093         for (int i=0; i<info.used_dofs.size(); ++i)
00094         {
00095                 if (info.used_dofs[i])
00096                 {
00097                         Dm(i, newcon.enable_t) = 1;
00098                         Dp(i, newcon.disable_t) = 1;
00099                 }
00100         }
00101 
00102         //Add advanced dependencies.
00103         for (int i=0; i<info.used_cnt.size(); ++i)
00104         {
00105                 int place_num = nameMap[info.used_cnt[i]].place_num;
00106                 Dm(place_num, newcon.enable_t) = 1;
00107                 Dp(place_num, newcon.disable_t) = 1;
00108         }
00109 
00110         I=Dp-Dm;
00111 }
00112 
00113 bool PNController::firing_rec(int des_place,
00114                 std::set<int>& skip_transitions,
00115                 std::set<int>& visited_places)
00116 {
00117         //Add place to visited places.
00118         visited_places.insert(des_place);
00119 
00120         //Find all possible transition activators for this place
00121         Eigen::VectorXi transitions = Dp.row(des_place);
00122         std::vector<int> activators;
00123         for (int i=0; i<transitions.size(); ++i)
00124         {
00125                 //If connected (!=0) AND not in skipped transitions.
00126                 if ((transitions(i) &&
00127                                 (skip_transitions.find(i) == skip_transitions.end())))
00128                 {
00129                         activators.push_back(i);
00130                 }
00131         }
00132 
00133         //If no activators this is a dead-end.
00134         if (activators.empty())
00135         {
00136                 std::cout<<"Place "<<placeMap[des_place]<<" is a dead-end."<<std::endl;
00137                 return false;
00138         }
00139 
00140         //Output debugging information for the curret
00141         std::cout<<"Place "<<placeMap[des_place]<<" depends on transitions:";
00142         for (int i=0; i<activators.size(); ++i) std::cout<<transitionMap[activators[i]]<<", ";
00143         std::cout<<std::endl;
00144 
00145         //For each activator test if there exists a posibility to trigger it.
00146         bool trigerable = false;
00147         for (int i=0; i<activators.size(); ++i)
00148         {
00149                 Eigen::VectorXi t_en = Dm.col(activators[i]);
00150                 std::vector<int> places;
00151                 for (int j=0; j<t_en.size(); ++j)
00152                 {
00153                         //If connected (!=0) AND not in visited places.
00154                         if ((t_en(j) &&
00155                                         (visited_places.find(j) == visited_places.end())))
00156                         {
00157                                 places.push_back(j);
00158                         }
00159                 }
00160 
00161                 if (places.size() == 0)
00162                 {
00163                         std::cout<<"Activator candidate transition "<<transitionMap[activators[i]];
00164                         std::cout<<" is a dead-end."<<std::endl;
00165                         continue;
00166                 }
00167 
00168                 //Debugging information for the activator places.
00169                 std::cout<<"Activator candidate transition "<<transitionMap[activators[i]];
00170                 std::cout<<" depends on:";
00171                 for (int j=0; j<places.size(); ++j) std::cout<<placeMap[places[j]]<<", ";
00172                 std::cout<<std::endl;
00173 
00174                 bool add_cur_seq=true;
00175                 //Places that are already marked add to visited places to avoid backlash.
00176                 for (int j=0; j<places.size(); ++j)
00177                         if (marking(places[j])) visited_places.insert(places[j]);
00178 
00179                 for (int j=0; j<places.size(); ++j)
00180                 {
00181                         //Recurse to search a lever higher for unmarked places
00182                         if (!marking(places[j]))
00183                         {
00184                                 if (!firing_rec(places[j], skip_transitions, visited_places))
00185                                 {
00186                                         add_cur_seq = false;
00187                                         break;
00188                                 }
00189                         }
00190                 }
00191 
00192                 if (add_cur_seq)
00193                 {
00194                         bool add = true;
00195                         for (int j=0; j<firing_seq.size(); ++j)
00196                         {
00197                                 if (firing_seq[j] == activators[i])
00198                                 {
00199                                         add = false;
00200                                         break;
00201                                 }
00202                         }
00203 
00204                         //Before adding check if still not skipped
00205                         if (add)
00206                         {
00207                                 std::cout<<"Adding to firing sequence:"<<transitionMap[activators[i]]<<std::endl;
00208                                 firing_seq.push_back(activators[i]);
00209                                 trigerable = true;
00210 
00211                                 //Skip other pair. There is no change in the marking if we trigger
00212                                 //a pair in the same sequence.
00213                                 if (activators[i]%2 == 0)
00214                                 {
00215                                         std::cout<<"Adding to skip sequence:"<<transitionMap[activators[i]+1]<<std::endl;
00216                                         skip_transitions.insert(activators[i]+1);
00217                                 }
00218                                 else
00219                                 {
00220                                         std::cout<<"Adding to skip sequence:"<<transitionMap[activators[i]-1]<<std::endl;
00221                                         skip_transitions.insert(activators[i]-1);
00222                                 }
00223                         }
00224                         else
00225                         {
00226                                 std::cout<<"Adding to skip sequence:"<<transitionMap[activators[i]]<<std::endl;
00227                                 skip_transitions.insert(activators[i]);
00228                         }
00229                 }
00230         }
00231         return trigerable;
00232 }
00233 
00234 void PNController::get_firing(const std::string& name)
00235 {
00236         //Desired place to activate
00237         int des_place = nameMap[name].place_num;
00238 
00239         while (marking(des_place) != 1)
00240         {
00241                 firing_seq.clear();
00242                 std::set<int> skip_transitions;
00243                 std::set<int> visited_places;
00244                 firing_rec(des_place, skip_transitions, visited_places);
00245 
00246                 std::cout<<"The final firing sequence is:";
00247                 for (int i=0; i<firing_seq.size(); ++i)
00248                 {
00249                         std::cout<<transitionMap[firing_seq[i]]<<" -> ";
00250                 }
00251                 std::cout<<"end"<<std::endl;
00252 
00253                 //Do the firing
00254                 //Add testing "simulation" of the firing before applying.
00255                 //Add enable/disable service calls for these.
00256                 for(int i=0; i<firing_seq.size(); ++i)
00257                 {
00258                         Eigen::VectorXi fire = Eigen::VectorXi::Zero(Dm.cols());
00259                         fire(firing_seq[i]) = 1;
00260 
00261                         Eigen::VectorXi enabled_trans = marking.transpose()*Dm;
00262                         if (enabled_trans(firing_seq[i]) == Dm.col(firing_seq[i]).sum())
00263                         {
00264                                 std::cout<<"Transition "<<transitionMap[firing_seq[i]]<<" is enabled."<<std::endl;
00265                         }
00266                         else
00267                         {
00268                                 std::cout<<"Transition "<<transitionMap[firing_seq[i]]<<" is not enabled."<<std::endl;
00269                                 std::cerr<<"Error - cannot fire transition."<<std::endl;
00270                                 break;
00271                         }
00272 
00273                         marking = marking + I*fire;
00274                         std::cout<<"Transition "<<transitionMap[firing_seq[i]]<<" fired, new marking:"<<marking<<std::endl;
00275                         if (marking(des_place))
00276                         {
00277                                 std::cout<<"Reached desired update in the marking."<<std::endl;
00278                                 break;
00279                         }
00280                 }
00281 
00282                 usleep(1000*1000);
00283         }
00284 }
00285 
00286 template <class Vector>
00287 void print_vec(const Vector& vec)
00288 {
00289         std::cout<<"(";
00290         int i;
00291         for(i=0; i<vec.size()-1;++i)
00292         {
00293                 std::cout<<vec(i)<<",";
00294         }
00295         std::cout<<vec(i)<<")";
00296 }
00297 
00298 template <typename DistanceMap, typename PredecessorMap>
00299 class bacon_number_recorder : public boost::default_bfs_visitor
00300 {
00301 public:
00302         bacon_number_recorder(DistanceMap dist, PredecessorMap p) : d(dist), p(p) { }
00303 
00304         template <typename Edge, typename Graph>
00305         void tree_edge(Edge e, const Graph& g) const
00306         {
00307                 typename boost::graph_traits<Graph>::vertex_descriptor
00308                 u = source(e, g), v = target(e, g);
00309                 d[v] = d[u] + 1;
00310                 p[v] = u;
00311         }
00312 private:
00313         DistanceMap d;
00314         PredecessorMap p;
00315 };
00316 // Convenience function
00317 template <typename DistanceMap, typename PredecessorMap>
00318 bacon_number_recorder<DistanceMap, PredecessorMap>
00319 record_bacon_number(DistanceMap d, PredecessorMap p)
00320 {
00321         return bacon_number_recorder<DistanceMap, PredecessorMap>(d,p);
00322 }
00323 
00324 void PNController::get_firing_r(const std::string& name)
00325 {
00326         //Desired place to activate
00327         int des_place = nameMap[name].place_num;
00328 
00329         std::cout<<"Desired place to activate is: "<<placeMap[des_place]<<" == "<<des_place<<std::endl;
00330 
00331         std::vector<Eigen::VectorXi> av_markings;
00332         std::vector<int> av_diff, av_diff2;
00333         for(int i=0; i<all_markings.size(); ++i)
00334         {
00335                 if (all_markings[i](des_place))
00336                 {
00337                         av_markings.push_back(all_markings[i]);
00338                         Eigen::VectorXi diff = (all_markings[i] - marking);
00339                         diff = diff.array().abs();
00340                         av_diff.push_back(diff.sum());
00341                         av_diff2.push_back(diff.sum());
00342                 }
00343         }
00344 
00345         std::sort(av_diff2.begin(),av_diff2.end());
00346         std::vector<int>::iterator it=std::find(av_diff.begin(), av_diff.end(),av_diff2[0]);
00347 
00348         std::cout<<"From "<<all_markings.size();
00349         std::cout<<" markings, "<<av_markings.size()<<" have the needed place enabled."<<std::endl;
00350         std::cout<<"The smallest difference marking is:";
00351         print_vec(av_markings[it-av_diff.begin()]);
00352         std::cout<<std::endl;
00353         std::cout<<"The current marking is:";
00354         print_vec(marking);
00355         std::cout<<std::endl;
00356         //marking = av_markings[it-av_diff.begin()];
00357 
00358         std::vector<Eigen::VectorXi>::iterator it2 = std::find(all_markings.begin(), all_markings.end(),marking);
00359 
00360         std::vector <int> bacon_number(boost::num_vertices(rgraph));
00361         std::vector <int> pred_map(boost::num_vertices(rgraph));
00362         //Create the distance map
00363         GraphType::vertex_descriptor curr_vx = all_idx[it2-all_markings.begin()];
00364         std::cout<<"The current marking index is: "<<it2-all_markings.begin()<<std::endl;
00365   boost::breadth_first_search(rgraph, all_idx[it2-all_markings.begin()],
00366                        boost::visitor(record_bacon_number(&bacon_number[0],&pred_map[0])));
00367 
00368   std::vector<int> av_idx;
00369   av_markings.clear();
00370   av_diff.clear();
00371   av_diff2.clear();
00372         for(int i=0; i<all_markings.size(); ++i)
00373         {
00374                 if (all_markings[i](des_place))
00375                 {
00376                         av_markings.push_back(all_markings[i]);
00377                         av_idx.push_back(all_idx[i]);
00378                         av_diff.push_back(bacon_number[all_idx[i]]);
00379                         av_diff2.push_back(bacon_number[all_idx[i]]);
00380                 }
00381         }
00382 
00383         std::sort(av_diff2.begin(),av_diff2.end());
00384         it=std::find(av_diff.begin(), av_diff.end(),av_diff2[0]);
00385 
00386   std::cout<<"The closest marking(s) is(are):"<<std::endl;
00387   bool run=true;
00388   std::vector<int>::iterator it_h = av_diff.begin();
00389   while (it_h != av_diff.end())
00390   {
00391         it_h=std::find(it_h, av_diff.end(),av_diff2[0]);
00392         if (it_h != av_diff.end())
00393         {
00394                 std::cout<<"\t";
00395                 print_vec(av_markings[it_h-av_diff.begin()]);
00396                 std::cout<<std::endl;
00397                 ++it_h;
00398         }
00399   }
00400   std::cout<<std::endl;
00401   std::cout<<"The current marking is:";
00402   print_vec(marking);
00403   std::cout<<std::endl;
00404   Eigen::VectorXi m_new = av_markings[it-av_diff.begin()];
00405 
00406   std::cout<<"Calculating firing seq."<<std::endl;
00407   //Firing sequence to closest based on predecessor map.
00408   GraphType::vertex_descriptor closest_vx = av_idx[it-av_diff.begin()];
00409   GraphType::vertex_descriptor curr_pred = closest_vx;
00410 
00411   boost::property_map<GraphType, boost::edge_name_t>::type edgeMap =
00412                 boost::get(boost::edge_name_t(), rgraph);
00413   std::cout<<"Get the edge map."<<std::endl;
00414 //  if (boost::edge(curr_pred, closest_vx, rgraph).second)
00415 //  {
00416 //      std::cout<<"Get edge connection between "<<curr_pred<<" and "<<closest_vx<<std::endl;
00417 //    firing_seq.push_back(edgeMap[boost::edge(curr_pred, closest_vx, rgraph).first]);
00418 //  }
00419 //  else
00420 //  {
00421 //      std::cout<<"No edge connection between "<<curr_pred<<" and "<<closest_vx<<std::endl;
00422 //  }
00423 
00424   std::cout<<"Loop across edges. The curr_pred = closest_vx = "<<closest_vx<<std::endl;
00425   //Go back...
00426   std::vector<int> firing_seq;
00427   while (curr_pred != curr_vx)
00428   {
00429     if (boost::edge(pred_map[curr_pred], curr_pred, rgraph).second)
00430     {
00431         std::cout<<"Get edge connection between "<<pred_map[curr_pred]<<" and "<<curr_pred<<std::endl;
00432         firing_seq.push_back(edgeMap[boost::edge(pred_map[curr_pred], curr_pred, rgraph).first]);
00433     }
00434     else
00435     {
00436         std::cout<<"No edge connection between "<<pred_map[curr_pred]<<" and "<<curr_pred<<std::endl;
00437     }
00438     curr_pred = pred_map[curr_pred];
00439   }
00440 
00441   std::cout<<"Triggering the following sequence:";
00442   for (int i=firing_seq.size()-1; i>=0; --i)
00443   {
00444         std::cout<<transitionMap[firing_seq[i]]<<" -> ";
00445   }
00446   std::cout<<"end"<<std::endl;
00447 
00448   marking = m_new;
00449 }
00450 
00451 void PNController::reachability()
00452 {
00453         rgraph.clear();
00454         Eigen::VectorXi m0=Eigen::VectorXi::Zero(marking.size());
00455         for (int i=0; i<6;++i) m0(i) = 1;
00456 
00457         typedef GraphType::vertex_descriptor IntType;
00458         std::queue<Eigen::VectorXi> new_markings;
00459         std::queue<IntType> new_idx;
00460 
00461         std::vector<Eigen::VectorXi> visited;
00462         std::vector<Eigen::VectorXi> all_markings;
00463         std::vector<IntType> all_markings_idx;
00464         std::vector<IntType> visited_idx;
00465         std::list<Eigen::VectorXi> dead_end;
00466 
00467         new_markings.push(m0);
00468         new_idx.push(boost::add_vertex(rgraph));
00469         rgraph[new_idx.back()].marking = m0;
00470 
00471         std::cout<<"Building reachability graph:"<<std::endl<<std::endl;
00472 
00473         while (!new_markings.empty())
00474         {
00475                 Eigen::VectorXi m;
00476                 IntType idx;
00477                 bool foundNew = false;
00478                 do
00479                 {
00480                         m = new_markings.front();
00481                         idx = new_idx.front();
00482                         new_markings.pop();
00483                         new_idx.pop();
00484                         foundNew = !(std::find(visited.begin(),visited.end(),m) != visited.end());
00485                 }
00486                 while ((!foundNew) && (!new_markings.empty()));
00487 
00488                 if (!foundNew) break;
00489                 std::cout<<"Selected new marking:"<<m<<std::endl;
00490                 visited.push_back(m);
00491                 visited_idx.push_back(idx);
00492                 if (std::find(all_markings.begin(),all_markings.end(),m) == all_markings.end())
00493                 {
00494                         all_markings.push_back(m);
00495                         all_markings_idx.push_back(idx);
00496                 }
00497 
00498                 //Check for enabled transitions
00499                 Eigen::VectorXi all_trans = m.transpose()*Dm;
00500                 for (int i=0; i<all_trans.size(); ++i)
00501                 {
00502                         if (all_trans(i) == Dm.col(i).sum())
00503                         {
00504                                 std::cout<<"Transition "<<transitionMap[i]<<" is enabled.";
00505                                 std::cout<<"("<<all_trans(i)<<", "<<Dm.col(i).sum()<<")"<<std::endl;
00506                                 Eigen::VectorXi fire = Eigen::VectorXi::Zero(Dm.cols());
00507                                 fire(i) = 1;
00508                                 Eigen::VectorXi newmarking = m + I*fire;
00509 
00510                                 //Check if we already found this marking.
00511                                 std::vector<Eigen::VectorXi>::iterator it;
00512                                 it = std::find(all_markings.begin(),all_markings.end(),newmarking);
00513 
00514                                 if (it == all_markings.end())
00515                                 {
00516                                         //New marking
00517                                         IntType nidx = boost::add_vertex(rgraph);
00518                                         new_markings.push(newmarking);
00519                                         new_idx.push(nidx);
00520                                         all_markings.push_back(newmarking);
00521                                         all_markings_idx.push_back(nidx);
00522                                         rgraph[nidx].marking = newmarking;
00523                                         boost::add_edge(idx, nidx,
00524                                                         i,rgraph);
00525                                 }
00526                                 else
00527                                 {
00528                                         //Old marking
00529                                         //Possibly suboptimal
00530                                         int old_idx = it - all_markings.begin();
00531                                         boost::add_edge(idx, all_markings_idx[old_idx],
00532                                                         i,rgraph);
00533                                 }
00534                         }
00535                 }
00536         }
00537 
00538         std::cout<<"Finished building graph."<<std::endl;
00539         this->all_markings.swap(all_markings);
00540         this->all_idx.swap(all_markings_idx);
00541 }
00542 
00543 void PNController::getDotDesc(std::string& desc)
00544 {
00545         using namespace boost;
00546         //Construct a label writer.
00547         std::ostringstream out;
00548         write_graphviz(out, rgraph,
00549                         pn_writer(rgraph),
00550                         make_edge_writer(
00551                                         boost::get(boost::edge_name_t(),rgraph),
00552                                         transitionMap));
00553         desc = out.str();
00554 }
00555 


labust_execution
Author(s): Gyula Nagy
autogenerated on Fri Feb 7 2014 11:36:24