]> git.tdb.fi Git - r2c2.git/commitdiff
Add a dialog and necessary support code to display departures
authorMikko Rasa <tdb@tdb.fi>
Thu, 2 Apr 2015 21:53:08 +0000 (00:53 +0300)
committerMikko Rasa <tdb@tdb.fi>
Thu, 2 Apr 2015 21:53:08 +0000 (00:53 +0300)
data/departuresdialog.ui [new file with mode: 0644]
data/r2c2.res
source/engineer/departuresdialog.cpp [new file with mode: 0644]
source/engineer/departuresdialog.h [new file with mode: 0644]
source/engineer/engineer.cpp
source/libr2c2/departures.cpp [new file with mode: 0644]
source/libr2c2/departures.h [new file with mode: 0644]

diff --git a/data/departuresdialog.ui b/data/departuresdialog.ui
new file mode 100644 (file)
index 0000000..f124fc7
--- /dev/null
@@ -0,0 +1,48 @@
+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";
index 18cdfc828260824bebd7dce262dfd25e5658f12d..fb673210f2a49ec69e23ef93ce876c7198ded5fd 100644 (file)
@@ -409,6 +409,28 @@ style "label-digital"
        };
 };
 
+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";
diff --git a/source/engineer/departuresdialog.cpp b/source/engineer/departuresdialog.cpp
new file mode 100644 (file)
index 0000000..bea7f79
--- /dev/null
@@ -0,0 +1,112 @@
+#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());
+       }
+}
diff --git a/source/engineer/departuresdialog.h b/source/engineer/departuresdialog.h
new file mode 100644 (file)
index 0000000..9dc99b8
--- /dev/null
@@ -0,0 +1,41 @@
+#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
index ef5b54d8c7110bef454e1f76b1966622bcae70df..da95505a1c2956191ab94086ddde7c6e54338a12 100644 (file)
 #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"
@@ -228,12 +230,23 @@ void Engineer::button_press(unsigned btn)
                                        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))
                {
diff --git a/source/libr2c2/departures.cpp b/source/libr2c2/departures.cpp
new file mode 100644 (file)
index 0000000..d21cc94
--- /dev/null
@@ -0,0 +1,168 @@
+#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
diff --git a/source/libr2c2/departures.h b/source/libr2c2/departures.h
new file mode 100644 (file)
index 0000000..b513975
--- /dev/null
@@ -0,0 +1,52 @@
+#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