-/* $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>
#include <msp/time/units.h>
#include <msp/time/utils.h>
#include "intellibox.h"
+#include "tracktype.h"
+#include "vehicletype.h"
using namespace std;
using namespace Msp;
namespace R2C2 {
Intellibox::Intellibox(const string &dev):
+ serial(dev),
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);
-
- write(serial_fd, "\xC4", 1);
+ serial.set_baud_rate(baud[i]);
+ serial.put('\xC4');
- 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);
}
const char *Intellibox::enumerate_protocols(unsigned i) const
{
+ ++i;
if(i==MM)
return "MM";
else if(i==MM_27)
return 0;
}
-void Intellibox::add_loco(unsigned addr, const string &proto_name)
+void Intellibox::add_loco(unsigned addr, const string &proto_name, const VehicleType &type)
{
Protocol proto = map_protocol(proto_name);
if(!locos.count(addr))
{
- locos[addr].protocol = proto;
+ Locomotive &loco = locos[addr];
+ loco.protocol = proto;
+ if(type.get_max_function()>4)
+ {
+ loco.ext_func = true;
+ locos[addr+1].protocol = NONE;
+ }
unsigned char data[2];
data[0] = addr&0xFF;
void Intellibox::set_loco_speed(unsigned addr, unsigned speed)
{
Locomotive &loco = locos[addr];
+ if(loco.protocol==NONE)
+ return;
+
if(speed==loco.speed)
{
if(loco.pending_half_step)
loco.pending_half_step = 0;
loco.half_step_delay = Time::TimeStamp();
- loco_command(addr, (speed+1)/2, loco.reverse, loco.funcs|0x100);
+ loco_command(addr, (speed+1)/2, loco.reverse, loco.funcs, false);
}
- else
+ else if(loco.protocol==MM)
{
if(speed>14)
speed = 14;
- loco_command(addr, speed, loco.reverse, loco.funcs|0x100);
+ loco_command(addr, speed, loco.reverse, loco.funcs, false);
}
loco.speed = speed;
}
void Intellibox::set_loco_reverse(unsigned addr, bool rev)
{
Locomotive &loco = locos[addr];
- if(rev==loco.reverse)
+ if(loco.protocol==NONE || rev==loco.reverse)
return;
+ loco.speed = 0;
loco.reverse = rev;
- loco_command(addr, loco.speed, rev, loco.funcs|0x100);
+ loco_command(addr, 0, rev, loco.funcs, false);
}
void Intellibox::set_loco_function(unsigned addr, unsigned func, bool state)
{
Locomotive &loco = locos[addr];
+ if(loco.protocol==NONE)
+ return;
+
if(state)
loco.funcs |= 1<<func;
else
loco.funcs &= ~(1<<func);
- loco_command(addr, loco.speed, loco.reverse, loco.funcs);
+ if(func<=4)
+ loco_command(addr, loco.speed, loco.reverse, loco.funcs, true);
+ else if(loco.ext_func && func<=8)
+ loco_command(addr+1, 0, false, (loco.funcs>>4)&0x1E, true);
signal_loco_function.emit(addr, func, state);
}
-void Intellibox::add_turnout(unsigned addr)
+void Intellibox::add_turnout(unsigned addr, const TrackType &type)
+{
+ add_turnout(addr, type.get_state_bits(), false);
+}
+
+void Intellibox::add_turnout(unsigned addr, unsigned bits, bool signal)
{
if(!turnouts.count(addr))
{
- turnouts[addr];
+ Turnout &turnout = turnouts[addr];
+ turnout.bits = bits;
+ turnout.signal = signal;
unsigned char data[2];
data[0] = addr&0xFF;
data[1] = (addr>>8)&0xFF;
command(CMD_TURNOUT_STATUS, addr, data, 2);
+ for(unsigned i=1; i<turnout.bits; ++i)
+ {
+ turnouts[addr+i].bits = 0;
+
+ ++data[0];
+ if(!data[0])
+ ++data[1];
+ command(CMD_TURNOUT_STATUS, addr+i, data, 2);
+ }
}
}
-void Intellibox::set_turnout(unsigned addr, bool state)
+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];
- if(state==turnout.state || state==turnout.pending)
+ unsigned mask = (1<<turnout.bits)-1;
+ if(((state^turnout.state)&mask)==0 || ((state^turnout.pending)&mask)==0 || !turnout.synced)
+ {
+ turnout.state = state;
+ turnout.pending = state;
+ turnout_state_changed(addr, turnout);
return;
+ }
+ turnout.state = (turnout.state&mask) | (state&~mask);
turnout.pending = state;
turnout.active = true;
turnout.off_timeout = Time::TimeStamp();
- turnout_command(addr, state, true);
+ for(unsigned i=0; i<turnout.bits; ++i)
+ if((state^turnout.state)&(1<<i))
+ turnout_command(addr+i, !(state&(1<<i)), true);
}
-bool Intellibox::get_turnout(unsigned addr) const
+unsigned Intellibox::get_turnout(unsigned addr) const
{
map<unsigned, Turnout>::const_iterator i = turnouts.find(addr);
if(i!=turnouts.end())
return i->second.state;
- return false;
+ return 0;
+}
+
+void Intellibox::add_signal(unsigned addr, const SignalType &)
+{
+ add_turnout(addr, 1, true);
+}
+
+void Intellibox::set_signal(unsigned addr, unsigned state)
+{
+ set_turnout(addr, state);
+}
+
+unsigned Intellibox::get_signal(unsigned addr) const
+{
+ return get_turnout(addr);
}
void Intellibox::add_sensor(unsigned addr)
i->second.speed += i->second.pending_half_step;
i->second.pending_half_step = 0;
i->second.half_step_delay = Time::TimeStamp();
- loco_command(i->first, (i->second.speed+1)/2, i->second.reverse, i->second.funcs|0x100);
+ loco_command(i->first, (i->second.speed+1)/2, i->second.reverse, i->second.funcs, false);
}
for(map<unsigned, Turnout>::iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
{
i->second.active = false;
i->second.off_timeout = Time::TimeStamp();
- turnout_command(i->first, i->second.state, false);
+ for(unsigned j=0; j<i->second.bits; ++j)
+ turnout_command(i->first+j, !(i->second.state&(1<<j)), false);
}
for(map<unsigned, Sensor>::iterator i=sensors.begin(); i!=sensors.end(); ++i)
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)
queue.push_back(slot);
}
-void Intellibox::loco_command(unsigned addr, unsigned speed, bool rev, unsigned funcs)
+void Intellibox::loco_command(unsigned addr, unsigned speed, bool rev, unsigned funcs, bool setf)
{
unsigned char data[4];
data[0] = addr&0xFF;
data[3] = (rev ? 0 : 0x20) | ((funcs&1) ? 0x10 : 0);
- if(!(funcs&0x100))
+ if(setf)
data[3] |= 0x80 | ((funcs>>1)&0xF);
command(CMD_LOK, addr, data, 4);
read_all(data, 2);
unsigned addr = data[0]+((data[1]&7)<<8);
+ unsigned mask = 1;
+ for(; !turnouts[addr].bits; --addr, mask<<=1) ;
Turnout &turnout = turnouts[addr];
- turnout.state = (data[1]&0x80)!=0;
+
+ 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)
{
unsigned addr = queue.front().addr;
Locomotive &loco = locos[addr];
- signal_loco_speed.emit(addr, loco.speed+loco.pending_half_step, loco.reverse);
- if(loco.pending_half_step)
- loco.half_step_delay = Time::now()+500*Time::msec;
+ if(loco.protocol)
+ {
+ signal_loco_speed.emit(addr, loco.speed+loco.pending_half_step, loco.reverse);
+ if(loco.pending_half_step)
+ loco.half_step_delay = Time::now()+500*Time::msec;
+ }
}
else
error(cmd, err);
read_status(&err);
unsigned addr = queue.front().addr;
+ unsigned mask = 1;
+ for(; !turnouts[addr].bits; --addr, mask<<=1) ;
Turnout &turnout = turnouts[addr];
if(err==ERR_NO_ERROR)
{
- turnout.state = turnout.pending;
+ turnout.state = (turnout.state&~mask) | (turnout.pending&mask);
if(turnout.active)
{
- signal_turnout.emit(addr, turnout.state);
+ if(turnout.state==turnout.pending)
+ turnout_state_changed(addr, turnout);
turnout.off_timeout = t+500*Time::msec;
}
}
queue.push_back(queue.front());
else
{
- turnout.pending = turnout.state;
+ turnout.pending = (turnout.pending&~mask) | (turnout.state&mask);
error(cmd, err);
}
}
read_all(&data, 1);
unsigned addr = queue.front().addr;
- bool state = data&0x04;
-
+ unsigned mask = 1;
+ for(; !turnouts[addr].bits; --addr, mask<<=1) ;
Turnout &turnout = turnouts[addr];
- if(state!=turnout.state)
+
+ bool bit = !(data&0x04);
+ if(bit!=((turnout.state&mask)!=0))
{
- turnout.state = state;
- turnout.pending = state;
- signal_turnout.emit(addr, turnout.state);
+ turnout.state = (turnout.state&~mask) | (bit*mask);
+ turnout.pending = 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():
+ ext_func(false),
speed(0),
reverse(false),
funcs(0)
Intellibox::Turnout::Turnout():
- state(false),
+ bits(1),
+ state(0),
active(false),
- pending(false)
+ synced(false),
+ pending(0),
+ signal(false)
{ }