-/* $Id$
-
-This file is part of R²C²
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
#include <fcntl.h>
#include <termios.h>
#include <sys/poll.h>
namespace R2C2 {
-Intellibox::Intellibox(const string &dev):
+Intellibox::Intellibox(const Options &opts):
+ serial(opts.get<string>(string(), "ttyUSB0")),
power(false),
halted(false),
update_sensors(false),
command_sent(false)
{
- serial_fd = ::open(dev.c_str(), O_RDWR);
- if(serial_fd<0)
- throw Exception("Couldn't open serial port\n");
+ static unsigned baud[]= { 2400, 4800, 9600, 19200, 0 };
- static unsigned baud[]=
- {
- 2400, B2400,
- 4800, B4800,
- 9600, B9600,
- 19200, B19200,
- 0
- };
-
- termios attr;
- tcgetattr(serial_fd, &attr);
- cfmakeraw(&attr);
- attr.c_cflag |= CSTOPB;
+ serial.set_stop_bits(2);
bool ok = false;
bool p50 = false;
- for(unsigned i=0; baud[i]; i+=2)
+ for(unsigned i=0; baud[i]; ++i)
{
- cfsetospeed(&attr, baud[i+1]);
- tcsetattr(serial_fd, TCSADRAIN, &attr);
+ serial.set_baud_rate(baud[i]);
+ serial.put('\xC4');
- write(serial_fd, "\xC4", 1);
-
- pollfd pfd = { serial_fd, POLLIN, 0 };
- if(poll(&pfd, 1, 500)>0)
+ if(IO::poll(serial, IO::P_INPUT, 500*Time::msec))
{
IO::print("IB detected at %d bits/s\n", baud[i]);
char buf[2];
- p50 = (read(serial_fd, buf, 2)==2);
+ p50 = (serial.read(buf, 2)==2);
ok = true;
break;
}
}
if(!ok)
- throw Exception("IB not detected");
+ throw runtime_error("IB not detected");
if(p50)
- write(serial_fd, "xZzA1\r", 6);
+ serial.write("xZzA1\r", 6);
command(CMD_STATUS);
}
return 0;
}
-void Intellibox::add_loco(unsigned addr, const string &proto_name, const VehicleType &type)
+unsigned Intellibox::add_loco(unsigned addr, const string &proto_name, const VehicleType &type)
{
Protocol proto = map_protocol(proto_name);
data[1] = (addr>>8)&0xFF;
command(CMD_LOK_STATUS, addr, data, 2);
}
+
+ return addr;
+}
+
+void Intellibox::remove_loco(unsigned addr)
+{
+ locos.erase(addr);
}
void Intellibox::set_loco_speed(unsigned addr, unsigned speed)
signal_loco_function.emit(addr, func, state);
}
-void Intellibox::add_turnout(unsigned addr, const TrackType &type)
+unsigned Intellibox::add_turnout(unsigned addr, const TrackType &type)
+{
+ return add_turnout(addr, type.get_state_bits(), false);
+}
+
+void Intellibox::remove_turnout(unsigned addr)
+{
+ turnouts.erase(addr);
+}
+
+unsigned Intellibox::add_turnout(unsigned addr, unsigned bits, bool signal)
{
if(!turnouts.count(addr))
{
Turnout &turnout = turnouts[addr];
- turnout.bits = type.get_state_bits();
+ turnout.bits = bits;
+ turnout.signal = signal;
unsigned char data[2];
data[0] = addr&0xFF;
command(CMD_TURNOUT_STATUS, addr+i, data, 2);
}
}
+
+ return addr;
+}
+
+void Intellibox::turnout_state_changed(unsigned addr, const Turnout &turnout) const
+{
+ if(turnout.signal)
+ signal_signal.emit(addr, turnout.state);
+ else
+ signal_turnout.emit(addr, turnout.state);
}
void Intellibox::set_turnout(unsigned addr, unsigned state)
{
Turnout &turnout = turnouts[addr];
unsigned mask = (1<<turnout.bits)-1;
- if(((state^turnout.state)&mask)==0 || ((state^turnout.pending)&mask)==0)
+ if(((state^turnout.state)&mask)==0 || ((state^turnout.pending)&mask)==0 || !turnout.synced)
{
turnout.state = state;
turnout.pending = state;
- signal_turnout.emit(addr, state);
+ turnout_state_changed(addr, turnout);
return;
}
return 0;
}
-void Intellibox::add_sensor(unsigned addr)
+unsigned Intellibox::add_signal(unsigned addr, const SignalType &)
+{
+ return add_turnout(addr, 1, true);
+}
+
+void Intellibox::remove_signal(unsigned addr)
+{
+ remove_turnout(addr);
+}
+
+void Intellibox::set_signal(unsigned addr, unsigned state)
+{
+ set_turnout(addr, state);
+}
+
+unsigned Intellibox::get_signal(unsigned addr) const
+{
+ return get_turnout(addr);
+}
+
+unsigned Intellibox::add_sensor(unsigned addr)
{
if(!sensors.count(addr))
{
sensors[addr];
update_sensors = true;
}
+
+ return addr;
+}
+
+void Intellibox::remove_sensor(unsigned addr)
+{
+ sensors.erase(addr);
+ update_sensors = true;
}
bool Intellibox::get_sensor(unsigned addr) const
if(!queue.empty() && command_sent)
{
- pollfd pfd = { serial_fd, POLLIN, 0 };
- if(poll(&pfd, 1, 0)>0)
+ if(IO::poll(serial, IO::P_INPUT, Time::zero))
{
process_reply(t);
queue.erase(queue.begin());
if(!queue.empty())
{
const CommandSlot &slot = queue.front();
- write(serial_fd, slot.data, slot.length);
+ serial.write(reinterpret_cast<const char *>(slot.data), slot.length);
command_sent = true;
}
}
void Intellibox::flush()
{
- Time::TimeStamp t = Time::now();
for(list<CommandSlot>::iterator i=queue.begin(); i!=queue.end(); ++i)
{
- write(serial_fd, i->data, i->length);
- pollfd pfd = { serial_fd, POLLIN, 0 };
+ serial.write(reinterpret_cast<const char *>(i->data), i->length);
bool first = true;
- while(poll(&pfd, 1, (first ? -1 : 0))>0)
+ while(first ? IO::poll(serial, IO::P_INPUT) : IO::poll(serial, IO::P_INPUT, Time::zero))
{
char data[16];
- read(serial_fd, data, 16);
+ serial.read(data, 16);
first = false;
}
}
else if(name=="MM-27")
return MM_27;
else
- throw InvalidParameterValue("Unknown protocol");
+ throw invalid_argument("Intellibox::map_protocol");
}
void Intellibox::command(Command cmd)
unsigned bit = !(data[1]&0x80);
turnout.state = (turnout.state&~mask) | (bit*mask);
turnout.pending = turnout.state;
- signal_turnout.emit(addr, turnout.state);
+ turnout_state_changed(addr,turnout);
}
}
else if(cmd==CMD_EVENT_SENSOR)
if(turnout.active)
{
if(turnout.state==turnout.pending)
- signal_turnout.emit(addr, turnout.state);
+ turnout_state_changed(addr, turnout);
turnout.off_timeout = t+500*Time::msec;
}
}
{
turnout.state = (turnout.state&~mask) | (bit*mask);
turnout.pending = turnout.state;
- signal_turnout.emit(addr, turnout.state);
+ turnout_state_changed(addr, turnout);
}
+
+ turnout.synced = true;
}
else
error(cmd, err);
unsigned speed = (data[0]<=1 ? 0 : data[0]*2/19+1);
bool reverse = !(data[1]&0x20);
- if(speed!=loco.speed || reverse!=loco.reverse)
- {
- loco.speed = speed;
- loco.reverse = reverse;
- signal_loco_speed.emit(addr, loco.speed, loco.reverse);
- }
+ bool speed_changed = (speed!=loco.speed || reverse!=loco.reverse);
+
+ loco.speed = speed;
+ loco.reverse = reverse;
unsigned funcs = (data[1]&0xF)<<1;
if(data[1]&0x10)
funcs |= 1;
- if(funcs!=loco.funcs)
- {
- unsigned changed = loco.funcs^funcs;
- loco.funcs = funcs;
- for(unsigned i=0; i<5; ++i)
- if(changed&(1<<i))
- signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
- }
+ unsigned funcs_changed = loco.funcs^funcs;
+ loco.funcs = funcs;
+
+ if(speed_changed)
+ signal_loco_speed.emit(addr, loco.speed, loco.reverse);
+ for(unsigned i=0; i<5; ++i)
+ if(funcs_changed&(1<<i))
+ signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
}
else
error(cmd, err);
{
unsigned pos = 0;
while(pos<len)
- pos += read(serial_fd, buf+pos, len-pos);
+ pos += serial.read(reinterpret_cast<char *>(buf+pos), len-pos);
return pos;
}
Intellibox::Locomotive::Locomotive():
+ protocol(NONE),
ext_func(false),
speed(0),
reverse(false),
- funcs(0)
+ funcs(0),
+ pending_half_step(0)
{ }
bits(1),
state(0),
active(false),
- pending(false)
+ synced(false),
+ pending(0),
+ signal(false)
{ }