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