package lenght mismatch per il pacchetto tipo 8 AdditionalInfo, aggiunto uchar foo alla fine per far sparire l'errore.. chi sa perchè?
259 lines
9.3 KiB
C++
259 lines
9.3 KiB
C++
/*
|
|
* URCLinterface.cpp
|
|
*
|
|
* Created on: Jan 14, 2019
|
|
* Author: emanuele
|
|
*
|
|
* Implementazione C++ del protocollo di comunicazione socket per Universal Robot e-series.
|
|
* Protocollo client interface, comunicazione 10Hz su porte 30001 - 30002, informazioni e messaggi parziali,
|
|
* invio di comandi URscript
|
|
*
|
|
* 20190114 - Prima Scittura
|
|
* 20190114 - Inizio implementazione della connessione socket con libreria boost, scritta funzione connect.
|
|
* 20190115 - Continuo scirttura funzione dataRead(), verificato il protocollo rispetto al manuale, la funzione legge e divide corretamente i pacchetti
|
|
* tra di loro. ora bisogna separare i sottopacchetti.
|
|
* 20190116 - La separazione dei sottopacchetti viene fatta da delle classi specializzate che scompattano e cambiano
|
|
* l'endianness dei dati se necessario.
|
|
* 20190117 - Prima scirttura delle classi che scompattano i dati, scirtta versionMessage, risolti alcuni dubbi
|
|
* e problemi con i puntatori. A quanto pare non è disponibile una libreria che faccia quello che
|
|
* fa struct.unpack di python
|
|
* 20190118 - Completata la scrittura delle classi che scompattano i dati, le strutture combaciano con il
|
|
* manuale. Inizio della scrittura finzioni che mandano i comandi URscript al controller,
|
|
* valutare se vale la pena implementare per ogn tipo di funzione movimento
|
|
* prevista in URscript o no..
|
|
* 20190121 - Passaggio a nuova Vm con Ubuntu 18.04LTS e g++ 7.3.0, risolti alcuni problemi di compilazione,
|
|
* passaggio a librerie boost-1.68
|
|
* Scritta funzione di invio script grezzi al controller, inizio scrittura a thread che continua a ricevere
|
|
* i dati dal robot.
|
|
*/
|
|
|
|
#include "URCLinterface.h"
|
|
|
|
using namespace subpackage;
|
|
using namespace std;
|
|
using boost::format;
|
|
using std::string;
|
|
|
|
|
|
UR_CLinterface::UR_CLinterface(shared_ptr<spdlog::logger> logger) {
|
|
log = logger;
|
|
log->info("Initializing UR Client Interface"); // @suppress("Ambiguous problem")
|
|
rsocket = new tcp::socket(io_service);
|
|
}
|
|
|
|
UR_CLinterface::~UR_CLinterface() {
|
|
// TODO Auto-generated destructor stub
|
|
if (rsocket->is_open()) this->disconnect();
|
|
delete rsocket;
|
|
}
|
|
|
|
bool UR_CLinterface::connect(const char* ip, uint16_t port) {
|
|
try {
|
|
rsocket->connect(
|
|
tcp::endpoint(boost::asio::ip::address::from_string(ip), port));
|
|
if (static_cast<bool>(rsocket->is_open())) {
|
|
log->info("Connected to {0} on port {1}", ip, port);
|
|
return true;
|
|
}
|
|
} catch (boost::system::error_code* e) {
|
|
log->error(e->message());
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void UR_CLinterface::disconnect(void) {
|
|
rsocket->close(error);
|
|
if (error){
|
|
log->error("Cannot close socket, code: {}", error.message()); // @suppress("Assignment in condition")
|
|
return;
|
|
}
|
|
log->info("Disconnected"); // @suppress("Ambiguous problem")
|
|
}
|
|
|
|
void UR_CLinterface::sendCommand(std::string command) {
|
|
char* dataOut=NULL;
|
|
if (rsocket->is_open()){
|
|
command = command + '\n';
|
|
dataOut = static_cast<char*>(malloc(command.size()));
|
|
strncpy(dataOut, command.c_str(), command.size());
|
|
log->debug("Sending: {}, s:{}",dataOut,command.size());
|
|
boost::asio::write(*rsocket, boost::asio::buffer(dataOut, command.size()), error);
|
|
free(dataOut);
|
|
}
|
|
if (!error) return;
|
|
else {
|
|
log->error("Failed to send data to socket, code: {}", error.message());
|
|
}
|
|
|
|
}
|
|
|
|
void UR_CLinterface::parseData(void){
|
|
uint32_t packageSize=lastDataLen;
|
|
uint32_t subPackageSize;
|
|
unsigned char packageType;
|
|
unsigned char subPackageType;
|
|
uint32_t currIndex=0;
|
|
clientSubpackage* subP=NULL;
|
|
|
|
//main message type is on 4th byte of the message
|
|
packageType = lastData[sizeof(uint32_t)];
|
|
|
|
switch(packageType){
|
|
case VERSION_MESSAGE: //begin version message parse
|
|
if (firstMessage){
|
|
log->info("Version Message: size[{}]",packageSize); // @suppress("Ambiguous problem")
|
|
subP = new versionSub(lastData, &clientData);
|
|
subP->parse();
|
|
firstMessage=false;
|
|
long t=static_cast<long>(clientData.versonMessage.timestamp);
|
|
std::string timestr=std::string(asctime(gmtime(&t)));
|
|
timestr.pop_back();
|
|
log->info("Connection Time: {}", timestr);
|
|
log->info("Controller Version {0}.{1}", clientData.versonMessage.majorVersion,
|
|
clientData.versonMessage.minorVersion);
|
|
log->info("ProjectName: {}", clientData.versonMessage.projectName->c_str());
|
|
}
|
|
break;
|
|
|
|
case ROBOT_MESSAGE: // begin subpacket parse
|
|
// move index to skip package leader
|
|
currIndex = sizeof(uint32_t)+sizeof(char);
|
|
log->debug("Robot Message: size[{}]",packageSize);
|
|
// iterate until end of package
|
|
while (currIndex < lastDataLen){
|
|
// free memory
|
|
if (subP != NULL) {
|
|
delete subP;
|
|
subP = NULL;
|
|
}
|
|
|
|
//extract subpackage size
|
|
memcpy(&subPackageSize, lastData+currIndex, sizeof(uint32_t));
|
|
subPackageSize=big_to_native(subPackageSize);
|
|
// extract subpackage type
|
|
subPackageType=lastData[currIndex+sizeof(uint32_t)];
|
|
|
|
try{
|
|
switch (subPackageType){
|
|
case ROBOT_MODE_DATA:
|
|
log->debug("SubP_ModeData"); // @suppress("Ambiguous problem")
|
|
subP = new robotModeSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case JOINT_DATA:
|
|
log->debug("SubP_JointData"); // @suppress("Ambiguous problem")
|
|
subP = new jointDataSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case TOOL_DATA:
|
|
log->debug("SubP_ToolData"); // @suppress("Ambiguous problem")
|
|
subP = new toolDataSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case MASTER_BOARD_DATA:
|
|
log->debug("SubP_MasterBoardData"); // @suppress("Ambiguous problem")
|
|
subP = new masterBoardSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case CARTESIAN_INFO:
|
|
log->debug("SubP_CartesianInfo"); // @suppress("Ambiguous problem")
|
|
subP = new cartesianInfoSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case KINEMATICS_INFO:
|
|
log->debug("SubP_KinematicsInfo"); // @suppress("Ambiguous problem")
|
|
subP = new kineInfoSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case CONFIGURATION_DATA:
|
|
log->debug("SubP_ConfigurationData"); // @suppress("Ambiguous problem")
|
|
subP = new configurationSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case FORCE_MODE_DATA:
|
|
log->debug("SubP_ForceModeData"); // @suppress("Ambiguous problem")
|
|
subP = new forceModeSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case ADDITIONAL_INFO:
|
|
log->debug("SubP_AdditionalInfo"); // @suppress("Ambiguous problem")
|
|
subP = new additionalInfoSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case CALIBRATION_DATA:
|
|
log->debug("SubP_CalibrationData"); // @suppress("Ambiguous problem")
|
|
subP = new calibrationSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case TOOL_COM_INFO:
|
|
log->debug("SubP_ToolComInfo"); // @suppress("Ambiguous problem")
|
|
subP = new toolComSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case TOOL_MODE_INFO:
|
|
log->debug("SubP_ToolModeInfo"); // @suppress("Ambiguous problem")
|
|
subP = new toolModeSub(&lastData[currIndex], &clientData);
|
|
break;
|
|
case SAFETY_DATA:
|
|
//TODO: not yet implemented
|
|
break;
|
|
default:
|
|
log->error("Unknown Subpackage: {}", subPackageType);
|
|
break;
|
|
}
|
|
if (subP != NULL) subP->parse();
|
|
currIndex+=subPackageSize;
|
|
} catch (long sizediff){
|
|
log->error("Package Length Mismatch: type_{:d}, diff_{:d}",subPackageType,sizediff);
|
|
currIndex+=subPackageSize;
|
|
}
|
|
}
|
|
log->debug("End of Package"); // @suppress("Ambiguous problem")
|
|
break;
|
|
|
|
default:
|
|
log->error("Unknown package type: {}",packageType);
|
|
break;
|
|
}
|
|
clientData.lastUpdate = time(NULL);
|
|
}
|
|
|
|
void UR_CLinterface::readData(void) {
|
|
uint32_t packlen;
|
|
uint32_t readata = 0;
|
|
string rawdata;
|
|
|
|
if (rsocket->is_open()){
|
|
// clear last data vector
|
|
if(lastData!=NULL)
|
|
free(lastData);
|
|
//read first incoming byte for packet length
|
|
boost::asio::read(*rsocket, receive_buffer, boost::asio::transfer_exactly(HEADERSIZE), error);
|
|
if (!error) {
|
|
const unsigned char* f = boost::asio::buffer_cast<const unsigned char*>(receive_buffer.data());
|
|
memcpy(&packlen, f, HEADERSIZE * sizeof(char));
|
|
// all number are represented in network byte order, conversion required
|
|
packlen = ntohl(packlen);
|
|
// allocate memory for incoming package
|
|
lastData = static_cast<unsigned char*>(malloc(packlen));
|
|
// blocking read of exactly packlen bytes
|
|
readata += boost::asio::read(*rsocket, receive_buffer,boost::asio::transfer_exactly(packlen), error);
|
|
//retreive all data from buffer
|
|
const unsigned char* ff = boost::asio::buffer_cast<const unsigned char*>(receive_buffer.data());
|
|
// flush buffer for reuse
|
|
receive_buffer.consume(packlen);
|
|
|
|
// print packet dump
|
|
log->debug("Successful read: {0} bytes", readata);
|
|
if (log->level() == spdlog::level::trace) {
|
|
char hh[5];
|
|
for (uint32_t k = 0; k < packlen; k++) {
|
|
sprintf(hh, "%02x|", ff[k]);
|
|
rawdata.append(hh);
|
|
if (k % 16 == 0 && k != 0)
|
|
rawdata.append("\n");
|
|
}
|
|
log->trace("\n{}\n", rawdata);
|
|
}
|
|
//export data to vector
|
|
memcpy(lastData, ff, packlen);
|
|
lastDataLen=packlen;
|
|
} else {
|
|
log->error("Failed to read data from socket, code: {}", error.message());
|
|
}
|
|
} else {
|
|
log->error("Robot not connected!, call connect(ip,port) instead"); // @suppress("Ambiguous problem")
|
|
}
|
|
}
|
|
|