X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;ds=sidebyside;f=source%2Flibmarklin%2Flayout.cpp;h=5f590dbb9a762473c9daa279356ddafe49061ffe;hb=9b05c573a38639827697fe393d55b7c76f5bde45;hp=38336ae8d98f5ff6c3dfe3cbd3b4bc0e3c4bb4ab;hpb=025c23f199c411cc2ec1a6d2e85bf24460150ceb;p=r2c2.git diff --git a/source/libmarklin/layout.cpp b/source/libmarklin/layout.cpp index 38336ae..5f590db 100644 --- a/source/libmarklin/layout.cpp +++ b/source/libmarklin/layout.cpp @@ -1,66 +1,237 @@ /* $Id$ This file is part of the MSP Märklin suite -Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa +Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa Distributed under the GPL */ +#include #include #include #include +#include +#include +#include "block.h" #include "catalogue.h" +#include "driver.h" #include "layout.h" +#include "route.h" +#include "track.h" #include "tracktype.h" +#include "train.h" +#include "vehicletype.h" using namespace std; using namespace Msp; namespace Marklin { -Layout::Layout(const Catalogue &c): - catalogue(c) -{ } +Layout::Layout(Catalogue &c, Driver *d): + catalogue(c), + driver(d), + next_turnout_id(0x800) +{ + if(driver) + driver->signal_sensor.connect(sigc::mem_fun(this, &Layout::sensor_event)); +} Layout::~Layout() { - for(set::iterator i=tracks.begin(); i!=tracks.end(); ++i) - delete *i; + delete driver; + while(!trains.empty()) + delete trains.begin()->second; + while(!routes.empty()) + delete *routes.begin(); + while(!tracks.empty()) + delete *tracks.begin(); + while(!blocks.empty()) + delete *blocks.begin(); +} + +Driver &Layout::get_driver() const +{ + if(!driver) + throw InvalidState("No driver"); + return *driver; } void Layout::add_track(Track &t) { if(tracks.insert(&t).second) + { + create_blocks(); signal_track_added.emit(t); + } } void Layout::remove_track(Track &t) { if(tracks.erase(&t)) + { + create_blocks(t); signal_track_removed.emit(t); + } +} + +unsigned Layout::allocate_turnout_id(bool dbl) +{ + set used_ids; + for(set::const_iterator i=tracks.begin(); i!=tracks.end(); ++i) + if((*i)->get_turnout_id()) + used_ids.insert((*i)->get_turnout_id()); + + unsigned result = next_turnout_id; + while(used_ids.count(result) || (dbl && used_ids.count(result+1))) + ++result; + next_turnout_id = result+1+dbl; + + return result; +} + +void Layout::add_block(Block &b) +{ + blocks.insert(&b); +} + +Block &Layout::get_block(unsigned id) const +{ + for(set::const_iterator i=blocks.begin(); i!=blocks.end(); ++i) + if((*i)->get_id()==id) + return **i; + + throw KeyError("Unknown block", lexical_cast(id)); +} + +void Layout::create_blocks() +{ + set used_tracks; + for(set::const_iterator i=blocks.begin(); i!=blocks.end(); ++i) + { + const set &btracks = (*i)->get_tracks(); + used_tracks.insert(btracks.begin(), btracks.end()); + } + + for(set::const_iterator i=tracks.begin(); i!=tracks.end(); ++i) + if(used_tracks.count(*i)==0) + { + Block *block = new Block(*this, **i); + used_tracks.insert(block->get_tracks().begin(), block->get_tracks().end()); + } + + for(set::iterator i=blocks.begin(); i!=blocks.end(); ++i) + for(set::iterator j=i; j!=blocks.end(); ++j) + if(j!=i) + (*i)->check_link(**j); +} + +void Layout::create_blocks(Track &track) +{ + /* Must collect the blocks in a set first while all tracks are still + guaranteed to have blocks and to avoid duplicate deletes */ + set del_blocks; + + del_blocks.insert(&track.get_block()); + + const vector &links = track.get_links(); + for(vector::const_iterator i=links.begin(); i!=links.end(); ++i) + if(*i) + del_blocks.insert(&(*i)->get_block()); + + for(set::iterator i=del_blocks.begin(); i!=del_blocks.end(); ++i) + delete *i; + + create_blocks(); +} + +void Layout::remove_block(Block &b) +{ + blocks.erase(&b); } void Layout::add_route(Route &r) { - if(routes.count(r.get_name())) - throw KeyError("Duplicate route name"); - routes[r.get_name()] = &r; - signal_route_added.emit(r); + if(routes.insert(&r).second) + signal_route_added.emit(r); } Route &Layout::get_route(const string &name) const { - map::const_iterator i = routes.find(name); - if(i==routes.end()) - throw KeyError("Unknown route", name); - return *i->second; + for(set::const_iterator i=routes.begin(); i!=routes.end(); ++i) + if((*i)->get_name()==name) + return **i; + throw KeyError("Unknown route", name); +} + +void Layout::update_routes() +{ + for(set::iterator i=routes.begin(); i!=routes.end(); ++i) + (*i)->update_turnouts(); } void Layout::remove_route(Route &r) { - if(routes.erase(r.get_name())) + if(routes.erase(&r)) signal_route_removed.emit(r); } +void Layout::add_train(Train &t) +{ + if(trains.count(t.get_address())) + throw KeyError("Duplicate train address", lexical_cast(t.get_address())); + + trains[t.get_address()] = &t; + signal_train_added.emit(t); +} + +Train &Layout::get_train(unsigned addr) const +{ + map::const_iterator i = trains.find(addr); + if(i==trains.end()) + throw KeyError("Unknown train", lexical_cast(addr)); + return *i->second; +} + +void Layout::remove_train(Train &t) +{ + if(trains.erase(t.get_address())) + signal_train_removed.emit(t); +} + +void Layout::add_vehicle(Vehicle &v) +{ + if(vehicles.insert(&v).second) + signal_vehicle_added.emit(v); +} + +void Layout::remove_vehicle(Vehicle &v) +{ + if(vehicles.erase(&v)) + signal_vehicle_removed.emit(v); +} + +void Layout::tick() +{ + if(driver) + driver->tick(); + + Time::TimeStamp t = Time::now(); + Time::TimeDelta dt; + if(last_tick) + dt = t-last_tick; + last_tick = t; + + for(map::iterator i=trains.begin(); i!=trains.end(); ++i) + i->second->tick(t, dt); +} + +void Layout::emergency(const string &msg) +{ + if(driver) + driver->halt(true); + IO::print("Emergency: %s\n", msg); + signal_emergency.emit(msg); +} + void Layout::save(const string &fn) { IO::BufferedFile out(fn, IO::M_WRITE); @@ -77,128 +248,104 @@ void Layout::save(const string &fn) writer.write(st); } - for(map::iterator i=routes.begin(); i!=routes.end(); ++i) + for(set::iterator i=routes.begin(); i!=routes.end(); ++i) { + if((*i)->is_temporary()) + continue; + DataFile::Statement st("route"); - st.append(i->first); - i->second->save(st.sub); + (*i)->save(st.sub); writer.write(st); } } -void Layout::check_links() +void Layout::save_trains(const string &fn) { - for(set::iterator i=tracks.begin(); i!=tracks.end(); ++i) - (*i)->break_links(); + IO::BufferedFile out(fn, IO::M_WRITE); + DataFile::Writer writer(out); - list flext; - for(set::iterator i=tracks.begin(); i!=tracks.end(); ++i) + for(map::const_iterator i=trains.begin(); i!=trains.end(); ++i) { - if((*i)->get_flex()) - flext.push_back(*i); - else - { - for(set::iterator j=i; j!=tracks.end(); ++j) - if(j!=i) - (*i)->snap_to(**j, true); - } + DataFile::Statement st("train"); + st.append(i->second->get_locomotive_type().get_article_number()); + st.append(i->second->get_address()); + st.append(i->second->get_protocol()); + i->second->save(st.sub); + writer.write(st); } - - for(list::iterator i=flext.begin(); i!=flext.end(); ++i) - for(set::iterator j=tracks.begin(); j!=tracks.end(); ++j) - if(*j!=*i) - (*i)->snap_to(**j, true); } -void Layout::check_routes() +void Layout::sensor_event(unsigned addr, bool state) { - for(map::iterator i=routes.begin(); i!=routes.end(); ++i) + if(state) { - // We must copy the turnout map, since adding tracks to the route will (temporarily) mess it up - const map turnouts = i->second->get_turnouts(); - - Track *track = 0; - unsigned trk_path = 0; - for(set::const_iterator j=tracks.begin(); j!=tracks.end(); ++j) - { - map::const_iterator k = turnouts.find((*j)->get_turnout_id()); - if(k!=turnouts.end()) + for(set::iterator i=blocks.begin(); i!=blocks.end(); ++i) + if((*i)->get_sensor_id()==addr) { - track = *j; - trk_path = k->second; + if(!(*i)->get_train()) + emergency(format("Unreserved sensor %d triggered", addr)); break; } - } - - if(!track) - continue; - - i->second->add_track(*track); - - const vector &eps = track->get_type().get_endpoints(); - unsigned ep = 0; - for(unsigned j=0; jtraverse(ep, trk_path); - Track *next = track->get_links()[out_ep]; - if(!next || next == start) - break; - ep = next->get_endpoint_by_link(*track); - if(next->get_type().get_n_paths()>1) - { - map::const_iterator j = turnouts.find(next->get_turnout_id()); - if(j==turnouts.end()) - break; - trk_path = j->second; - } - else - trk_path = 0; - i->second->add_track(*next); - track = next; - } } } Layout::Loader::Loader(Layout &l): - DataFile::BasicLoader(l) + DataFile::BasicLoader(l), + new_tracks(false) { add("base", &Layout::base); - add("route", &Loader::route); - add("track", &Loader::track); + add("route", static_cast(&Loader::route)); + add("route", static_cast(&Loader::route)); + add("track", static_cast(&Loader::track)); + add("track", static_cast(&Loader::track)); + add("train", static_cast(&Loader::train)); + add("train", static_cast(&Loader::train)); } void Layout::Loader::finish() { - obj.check_links(); - obj.check_routes(); - for(set::iterator i=obj.tracks.begin(); i!=obj.tracks.end(); ++i) (*i)->check_slope(); } +void Layout::Loader::route() +{ + Route *rte = new Route(obj); + load_sub(*rte); +} + void Layout::Loader::route(const string &n) { - RefPtr rte = new Route(obj, n); + Route *rte = new Route(obj); + rte->set_name(n); load_sub(*rte); - obj.add_route(*rte.release()); } void Layout::Loader::track(unsigned art_nr) { - TrackType &type = obj.catalogue.get_track(art_nr); + track(ArticleNumber(art_nr)); +} - RefPtr trk = new Track(type); +void Layout::Loader::track(ArticleNumber art_nr) +{ + Track *trk = new Track(obj, obj.catalogue.get_track(art_nr)); load_sub(*trk); - obj.add_track(*trk.release()); + new_tracks = true; + for(set::iterator i=obj.tracks.begin(); i!=obj.tracks.end(); ++i) + if(*i!=trk) + trk->snap_to(**i, true); +} + +void Layout::Loader::train(unsigned art_nr, unsigned addr, const std::string &proto) +{ + train(ArticleNumber(art_nr), addr, proto); +} + +void Layout::Loader::train(ArticleNumber art_nr, unsigned addr, const std::string &proto) +{ + Train *trn = new Train(obj, obj.catalogue.get_vehicle(art_nr), addr, proto); + load_sub(*trn); } } // namespace Marklin