]> git.tdb.fi Git - r2c2.git/blob - source/libmarklin/train.cpp
56832c837542d9447acb3022a8d655e1a8786939
[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         trfc_mgr.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
54 }
55
56 void Train::set_name(const string &n)
57 {
58         name = n;
59
60         signal_name_changed.emit(name);
61 }
62
63 void Train::set_speed(unsigned speed)
64 {
65         if(speed==target_speed)
66                 return;
67         travel_speed = static_cast<int>(round(get_real_speed(speed)*87*3.6/5))*5;
68
69         target_speed = speed;
70         if(!target_speed)
71         {
72                 pending_block = 0;
73                 trfc_mgr.get_control().set_timer(3*Time::sec).signal_timeout.connect(
74                         sigc::bind_return(sigc::bind(
75                                 sigc::mem_fun(this, static_cast<void (Train::*)(list<BlockRef> &)>(&Train::release_blocks)),
76                                 sigc::ref(rsv_blocks)), false));
77         }
78         else
79                 reserve_more();
80
81         signal_target_speed_changed.emit(target_speed);
82
83         update_speed();
84         pure_speed = false;
85 }
86
87 void Train::set_reverse(bool rev)
88 {
89         loco.set_reverse(rev);
90 }
91
92 void Train::set_route(const Route *r)
93 {
94         route = r;
95         signal_route_changed.emit(route);
96 }
97
98 void Train::place(Block &block, unsigned entry)
99 {
100         if(target_speed)
101                 set_speed(0);
102
103         release_blocks(rsv_blocks);
104         release_blocks(cur_blocks);
105
106         if(!block.reserve(this))
107         {
108                 set_status("Unplaced");
109                 return;
110         }
111
112         cur_blocks.push_back(BlockRef(&block, entry));
113         set_position(block.get_endpoints()[entry]);
114
115         set_status("Stopped");
116 }
117
118 bool Train::free_block(Block &block)
119 {
120         unsigned nsens = 0;
121         for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
122         {
123                 if(i->block==&block)
124                 {
125                         if(nsens<1)
126                                 return false;
127                         release_blocks(rsv_blocks, i, rsv_blocks.end());
128                         update_speed();
129                         return true;
130                 }
131                 else if(i->block->get_sensor_id())
132                         ++nsens;
133         }
134
135         return false;
136 }
137
138 int Train::get_entry_to_block(Block &block) const
139 {
140         for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
141                 if(i->block==&block)
142                         return i->entry;
143         for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
144                 if(i->block==&block)
145                         return i->entry;
146         return -1;
147 }
148
149 void Train::tick(const Time::TimeStamp &, const Time::TimeDelta &dt)
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         release_blocks(rsv_blocks);
210         reverse_blocks(cur_blocks);
211         reserve_more();
212
213         if(cur_track)
214         {
215                 unsigned path = 0;
216                 if(unsigned turnout = cur_track->get_turnout_id())
217                         path = trfc_mgr.get_control().get_turnout(turnout).get_path();
218                 cur_track_ep = cur_track->traverse(cur_track_ep, path);
219                 offset = cur_track->get_type().get_path_length(path)-offset;
220         }
221 }
222
223 void Train::sensor_event(bool state, Sensor *sensor)
224 {
225         unsigned addr = sensor->get_address();
226
227         if(state)
228         {
229                 // Find the first sensor block from our reserved blocks that isn't this sensor
230                 list<BlockRef>::iterator i;
231                 for(i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
232                         if(i->block->get_sensor_id() && i->block->get_sensor_id()!=addr)
233                                 break;
234
235                 if(i!=rsv_blocks.begin())
236                 {
237                         // Compute speed and update related state
238                         float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
239                         travel_speed = static_cast<int>(round(travel_dist/travel_time_secs*87*3.6/5))*5;
240
241                         if(pure_speed)
242                         {
243                                 RealSpeed &rs = real_speed[loco.get_speed()];
244                                 rs.add(travel_dist/travel_time_secs, travel_time_secs);
245                         }
246
247                         travel_dist = 0;
248                         float block_len;
249                         for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
250                         {
251                                 j->block->traverse(j->entry, &block_len);
252                                 travel_dist += block_len;
253
254                                 if(j->block->get_sensor_id()==addr)
255                                         set_position(j->block->get_endpoints()[j->entry]);
256                         }
257                         last_entry_time = Time::now();
258                         pure_speed = true;
259
260                         // Move blocks up to the next sensor to our current blocks
261                         cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
262
263                         // Try to get more blocks if we're moving
264                         if(target_speed && reserve_more()<2)
265                                 update_speed();
266                 }
267         }
268         else
269         {
270                 // Find the first sensor in our current blocks that's still active
271                 list<BlockRef>::iterator end = cur_blocks.begin();
272                 for(list<BlockRef>::iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
273                         if(i->block->get_sensor_id())
274                         {
275                                 if(trfc_mgr.get_control().get_sensor(i->block->get_sensor_id()).get_state())
276                                         break;
277                                 else
278                                         end = i;
279                         }
280                 
281                 if(end!=cur_blocks.begin())
282                 {
283                         // Free blocks up to the last inactive sensor
284                         ++end;
285                         release_blocks(cur_blocks, cur_blocks.begin(), end);
286                 }
287         }
288 }
289
290 void Train::turnout_path_changing(unsigned, Turnout *turnout)
291 {
292         unsigned tid = turnout->get_address();
293         for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
294                 if(i->block->get_turnout_id()==tid)
295                         throw TurnoutBusy(this);
296         
297         unsigned nsens = 0;
298         for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
299         {
300                 if(i->block->get_turnout_id()==tid)
301                 {
302                         if(nsens<1)
303                                 throw TurnoutBusy(this);
304                         break;
305                 }
306                 else if(i->block->get_sensor_id())
307                         ++nsens;
308         }
309 }
310
311 void Train::turnout_path_changed(unsigned, Turnout *turnout)
312 {
313         unsigned tid = turnout->get_address();
314         for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
315                 if(i->block->get_turnout_id()==tid)
316                 {
317                         release_blocks(rsv_blocks, i, rsv_blocks.end());
318                         reserve_more();
319                         return;
320                 }
321
322         if(pending_block && tid==pending_block->get_turnout_id())
323                 reserve_more();
324 }
325
326 void Train::block_reserved(const Block &block, const Train *train)
327 {
328         if(&block==pending_block && !train)
329                 reserve_more();
330 }
331
332 unsigned Train::reserve_more()
333 {
334         BlockRef *last = 0;
335         if(!rsv_blocks.empty())
336                 last = &rsv_blocks.back();
337         else if(!cur_blocks.empty())
338                 last = &cur_blocks.back();
339         if(!last)
340                 return 0;
341
342         pending_block = 0;
343
344         // See how many blocks we already have
345         unsigned nsens = 0;
346         for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
347                 if(i->block->get_sensor_id())
348                         ++nsens;
349
350         bool got_more = false;
351         BlockRef *good = last;
352         unsigned good_sens = nsens;
353         while(good_sens<3)
354         {
355                 // Traverse to the next block
356                 unsigned exit = last->block->traverse(last->entry);
357                 Block *link = last->block->get_link(exit);
358                 if(!link)
359                         break;
360
361                 int entry = link->get_endpoint_by_link(*last->block);
362                 if(entry<0)
363                         throw LogicError("Block links are inconsistent!");
364                 if(!link->reserve(this))
365                 {
366                         // If we found another train and it's not headed straight for us, we can keep the blocks we got
367                         int other_entry = link->get_train()->get_entry_to_block(*link);
368                         if(other_entry<0)
369                                 throw LogicError("Block reservation inconsistency");
370                         if(static_cast<unsigned>(entry)!=link->traverse(other_entry))
371                         {
372                                 good = last;
373                                 good_sens = nsens;
374                         }
375                         pending_block = link;
376                         break;
377                 }
378
379                 if(link->get_turnout_id())
380                 {
381                         const Block::Endpoint &ep = link->get_endpoints()[entry];
382                         const Endpoint &track_ep = ep.track->get_type().get_endpoints()[ep.track_ep];
383
384                         // Keep the blocks reserved so far, as either us or the other train can diverge
385                         good = last;
386                         good_sens = nsens;
387
388                         Turnout &turnout = trfc_mgr.get_control().get_turnout(link->get_turnout_id());
389
390                         // Figure out what path we'd like to take on the turnout
391                         int path = -1;
392                         if(route)
393                                 path = route->get_turnout(link->get_turnout_id());
394                         if(path<0)
395                                 path = turnout.get_path();
396                         if(!((track_ep.paths>>path)&1))
397                         {
398                                 for(unsigned i=0; track_ep.paths>>i; ++i)
399                                         if((track_ep.paths>>i)&1)
400                                                 path = i;
401                         }
402
403                         if(path!=turnout.get_path())
404                         {
405                                 // The turnout is set to wrong path - switch and wait for it
406                                 link->reserve(0);
407                                 pending_block = link;
408                                 turnout.set_path(path);
409                                 break;
410                         }
411                 }
412
413                 rsv_blocks.push_back(BlockRef(link, entry));
414                 last = &rsv_blocks.back();
415                 if(last->block->get_sensor_id())
416                 {
417                         ++nsens;
418                         got_more = true;
419                 }
420         }
421
422         // Unreserve blocks that were not good
423         while(!rsv_blocks.empty() && last!=good)
424         {
425                 last->block->reserve(0);
426                 rsv_blocks.erase(--rsv_blocks.end());
427                 if(!rsv_blocks.empty())
428                         last = &rsv_blocks.back();
429         }
430
431         if(got_more)
432                 update_speed();
433
434         return nsens;
435 }
436
437 void Train::update_speed()
438 {
439         if(!target_speed)
440         {
441                 loco.set_speed(0);
442                 set_status("Stopped");
443         }
444         else
445         {
446                 unsigned nsens = 0;
447                 for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
448                         if(i->block->get_sensor_id())
449                                 ++nsens;
450
451                 unsigned slow_speed = find_speed(0.1);  // 31.3 km/h
452                 if(nsens==0)
453                 {
454                         loco.set_speed(0);
455                         pure_speed = false;
456                         set_status("Blocked");
457                 }
458                 else if(nsens==1 && target_speed>slow_speed)
459                 {
460                         loco.set_speed(slow_speed);
461                         pure_speed = false;
462                         set_status("Slow");
463                 }
464                 else
465                 {
466                         loco.set_speed(target_speed);
467                         set_status(format("Traveling %d kmh", travel_speed));
468                 }
469         }
470 }
471
472 float Train::get_real_speed(unsigned i) const
473 {
474         if(real_speed[i].weight)
475                 return real_speed[i].speed;
476
477         unsigned low;
478         unsigned high;
479         for(low=i; low>0; --low)
480                 if(real_speed[low].weight)
481                         break;
482         for(high=i; high<14; ++high)
483                 if(real_speed[high].weight)
484                         break;
485
486         if(real_speed[high].weight)
487         {
488                 if(real_speed[low].weight)
489                 {
490                         float f = float(i-low)/(high-low);
491                         return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
492                 }
493                 else
494                         return real_speed[high].speed*float(i)/high;
495         }
496         else if(real_speed[low].weight)
497                 return real_speed[low].speed*float(i)/low;
498         else
499                 return 0;
500 }
501
502 unsigned Train::find_speed(float real) const
503 {
504         if(real<=real_speed[0].speed)
505                 return 0;
506
507         unsigned low = 0;
508         unsigned high = 0;
509         for(unsigned i=0; (!high && i<=14); ++i)
510                 if(real_speed[i].weight)
511                 {
512                         if(real_speed[i].speed<real)
513                                 low = i;
514                         else
515                                 high = i;
516                 }
517         if(!high)
518         {
519                 if(!low)
520                         return 0;
521                 return min(static_cast<unsigned>(low*real/real_speed[low].speed), 14U);
522         }
523
524         float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
525         return static_cast<unsigned>(low*(1-f)+high*f+0.5);
526 }
527
528 void Train::set_status(const string &s)
529 {
530         status = s;
531         signal_status_changed.emit(s);
532 }
533
534 void Train::set_position(const Block::Endpoint &bep)
535 {
536         cur_track = bep.track;
537         cur_track_ep = bep.track_ep;
538         offset = 0;
539         pos = cur_track->get_endpoint_position(cur_track_ep);
540 }
541
542 void Train::release_blocks(list<BlockRef> &blocks)
543 {
544         release_blocks(blocks, blocks.begin(), blocks.end());
545 }
546
547 void Train::release_blocks(list<BlockRef> &blocks, list<BlockRef>::iterator begin, list<BlockRef>::iterator end)
548 {
549         while(begin!=end)
550         {
551                 Block *block = begin->block;
552                 blocks.erase(begin++);
553                 block->reserve(0);
554         }
555 }
556
557 void Train::reverse_blocks(list<BlockRef> &blocks) const
558 {
559         blocks.reverse();
560         for(list<BlockRef>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
561                 i->entry = i->block->traverse(i->entry);
562 }
563
564
565 Train::RealSpeed::RealSpeed():
566         speed(0),
567         weight(0)
568 { }
569
570 void Train::RealSpeed::add(float s, float w)
571 {
572         speed = (speed*weight+s*w)/(weight+w);
573         weight = min(weight+w, 300.0f);
574 }
575
576
577 Train::Loader::Loader(Train &t):
578         DataFile::BasicLoader<Train>(t),
579         prev_block(0)
580 {
581         add("block",       &Loader::block);
582         add("block_hint",  &Loader::block_hint);
583         add("name",        &Loader::name);
584         add("real_speed",  &Loader::real_speed);
585         add("route",       &Loader::route);
586 }
587
588 void Train::Loader::block(unsigned id)
589 {
590         Block &blk = obj.trfc_mgr.get_block(id);
591         int entry = -1;
592         if(prev_block)
593                 entry = blk.get_endpoint_by_link(*prev_block);
594         if(entry<0)
595                 entry = 0;
596
597         blk.reserve(&obj);
598         obj.cur_blocks.push_back(BlockRef(&blk, entry));
599         obj.set_status("Stopped");
600         obj.set_position(blk.get_endpoints()[entry]);
601
602         prev_block = &blk;
603 }
604
605 void Train::Loader::block_hint(unsigned id)
606 {
607         prev_block = &obj.trfc_mgr.get_block(id);
608 }
609
610 void Train::Loader::name(const string &n)
611 {
612         obj.set_name(n);
613 }
614
615 void Train::Loader::real_speed(unsigned i, float speed, float weight)
616 {
617         obj.real_speed[i].speed = speed;
618         obj.real_speed[i].weight = weight;
619 }
620
621 void Train::Loader::route(const string &n)
622 {
623         obj.set_route(&obj.trfc_mgr.get_layout().get_route(n));
624 }
625
626 } // namespace Marklin