From: Mikko Rasa Date: Fri, 7 Jun 2013 10:40:16 +0000 (+0300) Subject: Create a base class to handle common operations in Block, Route and Zone X-Git-Url: http://git.tdb.fi/?p=r2c2.git;a=commitdiff_plain;h=73edcbd8841752a2722fc8b3927fee2abbbba750 Create a base class to handle common operations in Block, Route and Zone --- diff --git a/source/libr2c2/block.cpp b/source/libr2c2/block.cpp index 6103509..bf77433 100644 --- a/source/libr2c2/block.cpp +++ b/source/libr2c2/block.cpp @@ -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 diff --git a/source/libr2c2/block.h b/source/libr2c2/block.h index f71ee29..7d6211b 100644 --- a/source/libr2c2/block.h +++ b/source/libr2c2/block.h @@ -4,6 +4,7 @@ #include #include #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 signal_reserved; private: - Layout &layout; unsigned id; unsigned sensor_id; unsigned turnout_id; TrackCircuit *sensor; - std::set tracks; std::vector 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 &get_tracks() const { return tracks; } - bool has_track(Track &) const; const std::vector &get_endpoints() const { return endpoints; } const Endpoint &get_endpoint(unsigned) const; int get_endpoint_by_link(Block &) const; diff --git a/source/libr2c2/route.cpp b/source/libr2c2/route.cpp index 685ad66..a01b80b 100644 --- a/source/libr2c2/route.cpp +++ b/source/libr2c2/route.cpp @@ -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 &trks) -{ - set pending; - for(set::const_iterator i=trks.begin(); i!=trks.end(); ++i) - if(!tracks.count(*i)) - pending.insert(*i); - - if(tracks.empty()) - { - set::iterator i = pending.begin(); - tracks.insert(*i); - pending.erase(i); - } - - while(!pending.empty()) - { - RouteValidityMask valid = ROUTE_INVALID; - for(set::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 &st) const @@ -339,75 +276,6 @@ void Route::save(list &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 &links = trk.get_links(); - for(vector::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 &tlinks = (*i)->get_links(); - unsigned count = 0; - for(unsigned j=0; jget_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(result); -} - -void Route::object_removed(Object &o) -{ - if(Track *t = dynamic_cast(&o)) - tracks.erase(t); -} - Route *Route::find(const TrackIter &from, Track &to) { return create_route(from, TrackMatch(to)); diff --git a/source/libr2c2/route.h b/source/libr2c2/route.h index 0dd16f4..9b440cb 100644 --- a/source/libr2c2/route.h +++ b/source/libr2c2/route.h @@ -6,6 +6,7 @@ #include #include #include +#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 TurnoutMap; @@ -55,10 +37,8 @@ public: sigc::signal signal_name_changed; private: - Layout &layout; std::string name; bool temporary; - std::set tracks; TurnoutMap turnouts; public: @@ -74,17 +54,13 @@ public: int get_turnout(unsigned) const; unsigned get_path(Track &) const; const std::map &get_turnouts() const { return turnouts; } - void add_track(Track &); - void add_tracks(const std::set &); void add_track_chain(const TrackIter &, const TurnoutMap &); - const std::set &get_tracks() const { return tracks; } - bool has_track(Track &) const; - void save(std::list &) const; private: - RouteValidityMask check_validity(Track &) const; - void object_removed(Object &); + virtual void on_track_added(Track &); public: + void save(std::list &) 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 index 0000000..3d9d66a --- /dev/null +++ b/source/libr2c2/trackchain.cpp @@ -0,0 +1,135 @@ +#include +#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<(&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 index 0000000..e73e23b --- /dev/null +++ b/source/libr2c2/trackchain.h @@ -0,0 +1,62 @@ +#ifndef LIBR2C2_TRACKCHAIN_H_ +#define LIBR2C2_TRACKCHAIN_H_ + +#include +#include +#include +#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 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 diff --git a/source/libr2c2/zone.cpp b/source/libr2c2/zone.cpp index 72712a2..00edb57 100644 --- a/source/libr2c2/zone.cpp +++ b/source/libr2c2/zone.cpp @@ -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 &st) const { st.push_back((DataFile::Statement("group"), group)); @@ -99,19 +59,6 @@ void Zone::save(list &st) const st.push_back((DataFile::Statement("block"), *i)); } -bool Zone::is_valid(Track &t) const -{ - if(tracks.empty()) - return true; - - const vector &links = t.get_links(); - for(vector::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(z) diff --git a/source/libr2c2/zone.h b/source/libr2c2/zone.h index 9ff1ba6..7b1dd9f 100644 --- a/source/libr2c2/zone.h +++ b/source/libr2c2/zone.h @@ -4,13 +4,14 @@ #include #include #include +#include "trackchain.h" namespace R2C2 { class Layout; class Track; -class Zone +class Zone: public TrackChain { public: class Loader: public Msp::DataFile::ObjectLoader @@ -21,16 +22,12 @@ public: void block(unsigned); }; - typedef std::set TrackSet; - sigc::signal 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 &) const; -private: - bool is_valid(Track &) const; }; } // namespace R2C2