#include "macs.h" #include #include // timeval->tv_sec #include "timer.h" #include "network.h" Macs::Macs(BYTE_t nodeid, MLog &out) : NodeDrv(nodeid, out), fMacId(2*nodeid+1), fPos(0), fPosTime(0.0), fPdoPos(0), fPdoTime(0.0), fPosActive(0), fRpmActive(0) { fTimeout = new TTimer(this, 100, kFALSE); // 100ms } Macs::~Macs() { fTimerOn = kFALSE; delete fTimeout; } void Macs::HandleSDO(WORD_t idx, BYTE_t subidx, LWORD_t val, struct timeval *tv) { switch (idx) { case 0x100a: lout << "- Mac using Software Version V" << dec << (int)(val>>16) << "." << (int)(val&0xff) << endl; return; case 0x2002: cout << "Actual velocity: " << dec << val << endl; fVel = val; return; case 0x4000: switch (subidx) { case 1: cout << "Timeout timer is " << (val?"en":"dis") << "abled." << endl; return; case 2: cout << "Actual timeout time: " << dec << val << "ms" << endl; return; } break; case 0x6004: if (subidx) return; // lout << "Actual Position: " << dec << (signed long)val << endl; fPos = (LWORDS_t)val; fPosTime.SetTimer(tv); return; /* case 0x2001: cout << "Axe Status: 0x" << hex << val << endl; cout << " - Motor " << (val&0x01?"standing":"moving") << endl; cout << " - Positioning " << (val&0x02?"active":"inactive") << endl; cout << " - Rotary mode " << (val&0x04?"active":"inactive") << endl; cout << " - Attitude control: " << (val&0x40?"off":"on") << endl; cout << " - Axe resetted: " << (val&0x80?"yes":"no") << endl; fPosActive = val&0x02; fRpmActive = val&0x04; return; case 0x2003: if (!subidx) { cout << "Input State: "; for (int i=0; i<8; i++) cout << (int)(val&(1<>8)&0xff) << endl; cout << " - Attitude control: " << (val&0x04?"off":"on") << endl; cout << " - Startswitch active: " << (val&0x20?"yes":"no") << endl; cout << " - Referenceswitch active: " << (val&0x40?"yes":"no") << endl; cout << " - Endswitch active: " << (val&0x80?"yes":"no") << endl; return; */ case 0x6002: lout << "- Velocity resolution Node#" << (int)GetId() << ": " << dec << val << " ticks/min" << endl; fVelRes = val; return; } cout << "Macs: SDO, idx=0x"<< hex << idx << "/" << (int)subidx; cout << ", val=0x"< Turn Off. " << endl; fTimerOn = kFALSE; } else { if (ms>0) SetTimeoutTime(ms); cout << "--> Turn On." << endl; fTimerOn = kTRUE; fTimeout->Start(fTimeoutTime, kTRUE); SendSDO(0x4000, 1, string('o', 'n')); WaitForSdo(0x4000, 1); } } void Macs::ReqVelRes() { lout << "- Requesting velocity resolution (velres, 0x6002) of " << (int)GetId() << endl; RequestSDO(0x6002); WaitForSdo(0x6002); } void Macs::SetPDO1On(BYTE_t flag) { lout << "- " << (flag?"Enable":"Disable") << " PDO1 of #" << (int)GetId() << endl; SendSDO(0x1800, 1, (LWORD_t)(flag?0:1)<<31); WaitForSdo(0x1800, 1); } void Macs::InitDevice(Network *net) { NodeDrv::InitDevice(net); // SendSDO(0x4003, (LWORD_t)('E'<<24 | 'X'<<16 | 'I'<<8 'T')); // WaitForSdo(0x4003, 0); /* lout << "- Requesting SDO 0x2002 (vel) of " << (int)GetId() << endl; RequestSDO(0x2002); WaitForSdo(0x2002); lout << "- Requesting SDO 0x2003 of " << (int)GetId() << endl; RequestSDO(0x2003); WaitForSdo(0x2003); lout << "- Requesting SDO 0x2004 of " << (int)GetId() << endl; RequestSDO(0x2004); WaitForSdo(0x2004); */ EnableTimeout(kFALSE); lout << "- Requesting Mac Software Version of " << (int)GetId() << endl; RequestSDO(0x100a); WaitForSdo(0x100a); SetRpmMode(FALSE); ReqVelRes(); // Init fVelRes lout << "- Motor on of " << (int)GetId() << endl; SendSDO(0x3000, string('o', 'n')); WaitForSdo(0x3000); // SetHome(250000); // lout << "- Requesting SDO 0x2001 of " << (int)GetId() << endl; // RequestSDO(0x2001); // WaitForSdo(0x2001); SetPDO1On(FALSE); // this is a workaround for the Macs SetPDO1On(TRUE); SetNoWait(TRUE); } void Macs::StopMotor() { // // Stop the motor and switch off the position control unit // SendSDO(0x3000, string('s','t','o','p')); WaitForSdo(0x3000); } void Macs::StopDevice() { EnableTimeout(kFALSE); SetNoWait(FALSE); // // FIXME: This isn't called if the initialization isn't done completely! // SetRpmMode(FALSE); SetPDO1On(FALSE); lout << "- Motor off of " << (int)GetId() << endl; SendSDO(0x3000, string('o', 'f', 'f')); WaitForSdo(0x3000); /* lout << "- Stopping Program of " << (int)GetId() << endl; SendSDO(0x4000, (LWORD_t)0xaffe); WaitForSdo(); */ } void Macs::ReqPos() { lout << "- Requesting Position of #" << (int)GetId() << endl; RequestSDO(0x6004); WaitForSdo(0x6004); } void Macs::ReqVel() { lout << "- Requesting Velocity of #" << (int)GetId() << endl; RequestSDO(0x2002); WaitForSdo(0x2002); } void Macs::SetHome(LWORDS_t pos, WORD_t maxtime) { lout << "- Driving #" << (int)GetId() << " to home position, Offset=" << dec << pos << endl; SendSDO(0x6003, 2, (LWORD_t)pos); // home WaitForSdo(0x6003, 2); // home also defines the zero point of the system // maximum time allowd for home drive: 25.000ms SendSDO(0x3001, string('h','o','m','e')); // home WaitForSdo(0x3001, 0, maxtime*1000); lout << "- Home position of #" << (int)GetId() << " reached. " << endl; SendSDO(0x6003, 0, string('s','e','t')); // home WaitForSdo(0x6003, 0); } void Macs::SetVelocity(LWORD_t vel) { SendSDO(0x2002, vel); // velocity WaitForSdo(0x2002); } void Macs::SetAcceleration(LWORD_t acc) { SendSDO(0x2003, 0, acc); // acceleration WaitForSdo(0x2003, 0); } void Macs::SetDeceleration(LWORD_t dec) { SendSDO(0x2003, 1, dec); // acceleration WaitForSdo(0x2003, 1); } void Macs::SetRpmMode(BYTE_t mode) { // // SetRpmMode(FALSE) stop the motor, but lets the position control unit on // SendSDO(0x3006, 0, mode ? string('s','t','r','t') : string('s','t','o','p')); WaitForSdo(0x3006, 0); } void Macs::SetRpmVelocity(LWORDS_t cvel) { SendSDO(0x3006, 1, (LWORD_t)cvel); WaitForSdo(0x3006, 1); } void Macs::StartRelPos(LWORDS_t pos) { SendSDO(0x6004, 1, (LWORD_t)pos); } void Macs::StartAbsPos(LWORDS_t pos) { SendSDO(0x6004, 0, (LWORD_t)pos); } void Macs::SetNoWait(BYTE_t flag) { lout << "- Setting NOWAIT " << (flag?"ON":"OFF") << " #" << (int)GetId() << endl; SendSDO(0x3008, flag ? string('o', 'n') : string('o', 'f', 'f')); WaitForSdo(0x3008); } void Macs::StartVelSync() { // // The syncronization mode is disabled by a 'MOTOR STOP' // or by a positioning command (POSA, ...) // lout << "- Setting Vel Sync Mode #" << (int)GetId() << endl; SendSDO(0x3007, 0, string('s', 'y', 'n', 'c')); WaitForSdo(0x3007, 0); } void Macs::StartPosSync() { // // The syncronization mode is disabled by a 'MOTOR STOP' // or by a positioning command (POSA, ...) // lout << "- Setting Pos Sync Mode #" << (int)GetId() << endl; SendSDO(0x3007, 1, string('s', 'y', 'n', 'c')); WaitForSdo(0x3007, 1); } /* void Macs::ReqAxEnd() { RequestSDO(0x2001); WaitForSdo(0x2001); } */ void Macs::SendMsg(BYTE_t data[6]) { GetNetwork()->SendCanFrame(fMacId, 0, 0, data[0], data[1], data[2], data[3], data[4], data[5]); } void Macs::SendMsg(BYTE_t d0=0, BYTE_t d1=0, BYTE_t d2=0, BYTE_t d3=0, BYTE_t d4=0, BYTE_t d5=0) { GetNetwork()->SendCanFrame(fMacId, 0, 0, d0, d1, d2, d3, d4, d5); } void Macs::HandlePDO1(BYTE_t *data, struct timeval *tv) { fPdoPos = (data[4]<<24) | (data[5]<<16) | (data[6]<<8) | data[7]; fPosActive = data[3]&0x02; fRpmActive = data[3]&0x04; fPdoTime.SetTimer(tv); } void Macs::HandlePDO2(BYTE_t *data, struct timeval *tv) { LWORDS_t errnum = (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | data[3]; LWORDS_t errinf = (data[4]<<24) | (data[5]<<16) | (data[6]<<8) | data[7]; // // errnum==0 gives a sudden information that something happened. Now the // microcontroller is running inside its interrup procedure which // stopped the normal program. The interrupt procedure should try to clear // the error state of the hardware. This should never create a new error! // if (!errnum) { cout << "Mac #" << (int)GetId() << " reports Error occursion." << endl; SetError(-1); return; } // // Now the error is handled by the hardware now it is the software part // to react on it. The Error flag now is set to the correct value. // if (GetError()>0) cout << "Mac #" << (int)GetId() << " WARNING! Error #" << GetError() << " unhandled by software." << endl; SetError(errnum); cout << "Mac #" << (int)GetId() << " reports: "; switch (errnum) { case 6: cout << "Home position not the first positioning command." << endl; return; case 8: cout << "Control deviation overflow." << endl; return; case 9: cout << "Zero index not found." << endl; return; case 11: case 25: switch (errinf) { case -1: cout << "Negative"; break; case 1: cout << "Positive"; break; default: cout << "-unknown-"; } switch (errnum) { case 11: cout << " software endswitch activated." << endl; break; case 25: cout << " hardware endswitch activated." << endl; break; } return; case 84: cout << "Too many (>12) ON TIME calls." << endl; return; default: cout << "Error Nr. " << errnum << ", " << errinf << endl; } } void Macs::HandleError() { // // If there is no error we must not handle anything // if (!HasError()) return; // // If the program got into the: HandleError state before the hardware // has finished handeling the error we have to wait for the hardware // handeling the error // // FIXME: Timeout??? // while (GetError()<0) usleep(1); // // After this software and hardware should be in a state so that // we can go on working 'as usual' Eg. Initialize a Display Update // cout << "Mac #" << (int)GetId() << " Handling Error #" << GetError() << endl; switch (GetError()) { case 6: // home case 8: // control dev case 9: // zero idx case 84: // ON TIME case 100: // timeout // Stop program? return; case 11: // software endswitch case 25: // hardware endswitch DelError(); return; } } double Macs::GetTime() { return fPosTime.Now(); } double Macs::GetMjd() { return fPosTime.CalcMjd(); } double Macs::GetPdoTime() { return fPdoTime.Now(); } double Macs::GetPdoMjd() { return fPdoTime.CalcMjd(); } /* 0x2000 0 rw Maximum positioning error */ /* 1 rw Negative Software Endswitch */ /* 2 rw Positive Software Endswitch */ void Macs::SetNegEndswitch(LWORDS_t val) { SendSDO(0x2000, 1, (LWORD_t)val); WaitForSdo(0x2000, 1); } void Macs::SetPosEndswitch(LWORDS_t val) { SendSDO(0x2000, 2, (LWORD_t)val); WaitForSdo(0x2000, 2); } void Macs::EnableEndswitches(bool neg, bool pos) { SendSDO(0x2000, 3, (LWORD_t)(neg|(pos<<1))); WaitForSdo(0x2000, 3); } Bool_t Macs::HandleTimer(TTimer *t) { /* Fons: ----- timers never trigger at the same time or when in a TTimer::Notify. Little explanation: - there are two types of timers synchronous and a-synchronous. - synchronous timers are only handled via the ROOT eventloop (see TUnixSystem::DispatchOneEvent()). If there are no mouse/keyboard events then the synchronous timer queue is checked. So if the processing of a mouse/keyboard event takes a long time synchronous timers are not called for a while. To prevent this from happening one can call in long procedures gSystem->ProcessEvents(). The system schedules only the next timer in the queue when the current one's Notify() has finished. - a-synchronous timers are triggered via SIGALARM, i.e. the program is interupted and execution jumps to the Notify() function. When the notify is finished the next a-sync timer is scheduled and the system resumes from the place where it was initially interrupted. One of the things to remember when using a-sync timers is don't make any graphics calls in them. X11 is not re-entrant and it might be that the SIGALARM signal interrupted the system while being in X11. A-sync timers are best used to set flags that you can test at a convenient and controlled time. */ SendSDO(0x4000); WaitForSdo(0x4000, 0, kDontWait); if (fTimerOn) fTimeout->Start(fTimeoutTime, kTRUE); return kTRUE; }