]> git.tdb.fi Git - r2c2.git/commitdiff
Create a base class to handle common operations in Block, Route and Zone
authorMikko Rasa <tdb@tdb.fi>
Fri, 7 Jun 2013 10:40:16 +0000 (13:40 +0300)
committerMikko Rasa <tdb@tdb.fi>
Fri, 7 Jun 2013 10:40:16 +0000 (13:40 +0300)
source/libr2c2/block.cpp
source/libr2c2/block.h
source/libr2c2/route.cpp
source/libr2c2/route.h
source/libr2c2/trackchain.cpp [new file with mode: 0644]
source/libr2c2/trackchain.h [new file with mode: 0644]
source/libr2c2/zone.cpp
source/libr2c2/zone.h

index 6103509f3647ce053d7c653f3eae28ab3a2b3ca9..bf77433491df7b210631dd886c0d7435ced23e93 100644 (file)
@@ -14,7 +14,7 @@ using namespace Msp;
 namespace R2C2 {
 
 Block::Block(Layout &l, Track &start):
-       layout(l),
+       TrackChain(l),
        id(0),
        sensor_id(start.get_sensor_id()),
        turnout_id(start.get_turnout_id()),
@@ -38,7 +38,7 @@ Block::Block(Layout &l, Track &start):
                                if(links[i]->get_sensor_id()==sensor_id && links[i]->get_turnout_id()==turnout_id)
                                {
                                        queue.push_back(links[i]);
-                                       tracks.insert(links[i]);
+                                       add_track(*links[i]);
                                        links[i]->set_block(this);
                                }
                                else
@@ -79,9 +79,12 @@ Block::~Block()
        delete sensor;
 }
 
-bool Block::has_track(Track &t) const
+TrackChain::Validity Block::check_validity(Track &track) const
 {
-       return tracks.count(&t);
+       if(track.get_sensor_id()!=sensor_id || track.get_turnout_id()!=turnout_id)
+               return INCOMPATIBLE;
+
+       return TrackChain::check_validity(track);
 }
 
 const Block::Endpoint &Block::get_endpoint(unsigned i) const
index f71ee29b63f6871ccc2a419e9638a02a22dc54e2..7d6211b0a5b303b9e85669fb96d72882606c838d 100644 (file)
@@ -4,6 +4,7 @@
 #include <list>
 #include <set>
 #include "track.h"
+#include "trackchain.h"
 
 namespace R2C2 {
 
@@ -13,7 +14,7 @@ class TrackCircuit;
 class TrackIter;
 class Train;
 
-class Block
+class Block: public TrackChain
 {
 public:
        struct Endpoint
@@ -31,12 +32,10 @@ public:
        sigc::signal<void, Train *> signal_reserved;
 
 private:
-       Layout &layout;
        unsigned id;
        unsigned sensor_id;
        unsigned turnout_id;
        TrackCircuit *sensor;
-       std::set<Track *> tracks;
        std::vector<Endpoint> endpoints;
        Train *train;
 
@@ -44,12 +43,14 @@ public:
        Block(Layout &, Track &);
        ~Block();
 
+private:
+       virtual Validity check_validity(Track &) const;
+
+public:
        unsigned get_id() const { return id; }
        unsigned get_sensor_id() const { return sensor_id; }
        unsigned get_turnout_id() const { return turnout_id; }
        TrackCircuit &get_sensor() const { return *sensor; }
-       const std::set<Track *> &get_tracks() const { return tracks; }
-       bool has_track(Track &) const;
        const std::vector<Endpoint> &get_endpoints() const { return endpoints; }
        const Endpoint &get_endpoint(unsigned) const;
        int get_endpoint_by_link(Block &) const;
index 685ad666404688275cfa87eda6f7a48a0e60c9b4..a01b80b3b183be4a8591568e18ad9deaf4a538d1 100644 (file)
@@ -128,26 +128,11 @@ Route *create_route(const TrackIter &from, const Pred &goal)
 
 namespace R2C2 {
 
-string bad_route::get_message(RouteValidityMask valid)
-{
-       const char *reasons[3];
-       unsigned i = 0;
-       if(!(valid&1))
-               reasons[i++] = "unlinked";
-       if(!(valid&2))
-               reasons[i++] = "branching";
-       if(!(valid&4))
-               reasons[i++] = "not smooth";
-       return join(reasons, reasons+i, ", ");
-}
-
-
 Route::Route(Layout &l):
-       layout(l),
+       TrackChain(l),
        temporary(false)
 {
        layout.add_route(*this);
-       layout.signal_object_removed.connect(sigc::mem_fun(this, &Route::object_removed));
 }
 
 Route::~Route()
@@ -249,54 +234,6 @@ unsigned Route::get_path(Track &trk) const
        return trk.get_active_path();
 }
 
-void Route::add_track(Track &trk)
-{
-       if(tracks.count(&trk))
-               return;
-
-       if(!tracks.empty())
-       {
-               RouteValidityMask valid = check_validity(trk);
-               if(valid!=ROUTE_VALID)
-                       throw bad_route(valid);
-       }
-
-       tracks.insert(&trk);
-       update_turnouts();
-}
-
-void Route::add_tracks(const set<Track *> &trks)
-{
-       set<Track *> pending;
-       for(set<Track *>::const_iterator i=trks.begin(); i!=trks.end(); ++i)
-               if(!tracks.count(*i))
-                       pending.insert(*i);
-
-       if(tracks.empty())
-       {
-               set<Track *>::iterator i = pending.begin();
-               tracks.insert(*i);
-               pending.erase(i);
-       }
-
-       while(!pending.empty())
-       {
-               RouteValidityMask valid = ROUTE_INVALID;
-               for(set<Track *>::iterator i=pending.begin(); i!=pending.end(); ++i)
-                       if((valid=check_validity(**i))==ROUTE_VALID)
-                       {
-                               tracks.insert(*i);
-                               pending.erase(i);
-                               break;
-                       }
-
-               if(valid!=ROUTE_VALID)
-                       throw bad_route(valid);
-       }
-
-       update_turnouts();
-}
-
 void Route::add_track_chain(const TrackIter &start, const TurnoutMap &trnts)
 {
        if(!start)
@@ -327,9 +264,9 @@ void Route::add_track_chain(const TrackIter &start, const TurnoutMap &trnts)
        }
 }
 
-bool Route::has_track(Track &t) const
+void Route::on_track_added(Track &)
 {
-       return tracks.count(&t);
+       update_turnouts();
 }
 
 void Route::save(list<DataFile::Statement> &st) const
@@ -339,75 +276,6 @@ void Route::save(list<DataFile::Statement> &st) const
                st.push_back((DataFile::Statement("turnout"), i->first, i->second));
 }
 
-RouteValidityMask Route::check_validity(Track &trk) const
-{
-       unsigned result = ROUTE_SMOOTH;
-       const vector<Track *> &links = trk.get_links();
-       for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
-       {
-               if(!*i)
-                       continue;
-               if(!tracks.count(*i))
-                       continue;
-
-               // Linked to an existing track - good
-               result |= ROUTE_LINKED;
-
-               if(unsigned tid = (*i)->get_turnout_id())
-               {
-                       const TrackType::Endpoint &ep = (*i)->get_type().get_endpoint((*i)->get_link_slot(trk));
-                       int path = get_turnout(tid);
-                       if(path>=0)
-                       {
-                               // Linking to a turnout with path set is only good if we're continuing that path
-                               if(ep.has_path(path))
-                                       result |= ROUTE_LINEAR;
-                       }
-                       else
-                       {
-                               // Linked to a turnout with no path set - check other linked tracks 
-                               const vector<Track *> &tlinks = (*i)->get_links();
-                               unsigned count = 0;
-                               for(unsigned j=0; j<tlinks.size(); ++j)
-                                       if(tracks.count(tlinks[j]))
-                                       {
-                                               unsigned tid2 = tlinks[j]->get_turnout_id();
-                                               if(tid2)
-                                               {
-                                                       const TrackType::Endpoint &ep2 = tlinks[j]->get_type().get_endpoint(tlinks[j]->get_link_slot(**i));
-                                                       path = get_turnout(tid2);
-                                                       // Ignore a linked turnout with some other path set
-                                                       if(path>=0 && !ep2.has_path(path))
-                                                               continue;
-                                               }
-
-                                               ++count;
-
-                                               const TrackType::Endpoint &ep2 = (*i)->get_type().get_endpoint(j);
-                                               if(!ep.has_common_paths(ep2))
-                                                       // Impossible path through the turnout - not good
-                                                       result &= ~ROUTE_SMOOTH;
-                                       }
-
-                               // Only good if at most one other track is linked to the turnout
-                               if(count<=1)
-                                       result |= ROUTE_LINEAR;
-                       }
-               }
-               else
-                       // Linked to something linear - good
-                       result |= ROUTE_LINEAR;
-       }
-
-       return static_cast<RouteValidityMask>(result);
-}
-
-void Route::object_removed(Object &o)
-{
-       if(Track *t = dynamic_cast<Track *>(&o))
-               tracks.erase(t);
-}
-
 Route *Route::find(const TrackIter &from, Track &to)
 {
        return create_route(from, TrackMatch(to));
index 0dd16f44342c89b84650dcbc5de6865c0a9c2f04..9b440cb255bdb33bdf9f4981c1138be91ec466ca 100644 (file)
@@ -6,6 +6,7 @@
 #include <string>
 #include <sigc++/trackable.h>
 #include <msp/datafile/objectloader.h>
+#include "trackchain.h"
 
 namespace R2C2 {
 
@@ -15,27 +16,8 @@ class Track;
 class TrackIter;
 class Zone;
 
-enum RouteValidityMask
-{
-       ROUTE_INVALID = 0,
-       ROUTE_LINKED = 1,
-       ROUTE_LINEAR = 2,
-       ROUTE_SMOOTH = 4,
-       ROUTE_VALID = 7
-};
-
-class bad_route: public std::logic_error
-{
-public:
-       bad_route(RouteValidityMask m): std::logic_error(get_message(m)) { }
-       virtual ~bad_route() throw() { }
-
-private:
-       static std::string get_message(RouteValidityMask);
-};
-
 
-class Route: public sigc::trackable
+class Route: public TrackChain
 {
 public:
        typedef std::map<unsigned, int> TurnoutMap;
@@ -55,10 +37,8 @@ public:
        sigc::signal<void, const std::string &> signal_name_changed;
 
 private:
-       Layout &layout;
        std::string name;
        bool temporary;
-       std::set<Track *> tracks;
        TurnoutMap turnouts;
 
 public:
@@ -74,17 +54,13 @@ public:
        int get_turnout(unsigned) const;
        unsigned get_path(Track &) const;
        const std::map<unsigned, int> &get_turnouts() const { return turnouts; }
-       void add_track(Track &);
-       void add_tracks(const std::set<Track *> &);
        void add_track_chain(const TrackIter &, const TurnoutMap &);
-       const std::set<Track *> &get_tracks() const { return tracks; }
-       bool has_track(Track &) const;
-       void save(std::list<Msp::DataFile::Statement> &) const;
 private:
-       RouteValidityMask check_validity(Track &) const;
-       void object_removed(Object &);
+       virtual void on_track_added(Track &);
 
 public:
+       void save(std::list<Msp::DataFile::Statement> &) const;
+
        static Route *find(const TrackIter &, Track &);
        static Route *find(const TrackIter &, const Route &);
        static Route *find(const TrackIter &, const Zone &);
diff --git a/source/libr2c2/trackchain.cpp b/source/libr2c2/trackchain.cpp
new file mode 100644 (file)
index 0000000..3d9d66a
--- /dev/null
@@ -0,0 +1,135 @@
+#include <msp/strings/utils.h>
+#include "layout.h"
+#include "track.h"
+#include "trackchain.h"
+
+namespace R2C2 {
+
+TrackChain::TrackChain(Layout &l):
+       layout(l)
+{
+       layout.signal_object_removed.connect(sigc::mem_fun(this, &TrackChain::object_removed));
+}
+
+void TrackChain::add_track(Track &track)
+{
+       if(tracks.count(&track))
+               return;
+
+       Validity valid = check_validity(track);
+       if(valid!=VALID)
+               throw_bad_chain(valid);
+
+       tracks.insert(&track);
+       update_ends(track);
+       on_track_added(track);
+}
+
+void TrackChain::add_tracks(const TrackSet &trks)
+{
+       TrackSet pending;
+       for(TrackSet::const_iterator i=trks.begin(); i!=trks.end(); ++i)
+               if(!tracks.count(*i))
+                       pending.insert(*i);
+
+       while(!pending.empty())
+       {
+               Validity valid = UNLINKED;
+               for(TrackSet::iterator i=pending.begin(); i!=pending.end(); ++i)
+                       if((valid=check_validity(**i))==VALID)
+                       {
+                               tracks.insert(*i);
+                               update_ends(**i);
+                               on_track_added(**i);
+                               pending.erase(i);
+                               break;
+                       }
+
+               if(valid!=VALID)
+                       throw_bad_chain(valid);
+       }
+}
+
+TrackChain::Validity TrackChain::check_validity(Track &track) const
+{
+       if(tracks.empty())
+               return VALID;
+
+       if(!ends[1])
+       {
+               if(!ends[0])
+                       return UNLINKED;
+               return ends[0]->get_link_slot(track)>=0 ? VALID : UNLINKED;
+       }
+
+       Validity result = UNLINKED;
+       for(unsigned i=0; i<2; ++i)
+       {
+               int j = ends[i]->get_link_slot(track);
+               if(j>=0)
+               {
+                       const TrackType::Endpoint &ep1 = ends[i].endpoint();
+                       const TrackType::Endpoint &ep2 = ends[i]->get_type().get_endpoint(j);
+                       if(!ep1.has_common_paths(ep2))
+                               return BAD_PATH;
+
+                       result = VALID;
+               }
+       }
+
+       return result;
+}
+
+void TrackChain::throw_bad_chain(Validity v)
+{
+       if(v==UNLINKED)
+               throw bad_chain("unlinked");
+       else if(v==BAD_PATH)
+               throw bad_chain("bad path");
+       else if(v==INCOMPATIBLE)
+               throw bad_chain("incompatible");
+}
+
+void TrackChain::update_ends(Track &track)
+{
+       if(!ends[0])
+               // We don't really know the proper endpoint yet; just pick something
+               ends[0] = TrackIter(&track, 0);
+       else if(!ends[1])
+       {
+               // We're adding the second track; determine endpoints for both
+               ends[0] = TrackIter(&*ends[0], ends[0]->get_link_slot(track));
+               ends[1] = TrackIter(&track, track.get_link_slot(*ends[0]));
+       }
+       else
+       {
+               unsigned linked = 0;
+               for(unsigned i=0; i<2; ++i)
+                       if(ends[i]->get_link_slot(track)>=0)
+                               linked |= 1<<i;
+
+               if(linked==3)
+               {
+                       // Closed loop; clear the ends
+                       ends[0] = TrackIter();
+                       ends[1] = TrackIter();
+               }
+               else
+                       ends[linked-1] = TrackIter(&track, track.get_link_slot(*ends[linked-1]));
+       }
+}
+
+bool TrackChain::has_track(Track &t) const
+{
+       return tracks.count(&t);
+}
+
+void TrackChain::object_removed(Object &obj)
+{
+       if(Track *track = dynamic_cast<Track *>(&obj))
+               tracks.erase(track);
+               /* TODO If the track was in the middle of the chain, keep only the
+               longest fragment */
+}
+
+} // namespace R2C2
diff --git a/source/libr2c2/trackchain.h b/source/libr2c2/trackchain.h
new file mode 100644 (file)
index 0000000..e73e23b
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef LIBR2C2_TRACKCHAIN_H_
+#define LIBR2C2_TRACKCHAIN_H_
+
+#include <set>
+#include <stdexcept>
+#include <sigc++/trackable.h>
+#include "trackiter.h"
+
+namespace R2C2 {
+
+class Layout;
+class Object;
+
+class bad_chain: public std::logic_error
+{
+public:
+       bad_chain(const std::string &w): std::logic_error(w) { }
+       virtual ~bad_chain() throw() { }
+};
+
+
+class TrackChain: public sigc::trackable
+{
+public:
+       typedef std::set<Track *> TrackSet;
+
+protected:
+       enum Validity
+       {
+               VALID,
+               UNLINKED,
+               BAD_PATH,
+               INCOMPATIBLE
+       };
+
+       Layout &layout;
+       TrackSet tracks;
+       TrackIter ends[2];
+
+       TrackChain(Layout &);
+public:
+       virtual ~TrackChain() { }
+
+       void add_track(Track &);
+       void add_tracks(const TrackSet &);
+protected:
+       virtual void on_track_added(Track &) { }
+       virtual Validity check_validity(Track &) const;
+       static void throw_bad_chain(Validity);
+private:
+       void update_ends(Track &);
+public:
+       const TrackSet &get_tracks() const { return tracks; }
+       bool has_track(Track &) const;
+
+private:
+       void object_removed(Object &);
+};
+
+} // namespace R2C2
+
+#endif
index 72712a2f1d74a6d911faa967c01d948d4d133cbe..00edb5799f6d4d9b49f481f7d7ef3f76113666ce 100644 (file)
@@ -10,7 +10,7 @@ using namespace Msp;
 namespace R2C2 {
 
 Zone::Zone(Layout &l):
-       layout(l),
+       TrackChain(l),
        number(0)
 {
        layout.add_zone(*this);
@@ -43,46 +43,6 @@ string Zone::get_name() const
        return result;
 }
 
-void Zone::add_track(Track &track)
-{
-       if(!is_valid(track))
-               throw logic_error("unconnected");
-
-       tracks.insert(&track);
-}
-
-bool Zone::add_tracks(const TrackSet &trks)
-{
-       TrackSet pending = trks;
-       bool first = true;
-       while(1)
-       {
-               bool ok = false;
-               for(TrackSet::const_iterator i=pending.begin(); i!=pending.end(); ++i)
-                       if(is_valid(**i))
-                       {
-                               tracks.insert(*i);
-                               pending.erase(i);
-                               ok = true;
-                               break;
-                       }
-
-               if(!ok)
-               {
-                       if(first)
-                               throw logic_error("unconnected");
-                       return pending.empty();
-               }
-
-               first = false;
-       }
-}
-
-bool Zone::has_track(Track &t) const
-{
-       return tracks.count(&t);
-}
-
 void Zone::save(list<DataFile::Statement> &st) const
 {
        st.push_back((DataFile::Statement("group"), group));
@@ -99,19 +59,6 @@ void Zone::save(list<DataFile::Statement> &st) const
                st.push_back((DataFile::Statement("block"), *i));
 }
 
-bool Zone::is_valid(Track &t) const
-{
-       if(tracks.empty())
-               return true;
-
-       const vector<Track *> &links = t.get_links();
-       for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
-               if(*i && tracks.count(*i))
-                       return true;
-
-       return false;
-}
-
 
 Zone::Loader::Loader(Zone &z):
        DataFile::ObjectLoader<Zone>(z)
index 9ff1ba6282d4d1b08e730a06066c75747b70743e..7b1dd9fb30d9bf29266fbfbef3cc5a5cbd50d000 100644 (file)
@@ -4,13 +4,14 @@
 #include <set>
 #include <string>
 #include <msp/datafile/objectloader.h>
+#include "trackchain.h"
 
 namespace R2C2 {
 
 class Layout;
 class Track;
 
-class Zone
+class Zone: public TrackChain
 {
 public:
        class Loader: public Msp::DataFile::ObjectLoader<Zone>
@@ -21,16 +22,12 @@ public:
                void block(unsigned);
        };
 
-       typedef std::set<Track *> TrackSet;
-
        sigc::signal<void, const std::string &, const std::string &, unsigned> signal_name_changed;
 
 private:
-       Layout &layout;
        std::string group;
        std::string qualifier;
        unsigned number;
-       TrackSet tracks;
 
 public:
        Zone(Layout &);
@@ -42,14 +39,7 @@ public:
        unsigned get_number() const { return number; }
        std::string get_name() const;
 
-       void add_track(Track &);
-       bool add_tracks(const TrackSet &);
-       const TrackSet &get_tracks() const { return tracks; }
-       bool has_track(Track &) const;
-
        void save(std::list<Msp::DataFile::Statement> &) const;
-private:
-       bool is_valid(Track &) const;
 };
 
 } // namespace R2C2