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()),
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
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
#include <list>
#include <set>
#include "track.h"
+#include "trackchain.h"
namespace R2C2 {
class TrackIter;
class Train;
-class Block
+class Block: public TrackChain
{
public:
struct Endpoint
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;
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;
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()
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)
}
}
-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
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));
#include <string>
#include <sigc++/trackable.h>
#include <msp/datafile/objectloader.h>
+#include "trackchain.h"
namespace R2C2 {
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;
sigc::signal<void, const std::string &> signal_name_changed;
private:
- Layout &layout;
std::string name;
bool temporary;
- std::set<Track *> tracks;
TurnoutMap turnouts;
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 &);
--- /dev/null
+#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
--- /dev/null
+#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
namespace R2C2 {
Zone::Zone(Layout &l):
- layout(l),
+ TrackChain(l),
number(0)
{
layout.add_zone(*this);
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));
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)
#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>
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 &);
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