]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/centralstation.cpp
Add driver for Märklin Central Station
[r2c2.git] / source / libr2c2 / centralstation.cpp
1 /* $Id$
2
3 This file is part of R²C²
4 Copyright © 2010  Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <algorithm>
9 #include <msp/core/refptr.h>
10 #include <msp/io/print.h>
11 #include <msp/net/resolve.h>
12 #include <msp/strings/utils.h>
13 #include <msp/time/units.h>
14 #include <msp/time/utils.h>
15 #include "centralstation.h"
16 #include "tracktype.h"
17 #include "vehicletype.h"
18
19 using namespace std;
20 using namespace Msp;
21
22 namespace R2C2 {
23
24 CentralStation::CentralStation(const string &host):
25         socket(Net::INET),
26         pending_commands(0),
27         power(false),
28         halted(false),
29         locos_synced(false),
30         turnouts_synced(false),
31         sensors_synced(false)
32 {
33         RefPtr<Net::SockAddr> addr = Net::resolve(host+":15471");
34         socket.connect(*addr);
35
36         IO::print("Connected to central station at %s\n", addr->str());
37
38         command("get(1, status)");
39         command("request(1, view)");
40         command("queryObjects(10, addr, name)");
41         command("queryObjects(11, addr)");
42         command("queryObjects(26)");
43 }
44
45 CentralStation::~CentralStation()
46 {
47         command("release(1, view)", true);
48         for(LocoMap::iterator i=locos.begin(); (i!=locos.end() && !(i->first&0x10000)); ++i)
49                 command(format("release(%d, view, control)", i->first));
50         for(TurnoutMap::iterator i=turnouts.begin(); (i!=turnouts.end() && !(i->first&0x10000)); ++i)
51                 command(format("release(%d, view, control)", i->first));
52         while(IO::poll(socket, IO::P_INPUT, 100*Time::msec))
53                 while(receive()) ;
54 }
55
56 void CentralStation::set_power(bool p)
57 {
58         power = p;
59         command(format("set(1, %s)", (power ? "go" : "stop")));
60 }
61
62 void CentralStation::halt(bool h)
63 {
64         halted = h;
65         if(halted)
66         {
67                 for(LocoMap::iterator i=locos.begin(); i!=locos.end(); ++i)
68                         if(i->second.speed)
69                                 set_loco_speed(i->first, 0);
70         }
71
72         signal_halt.emit(halted);
73 }
74
75 const char *CentralStation::enumerate_protocols(unsigned index) const
76 {
77         if(index==MM)
78                 return "MM";
79         else if(index==MM_27)
80                 return "MM-27";
81         else if(index==MFX)
82                 return "MFX";
83         else
84                 return 0;
85 }
86
87 unsigned CentralStation::get_protocol_speed_steps(const string &name) const
88 {
89         switch(map_protocol(name))
90         {
91         case MM: return 14;
92         case MM_27: return 27;
93         case MFX: return 127;
94         default: return 0;
95         }
96 }
97
98 void CentralStation::add_loco(unsigned addr, const string &proto_name, const VehicleType &type)
99 {
100         Protocol proto = map_protocol(proto_name);
101
102         unsigned id = map_address(locos, loco_addr, addr);
103         if(!id)
104         {
105                 Locomotive &loco = locos[addr|0x10000];
106                 loco.name = type.get_name();
107                 loco.protocol = proto;
108                 loco.address = addr;
109
110                 if(locos_synced && proto!=MFX)
111                         command("create(10)");
112         }
113         else
114                 command(format("request(%d, view, control, force)", id));
115 }
116
117 void CentralStation::set_loco_speed(unsigned addr, unsigned speed)
118 {
119         if(speed && halted)
120                 return;
121
122         unsigned id = map_address(locos, loco_addr, addr);
123         if(id)
124                 command(format("set(%d, speedstep[%d])", id, speed));
125 }
126
127 void CentralStation::set_loco_reverse(unsigned addr, bool rev)
128 {
129         unsigned id = map_address(locos, loco_addr, addr);
130         if(id)
131                 command(format("set(%d, dir[%d])", id, rev));
132 }
133
134 void CentralStation::set_loco_function(unsigned addr, unsigned func, bool state)
135 {
136         unsigned id = map_address(locos, loco_addr, addr);
137         if(id)
138                 command(format("set(%d, func[%d, %d])", id, func, state));
139 }
140
141 void CentralStation::add_turnout(unsigned addr, const TrackType &type)
142 {
143         bool left = false;
144         bool right = false;
145         bool cross = false;
146
147         const vector<TrackPart> &parts = type.get_parts();
148         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
149         {
150                 TrackPoint start = i->get_point(0);
151                 TrackPoint end = i->get_point(i->get_length());
152                 if(end.dir>start.dir+0.01)
153                         left = true;
154                 else if(end.dir<start.dir-0.01)
155                         right = true;
156                 else if(start.dir<-0.01 || start.dir>0.01)
157                         cross = true;
158         }
159
160         unsigned symbol;
161         if(cross)
162                 symbol = Turnout::DOUBLESLIP;
163         else if(left && right)
164                 symbol = Turnout::THREEWAY;
165         else if(left)
166                 symbol = Turnout::LEFT;
167         else if(right)
168                 symbol = Turnout::RIGHT;
169
170         unsigned id = map_address(turnouts, turnout_addr, addr);
171         if(!id)
172         {
173                 id = addr|0x10000;
174
175                 Turnout &turnout = turnouts[id];
176                 turnout.address = addr;
177                 turnout.bits = type.get_state_bits();
178                 turnout.symbol = symbol;
179
180                 turnout_addr[addr] = id;
181
182                 if(turnouts_synced)
183                         command("create(11, append)");
184         }
185         else
186         {
187                 Turnout &turnout = turnouts[id];
188                 command(format("request(%d, view, control)", id));
189                 if(turnout.symbol!=symbol)
190                         command(format("set(%d, symbol[%d])", symbol));
191         }
192 }
193
194 void CentralStation::set_turnout(unsigned addr, unsigned state)
195 {
196         unsigned id = map_address(turnouts, turnout_addr, addr);
197         if(id)
198         {
199                 Turnout &turnout = turnouts[id];
200                 unsigned mask = (1<<turnout.bits)-1;
201
202                 if(((state^turnout.state)&mask)==0 || !turnout.synced)
203                 {
204                         turnout.state = state;
205                         signal_turnout.emit(addr, turnout.state);
206                         return;
207                 }
208
209                 turnout.state = (turnout.state&mask) | (state&~mask);
210
211                 command(format("set(%d, state[%d])", id, state&mask));
212         }
213 }
214
215 unsigned CentralStation::get_turnout(unsigned addr) const
216 {
217         unsigned id = map_address(turnouts, turnout_addr, addr);
218         if(id)
219         {
220                 TurnoutMap::const_iterator i = turnouts.find(id);
221                 if(i!=turnouts.end())
222                         return i->second.state;
223         }
224         return 0;
225 }
226
227 void CentralStation::add_sensor(unsigned addr)
228 {
229         sensors.insert(SensorMap::value_type(addr, Sensor()));
230
231         if(sensors_synced)
232         {
233                 if(addr>s88.size()*16)
234                         command("create(26, add[0])");
235         }
236 }
237
238 bool CentralStation::get_sensor(unsigned addr) const
239 {
240         SensorMap::const_iterator i = sensors.find(addr);
241         if(i!=sensors.end())
242                 return i->second.state;
243         return false;
244 }
245
246 void CentralStation::tick()
247 {
248         Time::TimeStamp t = Time::now();
249         for(SensorMap::iterator i=sensors.begin(); i!=sensors.end(); ++i)
250                 if(i->second.off_timeout && t>i->second.off_timeout)
251                 {
252                         i->second.state = false;
253                         i->second.off_timeout = Time::TimeStamp();
254                         signal_sensor.emit(i->first, i->second.state);
255                 }
256
257         while(Message msg = receive())
258         {
259                 if(msg.footer.code)
260                         IO::print("\033[31m*** ERROR: %s: %d %s ***\033[0m\n", msg.header.value, msg.footer.code, msg.footer.value);
261
262                 if(msg.header.type=="REPLY")
263                         process_reply(msg);
264                 else if(msg.header.type=="EVENT")
265                         process_event(msg);
266         }
267 }
268
269 void CentralStation::flush()
270 {
271 }
272
273 void CentralStation::command(const string &cmd, bool force)
274 {
275         if(pending_commands<10 || force)
276         {
277                 socket.write(cmd+"\r\n");
278                 ++pending_commands;
279         }
280         else
281                 cmd_queue.push_back(cmd);
282 }
283
284 CentralStation::Message CentralStation::receive()
285 {
286         while(IO::poll(socket, IO::P_INPUT, Time::zero))
287         {
288                 char rbuf[1024];
289                 unsigned len = socket.read(rbuf, sizeof(rbuf));
290                 if(!len)
291                         return Message();
292
293                 in_buffer.append(rbuf, len);
294         }
295
296         if(!in_buffer.empty())
297         {
298                 string::iterator iter = in_buffer.begin();
299                 if(Message msg = parse_message(iter, in_buffer.end()))
300                 {
301                         skip(iter, in_buffer.end(), "\r\n");
302                         in_buffer.erase(in_buffer.begin(), iter);
303
304                         if(msg.header.type=="REPLY" && pending_commands>0)
305                         {
306                                 --pending_commands;
307                                 if(!cmd_queue.empty())
308                                 {
309                                         command(cmd_queue.front());
310                                         cmd_queue.pop_front();
311                                 }
312                         }
313
314                         return msg;
315                 }
316         }
317
318         return Message();
319 }
320
321 void CentralStation::process_reply(const Message &msg)
322 {
323         if(!msg.header.value.compare(0, 4, "get("))
324         {
325                 for(Message::ObjectMap::const_iterator i=msg.content.begin(); i!=msg.content.end(); ++i)
326                 {
327                         if(turnouts.count(i->first))
328                                 turnouts[i->first].synced = true;
329
330                         process_object(i->first, i->second);
331                 }
332         }
333         else if(!msg.header.value.compare(0, 16, "queryObjects(10,"))
334         {
335                 for(Message::ObjectMap::const_iterator i=msg.content.begin(); i!=msg.content.end(); ++i)
336                 {
337                         LocoMap::iterator j = locos.find(i->first);
338                         if(j==locos.end())
339                         {
340                                 bool found = false;
341                                 Message::AttribMap::const_iterator k = i->second.find("addr");
342                                 if(k!=i->second.end())
343                                 {
344                                         unsigned addr = lexical_cast<unsigned>(k->second);
345
346                                         j = locos.find(addr|0x10000);
347                                         if(j!=locos.end())
348                                         {
349                                                 command(format("request(%d, view, control, force)", i->first));
350                                                 command(format("get(%d, dir, func[0])", i->first));
351
352                                                 locos.insert(LocoMap::value_type(i->first, j->second));
353                                                 locos.erase(j);
354
355                                                 found = true;
356                                         }
357                                 }
358
359                                 if(!found)
360                                         locos.insert(LocoMap::value_type(i->first, Locomotive()));
361                         }
362
363                         process_object(i->first, i->second);
364                 }
365
366                 locos_synced = true;
367
368                 if(locos.lower_bound(0x10000)!=locos.end())
369                         command("create(10)");
370         }
371         else if(!msg.header.value.compare(0, 16, "queryObjects(11,"))
372         {
373                 for(Message::ObjectMap::const_iterator i=msg.content.begin(); i!=msg.content.end(); ++i)
374                 {
375                         TurnoutMap::iterator j = turnouts.find(i->first);
376                         if(j==turnouts.end())
377                         {
378                                 bool found = false;
379                                 Message::AttribMap::const_iterator k = i->second.find("addr");
380                                 if(k!=i->second.end())
381                                 {
382                                         unsigned addr = lexical_cast<unsigned>(k->second);
383
384                                         j = turnouts.find(addr|0x10000);
385                                         if(j!=turnouts.end())
386                                         {
387                                                 command(format("request(%d, view, control)", i->first));
388                                                 command(format("set(%d, symbol[%d])", i->first, j->second.symbol));
389                                                 command(format("get(%d, state)", i->first));
390
391                                                 turnouts.insert(TurnoutMap::value_type(i->first, j->second));
392                                                 turnouts.erase(j);
393
394                                                 found = true;
395                                         }
396                                 }
397
398                                 if(!found)
399                                         turnouts.insert(TurnoutMap::value_type(i->first, Turnout()));
400                         }
401
402                         process_object(i->first, i->second);
403                 }
404
405                 turnouts_synced = true;
406
407                 for(TurnoutMap::const_iterator i=turnouts.lower_bound(0x10000); i!=turnouts.end(); ++i)
408                         command("create(11, append)");
409         }
410         else if(msg.header.value=="queryObjects(26)")
411         {
412                 s88.clear();
413                 for(Message::ObjectMap::const_iterator i=msg.content.begin(); i!=msg.content.end(); ++i)
414                 {
415                         s88.push_back(i->first);
416                         command(format("request(%d, view)", i->first));
417                 }
418
419                 sensors_synced = true;
420
421                 if(!sensors.empty())
422                 {
423                         unsigned high_addr = (--sensors.end())->first;
424                         if(high_addr>16*s88.size())
425                                 command("create(26, add[0])");
426                 }
427         }
428         else if(msg.header.value=="create(10)")
429         {
430                 Message::ObjectMap::const_iterator i = msg.content.find(10);
431                 if(i!=msg.content.end())
432                 {
433                         Message::AttribMap::const_iterator j = i->second.find("id");
434                         if(j!=i->second.end())
435                         {
436                                 unsigned id = lexical_cast<unsigned>(j->second);
437                                 LocoMap::iterator k = locos.lower_bound(0x10000);
438                                 if(k!=locos.end())
439                                 {
440                                         command(format("request(%d, view, control)", id));
441                                         command(format("set(%d, addr[%d], protocol[%s], name[\"%s\"])",
442                                                 id, k->second.address, (k->second.protocol==MM_27 ? "MM27" : "MM14"), k->second.name));
443                                         command("create(10, append)");
444
445                                         locos.insert(LocoMap::value_type(id, k->second));
446                                         locos.erase(k);
447                                 }
448                         }
449                 }
450
451                 if(locos.lower_bound(0x10000)!=locos.end())
452                         command("create(10)");
453         }
454         else if(!msg.header.value.compare(0, 10, "create(11,"))
455         {
456                 Message::ObjectMap::const_iterator i = msg.content.find(11);
457                 if(i!=msg.content.end())
458                 {
459                         Message::AttribMap::const_iterator j = i->second.find("id");
460                         if(j!=i->second.end())
461                         {
462                                 unsigned id = lexical_cast<unsigned>(j->second);
463                                 TurnoutMap::iterator k = turnouts.lower_bound(0x10000);
464                                 if(k!=turnouts.end())
465                                 {
466                                         command(format("request(%d, view, control)", id));
467                                         command(format("set(%d, addr[%d], symbol[%d], name1[\"Switch\"], name2[\"%d\"], name3[\"\"])",
468                                                 id, k->second.address, k->second.symbol, k->second.address));
469                                         command(format("set(%d, state[%d])", id, k->second.state&((1<<k->second.bits)-1)));
470
471                                         k->second.synced = true;
472                                         turnouts.insert(TurnoutMap::value_type(id, k->second));
473                                         turnouts.erase(k);
474                                 }
475                         }
476                 }
477         }
478         else if(!msg.header.value.compare(0, 10, "create(26,"))
479                 command("queryObjects(26)");
480 }
481
482 void CentralStation::process_event(const Message &msg)
483 {
484         for(Message::ObjectMap::const_iterator i=msg.content.begin(); i!=msg.content.end(); ++i)
485                 process_object(i->first, i->second);
486 }
487
488 void CentralStation::process_object(unsigned id, const Message::AttribMap &attribs)
489 {
490         if(id==1)
491         {
492                 for(Message::AttribMap::const_iterator i=attribs.begin(); i!=attribs.end(); ++i)
493                         if(i->first=="status")
494                         {
495                                 power = (i->second=="GO");
496                                 signal_power.emit(power);
497                         }
498         }
499         else if(locos.count(id))
500         {
501                 Locomotive &loco = locos[id];
502                 bool speed_changed = false;
503                 unsigned funcs_changed = 0;
504                 for(Message::AttribMap::const_iterator i=attribs.begin(); i!=attribs.end(); ++i)
505                 {
506                         if(i->first=="name")
507                                 loco.name = i->second.substr(1, i->second.size()-2);
508                         else if(i->first=="addr")
509                         {
510                                 loco_addr.erase(loco.address);
511                                 loco.address = lexical_cast<unsigned>(i->second);
512                                 loco_addr[loco.address] = id;
513                         }
514                         else if(i->first=="protocol")
515                         {
516                                 if(i->second=="MM")
517                                         loco.protocol = MM;
518                                 else if(i->second=="MM27")
519                                         loco.protocol = MM_27;
520                                 else if(i->second=="MFX")
521                                         loco.protocol = MFX;
522                         }
523                         else if(i->first=="speedstep")
524                         {
525                                 loco.speed = lexical_cast<unsigned>(i->second);
526                                 speed_changed = true;
527                         }
528                         else if(i->first=="dir")
529                         {
530                                 loco.reverse = i->second[0]!='0';
531                                 speed_changed = true;
532                         }
533                         else if(i->first=="func")
534                         {
535                                 vector<string> parts = split(i->second, ", ");
536                                 unsigned func = lexical_cast<unsigned>(parts[0]);
537                                 bool value = lexical_cast<unsigned>(parts[1]);
538                                 loco.funcs &= ~(1<<func);
539                                 if(value)
540                                         loco.funcs |= 1<<func;
541                                 funcs_changed |= 1<<func;
542                         }
543                         else if(i->first=="msg")
544                         {
545                                 if(i->second=="CONTROL_LOST")
546                                         command(format("request(%d, control, force)", id));
547                         }
548                 }
549
550                 if(speed_changed)
551                         signal_loco_speed.emit(loco.address, loco.speed, loco.reverse);
552                 for(unsigned i=0; funcs_changed>>i; ++i)
553                         if(funcs_changed&(1<<i))
554                                 signal_loco_function.emit(loco.address, i, loco.funcs&(1<<i));
555         }
556         else if(turnouts.count(id))
557         {
558                 Turnout &turnout = turnouts[id];
559                 bool state_changed = false;
560                 for(Message::AttribMap::const_iterator i=attribs.begin(); i!=attribs.end(); ++i)
561                 {
562                         if(i->first=="addr")
563                         {
564                                 turnout_addr.erase(turnout.address);
565                                 turnout.address = lexical_cast<unsigned>(i->second);
566                                 turnout_addr[turnout.address] = id;
567                         }
568                         else if(i->first=="state")
569                         {
570                                 unsigned state = lexical_cast<unsigned>(i->second);
571                                 unsigned mask = (1<<turnout.bits)-1;
572                                 turnout.state = (turnout.state&~mask) | (state&mask);
573                                 state_changed = true;
574                         }
575                 }
576
577                 if(state_changed)
578                         signal_turnout.emit(turnout.address, turnout.state);
579         }
580         else if(find(s88.begin(), s88.end(), id)!=s88.end())
581         {
582                 unsigned base = 0;
583                 for(; (base<s88.size() && s88[base]!=id); ++base) ;
584
585                 for(Message::AttribMap::const_iterator i=attribs.begin(); i!=attribs.end(); ++i)
586                 {
587                         if(i->first=="state")
588                         {
589                                 unsigned state = lexical_cast<unsigned>(i->second, "%i");
590                                 for(unsigned j=0; j<16; ++j)
591                                 {
592                                         unsigned addr = base*16+j+1;
593                                         Sensor &sensor = sensors[addr];
594                                         bool s = state&(1<<j);
595                                         if(s)
596                                         {
597                                                 sensor.off_timeout = Time::TimeStamp();
598                                                 if(!sensor.state)
599                                                 {
600                                                         sensor.state = true;
601                                                         signal_sensor.emit(addr, sensor.state);
602                                                 }
603                                         }
604                                         else if(sensor.state)
605                                                 sensor.off_timeout = Time::now()+700*Time::msec;
606                                 }
607                         }
608                 }
609         }
610 }
611
612 CentralStation::Protocol CentralStation::map_protocol(const string &name) const
613 {
614         if(name=="MM")
615                 return MM;
616         else if(name=="MM-27")
617                 return MM_27;
618         else if(name=="MFX")
619                 return MFX;
620         else
621                 throw InvalidParameterValue("Unknown protocol");
622 }
623
624 template<typename T>
625 unsigned CentralStation::map_address(const map<unsigned, T> &omap, const AddressMap &amap, unsigned addr) const
626 {
627         if(omap.count(addr))
628                 return addr;
629         else
630         {
631                 AddressMap::const_iterator i = amap.find(addr);
632                 if(i!=amap.end())
633                         return i->second;
634                 else
635                         return 0;
636         }
637 }
638
639 void CentralStation::skip(string::iterator &iter, const string::iterator &end, const string &what) const
640 {
641         for(; (iter!=end && what.find(*iter)!=string::npos); ++iter) ;
642 }
643
644 string CentralStation::parse_token(string::iterator &iter, const string::iterator &end, const string &stop) const
645 {
646         vector<char> parens;
647         bool quote = false;
648         string token;
649
650         skip(iter, end, stop);
651
652         for(; iter!=end; ++iter)
653         {
654                 if(stop.find(*iter)!=string::npos && parens.empty() && !quote)
655                         break;
656                 else if(*iter=='(' || *iter=='[')
657                         parens.push_back(*iter);
658                 else if((*iter==')' || *iter==']') && !parens.empty())
659                 {
660                         if((*iter==')' && parens.back()!='(') || (*iter==']' && parens.back()!='['))
661                                 IO::print("Mismatched parentheses\n");
662                         parens.pop_back();
663                 }
664                 else if(*iter=='"')
665                         quote = !quote;
666
667                 token += *iter;
668         }
669
670         return token;
671 }
672
673 CentralStation::Tag CentralStation::parse_tag(string::iterator &iter, const string::iterator &end) const
674 {
675         Tag tag;
676
677         for(; (iter!=end && *iter!='<'); ++iter) ;
678         if(iter==end)
679                 return Tag();
680
681         tag.type = parse_token(++iter, end, " >");
682         if(tag.type=="END")
683         {
684                 string code = parse_token(iter, end, " >");
685                 tag.code = lexical_cast<unsigned>(code);
686         }
687         skip(iter, end, " ");
688         tag.value = parse_token(iter, end, ">");
689         if(iter==end)
690                 return Tag();
691         ++iter;
692
693         return tag;
694 }
695
696 CentralStation::Message CentralStation::parse_message(string::iterator &iter, const string::iterator &end) const
697 {
698         Message msg;
699
700         msg.header = parse_tag(iter, end);
701
702         while(iter!=end)
703         {
704                 skip(iter, end, "\r\n");
705                 if(*iter=='<')
706                         break;
707
708                 string id = parse_token(iter, end, " \r\n<");
709                 Message::AttribMap &attribs = msg.content[lexical_cast<unsigned>(id)];
710                 while(iter!=end && *iter!='\n' && *iter!='\r')
711                 {
712                         string attr = parse_token(iter, end, " \r\n<");
713                         string::size_type open_bracket = attr.find('[');
714                         if(open_bracket!=string::npos)
715                         {
716                                 string::size_type close_bracket = attr.rfind(']');
717                                 attribs[attr.substr(0, open_bracket)] = attr.substr(open_bracket+1, close_bracket-open_bracket-1);
718                         }
719                         else
720                                 attribs[attr];
721                 }
722         }
723
724         msg.footer = parse_tag(iter, end);
725         if(msg.footer.type.empty())
726                 return Message();
727
728         return msg;
729 }
730
731
732 CentralStation::Tag::Tag():
733         code(0)
734 { }
735
736 CentralStation::Tag::operator bool() const
737 {
738         return !type.empty();
739 }
740
741
742 CentralStation::Message::operator bool() const
743 {
744         return header && footer;
745 }
746
747
748 CentralStation::Locomotive::Locomotive():
749         address(0),
750         speed(0),
751         reverse(false),
752         funcs(0),
753         control(false)
754 { }
755
756
757 CentralStation::Turnout::Turnout():
758         address(0),
759         symbol(0),
760         state(0),
761         bits(0),
762         synced(false)
763 { }
764
765
766 CentralStation::Sensor::Sensor():
767         state(false)
768 { }
769
770 } // namespace R2C2