From 7e27b311e33beda1746eb63e0945633f262427f6 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 21 Nov 2010 18:40:12 +0000 Subject: [PATCH] Add support for named zones --- source/designer/designer.cpp | 125 ++++++++++++++++----- source/designer/designer.h | 12 ++- source/designer/zonebar.cpp | 167 +++++++++++++++++++++++++++++ source/designer/zonebar.h | 36 +++++++ source/designer/zoneproperties.cpp | 72 +++++++++++++ source/designer/zoneproperties.h | 31 ++++++ source/libr2c2/layout.cpp | 72 +++++++++++-- source/libr2c2/layout.h | 19 +++- source/libr2c2/route.cpp | 6 ++ source/libr2c2/route.h | 2 + source/libr2c2/timetable.cpp | 94 +++++++++++----- source/libr2c2/timetable.h | 14 ++- source/libr2c2/train.cpp | 30 ++++++ source/libr2c2/train.h | 2 + source/libr2c2/zone.cpp | 134 +++++++++++++++++++++++ source/libr2c2/zone.h | 64 +++++++++++ 16 files changed, 808 insertions(+), 72 deletions(-) create mode 100644 source/designer/zonebar.cpp create mode 100644 source/designer/zonebar.h create mode 100644 source/designer/zoneproperties.cpp create mode 100644 source/designer/zoneproperties.h create mode 100644 source/libr2c2/zone.cpp create mode 100644 source/libr2c2/zone.h diff --git a/source/designer/designer.cpp b/source/designer/designer.cpp index 86fc349..5f125fd 100644 --- a/source/designer/designer.cpp +++ b/source/designer/designer.cpp @@ -28,6 +28,7 @@ Distributed under the GPL #include #include "libr2c2/route.h" #include "libr2c2/tracktype.h" +#include "libr2c2/zone.h" #include "3d/path.h" #include "designer.h" #include "input.h" @@ -39,6 +40,8 @@ Distributed under the GPL #include "svgexporter.h" #include "trackbar.h" #include "trackproperties.h" +#include "zonebar.h" +#include "zoneproperties.h" using namespace std; using namespace R2C2; @@ -52,6 +55,7 @@ Designer::Designer(int argc, char **argv): root(ui_res, window), base_object(0), cur_route(0), + cur_zone(0), mode(SELECT), manipulator(*this, root, selection), measure(*this), @@ -130,6 +134,7 @@ Designer::Designer(int argc, char **argv): toolbars.push_back(new Layoutbar(*this)); toolbars.push_back(new Trackbar(*this)); toolbars.push_back(new Routebar(*this)); + toolbars.push_back(new Zonebar(*this)); for(vector::iterator i=toolbars.begin(); i!=toolbars.end(); ++i) { root.add(**i); @@ -249,6 +254,22 @@ void Designer::flatten_tracks() manipulator.flatten(); } +void Designer::svg_export() +{ + InputDialog *input = new InputDialog(*this, "SVG export", FS::basepart(filename)+".svg"); + input->signal_accept.connect(sigc::mem_fun(this, &Designer::svg_export_accept)); +} + +void Designer::edit_route(Route *r) +{ + cur_route = r; + cur_zone = 0; + if(cur_route) + show_route(*cur_route); + else + clear_paths(); +} + void Designer::rename_route() { if(mode!=SELECT || !cur_route) @@ -258,33 +279,60 @@ void Designer::rename_route() input->signal_accept.connect(sigc::mem_fun(this, &Designer::route_name_accept)); } -void Designer::svg_export() +void Designer::add_selection_to_route() { - InputDialog *input = new InputDialog(*this, "SVG export", FS::basepart(filename)+".svg"); - input->signal_accept.connect(sigc::mem_fun(this, &Designer::svg_export_accept)); + if(!cur_route) + return; + + try + { + cur_route->add_tracks(selection.get_tracks()); + } + catch(const Exception &e) + { + lbl_status->set_text(e.what()); + } + + show_route(*cur_route); } -void Designer::edit_route(Route *r) +void Designer::edit_zone(Zone *z) { - cur_route = r; - show_route(r); + cur_zone = z; + cur_route = 0; + if(cur_zone) + show_zone(*cur_zone); + else + clear_paths(); } -void Designer::add_selection_to_route() +void Designer::zone_properties() { - if(!cur_route) + if(!cur_zone) + return; + + ZoneProperties *zone_prop = new ZoneProperties(*cur_zone); + root.add(*zone_prop); + const GLtk::Geometry &root_geom = root.get_geometry(); + const GLtk::Geometry &dlg_geom = zone_prop->get_geometry(); + zone_prop->set_position((root_geom.w-dlg_geom.w)/2, (root_geom.h-dlg_geom.h)/2); +} + +void Designer::add_selection_to_zone() +{ + if(!cur_zone) return; try { - cur_route->add_tracks(selection.get_tracks()); + cur_zone->add_tracks(selection.get_tracks()); } catch(const Exception &e) { lbl_status->set_text(e.what()); } - show_route(cur_route); + show_zone(*cur_zone); } Point Designer::map_pointer_to_ground(int x, int y) @@ -395,7 +443,12 @@ void Designer::key_press(unsigned key, unsigned mod, wchar_t) else if(key==Msp::Input::KEY_E) manipulator.even_slope(); else if(key==Msp::Input::KEY_A) - add_selection_to_route(); + { + if(cur_route) + add_selection_to_route(); + else if(cur_zone) + add_selection_to_zone(); + } else if(key==Msp::Input::KEY_C) connect_tracks(); else if(key==Msp::Input::KEY_V) @@ -649,28 +702,44 @@ string Designer::tooltip(int x, int y) return string(); } -void Designer::show_route(const Route *route) +void Designer::clear_paths() { const set <racks = layout->get_tracks(); for(set::iterator i=ltracks.begin(); i!=ltracks.end(); ++i) { Track3D &t3d = layout_3d->get_track(**i); - if(route && route->has_track(**i)) - { - t3d.get_path().set_color(GL::Color(0.5, 0.8, 1.0)); - if((*i)->get_type().is_turnout()) - { - unsigned tid = (*i)->get_turnout_id(); - int path = (tid ? route->get_turnout(tid) : -1); - if(path>=0) - t3d.get_path().set_path(path); - else - t3d.get_path().set_mask((*i)->get_type().get_paths()); - } - else - t3d.get_path().set_path(0); - } + t3d.get_path().set_mask(0); + } +} + +void Designer::show_route(const Route &route) +{ + clear_paths(); + + const set &rtracks = route.get_tracks(); + for(set::iterator i=rtracks.begin(); i!=rtracks.end(); ++i) + { + Track3D &t3d = layout_3d->get_track(**i); + t3d.get_path().set_color(GL::Color(0.5, 0.8, 1.0)); + int path = -1; + if(unsigned tid = (*i)->get_turnout_id()) + path = route.get_turnout(tid); + if(path>=0) + t3d.get_path().set_path(path); else - t3d.get_path().set_mask(0); + t3d.get_path().set_mask((*i)->get_type().get_paths()); + } +} + +void Designer::show_zone(const Zone &zone) +{ + clear_paths(); + + const Zone::TrackSet &ztracks = zone.get_tracks(); + for(Zone::TrackSet::const_iterator i=ztracks.begin(); i!=ztracks.end(); ++i) + { + Track3D &t3d = layout_3d->get_track(**i); + t3d.get_path().set_color(GL::Color(0.8, 1.0, 0.5)); + t3d.get_path().set_mask((*i)->get_type().get_paths()); } } diff --git a/source/designer/designer.h b/source/designer/designer.h index 2a53745..52904e4 100644 --- a/source/designer/designer.h +++ b/source/designer/designer.h @@ -61,6 +61,7 @@ private: R2C2::Overlay3D *overlay; Msp::GL::Object *base_object; R2C2::Route *cur_route; + R2C2::Zone *cur_zone; std::list new_tracks; Msp::GL::Pipeline *pipeline; Msp::GL::Camera camera; @@ -99,13 +100,18 @@ public: void extend_track(); void connect_tracks(); void flatten_tracks(); - void rename_route(); void svg_export(); void edit_route(R2C2::Route *); + void rename_route(); R2C2::Route *get_current_route() const { return cur_route; } void add_selection_to_route(); + void edit_zone(R2C2::Zone *); + void zone_properties(); + R2C2::Zone *get_current_zone() const { return cur_zone; } + void add_selection_to_zone(); + R2C2::Point map_pointer_to_ground(int, int); private: void tick(); @@ -128,7 +134,9 @@ private: void route_name_accept(const std::string &); void svg_export_accept(const std::string &); std::string tooltip(int, int); - void show_route(const R2C2::Route *); + void clear_paths(); + void show_route(const R2C2::Route &); + void show_zone(const R2C2::Zone &); }; #endif diff --git a/source/designer/zonebar.cpp b/source/designer/zonebar.cpp new file mode 100644 index 0000000..d4e0492 --- /dev/null +++ b/source/designer/zonebar.cpp @@ -0,0 +1,167 @@ +/* $Id$ + +This file is part of R²C² +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#include +#include "libr2c2/zone.h" +#include "designer.h" +#include "zonebar.h" + +using namespace std; +using namespace Msp; +using namespace R2C2; + +Zonebar::Zonebar(Designer &d): + Toolbar("Zone", 420), + designer(d) +{ + pnl_content->add(*(drp_groups = new GLtk::Dropdown)); + drp_groups->set_geometry(GLtk::Geometry(0, 10, 195, 20)); + drp_groups->set_tooltip("Select zone group to edit"); + drp_groups->signal_item_selected.connect(sigc::mem_fun(this, &Zonebar::group_selected)); + + pnl_content->add(*(drp_numbers = new GLtk::Dropdown)); + drp_numbers->set_geometry(GLtk::Geometry(200, 10, 100, 20)); + drp_groups->set_tooltip("Select zone to edit"); + drp_numbers->signal_item_selected.connect(sigc::mem_fun(this, &Zonebar::number_selected)); + + GLtk::Button *btn; + + pnl_content->add(*(btn = new GLtk::Button("Del"))); + btn->set_geometry(GLtk::Geometry(300, 10, 40, 24)); + btn->set_style("red"); + btn->set_tooltip("Delete selected zone"); + btn->signal_clicked.connect(sigc::mem_fun(this, &Zonebar::delete_zone_clicked)); + + pnl_content->add(*(btn = new GLtk::Button("Prop"))); + btn->set_geometry(GLtk::Geometry(340, 10, 40, 24)); + btn->set_tooltip("Change properties of the selected zone"); + btn->signal_clicked.connect(sigc::mem_fun(&designer, &Designer::zone_properties)); + + pnl_content->add(*(btn = new GLtk::Button("AddT"))); + btn->set_geometry(GLtk::Geometry(380, 10, 40, 24)); + btn->set_tooltip("Add selected tracks to zone"); + btn->signal_clicked.connect(sigc::mem_fun(&designer, &Designer::add_selection_to_zone)); + + Layout &layout = designer.get_layout(); + layout.signal_zone_added.connect(sigc::mem_fun(this, &Zonebar::zone_added)); + layout.signal_zone_removed.connect(sigc::hide(sigc::mem_fun(this, &Zonebar::update_groups))); + + const Layout::ZoneSet &zones = layout.get_zones(); + for(Layout::ZoneSet::const_iterator i=zones.begin(); i!=zones.end(); ++i) + (*i)->signal_name_changed.connect(sigc::mem_fun(this, &Zonebar::zone_renamed)); + + update_groups(); +} + +void Zonebar::zone_added(Zone &zone) +{ + zone.signal_name_changed.connect(sigc::mem_fun(this, &Zonebar::zone_renamed)); + update_groups(); +} + +void Zonebar::zone_renamed(const string &, const string &, unsigned) +{ + update_groups(); +} + +void Zonebar::group_selected(unsigned index, const string &group) +{ + if(index==drp_groups->get_n_items()-1) + { + Layout &layout = designer.get_layout(); + Zone *zone = new Zone(layout); + zone->set_name("New zone", "track", 1); + designer.edit_zone(zone); + update_groups(); + } + else + { + Zone *cur = designer.get_current_zone(); + if(cur && group==cur->get_group()) + return; + + designer.edit_zone(0); + update_numbers(group); + } +} + +void Zonebar::number_selected(unsigned index, const string &) +{ + if(drp_groups->get_selected_index()<0) + return; + + string group = drp_groups->get_selected(); + Layout::ZoneArray zones = designer.get_layout().get_zones(group); + if(index==drp_numbers->get_n_items()-1) + { + Zone *cur = designer.get_current_zone(); + string qualifier = (cur ? cur->get_qualifier() : "track"); + + Layout &layout = designer.get_layout(); + Zone *zone = new Zone(layout); + zone->set_name(group, qualifier, zones.size()+1); + designer.edit_zone(zone); + if(cur) + update_numbers(group); + else + update_groups(); + } + else if(index groups; + for(Layout::ZoneSet::const_iterator i=zones.begin(); i!=zones.end(); ++i) + groups.insert((*i)->get_group()); + + Zone *cur = designer.get_current_zone(); + + drp_groups->clear(); + int selected = -1; + unsigned n = 0; + for(set::iterator i=groups.begin(); i!=groups.end(); ++i, ++n) + { + drp_groups->append(*i); + if(cur && *i==cur->get_group()) + selected = n; + } + drp_groups->append("(new)"); + drp_groups->set_selected_index(selected); + + if(cur) + update_numbers(cur->get_group()); + else + drp_numbers->clear(); +} + +void Zonebar::update_numbers(const string &group) +{ + Layout::ZoneArray zones = designer.get_layout().get_zones(group); + Zone *cur = designer.get_current_zone(); + + drp_numbers->clear(); + int selected = -1; + unsigned n = 0; + for(Layout::ZoneArray::iterator i=zones.begin(); i!=zones.end(); ++i, ++n) + { + drp_numbers->append(format("%s %d", (*i)->get_qualifier(), (*i)->get_number())); + if(cur && *i==cur) + selected = n; + } + drp_numbers->append("(new)"); + drp_numbers->set_selected_index(selected); +} diff --git a/source/designer/zonebar.h b/source/designer/zonebar.h new file mode 100644 index 0000000..ded63ad --- /dev/null +++ b/source/designer/zonebar.h @@ -0,0 +1,36 @@ +/* $Id$ + +This file is part of R²C² +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#ifndef ZONEBAR_H_ +#define ZONEBAR_H_ + +#include +#include "toolbar.h" + +class Designer; + +class Zonebar: public Toolbar +{ +private: + Designer &designer; + Msp::GLtk::Dropdown *drp_groups; + Msp::GLtk::Dropdown *drp_numbers; + +public: + Zonebar(Designer &); + +private: + void zone_added(R2C2::Zone &); + void zone_renamed(const std::string &, const std::string &, unsigned); + void group_selected(unsigned, const std::string &); + void number_selected(unsigned, const std::string &); + void delete_zone_clicked(); + void update_groups(); + void update_numbers(const std::string &); +}; + +#endif diff --git a/source/designer/zoneproperties.cpp b/source/designer/zoneproperties.cpp new file mode 100644 index 0000000..8ddc338 --- /dev/null +++ b/source/designer/zoneproperties.cpp @@ -0,0 +1,72 @@ +/* $Id$ + +This file is part of R²C² +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#include +#include +#include "zoneproperties.h" + +using namespace std; +using namespace Msp; +using namespace R2C2; + +ZoneProperties::ZoneProperties(Zone &z): + zone(z) +{ + set_size(300, 140); + + GLtk::Label *lbl; + + add(*(lbl = new GLtk::Label("Zone properties"))); + lbl->set_geometry(GLtk::Geometry(10, geom.h-30, geom.w-20, 20)); + + add(*(lbl = new GLtk::Label("Group"))); + lbl->set_geometry(GLtk::Geometry(10, geom.h-65, 70, 20)); + + add(*(ent_group = new GLtk::Entry(zone.get_group()))); + ent_group->set_geometry(GLtk::Geometry(80, geom.h-65, geom.w-90, 20)); + + add(*(lbl = new GLtk::Label("Qualifier"))); + lbl->set_geometry(GLtk::Geometry(10, geom.h-95, 70, 20)); + + add(*(drp_qualifier = new GLtk::Dropdown)); + drp_qualifier->set_geometry(GLtk::Geometry(80, geom.h-95, 80, 20)); + const char *qualifiers[] = { "track", "platform", "siding", 0 }; + for(unsigned i=0; qualifiers[i]; ++i) + { + drp_qualifier->append(qualifiers[i]); + if(zone.get_qualifier()==qualifiers[i]) + drp_qualifier->set_selected_index(i); + } + + add(*(lbl = new GLtk::Label("Number"))); + lbl->set_geometry(GLtk::Geometry(170, geom.h-95, 70, 20)); + + add(*(ent_number = new GLtk::Entry(lexical_cast(zone.get_number())))); + ent_number->set_geometry(GLtk::Geometry(240, geom.h-95, 50, 20)); + + GLtk::Button *btn; + + add_button(*(btn = new GLtk::Button("Cncl")), 0); + btn->set_geometry(GLtk::Geometry(geom.w-90, 10, 40, 24)); + btn->set_style("red"); + + add_button(*(btn = new GLtk::Button("OK")), 1); + btn->set_geometry(GLtk::Geometry(geom.w-50, 10, 40, 24)); + btn->set_style("green"); +} + +void ZoneProperties::on_response(int code) +{ + if(code==1) + { + string qualifier; + if(drp_qualifier->get_selected_index()>=0) + qualifier = drp_qualifier->get_selected(); + unsigned number = lexical_cast(ent_number->get_text()); + zone.set_name(ent_group->get_text(), qualifier, number); + } +} diff --git a/source/designer/zoneproperties.h b/source/designer/zoneproperties.h new file mode 100644 index 0000000..fe58c9f --- /dev/null +++ b/source/designer/zoneproperties.h @@ -0,0 +1,31 @@ +/* $Id$ + +This file is part of R²C² +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#ifndef ZONEPROPERTIES_H_ +#define ZONEPROPERTIES_H_ + +#include +#include +#include +#include "libr2c2/zone.h" + +class ZoneProperties: public Msp::GLtk::Dialog +{ +private: + R2C2::Zone &zone; + Msp::GLtk::Entry *ent_group; + Msp::GLtk::Dropdown *drp_qualifier; + Msp::GLtk::Entry *ent_number; + +public: + ZoneProperties(R2C2::Zone &); + +private: + virtual void on_response(int); +}; + +#endif diff --git a/source/libr2c2/layout.cpp b/source/libr2c2/layout.cpp index 1c0d4cf..3e49f19 100644 --- a/source/libr2c2/layout.cpp +++ b/source/libr2c2/layout.cpp @@ -20,10 +20,21 @@ Distributed under the GPL #include "tracktype.h" #include "train.h" #include "vehicletype.h" +#include "zone.h" using namespace std; using namespace Msp; +namespace { + +bool zone_order(const R2C2::Zone *z1, const R2C2::Zone *z2) +{ + return z1->get_number()get_number(); +} + +} + + namespace R2C2 { Layout::Layout(Catalogue &c, Driver *d): @@ -174,6 +185,39 @@ void Layout::remove_route(Route &r) signal_route_removed.emit(r); } +void Layout::add_zone(Zone &z) +{ + if(zones.insert(&z).second) + signal_zone_added.emit(z); +} + +Layout::ZoneArray Layout::get_zones(const string &group) const +{ + ZoneArray result; + for(ZoneSet::const_iterator i=zones.begin(); i!=zones.end(); ++i) + if((*i)->get_group()==group) + result.push_back(*i); + + sort(result.begin(), result.end(), zone_order); + + return result; +} + +Zone &Layout::get_zone(const string &group, unsigned num) const +{ + for(ZoneSet::const_iterator i=zones.begin(); i!=zones.end(); ++i) + if((*i)->get_group()==group && (*i)->get_number()==num) + return **i; + + throw KeyError("Unknown zone", format("%s %d", group, num)); +} + +void Layout::remove_zone(Zone &z) +{ + if(zones.erase(&z)) + signal_zone_removed.emit(z); +} + void Layout::add_train(Train &t) { if(trains.count(t.get_address())) @@ -232,7 +276,7 @@ void Layout::emergency(const string &msg) signal_emergency.emit(msg); } -void Layout::save(const string &fn) +void Layout::save(const string &fn) const { IO::BufferedFile out(fn, IO::M_WRITE); DataFile::Writer writer(out); @@ -240,7 +284,7 @@ void Layout::save(const string &fn) if(!base.empty()) writer.write((DataFile::Statement("base"), base)); - for(set::iterator i=tracks.begin(); i!=tracks.end(); ++i) + for(set::const_iterator i=tracks.begin(); i!=tracks.end(); ++i) { DataFile::Statement st("track"); st.append((*i)->get_type().get_article_number()); @@ -248,7 +292,7 @@ void Layout::save(const string &fn) writer.write(st); } - for(set::iterator i=routes.begin(); i!=routes.end(); ++i) + for(set::const_iterator i=routes.begin(); i!=routes.end(); ++i) { if((*i)->is_temporary()) continue; @@ -257,9 +301,16 @@ void Layout::save(const string &fn) (*i)->save(st.sub); writer.write(st); } + + for(ZoneSet::const_iterator i=zones.begin(); i!=zones.end(); ++i) + { + DataFile::Statement st("zone"); + (*i)->save(st.sub); + writer.write(st); + } } -void Layout::save_trains(const string &fn) +void Layout::save_trains(const string &fn) const { IO::BufferedFile out(fn, IO::M_WRITE); DataFile::Writer writer(out); @@ -296,11 +347,14 @@ Layout::Loader::Loader(Layout &l): { add("base", &Layout::base); add("route", static_cast(&Loader::route)); + add("track", static_cast(&Loader::track)); + add("train", static_cast(&Loader::train)); + add("zone", &Loader::zone); + + // Deprecated aliases 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() @@ -348,4 +402,10 @@ void Layout::Loader::train(ArticleNumber art_nr, unsigned addr, const std::strin load_sub(*trn); } +void Layout::Loader::zone() +{ + Zone *zne = new Zone(obj); + load_sub(*zne); +} + } // namespace R2C2 diff --git a/source/libr2c2/layout.h b/source/libr2c2/layout.h index 86db658..ecb801f 100644 --- a/source/libr2c2/layout.h +++ b/source/libr2c2/layout.h @@ -23,6 +23,7 @@ class Route; class Track; class Train; class Vehicle; +class Zone; class Layout { @@ -42,13 +43,18 @@ public: void track(ArticleNumber); void train(unsigned, unsigned, const std::string &); void train(ArticleNumber, unsigned, const std::string &); + void zone(); }; -public: + typedef std::set ZoneSet; + typedef std::vector ZoneArray; + sigc::signal signal_track_added; sigc::signal signal_track_removed; sigc::signal signal_route_added; sigc::signal signal_route_removed; + sigc::signal signal_zone_added; + sigc::signal signal_zone_removed; sigc::signal signal_train_added; sigc::signal signal_train_removed; sigc::signal signal_vehicle_added; @@ -62,6 +68,7 @@ private: std::string base; std::set tracks; std::set routes; + ZoneSet zones; std::set blocks; std::map trains; std::set vehicles; @@ -95,6 +102,12 @@ public: void update_routes(); void remove_route(Route &); + void add_zone(Zone &); + const ZoneSet &get_zones() const { return zones; } + ZoneArray get_zones(const std::string &) const; + Zone &get_zone(const std::string &, unsigned) const; + void remove_zone(Zone &); + void add_train(Train &); Train &get_train(unsigned) const; const std::map &get_trains() const { return trains; } @@ -106,8 +119,8 @@ public: void tick(); void emergency(const std::string &); - void save(const std::string &); - void save_trains(const std::string &); + void save(const std::string &) const; + void save_trains(const std::string &) const; private: void sensor_event(unsigned, bool); }; diff --git a/source/libr2c2/route.cpp b/source/libr2c2/route.cpp index 4f7e778..6809c0a 100644 --- a/source/libr2c2/route.cpp +++ b/source/libr2c2/route.cpp @@ -12,6 +12,7 @@ Distributed under the GPL #include "track.h" #include "trackiter.h" #include "tracktype.h" +#include "zone.h" using namespace std; using namespace Msp; @@ -404,6 +405,11 @@ Route *Route::find(const TrackIter &from, const Route &to) return create_route(from, TrackInSet(to.get_tracks())); } +Route *Route::find(const TrackIter &from, const Zone &to) +{ + return create_route(from, TrackInSet(to.get_tracks())); +} + Route *Route::find(const TrackIter &from, const set &to) { return create_route(from, TrackInSet(to)); diff --git a/source/libr2c2/route.h b/source/libr2c2/route.h index 86a0f61..5c87971 100644 --- a/source/libr2c2/route.h +++ b/source/libr2c2/route.h @@ -19,6 +19,7 @@ namespace R2C2 { class Layout; class Track; class TrackIter; +class Zone; class Route: public sigc::trackable { @@ -72,6 +73,7 @@ private: public: static Route *find(const TrackIter &, Track &); static Route *find(const TrackIter &, const Route &); + static Route *find(const TrackIter &, const Zone &); static Route *find(const TrackIter &, const std::set &); }; diff --git a/source/libr2c2/timetable.cpp b/source/libr2c2/timetable.cpp index bc8ebe6..dd91d21 100644 --- a/source/libr2c2/timetable.cpp +++ b/source/libr2c2/timetable.cpp @@ -89,12 +89,16 @@ void Timetable::tick(const Time::TimeStamp &t) Row &row = rows[current_row]; switch(row.type) { - case GOTO: - if(!train.go_to(**parse_location(row.get_param(0)).get_tracks().begin())) + case GOTO_SENSOR: + if(!train.go_to(get_sensor(row.get_param(0)))) + set_enabled(false); + break; + case GOTO_ZONE: + if(!train.go_to(get_zone(row.get_param(0)))) set_enabled(false); break; case TRAVEL: - pending_block = &parse_location(row.get_param(0)); + pending_block = &get_sensor(row.get_param(0)).get_block(); pending_train = &train; executing = false; break; @@ -104,7 +108,7 @@ void Timetable::tick(const Time::TimeStamp &t) break; case WAIT_TRAIN: pending_train = &train.get_layout().get_train(row.get_param(0)); - pending_block = &parse_location(row.get_param(1)); + pending_block = &get_sensor(row.get_param(1)).get_block(); executing = false; break; case ARRIVE: @@ -133,11 +137,19 @@ void Timetable::save(list &st) const st.push_back(i->save()); } -Block &Timetable::parse_location(const string &loc) +Track &Timetable::get_sensor(unsigned id) { - if(!loc.compare(0, 7, "sensor ")) - return train.get_layout().get_block(lexical_cast(loc.substr(7))|0x1000); - throw Exception("Named blocks are not supported yet"); + Block &block = train.get_layout().get_block(id|0x1000); + return **block.get_tracks().begin(); +} + +Zone &Timetable::get_zone(const string &name) +{ + string::size_type space = name.rfind(' '); + if(space==string::npos || space==0) + throw InvalidParameterValue("Invalid zone name"); + unsigned number = lexical_cast(name.substr(space+1)); + return train.get_layout().get_zone(name.substr(0, space), number); } void Timetable::sensor_event(unsigned addr, bool state) @@ -184,7 +196,9 @@ string Timetable::Row::str() const { switch(type) { - case GOTO: + case GOTO_SENSOR: + return "set route to sensor "+get_param(0); + case GOTO_ZONE: return "set route to "+get_param(0); case TRAVEL: return "travel to "+get_param(0); @@ -209,14 +223,16 @@ DataFile::Statement Timetable::Row::save() const { switch(type) { - case GOTO: - return DataFile::Statement("goto"), get_param(0); + case GOTO_SENSOR: + return DataFile::Statement("goto_sensor"), get_param(0); + case GOTO_ZONE: + return DataFile::Statement("goto_zone"), get_param(0); case TRAVEL: return DataFile::Statement("travel"), get_param(0); case WAIT_TIME: return DataFile::Statement("wait"), get_param(0); case WAIT_TRAIN: - return DataFile::Statement("wait_train"), get_param(0), get_param(1); + return DataFile::Statement("wait_train"), get_param(0), get_param(1); case ARRIVE: return DataFile::Statement("arrive"); case SPEED: @@ -234,8 +250,8 @@ Timetable::Row Timetable::Row::parse(const string &s) { if(!s.compare(0, 7, "travel ")) { - if(!s.compare(7, 3, "to ")) - return Row(TRAVEL, s.substr(10)); + if(!s.compare(7, 10, "to sensor ")) + return Row(TRAVEL, lexical_cast(s.substr(17))); else if(!s.compare(7, string::npos, "until arrival")) return Row(ARRIVE); } @@ -250,11 +266,11 @@ Timetable::Row Timetable::Row::parse(const string &s) } else if(!s.compare(9, 6, "train ")) { - string::size_type at = s.find(" at ", 15); + string::size_type at = s.find(" at sensor ", 15); if(at!=string::npos) { Row row(WAIT_TRAIN, lexical_cast(s.substr(15, at-15))); - row.params.push_back(s.substr(at+4)); + row.params.push_back(lexical_cast(s.substr(at+11))); return row; } } @@ -271,7 +287,12 @@ Timetable::Row Timetable::Row::parse(const string &s) else if(!s.compare(0, 10, "set route ")) { if(!s.compare(10, 3, "to ")) - return Row(GOTO, s.substr(13)); + { + if(!s.compare(13, 7, "sensor ")) + return Row(GOTO_SENSOR, lexical_cast(s.substr(20))); + else + return Row(GOTO_ZONE, s.substr(13)); + } return Row(ROUTE, s.substr(10)); } @@ -282,14 +303,18 @@ Timetable::Row Timetable::Row::parse(const string &s) Timetable::Loader::Loader(Timetable &tt): DataFile::ObjectLoader(tt) { - add("arrive", &Loader::arrive); - add("goto", &Loader::go_to); - add("route", &Loader::route); - add("speed", &Loader::speed); - add("reverse", &Loader::reverse); - add("travel", &Loader::travel); - add("wait", &Loader::wait); - add("wait_train", &Loader::wait_train); + add("arrive", &Loader::arrive); + add("goto_sensor", static_cast(&Loader::goto_sensor)); + add("goto_zone", &Loader::goto_zone); + add("route", &Loader::route); + add("speed", &Loader::speed); + add("reverse", &Loader::reverse); + add("travel", &Loader::travel); + add("wait", &Loader::wait); + add("wait_train", &Loader::wait_train); + + // Deprecated alias + add("goto", static_cast(&Loader::goto_sensor)); } void Timetable::Loader::arrive() @@ -297,9 +322,20 @@ void Timetable::Loader::arrive() obj.rows.push_back(Row(ARRIVE)); } -void Timetable::Loader::go_to(const string &t) +void Timetable::Loader::goto_sensor(unsigned s) +{ + obj.rows.push_back(Row(GOTO_SENSOR, s)); +} + +void Timetable::Loader::goto_sensor(const string &s) +{ + if(!s.compare(0, 7, "sensor ")) + obj.rows.push_back(Row(GOTO_SENSOR, lexical_cast(s.substr(7)))); +} + +void Timetable::Loader::goto_zone(const string &z) { - obj.rows.push_back(Row(GOTO, t)); + obj.rows.push_back(Row(GOTO_ZONE, z)); } void Timetable::Loader::route(const string &r) @@ -327,10 +363,10 @@ void Timetable::Loader::wait(unsigned t) obj.rows.push_back(Row(WAIT_TIME, t)); } -void Timetable::Loader::wait_train(unsigned t, const string &b) +void Timetable::Loader::wait_train(unsigned t, unsigned s) { Row row(WAIT_TRAIN, t); - row.params.push_back(b); + row.params.push_back(s); obj.rows.push_back(row); } diff --git a/source/libr2c2/timetable.h b/source/libr2c2/timetable.h index 9cfda68..86b4ae2 100644 --- a/source/libr2c2/timetable.h +++ b/source/libr2c2/timetable.h @@ -17,7 +17,9 @@ Distributed under the GPL namespace R2C2 { class Block; +class Track; class Train; +class Zone; class Timetable: public sigc::trackable { @@ -28,18 +30,21 @@ public: Loader(Timetable &); private: void arrive(); - void go_to(const std::string &); + void goto_sensor(unsigned); + void goto_sensor(const std::string &); + void goto_zone(const std::string &); void route(const std::string &); void reverse(); void speed(unsigned); void travel(const std::string &); void wait(unsigned); - void wait_train(unsigned, const std::string &); + void wait_train(unsigned, unsigned); }; enum RowType { - GOTO, + GOTO_SENSOR, + GOTO_ZONE, TRAVEL, WAIT_TIME, WAIT_TRAIN, @@ -95,7 +100,8 @@ public: void tick(const Msp::Time::TimeStamp &); void save(std::list &) const; private: - Block &parse_location(const std::string &); + Track &get_sensor(unsigned); + Zone &get_zone(const std::string &); void sensor_event(unsigned, bool); void train_arrived(); }; diff --git a/source/libr2c2/train.cpp b/source/libr2c2/train.cpp index dd7139f..ffa436b 100644 --- a/source/libr2c2/train.cpp +++ b/source/libr2c2/train.cpp @@ -21,6 +21,7 @@ Distributed under the GPL #include "train.h" #include "vehicle.h" #include "vehicletype.h" +#include "zone.h" using namespace std; using namespace Msp; @@ -266,6 +267,35 @@ bool Train::go_to(Track &to) return set_route(route); } +bool Train::go_to(const Zone &to) +{ + set tracks; + for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i) + tracks.insert((*i)->get_tracks().begin(), (*i)->get_tracks().end()); + + const Zone::TrackSet &ztracks = to.get_tracks(); + unsigned union_size = 0; + for(Zone::TrackSet::const_iterator i=ztracks.begin(); i!=ztracks.end(); ++i) + union_size += tracks.count(*i); + + if(union_size==tracks.size() || union_size==ztracks.size()) + { + signal_arrived.emit(); + return set_route(0); + } + + free_noncritical_blocks(); + + TrackIter next = blocks.back().next().track_iter(); + + Route *route = Route::find(next, to); + if(!route) + return false; + create_lead_route(route, route); + route->add_tracks(ztracks); + return set_route(route); +} + bool Train::divert(Track &from) { if(!from.get_turnout_id()) diff --git a/source/libr2c2/train.h b/source/libr2c2/train.h index 99575bf..330eadd 100644 --- a/source/libr2c2/train.h +++ b/source/libr2c2/train.h @@ -22,6 +22,7 @@ class Route; class Timetable; class Vehicle; class VehicleType; +class Zone; class Train: public sigc::trackable { @@ -140,6 +141,7 @@ public: bool set_route(const Route *); bool go_to(Track &); + bool go_to(const Zone &); bool divert(Track &); const Route *get_route() const; void place(Block &, unsigned); diff --git a/source/libr2c2/zone.cpp b/source/libr2c2/zone.cpp new file mode 100644 index 0000000..4b95da5 --- /dev/null +++ b/source/libr2c2/zone.cpp @@ -0,0 +1,134 @@ +/* $Id$ + +This file is part of R²C² +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#include +#include "block.h" +#include "layout.h" +#include "track.h" +#include "zone.h" + +using namespace std; +using namespace Msp; + +namespace R2C2 { + +Zone::Zone(Layout &l): + layout(l), + number(0) +{ + layout.add_zone(*this); +} + +Zone::~Zone() +{ + layout.remove_zone(*this); +} + +void Zone::set_name(const string &g, const string &q, unsigned n) +{ + group = g; + qualifier = q; + number = n; + + signal_name_changed.emit(group, qualifier, number); +} + +string Zone::get_name() const +{ + string result = group; + if(!qualifier.empty()) + { + result += ' '; + result += qualifier; + } + if(number) + result += format(" %d", number); + return result; +} + +void Zone::add_track(Track &track) +{ + if(!is_valid(track)) + throw InvalidParameterValue("Can't add track to zone"); + + 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 InvalidParameterValue("Cound not add any tracks to zone"); + return pending.empty(); + } + + first = false; + } +} + +void Zone::save(list &st) const +{ + st.push_back((DataFile::Statement("group"), group)); + if(!qualifier.empty()) + st.push_back((DataFile::Statement("qualifier"), qualifier)); + if(number) + st.push_back((DataFile::Statement("number"), number)); + + set block_ids; + for(TrackSet::const_iterator i=tracks.begin(); i!=tracks.end(); ++i) + block_ids.insert((*i)->get_block().get_id()); + + for(set::const_iterator i=block_ids.begin(); i!=block_ids.end(); ++i) + 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) +{ + add("block", &Loader::block); + add("group", &Zone::group); + add("number", &Zone::number); + add("qualifier", &Zone::qualifier); +} + +void Zone::Loader::block(unsigned b) +{ + Block &blk = obj.layout.get_block(b); + const set &btracks = blk.get_tracks(); + obj.tracks.insert(btracks.begin(), btracks.end()); +} + +} // namespace R2C2 diff --git a/source/libr2c2/zone.h b/source/libr2c2/zone.h new file mode 100644 index 0000000..98feb67 --- /dev/null +++ b/source/libr2c2/zone.h @@ -0,0 +1,64 @@ +/* $Id$ + +This file is part of R²C² +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#ifndef LIBR2C2_ZONE_H_ +#define LIBR2C2_ZONE_H_ + +#include +#include +#include + +namespace R2C2 { + +class Layout; +class Track; + +class Zone +{ +public: + class Loader: public Msp::DataFile::ObjectLoader + { + public: + Loader(Zone &); + private: + 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 &); + ~Zone(); + + void set_name(const std::string &, const std::string &, unsigned); + const std::string &get_group() const { return group; } + const std::string &get_qualifier() const { return qualifier; } + 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 + +#endif -- 2.43.0