#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"
#include "svgexporter.h"
#include "trackbar.h"
#include "trackproperties.h"
+#include "zonebar.h"
+#include "zoneproperties.h"
using namespace std;
using namespace R2C2;
root(ui_res, window),
base_object(0),
cur_route(0),
+ cur_zone(0),
mode(SELECT),
manipulator(*this, root, selection),
measure(*this),
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);
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)
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)
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)
return string();
}
-void Designer::show_route(const Route *route)
+void Designer::clear_paths()
{
const set<Track *> <racks = 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());
}
}
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;
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();
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
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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
--- /dev/null
+/* $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);
+ }
+}
--- /dev/null
+/* $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
#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):
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()))
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);
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());
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;
(*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);
{
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()
load_sub(*trn);
}
+void Layout::Loader::zone()
+{
+ Zone *zne = new Zone(obj);
+ load_sub(*zne);
+}
+
} // namespace R2C2
class Track;
class Train;
class Vehicle;
+class Zone;
class Layout
{
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;
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;
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; }
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);
};
#include "track.h"
#include "trackiter.h"
#include "tracktype.h"
+#include "zone.h"
using namespace std;
using namespace Msp;
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));
class Layout;
class Track;
class TrackIter;
+class Zone;
class Route: public sigc::trackable
{
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 *> &);
};
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;
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:
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)
{
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);
{
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:
{
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);
}
}
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;
}
}
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));
}
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()
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)
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);
}
namespace R2C2 {
class Block;
+class Track;
class Train;
+class Zone;
class Timetable: public sigc::trackable
{
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,
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();
};
#include "train.h"
#include "vehicle.h"
#include "vehicletype.h"
+#include "zone.h"
using namespace std;
using namespace Msp;
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())
class Timetable;
class Vehicle;
class VehicleType;
+class Zone;
class Train: public sigc::trackable
{
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);
--- /dev/null
+/* $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
--- /dev/null
+/* $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