]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/train.cpp
Create another track iterator class that holds an offset as well
[r2c2.git] / source / libr2c2 / train.cpp
1 #include <algorithm>
2 #include <cmath>
3 #include <msp/core/maputils.h>
4 #include <msp/core/raii.h>
5 #include <msp/strings/format.h>
6 #include <msp/time/units.h>
7 #include <msp/time/utils.h>
8 #include "aicontrol.h"
9 #include "beamgate.h"
10 #include "block.h"
11 #include "catalogue.h"
12 #include "driver.h"
13 #include "layout.h"
14 #include "route.h"
15 #include "simplecontroller.h"
16 #include "speedquantizer.h"
17 #include "trackcircuit.h"
18 #include "trackiter.h"
19 #include "tracktype.h"
20 #include "train.h"
21 #include "trainrouter.h"
22 #include "vehicle.h"
23 #include "vehicletype.h"
24 #include "zone.h"
25
26 using namespace std;
27 using namespace Msp;
28
29 namespace R2C2 {
30
31 Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p):
32         layout(l),
33         loco_type(t),
34         address(a),
35         protocol(p),
36         preceding_train(0),
37         allocator(*this),
38         advancing(false),
39         controller(new SimpleController),
40         current_speed_step(0),
41         speed_changing(false),
42         reverse(false),
43         functions(0),
44         pure_speed(false),
45         speed_quantizer(0),
46         accurate_position(false),
47         overshoot_dist(false)
48 {
49         if(!loco_type.is_locomotive())
50                 throw invalid_argument("Train::Train");
51
52         unsigned speed_steps = layout.get_driver().get_protocol_speed_steps(protocol);
53         if(speed_steps)
54                 speed_quantizer = new SpeedQuantizer(speed_steps);
55
56         vehicles.push_back(new Vehicle(layout, loco_type));
57         vehicles.back()->set_train(this);
58
59         layout.add_train(*this);
60
61         layout.get_driver().add_loco(address, protocol, loco_type);
62         layout.get_driver().signal_loco_speed.connect(sigc::mem_fun(this, &Train::loco_speed_event));
63         layout.get_driver().signal_loco_function.connect(sigc::mem_fun(this, &Train::loco_func_event));
64
65         layout.signal_sensor_state_changed.connect(sigc::mem_fun(this, &Train::sensor_state_changed));
66
67         layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
68
69         controller->signal_control_changed.connect(sigc::mem_fun(this, &Train::control_changed));
70 }
71
72 Train::~Train()
73 {
74         delete controller;
75         for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
76                 delete *i;
77         layout.remove_train(*this);
78 }
79
80 void Train::set_name(const string &n)
81 {
82         name = n;
83
84         signal_name_changed.emit(name);
85 }
86
87 void Train::add_vehicle(const VehicleType &vt)
88 {
89         Vehicle *veh = new Vehicle(layout, vt);
90         vehicles.back()->attach_back(*veh);
91         vehicles.push_back(veh);
92         veh->set_train(this);
93         signal_vehicle_added.emit(vehicles.size()-1, *veh);
94 }
95
96 void Train::remove_vehicle(unsigned i)
97 {
98         if(i>=vehicles.size())
99                 throw out_of_range("Train::remove_vehicle");
100         if(i==0)
101                 throw logic_error("can't remove locomotive");
102
103         Vehicle *veh = vehicles[i];
104         vehicles.erase(vehicles.begin()+i);
105         veh->detach_front();
106         if(i<vehicles.size())
107         {
108                 veh->detach_back();
109                 vehicles[i-1]->attach_back(*vehicles[i]);
110         }
111         signal_vehicle_removed.emit(i, *veh);
112         delete veh;
113 }
114
115 unsigned Train::get_n_vehicles() const
116 {
117         return vehicles.size();
118 }
119
120 Vehicle &Train::get_vehicle(unsigned i)
121 {
122         if(i>=vehicles.size())
123                 throw out_of_range("Train::get_vehicle");
124         return *vehicles[i];
125 }
126
127 const Vehicle &Train::get_vehicle(unsigned i) const
128 {
129         if(i>=vehicles.size())
130                 throw out_of_range("Train::get_vehicle");
131         return *vehicles[i];
132 }
133
134 void Train::set_control(const string &n, float v)
135 {
136         controller->set_control(n, v);
137 }
138
139 void Train::set_function(unsigned func, bool state)
140 {
141         if(!loco_type.get_functions().count(func))
142                 throw invalid_argument("Train::set_function");
143         layout.get_driver().set_loco_function(address, func, state);
144 }
145
146 float Train::get_control(const string &ctrl) const
147 {
148         return controller->get_control(ctrl).value;
149 }
150
151 float Train::get_speed() const
152 {
153         return controller->get_speed();
154 }
155
156 float Train::get_quantized_speed() const
157 {
158         if(speed_quantizer)
159                 return speed_quantizer->quantize_speed(controller->get_speed());
160         else
161                 return controller->get_speed();
162 }
163
164 bool Train::get_function(unsigned func) const
165 {
166         return (functions>>func)&1;
167 }
168
169 void Train::add_ai(TrainAI &ai)
170 {
171         ais.push_back(&ai);
172         ai.signal_event.connect(sigc::bind<0>(signal_ai_event, sigc::ref(ai)));
173 }
174
175 void Train::remove_ai(TrainAI &ai)
176 {
177         list<TrainAI *>::iterator i = find(ais.begin(), ais.end(), &ai);
178         if(i!=ais.end())
179                 ais.erase(i);
180 }
181
182 void Train::ai_message(const TrainAI::Message &msg)
183 {
184         for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
185                 (*i)->message(msg);
186 }
187
188 void Train::place(const BlockIter &block)
189 {
190         if(!block)
191                 throw invalid_argument("Train::place");
192         if(controller->get_speed())
193                 throw logic_error("moving");
194
195         allocator.start_from(block);
196         accurate_position = false;
197         last_entry_block = BlockIter();
198
199         if(reverse)
200                 vehicles.front()->place(block.reverse().track_iter(), 0, Vehicle::FRONT_BUFFER);
201         else
202                 vehicles.back()->place(block.track_iter(), 0, Vehicle::BACK_BUFFER);
203 }
204
205 void Train::unplace()
206 {
207         if(controller->get_speed())
208                 throw logic_error("moving");
209
210         allocator.clear();
211         accurate_position = false;
212         last_entry_block = BlockIter();
213
214         for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
215                 (*i)->unplace();
216 }
217
218 void Train::stop_at(Block *block)
219 {
220         allocator.stop_at(block);
221 }
222
223 bool Train::is_block_critical(const Block &block) const
224 {
225         return get_reserved_distance_until(&block)<controller->get_braking_distance()*1.3;
226 }
227
228 BlockIter Train::get_first_noncritical_block() const
229 {
230         if(allocator.empty())
231                 return BlockIter();
232
233         BlockIter i = allocator.last_current().next();
234
235         if(controller->get_speed()==0)
236                 return i;
237
238         float margin = 10*layout.get_catalogue().get_scale();
239         float min_dist = controller->get_braking_distance()*1.3+margin;
240
241         float dist = 0;
242         bool sensor_seen = false;
243         for(; i->get_train()==this; i=i.next())
244         {
245                 if(dist>min_dist && sensor_seen)
246                         return i;
247
248                 dist += i->get_path_length(i.entry());
249
250                 if(i->get_sensor_id())
251                         sensor_seen = true;
252         }
253
254         return i;
255 }
256
257 void Train::refresh_blocks_from(Block &block)
258 {
259         if(is_block_critical(block))
260                 allocator.rewind_to(*get_first_noncritical_block());
261         else
262                 allocator.rewind_to(block);
263 }
264
265 float Train::get_reserved_distance() const
266 {
267         if(allocator.empty())
268                 return 0;
269
270         float margin = 0;
271         TrackIter next = allocator.last().next().track_iter();
272         if(next && next->get_type().is_turnout())
273                 margin = 15*layout.get_catalogue().get_scale();
274
275         return max(get_reserved_distance_until(0)-margin, 0.0f);
276 }
277
278 void Train::tick(const Time::TimeDelta &dt)
279 {
280         if(stop_timeout)
281         {
282                 stop_timeout -= dt;
283                 if(stop_timeout<=Time::zero)
284                 {
285                         allocator.set_active(false);
286                         stop_timeout = Time::TimeDelta();
287                 }
288         }
289
290         travel_time += dt;
291
292         Driver &driver = layout.get_driver();
293
294         bool intent_to_move = false;
295         for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
296         {
297                 (*i)->tick(dt);
298                 if((*i)->has_intent_to_move())
299                         intent_to_move = true;
300         }
301
302         controller->tick(dt);
303         float speed = controller->get_speed();
304         bool moving = speed>0;
305
306         if(controller->get_reverse()!=reverse)
307         {
308                 reverse = controller->get_reverse();
309                 bool r = reverse;
310                 if(loco_type.get_swap_direction())
311                         r = !r;
312                 driver.set_loco_reverse(address, r);
313
314                 allocator.reverse();
315                 last_entry_block = BlockIter();
316         }
317
318         if(speed_quantizer)
319         {
320                 unsigned speed_step = speed_quantizer->find_speed_step(speed);
321                 if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power())
322                 {
323                         speed_changing = true;
324                         driver.set_loco_speed(address, speed_step);
325
326                         pure_speed = false;
327                 }
328
329                 speed = speed_quantizer->get_speed(current_speed_step);
330         }
331
332         if(moving)
333         {
334                 if(!allocator.is_active())
335                         allocator.set_active(true);
336
337                 Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
338
339                 float d = speed*(dt/Time::sec);
340                 if(allocator.is_block_current(vehicle.get_track()->get_block()))
341                 {
342                         SetFlag setf(advancing);
343                         vehicle.advance(reverse ? -d : d);
344                 }
345                 else if(accurate_position)
346                 {
347                         overshoot_dist += d;
348                         if(overshoot_dist>40*layout.get_catalogue().get_scale())
349                         {
350                                 layout.emergency(name+" has not arrived at sensor");
351                                 accurate_position = false;
352                         }
353                 }
354         }
355         else if(intent_to_move && !allocator.is_active())
356                 allocator.set_active(true);
357         else if(allocator.is_active() && !intent_to_move && !stop_timeout)
358                 stop_timeout = 2*Time::sec;
359 }
360
361 void Train::save(list<DataFile::Statement> &st) const
362 {
363         st.push_back((DataFile::Statement("name"), name));
364
365         for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
366                 if(i!=vehicles.begin())
367                         st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
368
369         if(speed_quantizer)
370         {
371                 DataFile::Statement ss("quantized_speed");
372                 speed_quantizer->save(ss.sub);
373                 st.push_back(ss);
374         }
375
376         {
377                 DataFile::Statement ss("blocks");
378                 allocator.save(ss.sub);
379                 st.push_back(ss);
380         }
381
382         // XXX Need more generic way of saving AI state
383         for(list<TrainAI *>::const_iterator i=ais.begin(); i!=ais.end(); ++i)
384         {
385                 if(TrainRouter *router = dynamic_cast<TrainRouter *>(*i))
386                 {
387                         DataFile::Statement ss("router");
388                         router->save(ss.sub);
389                         st.push_back(ss);
390                 }
391         }
392 }
393
394 void Train::control_changed(const Controller::Control &ctrl)
395 {
396         signal_control_changed.emit(ctrl.name, ctrl.value);
397 }
398
399 void Train::loco_speed_event(unsigned addr, unsigned speed, bool rev)
400 {
401         if(addr==address)
402         {
403                 current_speed_step = speed;
404                 bool r = reverse;
405                 if(loco_type.get_swap_direction())
406                         r = !r;
407                 if(rev!=r)
408                         layout.get_driver().set_loco_reverse(address, r);
409                 speed_changing = false;
410                 pure_speed = false;
411         }
412 }
413
414 void Train::loco_func_event(unsigned addr, unsigned func, bool state)
415 {
416         if(addr==address)
417         {
418                 if(state)
419                         functions |= 1<<func;
420                 else
421                         functions &= ~(1<<func);
422
423                 signal_function_changed.emit(func, state);
424         }
425 }
426
427 void Train::sensor_state_changed(Sensor &sensor, Sensor::State state)
428 {
429         if(!current_speed_step || state!=Sensor::MAYBE_ACTIVE)
430                 return;
431
432         Block *block = sensor.get_block();
433         if(!block || block->get_train()!=this)
434                 return;
435
436         if(last_entry_block && &*last_entry_block!=block)
437         {
438                 for(BlockIter i=last_entry_block.next(); (i && &*i!=block); i=i.next())
439                         if(i->get_train()!=this || i->get_sensor_id())
440                                 return;
441         }
442
443         if(dynamic_cast<TrackCircuit *>(&sensor))
444         {
445                 if(last_entry_block && pure_speed && speed_quantizer)
446                 {
447                         float travel_distance = 0;
448
449                         for(BlockIter i=last_entry_block; &*i!=block; i=i.next())
450                                 travel_distance += i->get_path_length(i.entry());
451
452                         if(travel_distance>0)
453                         {
454                                 float travel_time_secs = travel_time/Time::sec;
455
456                                 if(travel_time_secs>=2)
457                                         speed_quantizer->learn(current_speed_step, travel_distance/travel_time_secs, travel_time_secs);
458                         }
459                 }
460
461                 last_entry_block = allocator.iter_for(*block);
462                 travel_time = Time::zero;
463                 pure_speed = true;
464                 accurate_position = true;
465                 overshoot_dist = 0;
466
467                 if(!advancing && vehicles.front()->get_track())
468                 {
469                         TrackIter track = last_entry_block.track_iter();
470                         if(reverse)
471                         {
472                                 track = track.flip();
473                                 vehicles.back()->place(track, 0, Vehicle::BACK_AXLE);
474                         }
475                         else
476                                 vehicles.front()->place(track, 0, Vehicle::FRONT_AXLE);
477                 }
478         }
479         else if(BeamGate *gate = dynamic_cast<BeamGate *>(&sensor))
480         {
481                 if(!advancing && vehicles.front()->get_track())
482                 {
483                         TrackIter track = allocator.iter_for(*block).track_iter();
484                         for(; (track && &track->get_block()==block); track=track.next())
485                                 if(track.track()==gate->get_track())
486                                 {
487                                         if(reverse)
488                                                 track = track.reverse();
489                                         float offset = gate->get_offset_from_endpoint(track.entry());
490                                         if(reverse)
491                                                 vehicles.back()->place(track, offset, Vehicle::BACK_BUFFER);
492                                         else
493                                                 vehicles.front()->place(track, offset, Vehicle::FRONT_BUFFER);
494                                         break;
495                                 }
496                 }
497         }
498 }
499
500 void Train::halt_event(bool h)
501 {
502         if(h)
503                 accurate_position = false;
504 }
505
506 float Train::get_reserved_distance_until(const Block *until_block) const
507 {
508         if(allocator.empty())
509                 return 0;
510
511         Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
512
513         TrackIter track = veh.get_track_iter().track_iter();
514         if(!track)  // XXX Probably unnecessary
515                 return 0;
516
517         if(&track->get_block()==until_block)
518                 return 0;
519
520         // Account for the vehicle's offset on its current track
521         float result = veh.get_offset();
522         if(reverse)
523                 track = track.reverse();
524         else
525                 result = track->get_type().get_path_length(track->get_active_path())-result;
526         result -= veh.get_type().get_length()/2;
527
528         BlockIter block = track.block_iter();
529
530         // Count remaining distance in the vehicle's current block
531         for(track=track.next(); &track->get_block()==&*block; track=track.next())
532                 result += track->get_type().get_path_length(track->get_active_path());
533
534         const BlockIter &last = allocator.last();
535         if(&*block==&*last)
536                 return result;
537
538         // Count any remaining blocks
539         for(block=block.next(); (&*block!=until_block && block->get_train()==this); block=block.next())
540         {
541                 result += block->get_path_length(block.entry());
542
543                 if(&*block==&*last)
544                         break;
545         }
546
547         return result;
548 }
549
550
551 Train::Loader::Loader(Train &t):
552         DataFile::ObjectLoader<Train>(t),
553         prev_block(0),
554         blocks_valid(true)
555 {
556         add("blocks",      &Loader::blocks);
557         add("name",        &Loader::name);
558         add("quantized_speed",  &Loader::quantized_speed);
559         add("router",      &Loader::router);
560         add("vehicle",     &Loader::vehicle);
561 }
562
563 void Train::Loader::finish()
564 {
565         if(!obj.allocator.empty())
566         {
567                 TrackIter track = obj.allocator.first().track_iter();
568                 float offset = 2*obj.layout.get_catalogue().get_scale();
569                 obj.vehicles.back()->place(track, offset, Vehicle::BACK_BUFFER);
570         }
571 }
572
573 void Train::Loader::blocks()
574 {
575         load_sub(obj.allocator);
576 }
577
578 void Train::Loader::name(const string &n)
579 {
580         obj.set_name(n);
581 }
582
583 void Train::Loader::quantized_speed()
584 {
585         if(obj.speed_quantizer)
586                 load_sub(*obj.speed_quantizer);
587 }
588
589 void Train::Loader::router()
590 {
591         TrainRouter *rtr = new TrainRouter(obj);
592         load_sub(*rtr);
593 }
594
595 void Train::Loader::vehicle(ArticleNumber art_nr)
596 {
597         const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(art_nr);
598         Vehicle *veh = new Vehicle(obj.layout, vtype);
599         obj.vehicles.back()->attach_back(*veh);
600         obj.vehicles.push_back(veh);
601         veh->set_train(&obj);
602 }
603
604 } // namespace R2C2