--- /dev/null
+layout
+{
+ margin
+ {
+ top 2;
+ horizontal 8;
+ bottom 6;
+ };
+};
+
+column
+{
+ row
+ {
+ label "lbl_title"
+ {
+ text "Departures";
+ };
+
+ split;
+
+ action_button "btn_close" 0
+ {
+ style "red_cross";
+ };
+ };
+
+ dropdown "drp_groups";
+
+ panel "pnl_departures"
+ {
+ style "digital_background";
+ layout
+ {
+ margin
+ {
+ horizontal 4;
+ vertical 4;
+ };
+ column_spacing 10;
+ };
+ };
+};
+
+draghandle "";
+expand true false;
+constraint COPY_HEIGHT "lbl_title";
+constraint LEFT_OF "btn_close";
};
};
+style "label-digital_borderless"
+{
+ font "digitalreadout-16.font";
+ font_color 0.3 1 0.3;
+
+ part "text"
+ {
+ fill 0.0 0.0;
+ align 0.0 0.5;
+ };
+};
+
+style "panel-digital_background"
+{
+ part
+ {
+ graphic NORMAL "sunken_black_bg";
+ };
+
+ part "children";
+};
+
graphic "sunken_white_bg"
{
texture "gui.png";
--- /dev/null
+#include <msp/core/maputils.h>
+#include <msp/gltk/grid.h>
+#include <msp/strings/format.h>
+#include "libr2c2/layout.h"
+#include "libr2c2/train.h"
+#include "libr2c2/zone.h"
+#include "departuresdialog.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+DeparturesDialog::DeparturesDialog(const Layout &l, const string &group):
+ layout(l),
+ departures(0)
+{
+ Loader::WidgetMap widgets;
+ DataFile::load(*this, "data/departuresdialog.ui", widgets);
+
+ lbl_title = dynamic_cast<GLtk::Label *>(get_item(widgets, "lbl_title"));
+ drp_groups = dynamic_cast<GLtk::Dropdown *>(get_item(widgets, "drp_groups"));
+ drp_groups->set_data(groups);
+ drp_groups->signal_item_selected.connect(sigc::mem_fun(this, &DeparturesDialog::group_selected));
+ pnl_departures = dynamic_cast<GLtk::Panel *>(get_item(widgets, "pnl_departures"));
+
+ GLtk::Grid grid(*pnl_departures->get_layout(), 2);
+ rows.resize(8);
+ for(unsigned i=0; i<rows.size(); ++i)
+ {
+ Row &row = rows[i];
+ pnl_departures->add(*(row.lbl_time = new GLtk::Label));
+ row.lbl_time->set_style("digital_borderless");
+ pnl_departures->add(*(row.lbl_train = new GLtk::Label));
+ row.lbl_train->set_style("digital_borderless");
+ }
+
+ const set<Zone *> &zones = layout.get_all<Zone>();
+ set<string> group_set;
+ for(set<Zone *>::const_iterator i=zones.begin(); i!=zones.end(); ++i)
+ group_set.insert((*i)->get_group());
+
+ for(set<string>::const_iterator i=group_set.begin(); i!=group_set.end(); ++i)
+ {
+ groups.append(*i);
+ if(*i==group)
+ drp_groups->set_selected_index(groups.size()-1);
+ }
+}
+
+void DeparturesDialog::group_selected(unsigned index)
+{
+ const string &group = groups.get(index);
+ lbl_title->set_text("Departures from "+group);
+
+ for(vector<Row>::iterator i=rows.begin(); i!=rows.end(); ++i)
+ i->set_departure(0);
+
+ delete departures;
+ departures = new Departures(layout, group);
+ departures->signal_departure_added.connect(sigc::mem_fun(this, &DeparturesDialog::departure_added));
+ departures->signal_departure_removed.connect(sigc::mem_fun(this, &DeparturesDialog::departure_removed));
+
+ update_rows();
+}
+
+void DeparturesDialog::departure_added(const Departures::Departure &)
+{
+ update_rows();
+}
+
+void DeparturesDialog::departure_removed(const Departures::Departure &)
+{
+ update_rows();
+}
+
+void DeparturesDialog::update_rows()
+{
+ const list<Departures::Departure> &deps = departures->get_departures();
+ list<Departures::Departure>::const_iterator i = deps.begin();
+ for(unsigned j=0; j<rows.size(); ++j)
+ {
+ if(i!=deps.end())
+ rows[j].set_departure(&*i++);
+ else
+ rows[j].set_departure(0);
+ }
+}
+
+
+DeparturesDialog::Row::Row():
+ departure(0),
+ lbl_time(0),
+ lbl_train(0)
+{ }
+
+void DeparturesDialog::Row::set_departure(const Departures::Departure *d)
+{
+ departure = d;
+ if(departure)
+ {
+ unsigned secs = departure->time/Time::sec;
+ unsigned hour = secs/3600%24;
+ unsigned min = secs/60%60;
+ lbl_time->set_text(format("%2d:%02d", hour, min));
+ lbl_train->set_text(departure->train->get_name());
+ }
+ else
+ {
+ lbl_time->set_text(string());
+ lbl_train->set_text(string());
+ }
+}
--- /dev/null
+#ifndef DEPARTURESDIALOG_H_
+#define DEPARTURESDIALOG_H_
+
+#include <msp/gltk/dialog.h>
+#include <msp/gltk/dropdown.h>
+#include <msp/gltk/label.h>
+#include "libr2c2/departures.h"
+
+class DeparturesDialog: public Msp::GLtk::Dialog
+{
+private:
+ struct Row
+ {
+ const R2C2::Departures::Departure *departure;
+ Msp::GLtk::Label *lbl_time;
+ Msp::GLtk::Label *lbl_train;
+
+ Row();
+
+ void set_departure(const R2C2::Departures::Departure *);
+ };
+
+ const R2C2::Layout &layout;
+ Msp::GLtk::Dropdown *drp_groups;
+ Msp::GLtk::Label *lbl_title;
+ Msp::GLtk::BasicListData<std::string> groups;
+ Msp::GLtk::Panel *pnl_departures;
+ R2C2::Departures *departures;
+ std::vector<Row> rows;
+
+public:
+ DeparturesDialog(const R2C2::Layout &, const std::string & = std::string());
+
+private:
+ void group_selected(unsigned);
+ void departure_added(const R2C2::Departures::Departure &);
+ void departure_removed(const R2C2::Departures::Departure &);
+ void update_rows();
+};
+
+#endif
#include "libr2c2/driver.h"
#include "libr2c2/trackcircuit.h"
#include "libr2c2/tracktype.h"
+#include "libr2c2/zone.h"
#include "3d/allocation.h"
#include "3d/path.h"
#include "3d/track.h"
#include "3d/trackcircuit.h"
#include "3d/vehicle.h"
+#include "departuresdialog.h"
#include "dynamicdialog.h"
#include "engineer.h"
#include "mainwindow.h"
set_status(format("Turnout %d", track->get_turnout_address()));
}
}
- if(unsigned saddr = track->get_sensor_address())
+ else if(unsigned saddr = track->get_sensor_address())
{
if(options.simulate)
layout.get_driver().set_sensor(saddr, !layout.get_driver().get_sensor(saddr));
set_status(format("Sensor %d", saddr));
}
+ else
+ {
+ const set<Zone *> &zones = layout.get_all<Zone>();
+ for(set<Zone *>::const_iterator i=zones.begin(); i!=zones.end(); ++i)
+ if((*i)->has_track(*track))
+ {
+ DeparturesDialog *dlg = new DeparturesDialog(layout, (*i)->get_group());
+ root->add(*dlg);
+ break;
+ }
+ }
}
else if(Vehicle *veh = dynamic_cast<Vehicle *>(obj))
{
--- /dev/null
+#include "departures.h"
+#include "layout.h"
+#include "timetable.h"
+#include "train.h"
+#include "trainrouter.h"
+#include "zone.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace {
+
+bool departure_time_order(const R2C2::Departures::Departure &d1, const R2C2::Departures::Departure &d2)
+{
+ return d1.time<d2.time;
+}
+
+}
+
+
+namespace R2C2 {
+
+Departures::Departures(const Layout &layout, const string &group):
+ zones(layout.get_zones(group))
+{
+ const map<unsigned, Train *> &trains = layout.get_trains();
+ for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
+ {
+ Timetable *timetable = i->second->get_ai_of_type<Timetable>();
+ (void)timetable;
+ TrainRouter *router = i->second->get_ai_of_type<TrainRouter>();
+ router->signal_route_changed.connect(sigc::bind(sigc::mem_fun(this, &Departures::train_route_changed), sigc::ref(*i->second)));
+ router->signal_departed.connect(sigc::bind(sigc::mem_fun(this, &Departures::train_departed), sigc::ref(*i->second)));
+
+ update_departures(*i->second);
+ }
+}
+
+void Departures::train_route_changed(const Route *, Train &train)
+{
+ update_departures(train);
+}
+
+void Departures::train_departed(Train &train)
+{
+ update_departures(train);
+}
+
+void Departures::update_departures(Train &train)
+{
+ const Clock &clock = train.get_layout().get_clock();
+ Time::TimeDelta t = clock.get_current_time();
+ float rate = clock.get_rate();
+
+ list<Departure> collected_departures;
+
+ Timetable *timetable = train.get_ai_of_type<Timetable>();
+ unsigned tt_length = timetable->get_length();
+ for(unsigned i=0; i<tt_length; ++i)
+ {
+ const Timetable::Row &row = timetable->get_row(i);
+ if(row.type==Timetable::DEPART)
+ if(Zone *zone = get_attached_zone(*row.target))
+ {
+ Time::TimeDelta dt = row.time;
+ while(dt<t)
+ dt += Time::day;
+ collected_departures.push_back(Departure(*zone, train, dt));
+ }
+ }
+
+ collected_departures.sort(departure_time_order);
+
+ TrainRouter *router = train.get_ai_of_type<TrainRouter>();
+ Time::TimeDelta departure_delay = router->get_departure_delay();
+ if(departure_delay)
+ if(Zone *zone = get_attached_zone(*train.get_block_allocator().last()))
+ {
+ Departure dep(*zone, train, t+departure_delay*rate);
+
+ list<Departure>::iterator i = collected_departures.begin();
+ bool duplicate = false;
+ for(; (i!=collected_departures.end() && !duplicate); ++i)
+ {
+ if(i->time>dep.time)
+ {
+ duplicate = (i->time<dep.time+Time::min);
+ break;
+ }
+ else
+ duplicate = (i->time+Time::min>dep.time);
+ }
+
+ if(!duplicate)
+ collected_departures.push_back(dep);
+ }
+
+ merge_departures(train, collected_departures);
+}
+
+void Departures::merge_departures(Train &train, const std::list<Departure> &collected_departures)
+{
+ list<Departure>::iterator i = departures.begin();
+ list<Departure>::const_iterator j = collected_departures.begin();
+ while(i!=departures.end() && j!=collected_departures.end())
+ {
+ if(i->time>j->time)
+ {
+ list<Departure>::iterator k = departures.insert(i, *j);
+ signal_departure_added.emit(*k);
+ ++j;
+ }
+ else if(i->train==&train)
+ {
+ if(i->time==j->time)
+ {
+ ++i;
+ ++j;
+ }
+ else
+ {
+ signal_departure_removed.emit(*i);
+ departures.erase(i++);
+ }
+ }
+ else
+ ++i;
+ }
+
+ for(; j!=collected_departures.end(); ++j)
+ {
+ departures.push_back(*j);
+ signal_departure_added.emit(departures.back());
+ }
+
+ for(; i!=departures.end(); )
+ {
+ if(i->train==&train)
+ {
+ signal_departure_removed.emit(*i);
+ departures.erase(i++);
+ }
+ else
+ ++i;
+ }
+}
+
+Zone *Departures::get_attached_zone(const TrackChain &chain) const
+{
+ const TrackChain::TrackSet &tracks = chain.get_tracks();
+ for(vector<Zone *>::const_iterator i=zones.begin(); i!=zones.end(); ++i)
+ {
+ for(TrackChain::TrackSet::const_iterator j=tracks.begin(); j!=tracks.end(); ++j)
+ if((*i)->has_track(**j))
+ return *i;
+ }
+
+ return 0;
+}
+
+
+Departures::Departure::Departure(Zone &z, Train &t, const Time::TimeDelta &m):
+ zone(&z),
+ train(&t),
+ time(m)
+{ }
+
+} // namespace R2C2
--- /dev/null
+#ifndef LIBR2C2_DEPARTURES_H_
+#define LIBR2C2_DEPARTURES_H_
+
+#include <list>
+#include <string>
+#include <vector>
+#include <sigc++/signal.h>
+#include <msp/time/timedelta.h>
+
+namespace R2C2 {
+
+class Layout;
+class Zone;
+class Route;
+class TrackChain;
+class Train;
+
+class Departures: public sigc::trackable
+{
+public:
+ struct Departure
+ {
+ Zone *zone;
+ Train *train;
+ Msp::Time::TimeDelta time;
+
+ Departure(Zone &, Train &, const Msp::Time::TimeDelta &);
+ };
+
+ sigc::signal<void, const Departure &> signal_departure_added;
+ sigc::signal<void, const Departure &> signal_departure_removed;
+
+private:
+ std::vector<Zone *> zones;
+ std::list<Departure> departures;
+
+public:
+ Departures(const Layout &, const std::string &);
+
+ const std::list<Departure> &get_departures() const { return departures; }
+
+private:
+ void train_route_changed(const Route *, Train &);
+ void train_departed(Train &);
+ void update_departures(Train &);
+ void merge_departures(Train &, const std::list<Departure> &);
+ Zone *get_attached_zone(const TrackChain &) const;
+};
+
+} // namespace R2C2
+
+#endif