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