-/* $Id$
-
-This file is part of R²C²
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
#include <algorithm>
#include <msp/core/refptr.h>
#include <msp/io/print.h>
namespace R2C2 {
-CentralStation::CentralStation(const string &host):
+CentralStation::CentralStation(const Options &opts):
socket(Net::INET),
pending_commands(0),
power(false),
halted(false),
locos_synced(false),
- turnouts_synced(false),
+ accessories_synced(false),
sensors_synced(false)
{
- RefPtr<Net::SockAddr> addr = Net::resolve(host+":15471");
+ RefPtr<Net::SockAddr> addr = Net::resolve(opts.get<string>(string())+":15471");
socket.connect(*addr);
IO::print("Connected to central station at %s\n", addr->str());
command("release(1, view)", true);
for(LocoMap::iterator i=locos.begin(); (i!=locos.end() && !(i->first&0x10000)); ++i)
command(format("release(%d, view, control)", i->first));
- for(TurnoutMap::iterator i=turnouts.begin(); (i!=turnouts.end() && !(i->first&0x10000)); ++i)
+ for(AccessoryMap::iterator i=accessories.begin(); (i!=accessories.end() && !(i->first&0x10000)); ++i)
command(format("release(%d, view, control)", i->first));
while(IO::poll(socket, IO::P_INPUT, 100*Time::msec))
while(receive()) ;
{
case MM: return 14;
case MM_27: return 27;
- case MFX: return 127;
+ case MFX: return 126;
default: return 0;
}
}
-void CentralStation::add_loco(unsigned addr, const string &proto_name, const VehicleType &type)
+unsigned CentralStation::add_loco(unsigned addr, const string &proto_name, const VehicleType &type)
{
Protocol proto = map_protocol(proto_name);
loco.protocol = proto;
loco.address = addr;
+ const VehicleType::FunctionMap &type_funcs = type.get_functions();
+ for(VehicleType::FunctionMap::const_iterator i=type_funcs.begin(); i!=type_funcs.end(); ++i)
+ loco.func_mask |= 1<<i->first;
+
if(locos_synced && proto!=MFX)
command("create(10)");
}
else
command(format("request(%d, view, control, force)", id));
+
+ return addr;
+}
+
+void CentralStation::remove_loco(unsigned addr)
+{
+ unsigned id = map_address(locos, loco_addr, addr);
+ if(id)
+ command(format("release(%d, view, control)", id));
}
void CentralStation::set_loco_speed(unsigned addr, unsigned speed)
unsigned id = map_address(locos, loco_addr, addr);
if(id)
+ {
+ Locomotive &loco = locos[id];
+ if(loco.protocol==MFX && speed)
+ ++speed;
command(format("set(%d, speedstep[%d])", id, speed));
+ }
}
void CentralStation::set_loco_reverse(unsigned addr, bool rev)
command(format("set(%d, func[%d, %d])", id, func, state));
}
-void CentralStation::add_turnout(unsigned addr, const TrackType &type)
+unsigned CentralStation::add_turnout(unsigned addr, const TrackType &type)
{
+ unsigned straight = type.get_paths();
bool left = false;
bool right = false;
bool cross = false;
const vector<TrackPart> &parts = type.get_parts();
for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
{
- TrackPoint start = i->get_point(0);
- TrackPoint end = i->get_point(i->get_length());
- if(end.dir>start.dir+0.01)
- left = true;
- else if(end.dir<start.dir-0.01)
- right = true;
- else if(start.dir<-0.01 || start.dir>0.01)
+ OrientedPoint start = i->get_point(0);
+ OrientedPoint end = i->get_point(i->get_length());
+ if(abs(end.rotation-start.rotation).radians()<0.01)
+ {
+ (end.rotation>start.rotation ? left : right) = true;
+ straight &= ~(1<<i->get_path());
+ }
+ else if(abs(start.rotation).radians()>0.01)
cross = true;
}
- unsigned symbol;
+ MagnetAccessory::Symbol symbol = MagnetAccessory::TURNOUT_LEFT;
if(cross)
- symbol = Turnout::DOUBLESLIP;
+ symbol = MagnetAccessory::TURNOUT_DOUBLESLIP;
else if(left && right)
- symbol = Turnout::THREEWAY;
+ symbol = MagnetAccessory::TURNOUT_THREEWAY;
else if(left)
- symbol = Turnout::LEFT;
+ symbol = (straight ? MagnetAccessory::TURNOUT_LEFT : MagnetAccessory::TURNOUT_CURVED_LEFT);
else if(right)
- symbol = Turnout::RIGHT;
+ symbol = (straight ? MagnetAccessory::TURNOUT_RIGHT : MagnetAccessory::TURNOUT_CURVED_RIGHT);
+
+ MagnetAccessory &turnout = add_accessory(addr, MagnetAccessory::TURNOUT, symbol);
+ turnout.bits = type.get_state_bits();
+
+ return addr;
+}
+
+void CentralStation::remove_turnout(unsigned addr)
+{
+ remove_accessory(addr);
+}
+
+void CentralStation::set_turnout(unsigned addr, unsigned state)
+{
+ set_accessory_state(addr, MagnetAccessory::TURNOUT, state);
+}
+
+unsigned CentralStation::get_turnout(unsigned addr) const
+{
+ return get_accessory_state(addr, MagnetAccessory::TURNOUT);
+}
+
+unsigned CentralStation::add_signal(unsigned addr, const SignalType &)
+{
+ add_accessory(addr, MagnetAccessory::SIGNAL, MagnetAccessory::SEMAPHORE_HOME);
+ return addr;
+}
+
+void CentralStation::remove_signal(unsigned addr)
+{
+ remove_accessory(addr);
+}
- unsigned id = map_address(turnouts, turnout_addr, addr);
+void CentralStation::set_signal(unsigned addr, unsigned state)
+{
+ set_accessory_state(addr, MagnetAccessory::SIGNAL, state);
+}
+
+unsigned CentralStation::get_signal(unsigned addr) const
+{
+ return get_accessory_state(addr, MagnetAccessory::SIGNAL);
+}
+
+CentralStation::MagnetAccessory &CentralStation::add_accessory(unsigned addr, MagnetAccessory::Type type, MagnetAccessory::Symbol symbol)
+{
+ unsigned id = map_address(accessories, accessory_addr, addr);
if(!id)
{
id = addr|0x10000;
- Turnout &turnout = turnouts[id];
- turnout.address = addr;
- turnout.bits = type.get_state_bits();
- turnout.symbol = symbol;
+ MagnetAccessory &accessory = accessories[id];
+ accessory.address = addr;
+ accessory.type = type;
+ accessory.symbol = symbol;
- turnout_addr[addr] = id;
+ accessory_addr[addr] = id;
- if(turnouts_synced)
+ if(accessories_synced)
command("create(11, append)");
+
+ return accessory;
}
else
{
- Turnout &turnout = turnouts[id];
+ MagnetAccessory &accessory = accessories[id];
command(format("request(%d, view, control)", id));
- if(turnout.symbol!=symbol)
+ if(accessory.symbol!=symbol)
command(format("set(%d, symbol[%d])", symbol));
+
+ return accessory;
}
}
-void CentralStation::set_turnout(unsigned addr, unsigned state)
+void CentralStation::remove_accessory(unsigned addr)
+{
+ unsigned id = map_address(accessories, accessory_addr, addr);
+ if(id)
+ command(format("release(%d, view, control)", id));
+}
+
+void CentralStation::set_accessory_state(unsigned addr, MagnetAccessory::Type type, unsigned state)
{
- unsigned id = map_address(turnouts, turnout_addr, addr);
+ unsigned id = map_address(accessories, accessory_addr, addr);
if(id)
{
- Turnout &turnout = turnouts[id];
- unsigned mask = (1<<turnout.bits)-1;
+ MagnetAccessory &accessory = accessories[id];
+ if(accessory.type!=type)
+ throw logic_error("accessory type conflict");
+
+ unsigned mask = (1<<accessory.bits)-1;
- if(((state^turnout.state)&mask)==0 || !turnout.synced)
+ if(((state^accessory.state)&mask)==0 || !accessory.synced)
{
- turnout.state = state;
- signal_turnout.emit(addr, turnout.state);
+ accessory.state = state;
+ accessory_state_changed(accessory);
return;
}
- turnout.state = (turnout.state&mask) | (state&~mask);
+ accessory.state = (accessory.state&mask) | (state&~mask);
command(format("set(%d, state[%d])", id, state&mask));
}
}
-unsigned CentralStation::get_turnout(unsigned addr) const
+unsigned CentralStation::get_accessory_state(unsigned addr, MagnetAccessory::Type type) const
{
- unsigned id = map_address(turnouts, turnout_addr, addr);
+ unsigned id = map_address(accessories, accessory_addr, addr);
if(id)
{
- TurnoutMap::const_iterator i = turnouts.find(id);
- if(i!=turnouts.end())
+ AccessoryMap::const_iterator i = accessories.find(id);
+ if(i!=accessories.end() && i->second.type==type)
return i->second.state;
}
return 0;
}
-void CentralStation::add_sensor(unsigned addr)
+void CentralStation::accessory_state_changed(const MagnetAccessory &accessory) const
+{
+ if(accessory.type==MagnetAccessory::TURNOUT)
+ signal_turnout.emit(accessory.address, accessory.state);
+ else if(accessory.type==MagnetAccessory::SIGNAL)
+ signal_signal.emit(accessory.address, accessory.state);
+}
+
+unsigned CentralStation::add_sensor(unsigned addr)
{
sensors.insert(SensorMap::value_type(addr, Sensor()));
if(addr>s88.size()*16)
command("create(26, add[0])");
}
+
+ return addr;
+}
+
+void CentralStation::remove_sensor(unsigned)
+{
}
bool CentralStation::get_sensor(unsigned addr) const
void CentralStation::tick()
{
- Time::TimeStamp t = Time::now();
- for(SensorMap::iterator i=sensors.begin(); i!=sensors.end(); ++i)
- if(i->second.off_timeout && t>i->second.off_timeout)
- {
- i->second.state = false;
- i->second.off_timeout = Time::TimeStamp();
- signal_sensor.emit(i->first, i->second.state);
- }
-
while(Message msg = receive())
{
if(msg.footer.code)
{
for(Message::ObjectMap::const_iterator i=msg.content.begin(); i!=msg.content.end(); ++i)
{
- if(turnouts.count(i->first))
- turnouts[i->first].synced = true;
+ if(accessories.count(i->first))
+ accessories[i->first].synced = true;
process_object(i->first, i->second);
}
if(j!=locos.end())
{
command(format("request(%d, view, control, force)", i->first));
- command(format("get(%d, dir, func[0])", i->first));
+ string cmd = format("get(%d, dir", i->first);
+ for(unsigned l=0; j->second.func_mask>>l; ++l)
+ if((j->second.func_mask>>l)&1)
+ cmd += format(", func[%d]", l);
+ cmd += ')';
+ command(cmd);
locos.insert(LocoMap::value_type(i->first, j->second));
locos.erase(j);
{
for(Message::ObjectMap::const_iterator i=msg.content.begin(); i!=msg.content.end(); ++i)
{
- TurnoutMap::iterator j = turnouts.find(i->first);
- if(j==turnouts.end())
+ AccessoryMap::iterator j = accessories.find(i->first);
+ if(j==accessories.end())
{
bool found = false;
Message::AttribMap::const_iterator k = i->second.find("addr");
{
unsigned addr = lexical_cast<unsigned>(k->second);
- j = turnouts.find(addr|0x10000);
- if(j!=turnouts.end())
+ j = accessories.find(addr|0x10000);
+ if(j!=accessories.end())
{
command(format("request(%d, view, control)", i->first));
command(format("set(%d, symbol[%d])", i->first, j->second.symbol));
command(format("get(%d, state)", i->first));
- turnouts.insert(TurnoutMap::value_type(i->first, j->second));
- turnouts.erase(j);
+ accessories.insert(AccessoryMap::value_type(i->first, j->second));
+ accessories.erase(j);
found = true;
}
}
if(!found)
- turnouts.insert(TurnoutMap::value_type(i->first, Turnout()));
+ accessories.insert(AccessoryMap::value_type(i->first, MagnetAccessory()));
}
process_object(i->first, i->second);
}
- turnouts_synced = true;
+ accessories_synced = true;
- for(TurnoutMap::const_iterator i=turnouts.lower_bound(0x10000); i!=turnouts.end(); ++i)
+ for(AccessoryMap::const_iterator i=accessories.lower_bound(0x10000); i!=accessories.end(); ++i)
command("create(11, append)");
}
else if(msg.header.value=="queryObjects(26)")
{
s88.push_back(i->first);
command(format("request(%d, view)", i->first));
+ command(format("get(%d, state)", i->first));
}
sensors_synced = true;
if(j!=i->second.end())
{
unsigned id = lexical_cast<unsigned>(j->second);
- TurnoutMap::iterator k = turnouts.lower_bound(0x10000);
- if(k!=turnouts.end())
+ AccessoryMap::iterator k = accessories.lower_bound(0x10000);
+ if(k!=accessories.end())
{
command(format("request(%d, view, control)", id));
- command(format("set(%d, addr[%d], symbol[%d], name1[\"Switch\"], name2[\"%d\"], name3[\"\"])",
- id, k->second.address, k->second.symbol, k->second.address));
+ const char *label = (k->second.type==MagnetAccessory::SIGNAL ? "Signal" : "Switch");
+ command(format("set(%d, addr[%d], symbol[%d], name1[\"%s\"], name2[\"%d\"], name3[\"\"])",
+ id, k->second.address, k->second.symbol, label, k->second.address));
command(format("set(%d, state[%d])", id, k->second.state&((1<<k->second.bits)-1)));
k->second.synced = true;
- turnouts.insert(TurnoutMap::value_type(id, k->second));
- turnouts.erase(k);
+ accessories.insert(AccessoryMap::value_type(id, k->second));
+ accessories.erase(k);
}
}
}
else if(i->first=="speedstep")
{
loco.speed = lexical_cast<unsigned>(i->second);
+ if(loco.protocol==MFX && loco.speed)
+ --loco.speed;
speed_changed = true;
}
else if(i->first=="dir")
if(funcs_changed&(1<<i))
signal_loco_function.emit(loco.address, i, loco.funcs&(1<<i));
}
- else if(turnouts.count(id))
+ else if(accessories.count(id))
{
- Turnout &turnout = turnouts[id];
+ MagnetAccessory &accessory = accessories[id];
bool state_changed = false;
for(Message::AttribMap::const_iterator i=attribs.begin(); i!=attribs.end(); ++i)
{
if(i->first=="addr")
{
- turnout_addr.erase(turnout.address);
- turnout.address = lexical_cast<unsigned>(i->second);
- turnout_addr[turnout.address] = id;
+ accessory_addr.erase(accessory.address);
+ accessory.address = lexical_cast<unsigned>(i->second);
+ accessory_addr[accessory.address] = id;
}
else if(i->first=="state")
{
unsigned state = lexical_cast<unsigned>(i->second);
- unsigned mask = (1<<turnout.bits)-1;
- turnout.state = (turnout.state&~mask) | (state&mask);
+ unsigned mask = (1<<accessory.bits)-1;
+ accessory.state = (accessory.state&~mask) | (state&mask);
state_changed = true;
}
}
if(state_changed)
- signal_turnout.emit(turnout.address, turnout.state);
+ accessory_state_changed(accessory);
}
else if(find(s88.begin(), s88.end(), id)!=s88.end())
{
unsigned addr = base*16+j+1;
Sensor &sensor = sensors[addr];
bool s = state&(1<<j);
- if(s)
+ if(s!=sensor.state)
{
- sensor.off_timeout = Time::TimeStamp();
- if(!sensor.state)
- {
- sensor.state = true;
- signal_sensor.emit(addr, sensor.state);
- }
+ sensor.state = s;
+ signal_sensor.emit(addr, sensor.state);
}
- else if(sensor.state)
- sensor.off_timeout = Time::now()+700*Time::msec;
}
}
}
else if(name=="MFX")
return MFX;
else
- throw InvalidParameterValue("Unknown protocol");
+ throw invalid_argument("CentralStation::map_protocol");
}
template<typename T>
if(open_bracket!=string::npos)
{
string::size_type close_bracket = attr.rfind(']');
- attribs[attr.substr(0, open_bracket)] = attr.substr(open_bracket+1, close_bracket-open_bracket-1);
+ string attr_name = attr.substr(0, open_bracket);
+ string attr_value = attr.substr(open_bracket+1, close_bracket-open_bracket-1);
+ attribs.insert(Message::AttribMap::value_type(attr_name, attr_value));
}
else
- attribs[attr];
+ attribs.insert(Message::AttribMap::value_type(attr, string()));
}
}
address(0),
speed(0),
reverse(false),
+ func_mask(0),
funcs(0),
control(false)
{ }
-CentralStation::Turnout::Turnout():
+CentralStation::MagnetAccessory::MagnetAccessory():
address(0),
- symbol(0),
+ type(TURNOUT),
+ symbol(TURNOUT_LEFT),
state(0),
- bits(0),
+ bits(1),
synced(false)
{ }