From 8a31a3ab3ab7abda30de0ed3a6d0753760f3bb1d Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Fri, 1 Nov 2013 01:10:57 +0200 Subject: [PATCH] Add driver for my custom Arduino-based control device --- source/libr2c2/arducontrol.cpp | 734 +++++++++++++++++++++++++++++++++ source/libr2c2/arducontrol.h | 265 ++++++++++++ source/libr2c2/driver.cpp | 3 + 3 files changed, 1002 insertions(+) create mode 100644 source/libr2c2/arducontrol.cpp create mode 100644 source/libr2c2/arducontrol.h diff --git a/source/libr2c2/arducontrol.cpp b/source/libr2c2/arducontrol.cpp new file mode 100644 index 0000000..a1d2381 --- /dev/null +++ b/source/libr2c2/arducontrol.cpp @@ -0,0 +1,734 @@ +#include +#include +#include +#include "arducontrol.h" +#include "tracktype.h" + +using namespace std; +using namespace Msp; + +namespace R2C2 { + +ArduControl::ArduControl(const string &dev): + serial(dev), + debug(true), + power(false), + next_refresh(refresh_cycle.end()), + refresh_counter(0), + active_accessory(0), + n_s88_octets(0), + thread(*this) +{ +} + +ArduControl::~ArduControl() +{ + thread.exit(); +} + +void ArduControl::set_power(bool p) +{ + if(p==power.pending) + return; + + power.pending = p; + ++power.serial; + + QueuedCommand cmd(POWER); + cmd.tag.serial = power.serial; + cmd.command[0] = (p ? POWER_ON : POWER_OFF); + cmd.length = 1; + push_command(cmd); +} + +void ArduControl::halt(bool) +{ +} + +const char *ArduControl::enumerate_protocols(unsigned i) const +{ + if(i==0) + return "MM"; + else + return 0; +} + +ArduControl::Protocol ArduControl::map_protocol(const string &proto_name) +{ + if(proto_name=="MM") + return MM; + else + throw invalid_argument("ArduControl::map_protocol"); +} + +unsigned ArduControl::get_protocol_speed_steps(const string &proto_name) const +{ + Protocol proto = map_protocol(proto_name); + if(proto==MM) + return 14; + else + return 0; +} + +void ArduControl::add_loco(unsigned addr, const string &proto_name, const VehicleType &) +{ + if(!addr) + throw invalid_argument("ArduControl::add_loco"); + + Protocol proto = map_protocol(proto_name); + + if(proto==MM) + { + if(addr>=80) + throw invalid_argument("ArduControl::add_loco"); + } + + insert_unique(locomotives, addr, Locomotive(proto, addr)); +} + +void ArduControl::remove_loco(unsigned addr) +{ + Locomotive &loco = get_item(locomotives, addr); + remove_loco_from_refresh(loco); + locomotives.erase(addr); +} + +void ArduControl::set_loco_speed(unsigned addr, unsigned speed) +{ + Locomotive &loco = get_item(locomotives, addr); + if(loco.speed.set(speed)) + { + QueuedCommand cmd(loco, Locomotive::SPEED); + push_command(cmd); + + add_loco_to_refresh(loco); + } +} + +void ArduControl::set_loco_reverse(unsigned addr, bool rev) +{ + Locomotive &loco = get_item(locomotives, addr); + if(loco.reverse.set(rev)) + { + QueuedCommand cmd(loco, Locomotive::REVERSE); + push_command(cmd); + + add_loco_to_refresh(loco); + } +} + +void ArduControl::set_loco_function(unsigned addr, unsigned func, bool state) +{ + Locomotive &loco = get_item(locomotives, addr); + unsigned mask = 1<0) + { + QueuedCommand cmd(loco, Locomotive::FUNCTIONS, func); + push_command(cmd); + } + + add_loco_to_refresh(loco); + } +} + +void ArduControl::add_loco_to_refresh(Locomotive &loco) +{ + MutexLock lock(mutex); + refresh_cycle.push_back(&loco); + if(refresh_cycle.size()>15) + { + LocomotivePtrList::iterator oldest = refresh_cycle.begin(); + for(LocomotivePtrList::iterator i=refresh_cycle.begin(); ++i!=refresh_cycle.end(); ) + if((*i)->last_change_age>(*oldest)->last_change_age) + oldest = i; + if(oldest==next_refresh) + advance_next_refresh(); + refresh_cycle.erase(oldest); + } + if(next_refresh==refresh_cycle.end()) + next_refresh = refresh_cycle.begin(); +} + +void ArduControl::remove_loco_from_refresh(Locomotive &loco) +{ + MutexLock lock(mutex); + for(LocomotivePtrList::iterator i=refresh_cycle.begin(); i!=refresh_cycle.end(); ++i) + if(*i==&loco) + { + if(i==next_refresh) + { + if(refresh_cycle.size()>1) + advance_next_refresh(); + else + next_refresh = refresh_cycle.end(); + } + refresh_cycle.erase(i); + return; + } +} + +ArduControl::Locomotive *ArduControl::get_loco_to_refresh() +{ + MutexLock lock(mutex); + if(refresh_cycle.empty()) + return 0; + + Locomotive *loco = *next_refresh; + advance_next_refresh(); + return loco; +} + +void ArduControl::advance_next_refresh() +{ + ++next_refresh; + if(next_refresh==refresh_cycle.end()) + { + next_refresh = refresh_cycle.begin(); + ++refresh_counter; + } +} + +void ArduControl::add_turnout(unsigned addr, const TrackType &type) +{ + if(!addr || !type.is_turnout()) + throw invalid_argument("ArduControl::add_turnout"); + + add_accessory(Accessory::TURNOUT, addr, type.get_state_bits()); +} + +void ArduControl::remove_turnout(unsigned addr) +{ + remove_accessory(Accessory::TURNOUT, addr); +} + +void ArduControl::set_turnout(unsigned addr, unsigned state) +{ + set_accessory(Accessory::TURNOUT, addr, state); +} + +unsigned ArduControl::get_turnout(unsigned addr) const +{ + return get_accessory(Accessory::TURNOUT, addr); +} + +void ArduControl::add_signal(unsigned addr, const SignalType &) +{ + add_accessory(Accessory::SIGNAL, addr, 1); +} + +void ArduControl::remove_signal(unsigned addr) +{ + remove_accessory(Accessory::SIGNAL, addr); +} + +void ArduControl::set_signal(unsigned addr, unsigned state) +{ + set_accessory(Accessory::SIGNAL, addr, state); +} + +unsigned ArduControl::get_signal(unsigned addr) const +{ + return get_accessory(Accessory::SIGNAL, addr); +} + +void ArduControl::add_accessory(Accessory::Kind kind, unsigned addr, unsigned bits) +{ + AccessoryMap::iterator i = accessories.lower_bound(addr); + AccessoryMap::iterator j = accessories.upper_bound(addr+bits-1); + if(i!=j) + throw key_error(addr); + if(i!=accessories.begin()) + { + --i; + if(i->first+i->second.bits>addr) + throw key_error(addr); + } + + insert_unique(accessories, addr, Accessory(kind, addr, bits)); +} + +void ArduControl::remove_accessory(Accessory::Kind kind, unsigned addr) +{ + Accessory &acc = get_item(accessories, addr); + if(acc.kind!=kind) + throw key_error(addr); + accessories.erase(addr); +} + +void ArduControl::set_accessory(Accessory::Kind kind, unsigned addr, unsigned state) +{ + Accessory &acc = get_item(accessories, addr); + if(acc.kind!=kind) + throw key_error(addr); + + if(state!=acc.target) + { + acc.target = state; + accessory_queue.push_back(&acc); + } +} + +unsigned ArduControl::get_accessory(Accessory::Kind kind, unsigned addr) const +{ + const Accessory &acc = get_item(accessories, addr); + if(acc.kind!=kind) + throw key_error(addr); + return acc.state; +} + +void ArduControl::add_sensor(unsigned addr) +{ + if(!addr) + throw invalid_argument("ArduControl::add_sensor"); + + insert_unique(sensors, addr, Sensor(addr)); + unsigned octet_index = (addr-1)/8; + if(octet_index>=n_s88_octets) + n_s88_octets = octet_index+1; +} + +void ArduControl::remove_sensor(unsigned addr) +{ + remove_existing(sensors, addr); + // TODO update n_s88_octets +} + +bool ArduControl::get_sensor(unsigned addr) const +{ + return get_item(sensors, addr).state; +} + +void ArduControl::tick() +{ + while(Tag tag = pop_completed_tag()) + { + if(tag.type==Tag::GENERAL) + { + if(tag.command==POWER) + { + if(power.commit(tag.serial)) + signal_power.emit(power.current); + } + } + else if(tag.type==Tag::LOCOMOTIVE) + { + LocomotiveMap::iterator i = locomotives.find(tag.address); + if(i==locomotives.end()) + continue; + + Locomotive &loco = i->second; + if(tag.command==Locomotive::SPEED) + { + if(loco.speed.commit(tag.serial)) + signal_loco_speed.emit(loco.address, loco.speed, loco.reverse); + } + else if(tag.command==Locomotive::REVERSE) + { + if(loco.reverse.commit(tag.serial)) + signal_loco_speed.emit(loco.address, loco.speed, loco.reverse); + } + else if(tag.command==Locomotive::FUNCTIONS) + { + unsigned old = loco.funcs; + if(loco.funcs.commit(tag.serial)) + { + unsigned changed = old^loco.funcs; + for(unsigned j=0; changed>>j; ++j) + if((changed>>j)&1) + signal_loco_function.emit(loco.address, j, (loco.funcs>>j)&1); + } + } + } + else if(tag.type==Tag::ACCESSORY) + { + AccessoryMap::iterator i = accessories.find(tag.address); + if(i==accessories.end()) + continue; + + Accessory &acc = i->second; + if(tag.command==Accessory::ACTIVATE) + { + off_timeout = Time::now()+acc.active_time; + } + else if(tag.command==Accessory::DEACTIVATE) + { + if(acc.state.commit(tag.serial)) + { + if(acc.state==acc.target) + { + if(acc.kind==Accessory::TURNOUT) + signal_turnout.emit(acc.address, acc.state); + else if(acc.kind==Accessory::SIGNAL) + signal_signal.emit(acc.address, acc.state); + } + if(&acc==active_accessory) + active_accessory = 0; + } + } + } + else if(tag.type==Tag::SENSOR) + { + SensorMap::iterator i = sensors.find(tag.address); + if(i==sensors.end()) + continue; + + Sensor &sensor = i->second; + if(tag.command==Sensor::STATE) + { + if(sensor.state.commit(tag.serial)) + signal_sensor.emit(sensor.address, sensor.state); + } + } + } + + while(!active_accessory && !accessory_queue.empty()) + { + Accessory &acc = *accessory_queue.front(); + + if(acc.state!=acc.target) + { + active_accessory = &acc; + + unsigned changes = acc.state^acc.target; + unsigned lowest_bit = changes&~(changes-1); + unsigned i; + for(i=0; (lowest_bit>>i)>1; ++i) ; + acc.state.set(acc.state^lowest_bit); + QueuedCommand cmd(acc, Accessory::ACTIVATE, i); + push_command(cmd); + } + else + accessory_queue.pop_front(); + } + + if(active_accessory && off_timeout) + { + Time::TimeStamp t = Time::now(); + if(t>off_timeout) + { + off_timeout = Time::TimeStamp(); + QueuedCommand cmd(*active_accessory, Accessory::DEACTIVATE); + push_command(cmd); + } + } +} + +void ArduControl::flush() +{ +} + +void ArduControl::push_command(const QueuedCommand &cmd) +{ + MutexLock lock(mutex); + command_queue.push_back(cmd); +} + +bool ArduControl::pop_command(QueuedCommand &cmd) +{ + MutexLock lock(mutex); + if(command_queue.empty()) + return false; + cmd = command_queue.front(); + command_queue.pop_front(); + return true; +} + +void ArduControl::push_completed_tag(const Tag &tag) +{ + MutexLock lock(mutex); + completed_commands.push_back(tag); +} + +ArduControl::Tag ArduControl::pop_completed_tag() +{ + MutexLock lock(mutex); + if(completed_commands.empty()) + return Tag(); + Tag tag = completed_commands.front(); + completed_commands.pop_front(); + return tag; +} + + +ArduControl::Tag::Tag(): + type(NONE), + command(0), + serial(0), + address(0) +{ } + + +ArduControl::Locomotive::Locomotive(Protocol p, unsigned a): + proto(p), + address(a), + speed(0), + reverse(false), + funcs(0), + last_change_age(0) +{ } + +unsigned ArduControl::Locomotive::create_speed_dir_command(char *buffer) const +{ + if(proto==MM) + { + buffer[0] = MOTOROLA_SPEED_DIRECTION; + buffer[1] = address; + buffer[2] = funcs.pending&1; + buffer[3] = speed.pending+reverse.pending*0x80; + return 4; + } + else + return 0; +} + +unsigned ArduControl::Locomotive::create_speed_func_command(unsigned f, char *buffer) const +{ + if(proto==MM) + { + if(f<1 || f>4) + throw invalid_argument("Locomotive::create_speed_func_command"); + + buffer[0] = MOTOROLA_SPEED_FUNCTION; + buffer[1] = address; + buffer[2] = (f<<4)|(((funcs.pending>>f)&1)<<1)|(funcs.pending&1); + buffer[3] = speed.pending; + return 4; + } + else + return 0; +} + + +ArduControl::Accessory::Accessory(Kind k, unsigned a, unsigned b): + kind(k), + address(a), + bits(b), + state(0), + active_time(500*Time::msec) +{ } + +unsigned ArduControl::Accessory::create_state_command(unsigned b, bool c, char *buffer) const +{ + if(b>=bits) + throw invalid_argument("Accessory::create_state_command"); + + unsigned a = (address+b+3)*2; + if((state.pending>>b)&1) + ++a; + buffer[0] = MOTOROLA_SOLENOID; + buffer[1] = a>>3; + buffer[2] = ((a&7)<<4)|c; + return 3; +} + + +ArduControl::Sensor::Sensor(unsigned a): + address(a), + state(false) +{ } + + +ArduControl::ControlThread::ControlThread(ArduControl &c): + control(c), + done(false) +{ + launch(); +} + +void ArduControl::ControlThread::exit() +{ + done = true; + join(); +} + +void ArduControl::ControlThread::main() +{ + char command[15]; + unsigned length = 0; + unsigned repeat_count = 0; + Tag tag; + Locomotive *loco = 0; + unsigned phase = 0; + unsigned s88_octets_remaining = 0; + while(!done) + { + if(!repeat_count) + { + tag = Tag(); + length = 0; + repeat_count = 1; + QueuedCommand qcmd; + if(control.pop_command(qcmd)) + { + length = qcmd.length; + copy(qcmd.command, qcmd.command+length, command); + if(qcmd.tag.type==Tag::LOCOMOTIVE) + repeat_count = 8; + tag = qcmd.tag; + } + else if(loco && phase==0) + { + length = loco->create_speed_func_command(control.refresh_counter%4+1, command); + repeat_count = 2; + ++phase; + } + else if(!s88_octets_remaining && control.n_s88_octets) + { + command[0] = S88_READ; + command[1] = control.n_s88_octets; + length = 2; + s88_octets_remaining = control.n_s88_octets; + } + else if((loco = control.get_loco_to_refresh())) + { + length = loco->create_speed_dir_command(command); + repeat_count = 2; + phase = 0; + } + else + { + // Send an idle packet for the MM protocol + command[0] = MOTOROLA_SPEED; + command[1] = 80; + command[2] = 0; + command[3] = 0; + length = 4; + } + } + + if(control.debug) + { + string cmd_hex; + for(unsigned i=0; i(command[i])); + IO::print("< %02X%s\n", length^0xFF, cmd_hex); + } + + control.serial.put(length^0xFF); + control.serial.write(command, length); + --repeat_count; + + bool got_reply = false; + bool got_data = false; + while(!got_reply || got_data) + { + if(got_reply) + got_data = IO::poll(control.serial, IO::P_INPUT, Time::zero); + else + got_data = IO::poll(control.serial, IO::P_INPUT); + + if(got_data) + { + unsigned rlength = control.serial.get()^0xFF; + if(rlength>15) + { + IO::print("Invalid length %02X\n", rlength); + continue; + } + + char reply[15]; + unsigned pos = 0; + while(pos(reply[i])); + IO::print("> %02X%s\n", rlength^0xFF, reply_hex); + } + + unsigned char type = reply[0]; + if((type&0xE0)==0x80) + { + got_reply = true; + if(type!=COMMAND_OK) + IO::print("Error %02X\n", type); + else if(tag && !repeat_count) + control.push_completed_tag(tag); + } + else if(type==S88_DATA && rlength>2) + { + unsigned offset = static_cast(reply[1]); + unsigned count = rlength-2; + + SensorMap::iterator begin = control.sensors.lower_bound(offset*8+1); + SensorMap::iterator end = control.sensors.upper_bound((offset+count)*8); + for(SensorMap::iterator i=begin; i!=end; ++i) + { + unsigned bit_index = i->first-1-offset*8; + bool state = (reply[2+bit_index/8]>>(7-bit_index%8))&1; + i->second.state.set(state); + + Tag stag; + stag.type = Tag::SENSOR; + stag.command = Sensor::STATE; + stag.serial = i->second.state.serial; + stag.address = i->first; + control.push_completed_tag(stag); + } + + if(count>s88_octets_remaining) + s88_octets_remaining = 0; + else + s88_octets_remaining -= count; + } + } + } + } +} + + +ArduControl::QueuedCommand::QueuedCommand(): + length(0) +{ } + +ArduControl::QueuedCommand::QueuedCommand(GeneralCommand cmd): + length(0) +{ + tag.type = Tag::GENERAL; + tag.command = cmd; +} + +ArduControl::QueuedCommand::QueuedCommand(Locomotive &loco, Locomotive::Command cmd, unsigned index) +{ + tag.type = Tag::LOCOMOTIVE; + tag.command = cmd; + tag.address = loco.address; + if(cmd==Locomotive::SPEED) + { + tag.serial = loco.speed.serial; + length = loco.create_speed_dir_command(command); + } + else if(cmd==Locomotive::REVERSE) + { + tag.serial = loco.reverse.serial; + length = loco.create_speed_dir_command(command); + } + else if(cmd==Locomotive::FUNCTIONS) + { + tag.serial = loco.funcs.serial; + length = loco.create_speed_func_command(index, command); + } + else + throw invalid_argument("QueuedCommand"); +} + +ArduControl::QueuedCommand::QueuedCommand(Accessory &acc, Accessory::Command cmd, unsigned index) +{ + tag.type = Tag::ACCESSORY; + tag.command = cmd; + tag.address = acc.address; + if(cmd==Accessory::ACTIVATE || cmd==Accessory::DEACTIVATE) + { + tag.serial = acc.state.serial; + length = acc.create_state_command(index, (cmd==Accessory::ACTIVATE), command); + } + else + throw invalid_argument("QueuedCommand"); +} + +} // namespace R2C2 diff --git a/source/libr2c2/arducontrol.h b/source/libr2c2/arducontrol.h new file mode 100644 index 0000000..cbdecad --- /dev/null +++ b/source/libr2c2/arducontrol.h @@ -0,0 +1,265 @@ +#ifndef LIBR2C2_ARDUCONTROL_H_ +#define LIBR2C2_ARDUCONTROL_H_ + +#include +#include +#include +#include +#include +#include "driver.h" + +namespace R2C2 { + +class ArduControl: public Driver +{ +private: + enum Command + { + POWER_ON = 0x01, + POWER_OFF = 0x02, + READ_TRACK_CURRENT = 0x08, + SET_OVERCURRENT_LIMIT = 0x09, + READ_INPUT_VOLTAGE = 0x0A, + MOTOROLA_SPEED = 0x11, + MOTOROLA_REVERSE = 0x12, + MOTOROLA_SPEED_DIRECTION = 0x13, + MOTOROLA_SPEED_FUNCTION = 0x14, + MOTOROLA_SOLENOID = 0x15, + S88_READ = 0x30, + COMMAND_OK = 0x80, + RECEIVE_OVERRUN = 0x81, + FRAMING_ERROR = 0x82, + INVALID_COMMAND = 0x83, + LENGTH_ERROR = 0x84, + INVALID_VALUE = 0x85, + OVERCURRENT = 0xA0, + TRACK_CURRENT = 0xC0, + INPUT_VOLTAGE = 0xC1, + S88_DATA = 0xD0 + }; + + struct Tag + { + enum Type + { + NONE, + GENERAL, + LOCOMOTIVE, + ACCESSORY, + SENSOR + }; + + Type type; + unsigned char command; + unsigned short serial; + unsigned address; + + Tag(); + + operator bool() const { return type!=NONE; } + }; + + enum GeneralCommand + { + POWER + }; + + enum Protocol + { + NONE, + MM + }; + + template + struct ControlledVariable + { + T current; + T pending; + unsigned short serial; + + ControlledVariable(): current(), pending(), serial(0) { } + ControlledVariable(T v): current(v), pending(v), serial(0) { } + + bool set(T v) { if(v==pending) return false; pending = v; ++serial; return true; } + bool commit(unsigned short s) { if(s!=serial) return false; current = pending; return true; } + + operator T() const { return current; } + }; + + struct Locomotive + { + enum Command + { + SPEED, + REVERSE, + FUNCTIONS + }; + + Protocol proto; + unsigned address; + ControlledVariable speed; + ControlledVariable reverse; + ControlledVariable funcs; + unsigned last_change_age; + + Locomotive(Protocol, unsigned); + + unsigned create_speed_dir_command(char *) const; + unsigned create_speed_func_command(unsigned, char *) const; + }; + + struct Accessory + { + enum Kind + { + TURNOUT, + SIGNAL + }; + + enum Command + { + ACTIVATE, + DEACTIVATE + }; + + Kind kind; + unsigned address; + unsigned bits; + ControlledVariable state; + unsigned target; + Msp::Time::TimeDelta active_time; + + Accessory(Kind, unsigned, unsigned); + + unsigned create_state_command(unsigned, bool, char *) const; + }; + + struct Sensor + { + enum Command + { + STATE + }; + + unsigned address; + ControlledVariable state; + + Sensor(unsigned); + }; + + class ControlThread: public Msp::Thread + { + private: + ArduControl &control; + bool done; + + public: + ControlThread(ArduControl &); + + void exit(); + private: + virtual void main(); + }; + + struct QueuedCommand + { + Tag tag; + char command[15]; + unsigned char length; + + QueuedCommand(); + QueuedCommand(GeneralCommand); + QueuedCommand(Locomotive &, Locomotive::Command, unsigned = 0); + QueuedCommand(Accessory &, Accessory::Command, unsigned = 0); + }; + + typedef std::map LocomotiveMap; + typedef std::list LocomotivePtrList; + typedef std::map AccessoryMap; + typedef std::list AccessoryPtrList; + typedef std::map SensorMap; + + Msp::IO::Serial serial; + bool debug; + + ControlledVariable power; + + LocomotiveMap locomotives; + LocomotivePtrList refresh_cycle; + LocomotivePtrList::iterator next_refresh; + unsigned refresh_counter; + AccessoryMap accessories; + AccessoryPtrList accessory_queue; + Accessory *active_accessory; + Msp::Time::TimeStamp off_timeout; + std::list command_queue; + std::list completed_commands; + + SensorMap sensors; + unsigned n_s88_octets; + + Msp::Mutex mutex; + ControlThread thread; + +public: + ArduControl(const std::string &); + ~ArduControl(); + + virtual void set_power(bool); + virtual bool get_power() const { return power; } + virtual void halt(bool); + virtual bool is_halted() const { return false; } + + virtual const char *enumerate_protocols(unsigned) const; +private: + static Protocol map_protocol(const std::string &); +public: + virtual unsigned get_protocol_speed_steps(const std::string &) const; + + virtual void add_loco(unsigned, const std::string &, const VehicleType &); + virtual void remove_loco(unsigned); + virtual void set_loco_speed(unsigned, unsigned); + virtual void set_loco_reverse(unsigned, bool); + virtual void set_loco_function(unsigned, unsigned, bool); +private: + void add_loco_to_refresh(Locomotive &); + void remove_loco_from_refresh(Locomotive &); + Locomotive *get_loco_to_refresh(); + void advance_next_refresh(); + +public: + virtual void add_turnout(unsigned, const TrackType &); + virtual void remove_turnout(unsigned); + virtual void set_turnout(unsigned, unsigned); + virtual unsigned get_turnout(unsigned) const; + + virtual void add_signal(unsigned, const SignalType &); + virtual void remove_signal(unsigned); + virtual void set_signal(unsigned, unsigned); + virtual unsigned get_signal(unsigned) const; + +private: + void add_accessory(Accessory::Kind, unsigned, unsigned); + void remove_accessory(Accessory::Kind, unsigned); + void set_accessory(Accessory::Kind, unsigned, unsigned); + unsigned get_accessory(Accessory::Kind, unsigned) const; + +public: + virtual void add_sensor(unsigned); + virtual void remove_sensor(unsigned); + virtual void set_sensor(unsigned, bool) { } + virtual bool get_sensor(unsigned) const; + + virtual void tick(); + virtual void flush(); + +private: + void push_command(const QueuedCommand &); + bool pop_command(QueuedCommand &); + void push_completed_tag(const Tag &); + Tag pop_completed_tag(); +}; + +} // namespace R2C2 + +#endif diff --git a/source/libr2c2/driver.cpp b/source/libr2c2/driver.cpp index 3b6d649..04df5f4 100644 --- a/source/libr2c2/driver.cpp +++ b/source/libr2c2/driver.cpp @@ -1,3 +1,4 @@ +#include "arducontrol.h" #include "centralstation.h" #include "driver.h" #include "dummy.h" @@ -20,6 +21,8 @@ Driver *Driver::create(const string &str) return new Intellibox(params); else if(type=="cs" || type=="centralstation") return new CentralStation(params); + else if(type=="ac" || type=="arducontrol") + return new ArduControl(params); else if(type=="dummy") return new Dummy(params); -- 2.45.2