]> git.tdb.fi Git - r2c2.git/blob - source/libmarklin/train.cpp
Improve the handling of sensor events in Train
[r2c2.git] / source / libmarklin / train.cpp
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <cmath>
9 #include <msp/strings/formatter.h>
10 #include <msp/time/units.h>
11 #include <msp/time/utils.h>
12 #include "control.h"
13 #include "except.h"
14 #include "layout.h"
15 #include "route.h"
16 #include "tracktype.h"
17 #include "trafficmanager.h"
18 #include "train.h"
19
20 using namespace std;
21 using namespace Msp;
22
23 namespace Marklin {
24
25 Train::Train(TrafficManager &tm, Locomotive &l):
26         trfc_mgr(tm),
27         loco(l),
28         pending_block(0),
29         target_speed(0),
30         route(0),
31         status("Unplaced"),
32         travel_dist(0),
33         travel_speed(0),
34         pure_speed(false),
35         real_speed(15),
36         cur_track(0)
37 {
38         trfc_mgr.add_train(this);
39
40         loco.signal_reverse_changed.connect(sigc::mem_fun(this, &Train::locomotive_reverse_changed));
41
42         const map<unsigned, Sensor *> &sensors = trfc_mgr.get_control().get_sensors();
43         for(map<unsigned, Sensor *>::const_iterator i=sensors.begin(); i!=sensors.end(); ++i)
44                 i->second->signal_state_changed.connect(sigc::bind(sigc::mem_fun(this, &Train::sensor_event), i->second));
45
46         const map<unsigned, Turnout *> &turnouts = trfc_mgr.get_control().get_turnouts();
47         for(map<unsigned, Turnout *>::const_iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
48         {
49                 i->second->signal_path_changing.connect(sigc::bind(sigc::mem_fun(this, &Train::turnout_path_changing), i->second));
50                 i->second->signal_path_changed.connect(sigc::bind(sigc::mem_fun(this, &Train::turnout_path_changed), i->second));
51         }
52 }
53
54 void Train::set_name(const string &n)
55 {
56         name = n;
57
58         signal_name_changed.emit(name);
59 }
60
61 void Train::set_speed(unsigned speed)
62 {
63         if(speed==target_speed)
64                 return;
65         travel_speed = static_cast<int>(round(get_real_speed(speed)*87*3.6/5))*5;
66
67         target_speed = speed;
68         if(!target_speed)
69         {
70                 trfc_mgr.get_control().set_timer(3*Time::sec).signal_timeout.connect(
71                         sigc::bind_return(sigc::bind(sigc::mem_fun(this, &Train::release_blocks), sigc::ref(rsv_blocks)), false));
72         }
73         else
74                 reserve_more();
75
76         signal_target_speed_changed.emit(target_speed);
77
78         update_speed();
79         pure_speed = false;
80 }
81
82 void Train::set_reverse(bool rev)
83 {
84         loco.set_reverse(rev);
85 }
86
87 void Train::set_route(const Route *r)
88 {
89         route = r;
90         signal_route_changed.emit(route);
91 }
92
93 void Train::place(Block &block, unsigned entry)
94 {
95         if(target_speed)
96                 set_speed(0);
97
98         release_blocks(rsv_blocks);
99         release_blocks(cur_blocks);
100
101         if(!block.reserve(this))
102         {
103                 set_status("Unplaced");
104                 return;
105         }
106
107         cur_blocks.push_back(BlockRef(&block, entry));
108         set_position(block.get_endpoints()[entry]);
109
110         set_status("Stopped");
111 }
112
113 bool Train::free_block(Block &block)
114 {
115         unsigned nsens = 0;
116         for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
117         {
118                 if(i->block==&block)
119                 {
120                         if(nsens<1)
121                                 return false;
122                         for(list<BlockRef>::iterator j=i; j!=rsv_blocks.end(); ++j)
123                                 j->block->reserve(0);
124                         rsv_blocks.erase(i, rsv_blocks.end());
125                         update_speed();
126                         return true;
127                 }
128                 else if(i->block->get_sensor_id())
129                         ++nsens;
130         }
131
132         return false;
133 }
134
135 int Train::get_entry_to_block(Block &block) const
136 {
137         for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
138                 if(i->block==&block)
139                         return i->entry;
140         for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
141                 if(i->block==&block)
142                         return i->entry;
143         return -1;
144 }
145
146 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
147 {
148         if(try_reserve && t>try_reserve)
149                 reserve_more();
150
151         if(cur_track)
152         {
153                 unsigned path = 0;
154                 if(cur_track->get_turnout_id())
155                         path = trfc_mgr.get_control().get_turnout(cur_track->get_turnout_id()).get_path();
156
157                 offset += get_real_speed(loco.get_speed())*(dt/Time::sec);
158                 float path_len = cur_track->get_type().get_path_length(path);
159                 if(offset>path_len)
160                 {
161                         unsigned out = cur_track->traverse(cur_track_ep, path);
162                         Track *next = cur_track->get_link(out);
163
164                         bool ok = false;
165                         for(list<BlockRef>::const_iterator i=cur_blocks.begin(); (!ok && i!=cur_blocks.end()); ++i)
166                                 ok = i->block->get_tracks().count(next);
167
168                         if(ok)
169                         {
170                                 if(next)
171                                         cur_track_ep = next->get_endpoint_by_link(*cur_track);
172                                 cur_track = next;
173                                 offset = 0;
174                         }
175                         else
176                                 offset = path_len-0.001;
177                 }
178
179                 if(cur_track)
180                         pos = cur_track->get_point(cur_track_ep, path, offset);
181         }
182 }
183
184 void Train::save(list<DataFile::Statement> &st) const
185 {
186         st.push_back((DataFile::Statement("name"), name));
187         for(unsigned i=0; i<=14; ++i)
188                 if(real_speed[i].weight)
189                         st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
190         if(route)
191                 st.push_back((DataFile::Statement("route"), route->get_name()));
192
193         if(!cur_blocks.empty())
194         {
195                 list<BlockRef> blocks = cur_blocks;
196                 if(loco.get_reverse())
197                         reverse_blocks(blocks);
198
199                 Block *prev = blocks.front().block->get_endpoints()[blocks.front().entry].link;
200                 st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
201
202                 for(list<BlockRef>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
203                         st.push_back((DataFile::Statement("block"), i->block->get_id()));
204         }
205 }
206
207 void Train::locomotive_reverse_changed(bool)
208 {
209         for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
210                 i->block->reserve(0);
211         rsv_blocks.clear();
212         reverse_blocks(cur_blocks);
213         reserve_more();
214
215         if(cur_track)
216         {
217                 unsigned path = 0;
218                 if(unsigned turnout = cur_track->get_turnout_id())
219                         path = trfc_mgr.get_control().get_turnout(turnout).get_path();
220                 cur_track_ep = cur_track->traverse(cur_track_ep, path);
221                 offset = cur_track->get_type().get_path_length(path)-offset;
222         }
223 }
224
225 void Train::sensor_event(bool state, Sensor *sensor)
226 {
227         unsigned addr = sensor->get_address();
228
229         if(state)
230         {
231                 // Find the first sensor block from our reserved blocks that isn't this sensor
232                 list<BlockRef>::iterator i;
233                 for(i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
234                         if(i->block->get_sensor_id() && i->block->get_sensor_id()!=addr)
235                                 break;
236
237                 if(i!=rsv_blocks.begin())
238                 {
239                         // Compute speed and update related state
240                         float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
241                         travel_speed = static_cast<int>(round(travel_dist/travel_time_secs*87*3.6/5))*5;
242
243                         if(pure_speed)
244                         {
245                                 RealSpeed &rs = real_speed[loco.get_speed()];
246                                 rs.add(travel_dist/travel_time_secs, travel_time_secs);
247                         }
248
249                         travel_dist = 0;
250                         float block_len;
251                         for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
252                         {
253                                 j->block->traverse(j->entry, &block_len);
254                                 travel_dist += block_len;
255
256                                 if(j->block->get_sensor_id()==addr)
257                                         set_position(j->block->get_endpoints()[j->entry]);
258                         }
259                         last_entry_time = Time::now();
260                         pure_speed = true;
261
262                         // Move blocks up to the next sensor to our current blocks
263                         cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
264
265                         // Try to get more blocks if we're moving
266                         if(target_speed && reserve_more()<2)
267                                 update_speed();
268                 }
269         }
270         else
271         {
272                 // Find the first sensor in our current blocks that's still active
273                 list<BlockRef>::iterator end = cur_blocks.begin();
274                 for(list<BlockRef>::iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
275                         if(i->block->get_sensor_id())
276                         {
277                                 if(trfc_mgr.get_control().get_sensor(i->block->get_sensor_id()).get_state())
278                                         break;
279                                 else
280                                         end = i;
281                         }
282                 
283                 if(end!=cur_blocks.begin())
284                 {
285                         // Free blocks up to the last inactive sensor
286                         ++end;
287                         for(list<BlockRef>::iterator i=cur_blocks.begin(); i!=end; ++i)
288                                 i->block->reserve(0);
289                         cur_blocks.erase(cur_blocks.begin(), end);
290                 }
291
292                 // XXX Should watch for trfc_mgr.signal_block_reserved rather than sensors
293                 if(target_speed && pending_block && addr==pending_block->get_sensor_id())
294                         reserve_more();
295         }
296 }
297
298 void Train::turnout_path_changing(unsigned, Turnout *turnout)
299 {
300         unsigned tid = turnout->get_address();
301         for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
302                 if(i->block->get_turnout_id()==tid)
303                         throw TurnoutBusy(this);
304         
305         unsigned nsens = 0;
306         for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
307         {
308                 if(i->block->get_turnout_id()==tid)
309                 {
310                         if(nsens<1)
311                                 throw TurnoutBusy(this);
312                         break;
313                 }
314                 else if(i->block->get_sensor_id())
315                         ++nsens;
316         }
317 }
318
319 void Train::turnout_path_changed(unsigned, Turnout *turnout)
320 {
321         unsigned tid = turnout->get_address();
322         for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
323                 if(i->block->get_turnout_id()==tid)
324                 {
325                         while(i!=rsv_blocks.end())
326                         {
327                                 i->block->reserve(0);
328                                 i = rsv_blocks.erase(i);
329                         }
330                         reserve_more();
331                         return;
332                 }
333
334         if(pending_block && tid==pending_block->get_turnout_id())
335                 reserve_more();
336 }
337
338 unsigned Train::reserve_more()
339 {
340         BlockRef *last = 0;
341         if(!rsv_blocks.empty())
342                 last = &rsv_blocks.back();
343         else if(!cur_blocks.empty())
344                 last = &cur_blocks.back();
345         if(!last)
346                 return 0;
347
348         pending_block = 0;
349
350         // See how many blocks we already have
351         unsigned nsens = 0;
352         for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
353                 if(i->block->get_sensor_id())
354                         ++nsens;
355
356         bool got_more = false;
357         BlockRef *good = last;
358         unsigned good_sens = nsens;
359         while(good_sens<3)
360         {
361                 // Traverse to the next block
362                 unsigned exit = last->block->traverse(last->entry);
363                 Block *link = last->block->get_link(exit);
364                 if(!link)
365                         break;
366
367                 int entry = link->get_endpoint_by_link(*last->block);
368                 if(!link->reserve(this))
369                 {
370                         // If we found another train going in the same direction as us, we can keep the blocks we got
371                         int other_entry = link->get_train()->get_entry_to_block(*link);
372                         if(other_entry==entry || link->traverse(entry)==link->traverse(other_entry))
373                         {
374                                 good = last;
375                                 good_sens = nsens;
376                         }
377                         pending_block = link;
378                         break;
379                 }
380
381                 if(link->get_turnout_id())
382                 {
383                         const Block::Endpoint &ep = link->get_endpoints()[entry];
384                         const Endpoint &track_ep = ep.track->get_type().get_endpoints()[ep.track_ep];
385
386                         if(track_ep.paths&(track_ep.paths-1))
387                         {
388                                 // We're facing the points - keep the blocks reserved so far
389                                 good = last;
390                                 good_sens = nsens;
391                         }
392
393                         Turnout &turnout = trfc_mgr.get_control().get_turnout(link->get_turnout_id());
394
395                         // Figure out what path we'd like to take on the turnout
396                         int path = -1;
397                         if(route)
398                                 path = route->get_turnout(link->get_turnout_id());
399                         if(path<0)
400                                 path = turnout.get_path();
401                         if(!((track_ep.paths>>path)&1))
402                         {
403                                 for(unsigned i=0; track_ep.paths>>i; ++i)
404                                         if((track_ep.paths>>i)&1)
405                                                 path = i;
406                         }
407
408                         if(path!=turnout.get_path())
409                         {
410                                 // The turnout is set to wrong path - switch and wait for it
411                                 link->reserve(0);
412                                 pending_block = link;
413                                 turnout.set_path(path);
414                                 break;
415                         }
416                 }
417
418                 rsv_blocks.push_back(BlockRef(link, entry));
419                 last = &rsv_blocks.back();
420                 if(last->block->get_sensor_id())
421                 {
422                         ++nsens;
423                         got_more = true;
424                 }
425         }
426
427         // Unreserve blocks that were not good
428         while(!rsv_blocks.empty() && last!=good)
429         {
430                 last->block->reserve(0);
431                 rsv_blocks.erase(--rsv_blocks.end());
432                 if(!rsv_blocks.empty())
433                         last = &rsv_blocks.back();
434         }
435
436         if(got_more)
437                 update_speed();
438
439         return nsens;
440 }
441
442 void Train::update_speed()
443 {
444         if(!target_speed)
445         {
446                 loco.set_speed(0);
447                 try_reserve = Time::TimeStamp();
448                 set_status("Stopped");
449         }
450         else
451         {
452                 unsigned nsens = 0;
453                 for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
454                         if(i->block->get_sensor_id())
455                                 ++nsens;
456
457                 unsigned slow_speed = find_speed(0.1);  // 31.3 km/h
458                 if(nsens==0)
459                 {
460                         loco.set_speed(0);
461                         pure_speed = false;
462                         try_reserve = Time::now()+2*Time::sec;
463                         set_status("Blocked");
464                 }
465                 else if(nsens==1 && target_speed>slow_speed)
466                 {
467                         loco.set_speed(slow_speed);
468                         pure_speed = false;
469                         try_reserve = Time::now()+2*Time::sec;
470                         set_status("Slow");
471                 }
472                 else
473                 {
474                         loco.set_speed(target_speed);
475                         try_reserve = Time::TimeStamp();
476                         set_status(format("Traveling %d kmh", travel_speed));
477                 }
478         }
479 }
480
481 float Train::get_real_speed(unsigned i) const
482 {
483         if(real_speed[i].weight)
484                 return real_speed[i].speed;
485
486         unsigned low;
487         unsigned high;
488         for(low=i; low>0; --low)
489                 if(real_speed[low].weight)
490                         break;
491         for(high=i; high<14; ++high)
492                 if(real_speed[high].weight)
493                         break;
494
495         if(real_speed[high].weight)
496         {
497                 if(real_speed[low].weight)
498                 {
499                         float f = float(i-low)/(high-low);
500                         return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
501                 }
502                 else
503                         return real_speed[high].speed*float(i)/high;
504         }
505         else if(real_speed[low].weight)
506                 return real_speed[low].speed*float(i)/low;
507         else
508                 return 0;
509 }
510
511 unsigned Train::find_speed(float real) const
512 {
513         if(real<=real_speed[0].speed)
514                 return 0;
515
516         unsigned low = 0;
517         unsigned high = 0;
518         for(unsigned i=0; (!high && i<=14); ++i)
519                 if(real_speed[i].weight)
520                 {
521                         if(real_speed[i].speed<real)
522                                 low = i;
523                         else
524                                 high = i;
525                 }
526         if(!high)
527         {
528                 if(!low)
529                         return 0;
530                 return min(static_cast<unsigned>(low*real/real_speed[low].speed), 14U);
531         }
532
533         float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
534         return static_cast<unsigned>(low*(1-f)+high*f+0.5);
535 }
536
537 void Train::set_status(const string &s)
538 {
539         status = s;
540         signal_status_changed.emit(s);
541 }
542
543 void Train::set_position(const Block::Endpoint &bep)
544 {
545         cur_track = bep.track;
546         cur_track_ep = bep.track_ep;
547         offset = 0;
548         pos = cur_track->get_endpoint_position(cur_track_ep);
549 }
550
551 void Train::release_blocks(list<BlockRef> &blocks)
552 {
553         for(list<BlockRef>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
554                 i->block->reserve(0);
555         blocks.clear();
556 }
557
558 void Train::reverse_blocks(list<BlockRef> &blocks) const
559 {
560         blocks.reverse();
561         for(list<BlockRef>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
562                 i->entry = i->block->traverse(i->entry);
563 }
564
565
566 Train::RealSpeed::RealSpeed():
567         speed(0),
568         weight(0)
569 { }
570
571 void Train::RealSpeed::add(float s, float w)
572 {
573         speed = (speed*weight+s*w)/(weight+w);
574         weight = min(weight+w, 300.0f);
575 }
576
577
578 Train::Loader::Loader(Train &t):
579         DataFile::BasicLoader<Train>(t),
580         prev_block(0)
581 {
582         add("block",       &Loader::block);
583         add("block_hint",  &Loader::block_hint);
584         add("name",        &Train::name);
585         add("real_speed",  &Loader::real_speed);
586         add("route",       &Loader::route);
587 }
588
589 void Train::Loader::block(unsigned id)
590 {
591         Block &blk = obj.trfc_mgr.get_block(id);
592         int entry = -1;
593         if(prev_block)
594                 entry = blk.get_endpoint_by_link(*prev_block);
595         if(entry<0)
596                 entry = 0;
597
598         blk.reserve(&obj);
599         obj.cur_blocks.push_back(BlockRef(&blk, entry));
600         obj.status = "Stopped";
601         obj.set_position(blk.get_endpoints()[entry]);
602
603         prev_block = &blk;
604 }
605
606 void Train::Loader::block_hint(unsigned id)
607 {
608         prev_block = &obj.trfc_mgr.get_block(id);
609 }
610
611 void Train::Loader::real_speed(unsigned i, float speed, float weight)
612 {
613         obj.real_speed[i].speed = speed;
614         obj.real_speed[i].weight = weight;
615 }
616
617 void Train::Loader::route(const string &n)
618 {
619         obj.route = &obj.trfc_mgr.get_layout().get_route(n);
620 }
621
622 } // namespace Marklin