00001 #ifndef CANOPEN_402_MOTOR_H
00002 #define CANOPEN_402_MOTOR_H
00003
00004 #include <canopen_402/base.h>
00005 #include <canopen_master/canopen.h>
00006 #include <boost/function.hpp>
00007 #include <boost/container/flat_map.hpp>
00008
00009 #include <boost/numeric/conversion/cast.hpp>
00010 #include <limits>
00011 #include <algorithm>
00012
00013 namespace canopen
00014 {
00015
00016 class State402{
00017 public:
00018 enum StatusWord
00019 {
00020 SW_Ready_To_Switch_On=0,
00021 SW_Switched_On=1,
00022 SW_Operation_enabled=2,
00023 SW_Fault=3,
00024 SW_Voltage_enabled=4,
00025 SW_Quick_stop=5,
00026 SW_Switch_on_disabled=6,
00027 SW_Warning=7,
00028 SW_Manufacturer_specific0=8,
00029 SW_Remote=9,
00030 SW_Target_reached=10,
00031 SW_Internal_limit=11,
00032 SW_Operation_mode_specific0=12,
00033 SW_Operation_mode_specific1=13,
00034 SW_Manufacturer_specific1=14,
00035 SW_Manufacturer_specific2=15
00036 };
00037 enum InternalState
00038 {
00039 Unknown = 0,
00040 Start = 0,
00041 Not_Ready_To_Switch_On = 1,
00042 Switch_On_Disabled = 2,
00043 Ready_To_Switch_On = 3,
00044 Switched_On = 4,
00045 Operation_Enable = 5,
00046 Quick_Stop_Active = 6,
00047 Fault_Reaction_Active = 7,
00048 Fault = 8,
00049 };
00050 InternalState getState();
00051 InternalState read(uint16_t sw);
00052 bool waitForNewState(const time_point &abstime, InternalState &state);
00053 State402() : state_(Unknown) {}
00054 private:
00055 boost::condition_variable cond_;
00056 boost::mutex mutex_;
00057 InternalState state_;
00058 };
00059
00060 class Command402 {
00061 struct Op {
00062 uint16_t to_set_;
00063 uint16_t to_reset_;
00064 Op(uint16_t to_set ,uint16_t to_reset) : to_set_(to_set), to_reset_(to_reset) {}
00065 void operator()(uint16_t &val) const {
00066 val = (val & ~to_reset_) | to_set_;
00067 }
00068 };
00069 class TransitionTable {
00070 boost::container::flat_map< std::pair<State402::InternalState, State402::InternalState>, Op > transitions_;
00071 void add(const State402::InternalState &from, const State402::InternalState &to, Op op){
00072 transitions_.insert(std::make_pair(std::make_pair(from, to), op));
00073 }
00074 public:
00075 TransitionTable();
00076 const Op& get(const State402::InternalState &from, const State402::InternalState &to) const {
00077 return transitions_.at(std::make_pair(from, to));
00078 }
00079 };
00080 static const TransitionTable transitions_;
00081 static State402::InternalState nextStateForEnabling(State402::InternalState state);
00082 Command402();
00083 public:
00084 enum ControlWord
00085 {
00086 CW_Switch_On=0,
00087 CW_Enable_Voltage=1,
00088 CW_Quick_Stop=2,
00089 CW_Enable_Operation=3,
00090 CW_Operation_mode_specific0=4,
00091 CW_Operation_mode_specific1=5,
00092 CW_Operation_mode_specific2=6,
00093 CW_Fault_Reset=7,
00094 CW_Halt=8,
00095 CW_Operation_mode_specific3=9,
00096
00097 CW_Manufacturer_specific0=11,
00098 CW_Manufacturer_specific1=12,
00099 CW_Manufacturer_specific2=13,
00100 CW_Manufacturer_specific3=14,
00101 CW_Manufacturer_specific4=15,
00102 };
00103 static bool setTransition(uint16_t &cw, const State402::InternalState &from, const State402::InternalState &to, State402::InternalState *next);
00104 };
00105
00106 template<uint16_t MASK> class WordAccessor{
00107 uint16_t &word_;
00108 public:
00109 WordAccessor(uint16_t &word) : word_(word) {}
00110 bool set(uint8_t bit){
00111 uint16_t val = MASK & (1<<bit);
00112 word_ |= val;
00113 return val;
00114 }
00115 bool reset(uint8_t bit){
00116 uint16_t val = MASK & (1<<bit);
00117 word_ &= ~val;
00118 return val;
00119 }
00120 bool get(uint8_t bit) const { return word_ & (1<<bit); }
00121 uint16_t get() const { return word_ & MASK; }
00122 WordAccessor & operator=(const uint16_t &val){
00123 uint16_t was = word_;
00124 word_ = (word_ & ~MASK) | (val & MASK);
00125 return *this;
00126 }
00127 };
00128
00129 class Mode {
00130 public:
00131 const uint16_t mode_id_;
00132 Mode(uint16_t id) : mode_id_(id) {}
00133 typedef WordAccessor<(1<<Command402::CW_Operation_mode_specific0)|(1<<Command402::CW_Operation_mode_specific1)|(1<<Command402::CW_Operation_mode_specific2)|(1<<Command402::CW_Operation_mode_specific3)> OpModeAccesser;
00134 virtual bool start() = 0;
00135 virtual bool read(const uint16_t &sw) = 0;
00136 virtual bool write(OpModeAccesser& cw) = 0;
00137 virtual bool setTarget(const double &val) { LOG("not implemented"); return false; }
00138 virtual ~Mode() {}
00139 };
00140
00141
00142 template<typename T> class ModeTargetHelper : public Mode {
00143 T target_;
00144 boost::atomic<bool> has_target_;
00145
00146 public:
00147 ModeTargetHelper(uint16_t mode) : Mode (mode) {}
00148 bool hasTarget() { return has_target_; }
00149 T getTarget() { return target_; }
00150 virtual bool setTarget(const double &val) {
00151 if(isnan(val)){
00152 LOG("target command is not a number");
00153 return false;
00154 }
00155
00156 using boost::numeric_cast;
00157 using boost::numeric::positive_overflow;
00158 using boost::numeric::negative_overflow;
00159
00160 try
00161 {
00162 target_= numeric_cast<T>(val);
00163 }
00164 catch(negative_overflow&) {
00165 LOG("Command " << val << " does not fit into target, clamping to min limit");
00166 target_= std::numeric_limits<T>::min();
00167 }
00168 catch(positive_overflow&) {
00169 LOG("Command " << val << " does not fit into target, clamping to max limit");
00170 target_= std::numeric_limits<T>::max();
00171 }
00172 catch(...){
00173 LOG("Was not able to cast command " << val);
00174 return false;
00175 }
00176
00177 has_target_ = true;
00178 return true;
00179 }
00180 virtual bool start() { has_target_ = false; return true; }
00181 };
00182
00183 template<uint16_t ID, typename TYPE, uint16_t OBJ, uint8_t SUB, uint16_t CW_MASK> class ModeForwardHelper : public ModeTargetHelper<TYPE> {
00184 canopen::ObjectStorage::Entry<TYPE> target_entry_;
00185 public:
00186 ModeForwardHelper(boost::shared_ptr<ObjectStorage> storage) : ModeTargetHelper<TYPE>(ID) {
00187 if(SUB) storage->entry(target_entry_, OBJ, SUB);
00188 else storage->entry(target_entry_, OBJ);
00189 }
00190 virtual bool read(const uint16_t &sw) { return true;}
00191 virtual bool write(Mode::OpModeAccesser& cw) {
00192 if(this->hasTarget()){
00193 cw = cw.get() | CW_MASK;
00194 target_entry_.set(this->getTarget());
00195 return true;
00196 }else{
00197 cw = cw.get() & ~CW_MASK;
00198 return false;
00199 }
00200 }
00201 };
00202
00203 typedef ModeForwardHelper<MotorBase::Profiled_Velocity, int32_t, 0x60FF, 0, 0> ProfiledVelocityMode;
00204 typedef ModeForwardHelper<MotorBase::Profiled_Torque, int16_t, 0x6071, 0, 0> ProfiledTorqueMode;
00205 typedef ModeForwardHelper<MotorBase::Cyclic_Synchronous_Position, int32_t, 0x607A, 0, 0> CyclicSynchronousPositionMode;
00206 typedef ModeForwardHelper<MotorBase::Cyclic_Synchronous_Velocity, int32_t, 0x60FF, 0, 0> CyclicSynchronousVelocityMode;
00207 typedef ModeForwardHelper<MotorBase::Cyclic_Synchronous_Torque, int16_t, 0x6071, 0, 0> CyclicSynchronousTorqueMode;
00208 typedef ModeForwardHelper<MotorBase::Velocity, int16_t, 0x6042, 0, (1<<Command402::CW_Operation_mode_specific0)|(1<<Command402::CW_Operation_mode_specific1)|(1<<Command402::CW_Operation_mode_specific2)> VelocityMode;
00209 typedef ModeForwardHelper<MotorBase::Interpolated_Position, int32_t, 0x60C1, 0x01, (1<<Command402::CW_Operation_mode_specific0)> InterpolatedPositionMode;
00210
00211 class ProfiledPositionMode : public ModeTargetHelper<int32_t> {
00212 canopen::ObjectStorage::Entry<int32_t> target_position_;
00213 int32_t last_target_;
00214 uint16_t sw_;
00215 public:
00216 enum SW_masks {
00217 MASK_Reached = (1<<State402::SW_Target_reached),
00218 MASK_Acknowledged = (1<<State402::SW_Operation_mode_specific0),
00219 MASK_Error = (1<<State402::SW_Operation_mode_specific1),
00220 };
00221 enum CW_bits {
00222 CW_NewPoint = Command402::CW_Operation_mode_specific0,
00223 CW_Immediate = Command402::CW_Operation_mode_specific1,
00224 CW_Blending = Command402::CW_Operation_mode_specific3,
00225 };
00226 ProfiledPositionMode(boost::shared_ptr<ObjectStorage> storage) : ModeTargetHelper(MotorBase::Profiled_Position) {
00227 storage->entry(target_position_, 0x607A);
00228 }
00229 virtual bool start() { sw_ = 0; last_target_= std::numeric_limits<double>::quiet_NaN(); return ModeTargetHelper::start(); }
00230 virtual bool read(const uint16_t &sw) { sw_ = sw; return (sw & MASK_Error) == 0; }
00231 virtual bool write(OpModeAccesser& cw) {
00232 cw.set(CW_Immediate);
00233 if(hasTarget()){
00234 int32_t target = getTarget();
00235 if((sw_ & MASK_Acknowledged) == 0 && target != last_target_){
00236 if(cw.get(CW_NewPoint)){
00237 cw.reset(CW_NewPoint);
00238 }else{
00239 target_position_.set(target);
00240 cw.set(CW_NewPoint);
00241 last_target_ = target;
00242 }
00243 } else if (sw_ & MASK_Acknowledged){
00244 cw.reset(CW_NewPoint);
00245 }
00246 return true;
00247 }
00248 return false;
00249 }
00250 };
00251
00252 class HomingMode: public Mode{
00253 protected:
00254 enum SW_bits {
00255 SW_Attained = State402::SW_Operation_mode_specific0,
00256 SW_Error = State402::SW_Operation_mode_specific1,
00257 };
00258 enum CW_bits {
00259 CW_StartHoming = Command402::CW_Operation_mode_specific0,
00260 };
00261 public:
00262 HomingMode() : Mode(MotorBase::Homing) {}
00263 virtual bool executeHoming(canopen::LayerStatus &status) = 0;
00264 };
00265
00266 class DefaultHomingMode: public HomingMode{
00267 canopen::ObjectStorage::Entry<int8_t> homing_method_;
00268 boost::atomic<bool> execute_;
00269
00270 boost::mutex mutex_;
00271 boost::condition_variable cond_;
00272 uint16_t status_;
00273
00274 enum SW_masks {
00275 MASK_Reached = (1<<State402::SW_Target_reached),
00276 MASK_Attained = (1<<SW_Attained),
00277 MASK_Error = (1<<SW_Error),
00278 };
00279 bool error(canopen::LayerStatus &status, const std::string& msg) { execute_= false; status.error(msg); return false; }
00280 public:
00281 DefaultHomingMode(boost::shared_ptr<ObjectStorage> storage) {
00282 storage->entry(homing_method_, 0x6098);
00283 }
00284 virtual bool start();
00285 virtual bool read(const uint16_t &sw);
00286 virtual bool write(OpModeAccesser& cw);
00287
00288 virtual bool executeHoming(canopen::LayerStatus &status);
00289 };
00290
00291 class Motor402 : public MotorBase
00292 {
00293 public:
00294
00295 Motor402(const std::string &name, boost::shared_ptr<ObjectStorage> storage, const canopen::Settings &settings)
00296 : MotorBase(name), status_word_(0),control_word_(0),
00297 switching_state_(State402::InternalState(settings.get_optional<unsigned int>("switching_state", static_cast<unsigned int>(State402::Operation_Enable)))),
00298 monitor_mode_(settings.get_optional<bool>("monitor_mode", true))
00299 {
00300 storage->entry(status_word_entry_, 0x6041);
00301 storage->entry(control_word_entry_, 0x6040);
00302 storage->entry(op_mode_display_, 0x6061);
00303 storage->entry(op_mode_, 0x6060);
00304 try{
00305 storage->entry(supported_drive_modes_, 0x6502);
00306 }
00307 catch(...){
00308 }
00309 }
00310
00311 virtual bool setTarget(double val);
00312 virtual bool enterModeAndWait(uint16_t mode);
00313 virtual bool isModeSupported(uint16_t mode);
00314 virtual uint16_t getMode();
00315
00316 template<typename T> bool registerMode(uint16_t mode) {
00317 return mode_allocators_.insert(std::make_pair(mode, boost::bind(&Motor402::createAndRegister<T>, this, mode))).second;
00318 }
00319 template<typename T, typename T1> bool registerMode(uint16_t mode, const T1& t1) {
00320 return mode_allocators_.insert(std::make_pair(mode, boost::bind(&Motor402::createAndRegister<T,T1>, this, mode, t1))).second;
00321 }
00322 template<typename T, typename T1, typename T2> bool registerMode(uint16_t mode, const T1& t1, const T2& t2) {
00323 return mode_allocators_.insert(std::make_pair(mode, boost::bind(&Motor402::createAndRegister<T,T1,T2>, this, mode, t1, t2))).second;
00324 }
00325
00326 virtual void registerDefaultModes(boost::shared_ptr<ObjectStorage> storage){
00327 registerMode<ProfiledPositionMode> (MotorBase::Profiled_Position, storage);
00328 registerMode<VelocityMode> (MotorBase::Velocity, storage);
00329 registerMode<ProfiledVelocityMode> (MotorBase::Profiled_Velocity, storage);
00330 registerMode<ProfiledTorqueMode> (MotorBase::Profiled_Torque, storage);
00331 registerMode<DefaultHomingMode> (MotorBase::Homing, storage);
00332 registerMode<InterpolatedPositionMode> (MotorBase::Interpolated_Position, storage);
00333 registerMode<CyclicSynchronousPositionMode> (MotorBase::Cyclic_Synchronous_Position, storage);
00334 registerMode<CyclicSynchronousVelocityMode> (MotorBase::Cyclic_Synchronous_Velocity, storage);
00335 registerMode<CyclicSynchronousTorqueMode> (MotorBase::Cyclic_Synchronous_Torque, storage);
00336 }
00337
00338 class Allocator : public MotorBase::Allocator{
00339 public:
00340 virtual boost::shared_ptr<MotorBase> allocate(const std::string &name, boost::shared_ptr<ObjectStorage> storage, const canopen::Settings &settings);
00341 };
00342 protected:
00343 virtual void handleRead(LayerStatus &status, const LayerState ¤t_state);
00344 virtual void handleWrite(LayerStatus &status, const LayerState ¤t_state);
00345 virtual void handleDiag(LayerReport &report);
00346 virtual void handleInit(LayerStatus &status);
00347 virtual void handleShutdown(LayerStatus &status);
00348 virtual void handleHalt(LayerStatus &status);
00349 virtual void handleRecover(LayerStatus &status);
00350
00351 private:
00352 template<typename T> void createAndRegister0(uint16_t mode){
00353 if(isModeSupportedByDevice(mode)) registerMode(mode, boost::shared_ptr<Mode>(new T()));
00354 }
00355 template<typename T, typename T1> void createAndRegister(uint16_t mode, const T1& t1){
00356 if(isModeSupportedByDevice(mode)) registerMode(mode, boost::shared_ptr<Mode>(new T(t1)));
00357 }
00358 template<typename T, typename T1, typename T2> void createAndRegister(uint16_t mode, const T1& t1, const T2& t2){
00359 if(isModeSupportedByDevice(mode)) registerMode(mode, boost::shared_ptr<Mode>(new T(t1,t2)));
00360 }
00361
00362 virtual bool isModeSupportedByDevice(uint16_t mode);
00363 void registerMode(uint16_t id, const boost::shared_ptr<Mode> &m);
00364
00365 boost::shared_ptr<Mode> allocMode(uint16_t mode);
00366
00367 bool readState(LayerStatus &status, const LayerState ¤t_state);
00368 bool switchMode(LayerStatus &status, uint16_t mode);
00369 bool switchState(LayerStatus &status, const State402::InternalState &target);
00370
00371 boost::atomic<uint16_t> status_word_;
00372 uint16_t control_word_;
00373 boost::mutex cw_mutex_;
00374 boost::atomic<bool> start_fault_reset_;
00375 boost::atomic<State402::InternalState> target_state_;
00376
00377
00378 State402 state_handler_;
00379
00380 boost::mutex map_mutex_;
00381 boost::unordered_map<uint16_t, boost::shared_ptr<Mode> > modes_;
00382 boost::unordered_map<uint16_t, boost::function<void()> > mode_allocators_;
00383
00384 boost::shared_ptr<Mode> selected_mode_;
00385 uint16_t mode_id_;
00386 boost::condition_variable mode_cond_;
00387 boost::mutex mode_mutex_;
00388 const State402::InternalState switching_state_;
00389 const bool monitor_mode_;
00390
00391 canopen::ObjectStorage::Entry<uint16_t> status_word_entry_;
00392 canopen::ObjectStorage::Entry<uint16_t > control_word_entry_;
00393 canopen::ObjectStorage::Entry<int8_t> op_mode_display_;
00394 canopen::ObjectStorage::Entry<int8_t> op_mode_;
00395 canopen::ObjectStorage::Entry<uint32_t> supported_drive_modes_;
00396 };
00397
00398 }
00399
00400 #endif