]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/layout.cpp
Add support for named zones
[r2c2.git] / source / libr2c2 / layout.cpp
1 /* $Id$
2
3 This file is part of R²C²
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 #include "zone.h"
24
25 using namespace std;
26 using namespace Msp;
27
28 namespace {
29
30 bool zone_order(const R2C2::Zone *z1, const R2C2::Zone *z2)
31 {
32         return z1->get_number()<z2->get_number();
33 }
34
35 }
36
37
38 namespace R2C2 {
39
40 Layout::Layout(Catalogue &c, Driver *d):
41         catalogue(c),
42         driver(d),
43         next_turnout_id(0x800)
44 {
45         if(driver)
46                 driver->signal_sensor.connect(sigc::mem_fun(this, &Layout::sensor_event));
47 }
48
49 Layout::~Layout()
50 {
51         delete driver;
52         while(!trains.empty())
53                 delete trains.begin()->second;
54         while(!routes.empty())
55                 delete *routes.begin();
56         while(!tracks.empty())
57                 delete *tracks.begin();
58         while(!blocks.empty())
59                 delete *blocks.begin();
60 }
61
62 Driver &Layout::get_driver() const
63 {
64         if(!driver)
65                 throw InvalidState("No driver");
66         return *driver;
67 }
68
69 void Layout::add_track(Track &t)
70 {
71         if(tracks.insert(&t).second)
72         {
73                 create_blocks();
74                 signal_track_added.emit(t);
75         }
76 }
77
78 void Layout::remove_track(Track &t)
79 {
80         if(tracks.erase(&t))
81         {
82                 create_blocks(t);
83                 signal_track_removed.emit(t);
84         }
85 }
86
87 unsigned Layout::allocate_turnout_id(bool dbl)
88 {
89         set<unsigned> used_ids;
90         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
91                 if((*i)->get_turnout_id())
92                         used_ids.insert((*i)->get_turnout_id());
93
94         unsigned result = next_turnout_id;
95         while(used_ids.count(result) || (dbl && used_ids.count(result+1)))
96                 ++result;
97         next_turnout_id = result+1+dbl;
98
99         return result;
100 }
101
102 void Layout::add_block(Block &b)
103 {
104         blocks.insert(&b);
105 }
106
107 Block &Layout::get_block(unsigned id) const
108 {
109         for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
110                 if((*i)->get_id()==id)
111                         return **i;
112
113         throw KeyError("Unknown block", lexical_cast(id));
114 }
115
116 void Layout::create_blocks()
117 {
118         set<Track *> used_tracks;
119         for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
120         {
121                 const set<Track *> &btracks = (*i)->get_tracks();
122                 used_tracks.insert(btracks.begin(), btracks.end());
123         }
124
125         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
126                 if(used_tracks.count(*i)==0)
127                 {
128                         Block *block = new Block(*this, **i);
129                         used_tracks.insert(block->get_tracks().begin(), block->get_tracks().end());
130                 }
131
132         for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
133                 for(set<Block *>::iterator j=i; j!=blocks.end(); ++j)
134                         if(j!=i)
135                                 (*i)->check_link(**j);
136 }
137
138 void Layout::create_blocks(Track &track)
139 {
140         /* Must collect the blocks in a set first while all tracks are still
141         guaranteed to have blocks and to avoid duplicate deletes */
142         set<Block *> del_blocks;
143
144         del_blocks.insert(&track.get_block());
145
146         const vector<Track *> &links = track.get_links();
147         for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
148                 if(*i)
149                         del_blocks.insert(&(*i)->get_block());
150
151         for(set<Block *>::iterator i=del_blocks.begin(); i!=del_blocks.end(); ++i)
152                 delete *i;
153
154         create_blocks();
155 }
156
157 void Layout::remove_block(Block &b)
158 {
159         blocks.erase(&b);
160 }
161
162 void Layout::add_route(Route &r)
163 {
164         if(routes.insert(&r).second)
165                 signal_route_added.emit(r);
166 }
167
168 Route &Layout::get_route(const string &name) const
169 {
170         for(set<Route *>::const_iterator i=routes.begin(); i!=routes.end(); ++i)
171                 if((*i)->get_name()==name)
172                         return **i;
173         throw KeyError("Unknown route", name);
174 }
175
176 void Layout::update_routes()
177 {
178         for(set<Route *>::iterator i=routes.begin(); i!=routes.end(); ++i)
179                 (*i)->update_turnouts();
180 }
181
182 void Layout::remove_route(Route &r)
183 {
184         if(routes.erase(&r))
185                 signal_route_removed.emit(r);
186 }
187
188 void Layout::add_zone(Zone &z)
189 {
190         if(zones.insert(&z).second)
191                 signal_zone_added.emit(z);
192 }
193
194 Layout::ZoneArray Layout::get_zones(const string &group) const
195 {
196         ZoneArray result;
197         for(ZoneSet::const_iterator i=zones.begin(); i!=zones.end(); ++i)
198                 if((*i)->get_group()==group)
199                         result.push_back(*i);
200
201         sort(result.begin(), result.end(), zone_order);
202
203         return result;
204 }
205
206 Zone &Layout::get_zone(const string &group, unsigned num) const
207 {
208         for(ZoneSet::const_iterator i=zones.begin(); i!=zones.end(); ++i)
209                 if((*i)->get_group()==group && (*i)->get_number()==num)
210                         return **i;
211
212         throw KeyError("Unknown zone", format("%s %d", group, num));
213 }
214
215 void Layout::remove_zone(Zone &z)
216 {
217         if(zones.erase(&z))
218                 signal_zone_removed.emit(z);
219 }
220
221 void Layout::add_train(Train &t)
222 {
223         if(trains.count(t.get_address()))
224                 throw KeyError("Duplicate train address", lexical_cast(t.get_address()));
225
226         trains[t.get_address()] = &t;
227         signal_train_added.emit(t);
228 }
229
230 Train &Layout::get_train(unsigned addr) const
231 {
232         map<unsigned, Train *>::const_iterator i = trains.find(addr);
233         if(i==trains.end())
234                 throw KeyError("Unknown train", lexical_cast(addr));
235         return *i->second;
236 }
237
238 void Layout::remove_train(Train &t)
239 {
240         if(trains.erase(t.get_address()))
241                 signal_train_removed.emit(t);
242 }
243
244 void Layout::add_vehicle(Vehicle &v)
245 {
246         if(vehicles.insert(&v).second)
247                 signal_vehicle_added.emit(v);
248 }
249
250 void Layout::remove_vehicle(Vehicle &v)
251 {
252         if(vehicles.erase(&v))
253                 signal_vehicle_removed.emit(v);
254 }
255
256 void Layout::tick()
257 {
258         if(driver)
259                 driver->tick();
260
261         Time::TimeStamp t = Time::now();
262         Time::TimeDelta dt;
263         if(last_tick)
264                 dt = t-last_tick;
265         last_tick = t;
266
267         for(map<unsigned, Train *>::iterator i=trains.begin(); i!=trains.end(); ++i)
268                 i->second->tick(t, dt);
269 }
270
271 void Layout::emergency(const string &msg)
272 {
273         if(driver)
274                 driver->halt(true);
275         IO::print("Emergency: %s\n", msg);
276         signal_emergency.emit(msg);
277 }
278
279 void Layout::save(const string &fn) const
280 {
281         IO::BufferedFile out(fn, IO::M_WRITE);
282         DataFile::Writer writer(out);
283
284         if(!base.empty())
285                 writer.write((DataFile::Statement("base"), base));
286
287         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
288         {
289                 DataFile::Statement st("track");
290                 st.append((*i)->get_type().get_article_number());
291                 (*i)->save(st.sub);
292                 writer.write(st);
293         }
294
295         for(set<Route *>::const_iterator i=routes.begin(); i!=routes.end(); ++i)
296         {
297                 if((*i)->is_temporary())
298                         continue;
299
300                 DataFile::Statement st("route");
301                 (*i)->save(st.sub);
302                 writer.write(st);
303         }
304
305         for(ZoneSet::const_iterator i=zones.begin(); i!=zones.end(); ++i)
306         {
307                 DataFile::Statement st("zone");
308                 (*i)->save(st.sub);
309                 writer.write(st);
310         }
311 }
312
313 void Layout::save_trains(const string &fn) const
314 {
315         IO::BufferedFile out(fn, IO::M_WRITE);
316         DataFile::Writer writer(out);
317
318         for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
319         {
320                 DataFile::Statement st("train");
321                 st.append(i->second->get_locomotive_type().get_article_number());
322                 st.append(i->second->get_address());
323                 st.append(i->second->get_protocol());
324                 i->second->save(st.sub);
325                 writer.write(st);
326         }
327 }
328
329 void Layout::sensor_event(unsigned addr, bool state)
330 {
331         if(state)
332         {
333                 for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
334                         if((*i)->get_sensor_id()==addr)
335                         {
336                                 if(!(*i)->get_train())
337                                         emergency(format("Unreserved sensor %d triggered", addr));
338                                 break;
339                         }
340         }
341 }
342
343
344 Layout::Loader::Loader(Layout &l):
345         DataFile::BasicLoader<Layout>(l),
346         new_tracks(false)
347 {
348         add("base",  &Layout::base);
349         add("route", static_cast<void (Loader::*)()>(&Loader::route));
350         add("track", static_cast<void (Loader::*)(ArticleNumber)>(&Loader::track));
351         add("train", static_cast<void (Loader::*)(ArticleNumber, unsigned, const std::string &)>(&Loader::train));
352         add("zone",  &Loader::zone);
353
354         // Deprecated aliases
355         add("route", static_cast<void (Loader::*)(const string &)>(&Loader::route));
356         add("track", static_cast<void (Loader::*)(unsigned)>(&Loader::track));
357         add("train", static_cast<void (Loader::*)(unsigned, unsigned, const std::string &)>(&Loader::train));
358 }
359
360 void Layout::Loader::finish()
361 {
362         for(set<Track *>::iterator i=obj.tracks.begin(); i!=obj.tracks.end(); ++i)
363                 (*i)->check_slope();
364 }
365
366 void Layout::Loader::route()
367 {
368         Route *rte = new Route(obj);
369         load_sub(*rte);
370 }
371
372 void Layout::Loader::route(const string &n)
373 {
374         Route *rte = new Route(obj);
375         rte->set_name(n);
376         load_sub(*rte);
377 }
378
379 void Layout::Loader::track(unsigned art_nr)
380 {
381         track(ArticleNumber(art_nr));
382 }
383
384 void Layout::Loader::track(ArticleNumber art_nr)
385 {
386         Track *trk = new Track(obj, obj.catalogue.get_track(art_nr));
387         load_sub(*trk);
388         new_tracks = true;
389         for(set<Track *>::iterator i=obj.tracks.begin(); i!=obj.tracks.end(); ++i)
390                 if(*i!=trk)
391                         trk->snap_to(**i, true);
392 }
393
394 void Layout::Loader::train(unsigned art_nr, unsigned addr, const std::string &proto)
395 {
396         train(ArticleNumber(art_nr), addr, proto);
397 }
398
399 void Layout::Loader::train(ArticleNumber art_nr, unsigned addr, const std::string &proto)
400 {
401         Train *trn = new Train(obj, obj.catalogue.get_vehicle(art_nr), addr, proto);
402         load_sub(*trn);
403 }
404
405 void Layout::Loader::zone()
406 {
407         Zone *zne = new Zone(obj);
408         load_sub(*zne);
409 }
410
411 } // namespace R2C2