X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Flibmarklin%2Flayout.cpp;h=c0930352fb7d2b171f7279a55c0dd1fddffc454b;hb=651698847d5293cfb15b6fb23a394701388c0151;hp=1fe06c7e4f5aed39948ae18e6ca9a46482545474;hpb=6c61179fe09af2f5366d50f10aadbf5f83438087;p=r2c2.git diff --git a/source/libmarklin/layout.cpp b/source/libmarklin/layout.cpp index 1fe06c7..c093035 100644 --- a/source/libmarklin/layout.cpp +++ b/source/libmarklin/layout.cpp @@ -1,134 +1,375 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + #include -#include -#include +#include +#include +#include +#include +#include "block.h" #include "catalogue.h" +#include "driver.h" #include "layout.h" +#include "locotype.h" +#include "route.h" +#include "track.h" +#include "tracktype.h" +#include "train.h" using namespace std; using namespace Msp; namespace Marklin { -Layout::Layout(Catalogue &c): - catalogue(c) +Layout::Layout(Catalogue &c, Driver *d): + catalogue(c), + driver(d) { } -void Layout::add_track(Track *t) +Layout::~Layout() +{ + delete driver; + while(!trains.empty()) + delete trains.begin()->second; + while(!routes.empty()) + delete routes.begin()->second; + while(!tracks.empty()) + delete *tracks.begin(); + while(!blocks.empty()) + delete *blocks.begin(); +} + +Driver &Layout::get_driver() const { - if(find(tracks.begin(), tracks.end(), t)==tracks.end()) + if(!driver) + throw InvalidState("No driver"); + return *driver; +} + +void Layout::add_track(Track &t) +{ + if(tracks.insert(&t).second) { - tracks.push_back(t); + create_blocks(); signal_track_added.emit(t); } } -void Layout::remove_track(Track *t) +void Layout::remove_track(Track &t) { - TrackSeq::iterator i=remove_if(tracks.begin(), tracks.end(), bind1st(equal_to(), t)); - if(i!=tracks.end()) + if(tracks.erase(&t)) { - tracks.erase(i, tracks.end()); + create_blocks(t); signal_track_removed.emit(t); } } -void Layout::check_links() +void Layout::add_block(Block &b) { - for(TrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) - (*i)->break_links(); + 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)); +} + +Block &Layout::get_block_by_track(const Track &t) const +{ + for(set::const_iterator i=blocks.begin(); i!=blocks.end(); ++i) + if((*i)->get_tracks().count(const_cast(&t))) + return **i; + + throw InvalidParameterValue("No block found for track"); +} + +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(TrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) - for(TrackSeq::iterator j=i; j!=tracks.end(); ++j) + 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)->snap_to(**j, true); + (*i)->check_link(**j); } -void Layout::load(const string &fn) +void Layout::create_blocks(const Track &track) { - ifstream in(fn.c_str()); - if(!in) - throw Exception("Couldn't open file"); + const vector &links = track.get_links(); + for(set::iterator i=blocks.begin(); i!=blocks.end();) + { + bool del = (*i)->get_tracks().count(const_cast(&track)); + for(vector::const_iterator j=links.begin(); (!del && j!=links.end()); ++j) + del = (*i)->get_tracks().count(*j); - filename=fn; - Parser::Parser parser(in, fn); - Loader loader(*this); - loader.load(parser); + if(del) + delete *i++; + else + ++i; + } - check_links(); + create_blocks(); +} - for(TrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) - (*i)->check_slope(); +void Layout::remove_block(Block &b) +{ + blocks.erase(&b); } -int Layout::save(const string &fn) +void Layout::add_route(Route &r) { - ofstream out(fn.c_str()); - if(!out) return -1; + if(routes.count(r.get_name())) + throw KeyError("Duplicate route name", r.get_name()); - filename=fn; + routes[r.get_name()] = &r; + 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; +} - if(base.size()) - out<<"base \""<::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::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::save(const string &fn) +{ + IO::BufferedFile out(fn, IO::M_WRITE); + DataFile::Writer writer(out); + + if(!base.empty()) + writer.write((DataFile::Statement("base"), base)); + + for(set::iterator i=tracks.begin(); i!=tracks.end(); ++i) { - out<<"track "<<(*i)->get_article_number()<<"\n{\n"; - const Point &p=(*i)->get_position(); - out<<"\tposition "<get_rotation()<<";\n"; - out<<"\tslope "<<(*i)->get_slope()<<";\n"; + DataFile::Statement st("track"); + st.append((*i)->get_type().get_article_number()); + (*i)->save(st.sub); + writer.write(st); + } - unsigned id=(*i)->get_turnout_id(); - if(id) - out<<"\tturnout_id "<::iterator i=routes.begin(); i!=routes.end(); ++i) + { + DataFile::Statement st("route"); + st.append(i->first); + i->second->save(st.sub); + writer.write(st); + } +} - id=(*i)->get_sensor_id(); - if(id) - out<<"\tsensor_id "<get_flex()) - out<<"\tflex true;\n"; + for(map::const_iterator i=trains.begin(); i!=trains.end(); ++i) + { + DataFile::Statement st("train"); + st.append(i->second->get_locomotive_type().get_article_number()); + st.append(i->second->get_address()); + i->second->save(st.sub); + writer.write(st); + } +} + +void Layout::check_links() +{ + for(set::iterator i=tracks.begin(); i!=tracks.end(); ++i) + (*i)->break_links(); - out<<"};\n"; + list flext; + for(set::iterator i=tracks.begin(); i!=tracks.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); + } } - return 0; + 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); } -Layout::~Layout() +void Layout::check_routes() { - for(TrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) - delete *i; + for(map::iterator i=routes.begin(); i!=routes.end(); ++i) + { + /* We must copy the turnout map, since adding tracks to the route will + (temporarily) mess it up */ + const map turnouts = i->second->get_turnouts(); + + // Find any turnout in the route + 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()) + { + track = *j; + trk_path = k->second; + break; + } + } + + if(!track) + continue; + + // Find an applicable endpoint + 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().is_turnout()) + { + // Select correct path across the turnout, or break if we hit an unknown turnout + map::const_iterator j = turnouts.find(next->get_turnout_id()); + if(j==turnouts.end()) + break; + trk_path = j->second; + } + else + { + trk_path = 0; + + /* Start adding tracks when we find the first non-turnout. This + prevents the occurrence of ambiguities while adding the tracks */ + if(!start) + start = next; + } + + if(start) + i->second->add_track(*next); + + track = next; + } + } } -/******************* -** Layout::Loader -*/ Layout::Loader::Loader(Layout &l): - layout(l) + DataFile::BasicLoader(l), + new_tracks(false) { add("base", &Layout::base); + add("route", &Loader::route); add("track", &Loader::track); + add("train", &Loader::train); +} + +void Layout::Loader::finish() +{ + if(new_tracks) + 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(const string &n) +{ + Route *rte = new Route(obj, n); + load_sub(*rte); } void Layout::Loader::track(unsigned art_nr) { - Track *tmpl=layout.catalogue.get_track(art_nr); - if(!tmpl) - throw Exception("Unknown track"); + Track *trk = new Track(obj, obj.catalogue.get_track(art_nr)); + load_sub(*trk); + new_tracks = true; +} - Track *trk=tmpl->copy(); - try - { - load_sub(*trk); - } - catch(Msp::Exception &e) - { - delete trk; - throw; - } - layout.tracks.push_back(trk); - layout.signal_track_added.emit(trk); +void Layout::Loader::train(unsigned art_nr, unsigned addr) +{ + Train *trn = new Train(obj, obj.catalogue.get_locomotive(art_nr), addr); + load_sub(*trn); } } // namespace Marklin