]> git.tdb.fi Git - r2c2.git/commitdiff
Add support for named zones
authorMikko Rasa <tdb@tdb.fi>
Sun, 21 Nov 2010 18:40:12 +0000 (18:40 +0000)
committerMikko Rasa <tdb@tdb.fi>
Sun, 21 Nov 2010 18:40:12 +0000 (18:40 +0000)
16 files changed:
source/designer/designer.cpp
source/designer/designer.h
source/designer/zonebar.cpp [new file with mode: 0644]
source/designer/zonebar.h [new file with mode: 0644]
source/designer/zoneproperties.cpp [new file with mode: 0644]
source/designer/zoneproperties.h [new file with mode: 0644]
source/libr2c2/layout.cpp
source/libr2c2/layout.h
source/libr2c2/route.cpp
source/libr2c2/route.h
source/libr2c2/timetable.cpp
source/libr2c2/timetable.h
source/libr2c2/train.cpp
source/libr2c2/train.h
source/libr2c2/zone.cpp [new file with mode: 0644]
source/libr2c2/zone.h [new file with mode: 0644]

index 86fc3494a424508b20ad93de6c685db00cc8ecbd..5f125fd11c64a0ed44e90321568364a948b30994 100644 (file)
@@ -28,6 +28,7 @@ Distributed under the GPL
 #include <msp/time/utils.h>
 #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<Toolbar *>::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<Track *> &ltracks = layout->get_tracks();
        for(set<Track *>::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<Track *> &rtracks = route.get_tracks();
+       for(set<Track *>::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());
        }
 }
index 2a53745db8634ee450fbe592c957d527c953967a..52904e44a13d01e458ca7755f76f8809af977410 100644 (file)
@@ -61,6 +61,7 @@ private:
        R2C2::Overlay3D *overlay;
        Msp::GL::Object *base_object;
        R2C2::Route *cur_route;
+       R2C2::Zone *cur_zone;
        std::list<R2C2::Track *> 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 (file)
index 0000000..d4e0492
--- /dev/null
@@ -0,0 +1,167 @@
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/strings/formatter.h>
+#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<zones.size())
+               designer.edit_zone(zones[index]);
+}
+
+void Zonebar::delete_zone_clicked()
+{
+       Zone *cur = designer.get_current_zone();
+       designer.edit_zone(0);
+       delete cur;
+}
+
+void Zonebar::update_groups()
+{
+       const Layout::ZoneSet &zones = designer.get_layout().get_zones();
+       set<string> 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<string>::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 (file)
index 0000000..ded63ad
--- /dev/null
@@ -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 <msp/gltk/dropdown.h>
+#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 (file)
index 0000000..8ddc338
--- /dev/null
@@ -0,0 +1,72 @@
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/gltk/button.h>
+#include <msp/gltk/label.h>
+#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<unsigned>(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 (file)
index 0000000..fe58c9f
--- /dev/null
@@ -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 <msp/gltk/dialog.h>
+#include <msp/gltk/dropdown.h>
+#include <msp/gltk/entry.h>
+#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
index 1c0d4cff76e125b2b8bb9525162e41f55f4d9c2b..3e49f19b836fa56351f4b42439e9be6b00e588be 100644 (file)
@@ -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()<z2->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<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
+       for(set<Track *>::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<Route *>::iterator i=routes.begin(); i!=routes.end(); ++i)
+       for(set<Route *>::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<void (Loader::*)()>(&Loader::route));
+       add("track", static_cast<void (Loader::*)(ArticleNumber)>(&Loader::track));
+       add("train", static_cast<void (Loader::*)(ArticleNumber, unsigned, const std::string &)>(&Loader::train));
+       add("zone",  &Loader::zone);
+
+       // Deprecated aliases
        add("route", static_cast<void (Loader::*)(const string &)>(&Loader::route));
        add("track", static_cast<void (Loader::*)(unsigned)>(&Loader::track));
-       add("track", static_cast<void (Loader::*)(ArticleNumber)>(&Loader::track));
        add("train", static_cast<void (Loader::*)(unsigned, unsigned, const std::string &)>(&Loader::train));
-       add("train", static_cast<void (Loader::*)(ArticleNumber, unsigned, const std::string &)>(&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
index 86db65895c3fead5aa8b83206a2d26e84043b3ee..ecb801f0e07c5bc8d4c4af4f9edb300967fba6db 100644 (file)
@@ -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<Zone *> ZoneSet;
+       typedef std::vector<Zone *> ZoneArray;
+
        sigc::signal<void, Track &> signal_track_added;
        sigc::signal<void, Track &> signal_track_removed;
        sigc::signal<void, Route &> signal_route_added;
        sigc::signal<void, Route &> signal_route_removed;
+       sigc::signal<void, Zone &> signal_zone_added;
+       sigc::signal<void, Zone &> signal_zone_removed;
        sigc::signal<void, Train &> signal_train_added;
        sigc::signal<void, Train &> signal_train_removed;
        sigc::signal<void, Vehicle &> signal_vehicle_added;
@@ -62,6 +68,7 @@ private:
        std::string base;
        std::set<Track *> tracks;
        std::set<Route *> routes;
+       ZoneSet zones;
        std::set<Block *> blocks;
        std::map<unsigned, Train *> trains;
        std::set<Vehicle *> 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<unsigned, Train *> &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);
 };
index 4f7e778441bdf2e03aa9b12cfeb9b96e677cdaf5..6809c0ad7fcd13379ca42a29070f3d0ee3160b3e 100644 (file)
@@ -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<Track *> &to)
 {
        return create_route(from, TrackInSet(to));
index 86a0f61780e1423ec68a1644f160f77bb9e55bb2..5c87971280300067ed055d5a6d15c3df31f0e576 100644 (file)
@@ -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<Track *> &);
 };
 
index bc8ebe6c43012d628b695ff1ba036f5022489c7f..dd91d219ab21f22be20b156348c2c17bb6643875 100644 (file)
@@ -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<string>(0)).get_tracks().begin()))
+               case GOTO_SENSOR:
+                       if(!train.go_to(get_sensor(row.get_param<unsigned>(0))))
+                               set_enabled(false);
+                       break;
+               case GOTO_ZONE:
+                       if(!train.go_to(get_zone(row.get_param<string>(0))))
                                set_enabled(false);
                        break;
                case TRAVEL:
-                       pending_block = &parse_location(row.get_param<string>(0));
+                       pending_block = &get_sensor(row.get_param<unsigned>(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<unsigned>(0));
-                       pending_block = &parse_location(row.get_param<string>(1));
+                       pending_block = &get_sensor(row.get_param<unsigned>(1)).get_block();
                        executing = false;
                        break;
                case ARRIVE:
@@ -133,11 +137,19 @@ void Timetable::save(list<DataFile::Statement> &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<unsigned>(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<unsigned>(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<unsigned>(0);
+       case GOTO_ZONE:
                return "set route to "+get_param<string>(0);
        case TRAVEL:
                return "travel to "+get_param<string>(0);
@@ -209,14 +223,16 @@ DataFile::Statement Timetable::Row::save() const
 {
        switch(type)
        {
-       case GOTO:
-               return DataFile::Statement("goto"), get_param<string>(0);
+       case GOTO_SENSOR:
+               return DataFile::Statement("goto_sensor"), get_param<unsigned>(0);
+       case GOTO_ZONE:
+               return DataFile::Statement("goto_zone"), get_param<string>(0);
        case TRAVEL:
                return DataFile::Statement("travel"), get_param<string>(0);
        case WAIT_TIME:
                return DataFile::Statement("wait"), get_param<unsigned>(0);
        case WAIT_TRAIN:
-               return DataFile::Statement("wait_train"), get_param<unsigned>(0), get_param<string>(1);
+               return DataFile::Statement("wait_train"), get_param<unsigned>(0), get_param<unsigned>(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<unsigned>(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<unsigned>(s.substr(15, at-15)));
-                               row.params.push_back(s.substr(at+4));
+                               row.params.push_back(lexical_cast<unsigned>(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<unsigned>(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<Timetable>(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<void (Loader::*)(unsigned)>(&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<void (Loader::*)(const string &)>(&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<unsigned>(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);
 }
 
index 9cfda68df5df05a2876e920169efafe6debf85fd..86b4ae2944c8e21ebb207d49e27e54a946d62dbf 100644 (file)
@@ -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<Msp::DataFile::Statement> &) 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();
 };
index dd7139f864824c7aac2e80b1eea4f8c40691c6ab..ffa436b1d6a0c07795bf876d304077bb08a80fcd 100644 (file)
@@ -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<Track *> 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())
index 99575bf541912fe679bd2c0a6bd66265f5eb4f48..330eadd32e93f6f504963b8429eea4c1d8803334 100644 (file)
@@ -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 (file)
index 0000000..4b95da5
--- /dev/null
@@ -0,0 +1,134 @@
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/strings/formatter.h>
+#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<DataFile::Statement> &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<unsigned> block_ids;
+       for(TrackSet::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
+               block_ids.insert((*i)->get_block().get_id());
+
+       for(set<unsigned>::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<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)
+{
+       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<Track *> &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 (file)
index 0000000..98feb67
--- /dev/null
@@ -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 <set>
+#include <string>
+#include <msp/datafile/objectloader.h>
+
+namespace R2C2 {
+
+class Layout;
+class Track;
+
+class Zone
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<Zone>
+       {
+       public:
+               Loader(Zone &);
+       private:
+               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 &);
+       ~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<Msp::DataFile::Statement> &) const;
+private:
+       bool is_valid(Track &) const;
+};
+
+} // namespace R2C2
+
+#endif