]> git.tdb.fi Git - r2c2.git/blob - source/libmarklin/layout.cpp
Mark generated routes as temporary and don't show or save them
[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                 if(i->second->is_temporary())
241                         continue;
242
243                 DataFile::Statement st("route");
244                 st.append(i->first);
245                 i->second->save(st.sub);
246                 writer.write(st);
247         }
248 }
249
250 void Layout::save_trains(const string &fn)
251 {
252         IO::BufferedFile out(fn, IO::M_WRITE);
253         DataFile::Writer writer(out);
254
255         for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
256         {
257                 DataFile::Statement st("train");
258                 st.append(i->second->get_locomotive_type().get_article_number());
259                 st.append(i->second->get_address());
260                 i->second->save(st.sub);
261                 writer.write(st);
262         }
263 }
264
265 void Layout::check_links()
266 {
267         for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
268                 (*i)->break_links();
269
270         list<Track *> flext;
271         for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
272         {
273                 if((*i)->get_flex())
274                         flext.push_back(*i);
275                 else
276                 {
277                         for(set<Track *>::iterator j=i; j!=tracks.end(); ++j)
278                                 if(j!=i)
279                                         (*i)->snap_to(**j, true);
280                 }
281         }
282
283         for(list<Track *>::iterator i=flext.begin(); i!=flext.end(); ++i)
284                 for(set<Track *>::iterator j=tracks.begin(); j!=tracks.end(); ++j)
285                         if(*j!=*i)
286                                 (*i)->snap_to(**j, true);
287 }
288
289 void Layout::check_routes()
290 {
291         for(map<string, Route *>::iterator i=routes.begin(); i!=routes.end(); ++i)
292         {
293                 if(i->second->is_temporary())
294                         continue;
295
296                 /* We must copy the turnout map, since adding tracks to the route will
297                 (temporarily) mess it up */
298                 const map<unsigned, int> turnouts = i->second->get_turnouts();
299
300                 // Find any turnout in the route
301                 Track *track = 0;
302                 unsigned trk_path = 0;
303                 for(set<Track *>::const_iterator j=tracks.begin(); j!=tracks.end(); ++j)
304                 {
305                         map<unsigned, int>::const_iterator k = turnouts.find((*j)->get_turnout_id());
306                         if(k!=turnouts.end())
307                         {
308                                 track = *j;
309                                 trk_path = k->second;
310                                 break;
311                         }
312                 }
313
314                 if(!track)
315                         continue;
316
317                 // Find an applicable endpoint
318                 const vector<Endpoint> &eps = track->get_type().get_endpoints();
319                 unsigned ep = 0;
320                 for(unsigned j=0; j<eps.size(); ++j)
321                         if(eps[j].paths&(1<<trk_path))
322                         {
323                                 ep = j;
324                                 break;
325                         }
326
327                 Track *start = 0;
328                 while(1)
329                 {
330                         // Traverse the track and get the next one
331                         if(track->get_type().get_endpoints().size()<2)
332                                 break;
333                         unsigned out_ep = track->traverse(ep, trk_path);
334                         Track *next = track->get_links()[out_ep];
335                         if(!next || next == start)
336                                 break;
337
338                         ep = next->get_endpoint_by_link(*track);
339                         if(next->get_type().is_turnout())
340                         {
341                                 // Select correct path across the turnout, or break if we hit an unknown turnout
342                                 map<unsigned, int>::const_iterator j = turnouts.find(next->get_turnout_id());
343                                 if(j==turnouts.end())
344                                         break;
345                                 trk_path = j->second;
346                         }
347                         else
348                         {
349                                 trk_path = 0;
350
351                                 /* Start adding tracks when we find the first non-turnout.  This
352                                 prevents the occurrence of ambiguities while adding the tracks */
353                                 if(!start)
354                                         start = next;
355                         }
356
357                         if(start)
358                                 i->second->add_track(*next);
359
360                         track = next;
361                 }
362         }
363 }
364
365 void Layout::sensor_event(unsigned addr, bool state)
366 {
367         if(state)
368         {
369                 for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
370                         if((*i)->get_sensor_id()==addr)
371                         {
372                                 if(!(*i)->get_train())
373                                         emergency("Unreserved sensor triggered");
374                                 break;
375                         }
376         }
377 }
378
379
380 Layout::Loader::Loader(Layout &l):
381         DataFile::BasicLoader<Layout>(l),
382         new_tracks(false)
383 {
384         add("base",  &Layout::base);
385         add("route", &Loader::route);
386         add("track", &Loader::track);
387         add("train", &Loader::train);
388 }
389
390 void Layout::Loader::finish()
391 {
392         if(new_tracks)
393                 obj.check_links();
394         obj.check_routes();
395
396         for(set<Track *>::iterator i=obj.tracks.begin(); i!=obj.tracks.end(); ++i)
397                 (*i)->check_slope();
398 }
399
400 void Layout::Loader::route(const string &n)
401 {
402         Route *rte = new Route(obj, n);
403         load_sub(*rte);
404 }
405
406 void Layout::Loader::track(unsigned art_nr)
407 {
408         Track *trk = new Track(obj, obj.catalogue.get_track(art_nr));
409         load_sub(*trk);
410         new_tracks = true;
411 }
412
413 void Layout::Loader::train(unsigned art_nr, unsigned addr)
414 {
415         Train *trn = new Train(obj, obj.catalogue.get_locomotive(art_nr), addr);
416         load_sub(*trn);
417 }
418
419 } // namespace Marklin