]> git.tdb.fi Git - r2c2.git/blob - source/libmarklin/layout.cpp
Halt all trains in various unexpected situations
[r2c2.git] / source / libmarklin / layout.cpp
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2006-2010  Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <algorithm>
9 #include <msp/core/refptr.h>
10 #include <msp/datafile/parser.h>
11 #include <msp/datafile/writer.h>
12 #include <msp/time/utils.h>
13 #include "block.h"
14 #include "catalogue.h"
15 #include "driver.h"
16 #include "layout.h"
17 #include "locotype.h"
18 #include "route.h"
19 #include "track.h"
20 #include "tracktype.h"
21 #include "train.h"
22
23 using namespace std;
24 using namespace Msp;
25
26 namespace Marklin {
27
28 Layout::Layout(Catalogue &c, Driver *d):
29         catalogue(c),
30         driver(d)
31 {
32         if(driver)
33                 driver->signal_sensor.connect(sigc::mem_fun(this, &Layout::sensor_event));
34 }
35
36 Layout::~Layout()
37 {
38         delete driver;
39         while(!trains.empty())
40                 delete trains.begin()->second;
41         while(!routes.empty())
42                 delete routes.begin()->second;
43         while(!tracks.empty())
44                 delete *tracks.begin();
45         while(!blocks.empty())
46                 delete *blocks.begin();
47 }
48
49 Driver &Layout::get_driver() const
50 {
51         if(!driver)
52                 throw InvalidState("No driver");
53         return *driver;
54 }
55
56 void Layout::add_track(Track &t)
57 {
58         if(tracks.insert(&t).second)
59         {
60                 create_blocks();
61                 signal_track_added.emit(t);
62         }
63 }
64
65 void Layout::remove_track(Track &t)
66 {
67         if(tracks.erase(&t))
68         {
69                 create_blocks(t);
70                 signal_track_removed.emit(t);
71         }
72 }
73
74 void Layout::add_block(Block &b)
75 {
76         blocks.insert(&b);
77 }
78
79 Block &Layout::get_block(unsigned id) const
80 {
81         for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
82                 if((*i)->get_id()==id)
83                         return **i;
84
85         throw KeyError("Unknown block", lexical_cast(id));
86 }
87
88 Block &Layout::get_block_by_track(const Track &t) const
89 {
90         for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
91                 if((*i)->get_tracks().count(const_cast<Track *>(&t)))
92                         return **i;
93
94         throw InvalidParameterValue("No block found for track");
95 }
96
97 void Layout::create_blocks()
98 {
99         set<Track *> used_tracks;
100         for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
101         {
102                 const set<Track *> &btracks = (*i)->get_tracks();
103                 used_tracks.insert(btracks.begin(), btracks.end());
104         }
105
106         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
107                 if(used_tracks.count(*i)==0)
108                 {
109                         Block *block = new Block(*this, **i);
110                         used_tracks.insert(block->get_tracks().begin(), block->get_tracks().end());
111                 }
112
113         for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
114                 for(set<Block *>::iterator j=i; j!=blocks.end(); ++j)
115                         if(j!=i)
116                                 (*i)->check_link(**j);
117 }
118
119 void Layout::create_blocks(const Track &track)
120 {
121         const vector<Track *> &links = track.get_links();
122         for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end();)
123         {
124                 bool del = (*i)->get_tracks().count(const_cast<Track *>(&track));
125                 for(vector<Track *>::const_iterator j=links.begin(); (!del && j!=links.end()); ++j)
126                         del = (*i)->get_tracks().count(*j);
127
128                 if(del)
129                         delete *i++;
130                 else
131                         ++i;
132         }
133
134         create_blocks();
135 }
136
137 void Layout::remove_block(Block &b)
138 {
139         blocks.erase(&b);
140 }
141
142 void Layout::add_route(Route &r)
143 {
144         if(routes.count(r.get_name()))
145                 throw KeyError("Duplicate route name", r.get_name());
146
147         routes[r.get_name()] = &r;
148         signal_route_added.emit(r);
149 }
150
151 Route &Layout::get_route(const string &name) const
152 {
153         map<string, Route *>::const_iterator i = routes.find(name);
154         if(i==routes.end())
155                 throw KeyError("Unknown route", name);
156         return *i->second;
157 }
158
159 void Layout::remove_route(Route &r)
160 {
161         if(routes.erase(r.get_name()))
162                 signal_route_removed.emit(r);
163 }
164
165 void Layout::add_train(Train &t)
166 {
167         if(trains.count(t.get_address()))
168                 throw KeyError("Duplicate train address", lexical_cast(t.get_address()));
169
170         trains[t.get_address()] = &t;
171         signal_train_added.emit(t);
172 }
173
174 Train &Layout::get_train(unsigned addr) const
175 {
176         map<unsigned, Train *>::const_iterator i = trains.find(addr);
177         if(i==trains.end())
178                 throw KeyError("Unknown train", lexical_cast(addr));
179         return *i->second;
180 }
181
182 void Layout::remove_train(Train &t)
183 {
184         if(trains.erase(t.get_address()))
185                 signal_train_removed.emit(t);
186 }
187
188 void Layout::add_vehicle(Vehicle &v)
189 {
190         if(vehicles.insert(&v).second)
191                 signal_vehicle_added.emit(v);
192 }
193
194 void Layout::remove_vehicle(Vehicle &v)
195 {
196         if(vehicles.erase(&v))
197                 signal_vehicle_removed.emit(v);
198 }
199
200 void Layout::tick()
201 {
202         if(driver)
203                 driver->tick();
204
205         Time::TimeStamp t = Time::now();
206         Time::TimeDelta dt;
207         if(last_tick)
208                 dt = t-last_tick;
209         last_tick = t;
210
211         for(map<unsigned, Train *>::iterator i=trains.begin(); i!=trains.end(); ++i)
212                 i->second->tick(t, dt);
213 }
214
215 void Layout::emergency(const string &msg)
216 {
217         if(driver)
218                 driver->halt(true);
219         signal_emergency.emit(msg);
220 }
221
222 void Layout::save(const string &fn)
223 {
224         IO::BufferedFile out(fn, IO::M_WRITE);
225         DataFile::Writer writer(out);
226
227         if(!base.empty())
228                 writer.write((DataFile::Statement("base"), base));
229
230         for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
231         {
232                 DataFile::Statement st("track");
233                 st.append((*i)->get_type().get_article_number());
234                 (*i)->save(st.sub);
235                 writer.write(st);
236         }
237
238         for(map<string, Route *>::iterator i=routes.begin(); i!=routes.end(); ++i)
239         {
240                 DataFile::Statement st("route");
241                 st.append(i->first);
242                 i->second->save(st.sub);
243                 writer.write(st);
244         }
245 }
246
247 void Layout::save_trains(const string &fn)
248 {
249         IO::BufferedFile out(fn, IO::M_WRITE);
250         DataFile::Writer writer(out);
251
252         for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
253         {
254                 DataFile::Statement st("train");
255                 st.append(i->second->get_locomotive_type().get_article_number());
256                 st.append(i->second->get_address());
257                 i->second->save(st.sub);
258                 writer.write(st);
259         }
260 }
261
262 void Layout::check_links()
263 {
264         for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
265                 (*i)->break_links();
266
267         list<Track *> flext;
268         for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
269         {
270                 if((*i)->get_flex())
271                         flext.push_back(*i);
272                 else
273                 {
274                         for(set<Track *>::iterator j=i; j!=tracks.end(); ++j)
275                                 if(j!=i)
276                                         (*i)->snap_to(**j, true);
277                 }
278         }
279
280         for(list<Track *>::iterator i=flext.begin(); i!=flext.end(); ++i)
281                 for(set<Track *>::iterator j=tracks.begin(); j!=tracks.end(); ++j)
282                         if(*j!=*i)
283                                 (*i)->snap_to(**j, true);
284 }
285
286 void Layout::check_routes()
287 {
288         for(map<string, Route *>::iterator i=routes.begin(); i!=routes.end(); ++i)
289         {
290                 /* We must copy the turnout map, since adding tracks to the route will
291                 (temporarily) mess it up */
292                 const map<unsigned, int> turnouts = i->second->get_turnouts();
293
294                 // Find any turnout in the route
295                 Track *track = 0;
296                 unsigned trk_path = 0;
297                 for(set<Track *>::const_iterator j=tracks.begin(); j!=tracks.end(); ++j)
298                 {
299                         map<unsigned, int>::const_iterator k = turnouts.find((*j)->get_turnout_id());
300                         if(k!=turnouts.end())
301                         {
302                                 track = *j;
303                                 trk_path = k->second;
304                                 break;
305                         }
306                 }
307
308                 if(!track)
309                         continue;
310
311                 // Find an applicable endpoint
312                 const vector<Endpoint> &eps = track->get_type().get_endpoints();
313                 unsigned ep = 0;
314                 for(unsigned j=0; j<eps.size(); ++j)
315                         if(eps[j].paths&(1<<trk_path))
316                         {
317                                 ep = j;
318                                 break;
319                         }
320
321                 Track *start = 0;
322                 while(1)
323                 {
324                         // Traverse the track and get the next one
325                         unsigned out_ep = track->traverse(ep, trk_path);
326                         Track *next = track->get_links()[out_ep];
327                         if(!next || next == start)
328                                 break;
329
330                         ep = next->get_endpoint_by_link(*track);
331                         if(next->get_type().is_turnout())
332                         {
333                                 // Select correct path across the turnout, or break if we hit an unknown turnout
334                                 map<unsigned, int>::const_iterator j = turnouts.find(next->get_turnout_id());
335                                 if(j==turnouts.end())
336                                         break;
337                                 trk_path = j->second;
338                         }
339                         else
340                         {
341                                 trk_path = 0;
342
343                                 /* Start adding tracks when we find the first non-turnout.  This
344                                 prevents the occurrence of ambiguities while adding the tracks */
345                                 if(!start)
346                                         start = next;
347                         }
348
349                         if(start)
350                                 i->second->add_track(*next);
351
352                         track = next;
353                 }
354         }
355 }
356
357 void Layout::sensor_event(unsigned addr, bool state)
358 {
359         if(state)
360         {
361                 for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
362                         if((*i)->get_sensor_id()==addr)
363                         {
364                                 if(!(*i)->get_train())
365                                         emergency("Unreserved sensor triggered");
366                                 break;
367                         }
368         }
369 }
370
371
372 Layout::Loader::Loader(Layout &l):
373         DataFile::BasicLoader<Layout>(l),
374         new_tracks(false)
375 {
376         add("base",  &Layout::base);
377         add("route", &Loader::route);
378         add("track", &Loader::track);
379         add("train", &Loader::train);
380 }
381
382 void Layout::Loader::finish()
383 {
384         if(new_tracks)
385                 obj.check_links();
386         obj.check_routes();
387
388         for(set<Track *>::iterator i=obj.tracks.begin(); i!=obj.tracks.end(); ++i)
389                 (*i)->check_slope();
390 }
391
392 void Layout::Loader::route(const string &n)
393 {
394         Route *rte = new Route(obj, n);
395         load_sub(*rte);
396 }
397
398 void Layout::Loader::track(unsigned art_nr)
399 {
400         Track *trk = new Track(obj, obj.catalogue.get_track(art_nr));
401         load_sub(*trk);
402         new_tracks = true;
403 }
404
405 void Layout::Loader::train(unsigned art_nr, unsigned addr)
406 {
407         Train *trn = new Train(obj, obj.catalogue.get_locomotive(art_nr), addr);
408         load_sub(*trn);
409 }
410
411 } // namespace Marklin