diff --git a/CPP/hriboard.cpp b/CPP/hriboard.cpp index 861ead1..bc2562d 100644 --- a/CPP/hriboard.cpp +++ b/CPP/hriboard.cpp @@ -1,307 +1,307 @@ #include "hriboard.h" #include #include #include const int SYNCVAR_LIST_ITEM_SIZE = SYNCVAR_NAME_SIZE + 3; /** * @brief Constructor. */ HriBoard::HriBoard() { } /** * @brief Establish the link with the board. * Establish the link with the board, then stops the streaming and requests the * variables list. * @param comPortName serial port name, in the format "COM1" on Windows, or * "/dev/ttyO1" on UNIX. * @throws A runtime_error is thrown if the serial port could not be opened. */ void HriBoard::openLink(QString comPortName) { streamID = 0; // Setup the serial port. serial.setPortName(comPortName); serial.setBaudRate(UART_BAUDRATE); serial.setDataBits(QSerialPort::Data8); serial.setFlowControl(QSerialPort::NoFlowControl); serial.setParity(QSerialPort::NoParity); connect(&serial, SIGNAL(readyRead()), this, SLOT(onReceivedData())); qDebug() << "Opening the serial COM port..."; if(serial.open(QIODevice::ReadWrite)) qDebug() << "COM port opened successfully."; else throw std::runtime_error("Can't open the COM port."); // Stop the streaming, in case it was enabled. QByteArray ba; ba.append((char)0); sendPacket(PC_MESSAGE_SET_STREAMED_VAR, ba); // Request the variables list. sendPacket(PC_MESSAGE_GET_VARS_LIST); } /** * @brief Sets the SyncVars to stream. * @param varsToStream list of the SyncVars to stream. */ void HriBoard::setStreamedVars(QList varsToStream) { // Copy the list of variables to stream, and compute the size of a streaming // packet. streamedVars.clear(); streamPacketSize = sizeof(quint8) + sizeof(quint32); // Stream ID + timestamp. for(SyncVarPointerBase* svpb : varsToStream) { streamedVars.append(&syncVars[svpb->getVar()->getIndex()]); streamPacketSize += svpb->getVar()->getSize(); } // streamID++; QByteArray ba; ba.append((quint8)varsToStream.size()); ba.append(streamID); for(SyncVarPointerBase* svpb : varsToStream) { int varIndex = svpb->getVar()->getIndex(); ba.append((quint8)varIndex); } sendPacket(PC_MESSAGE_SET_STREAMED_VAR, ba); } /** * @brief Associates a SyncVarPointer to an actual SyncVar, from its name. * @param svp the SyncVarPointer to the SyncVar. * @param name the name of the SyncVar to associate to the pointer. * @return true if a variable with the given name was found, false otherwise. */ bool HriBoard::associate(SyncVarPointerBase &svp, QString name) { for(SyncVar &sv : syncVars) { - if(sv.getName() == name) + if(sv.getName() == name && sv.getType() == svp.getType()) { svp.associate(this, &sv); return true; } } svp.associate(this, nullptr); return false; } /** * @brief Updates a SyncVar on the board with the value of the local SyncVar. * @param var the SyncVar to synchronize. */ void HriBoard::writeRemoteVar(SyncVar *var) { QByteArray ba; ba.append(var->getIndex()); ba.append(var->getData()); sendPacket(PC_MESSAGE_SET_VAR, ba); } /** * @brief Updates a local SyncVar with the value of the SyncVar on the board. * @param var the SyncVar to synchronize. */ void HriBoard::readRemoteVar(SyncVar *var) { QByteArray ba; ba.append(var->getIndex()); sendPacket(PC_MESSAGE_GET_VAR, ba); } /** * @brief Makes a list of all the candidate serial ports. * @return a list of all the serial port that use the right USB-to-UART chip. */ QStringList HriBoard::getComPorts() { QList ports = QSerialPortInfo::availablePorts(); for(int i=ports.size()-1; i >= 0; i--) { // Remove from the list all the serial COM ports that are not the CP210x // USB-to-serial chip. if(!ports[i].description().contains("CP210x")) ports.removeAt(i); } // Make a list of the COM ports names. QStringList portsNames; for(QSerialPortInfo comInfo : ports) portsNames << comInfo.portName(); return portsNames; } /** * @brief Interprets the received bytes, and reacts accordingly. */ void HriBoard::onReceivedData() { QByteArray rxData = serial.readAll(); //qDebug() << "RX:" << rxData; for(int i=0; i 0) rxDataBytesBuffer[dataBytesReady-1] = (firstHalfByte<<4) + (rxByte & 0xf); switch(rxCurrentMessageType) { case STM_MESSAGE_START_INFO: if(dataBytesReady == 0) { // Request variables list. sendPacket(PC_MESSAGE_GET_VARS_LIST); } break; case STM_MESSAGE_VAR: if(dataBytesReady >= 1) { quint8 varIndex = rxDataBytesBuffer[0]; if(dataBytesReady == 1 + syncVars[varIndex].getSize()) { QByteArray ba((char*)&rxDataBytesBuffer[1], syncVars[varIndex].getSize()); syncVars[varIndex].setData(ba); } } break; case STM_MESSAGE_VARS_LIST: if(dataBytesReady >= 1) { quint8 nVars = rxDataBytesBuffer[0]; if(dataBytesReady == 1 + nVars * SYNCVAR_LIST_ITEM_SIZE) { syncVars.clear(); quint8* p = &rxDataBytesBuffer[1]; for(int i=0; igetSize()); streamedVars[i]->setData(value); p += streamedVars[i]->getSize(); } emit syncVarsUpdated(); } } break; case STM_MESSAGE_DEBUG_TEXT: if(dataBytesReady > 0 && rxDataBytesBuffer[dataBytesReady-1] == '\0') { qDebug() << QString((const char*)rxDataBytesBuffer); rxCurrentMessageType = 0; } break; default: // Ignore. break; } } } } /** * @brief Sends a communication packet to the board. * @param messageType the type of the message. * @param dataBytes the data that will be part of the packet, which is * type-specific. If omitted, there will be no data in the message, so it will * contain only the type. */ void HriBoard::sendPacket(comm_PcMessage messageType, QByteArray dataBytes) { QByteArray txBuffer; txBuffer.append((1<<7) + (uint8_t)messageType); for(int i=0; i> 4); // MSB. txBuffer.append(((quint8)dataBytes[i]) & 0xf); // LSB. } serial.write(txBuffer); serial.waitForBytesWritten(-1); } diff --git a/CPP/syncvar.cpp b/CPP/syncvar.cpp index 3dc64da..85c70b5 100644 --- a/CPP/syncvar.cpp +++ b/CPP/syncvar.cpp @@ -1,168 +1,192 @@ #include "syncvar.h" #include "hriboard.h" #include VarType getType(const bool&) { return VarType::BOOL; } VarType getType(const uint8_t&) { return VarType::UINT8; } VarType getType(const int8_t&) { return VarType::INT8; } VarType getType(const uint16_t&) { return VarType::UINT16; } VarType getType(const int16_t&) { return VarType::INT16; } VarType getType(const uint32_t&) { return VarType::UINT32; } VarType getType(const int32_t&) { return VarType::INT32; } VarType getType(const uint64_t&) { return VarType::UINT64; } VarType getType(const int64_t&) { return VarType::INT64; } VarType getType(const float&) { return VarType::FLOAT32; } VarType getType(const double&) { return VarType::FLOAT64; } const int VAR_SIZE[] = { 1, 1, 1, 2, 2, 4, 4, 8, 8, 4, 8 }; /** * @brief Constructor * @param index index in the SyncVars list. * @param name name of the SyncVar. * @param type type of the SyncVar value. * @param access access right of the SyncVar. */ SyncVar::SyncVar(int index, QString name, VarType type, VarAccess access) : index(index), name(name), type(type), access(access) { data.resize(VAR_SIZE[(int)type]); upToDate = false; } /** * @brief Gets the index in the SyncVars list. * @return the index. */ int SyncVar::getIndex() const { return index; } /** * @brief Gets the name of the SyncVar. * @return the name. */ QString SyncVar::getName() const { return name; } /** * @brief Gets the size of the SyncVar value. * @return the value size [byte]. */ int SyncVar::getSize() const { return data.size(); } +/** + * @brief Gets the variable type. + * @return the variable type. + */ +VarType SyncVar::getType() const +{ + return type; +} + +/** + * @brief Gets the SyncVar access rights. + * @return the SyncVar access rights. + */ +VarAccess SyncVar::getAccess() const +{ + return access; +} + /** * @brief Gets the local value in raw bytes form. * @return a copy of the value data. * @remark this value may not match the actual value of the variable on the * board, because this function does not perform the synchronisation. */ QByteArray SyncVar::getData() const { return data; } /** * @brief Sets the local value with raw bytes. * @param newData the new value data. * @remark this function only sets the local value of the SyncVar, not the value * of the one on the board (no synchronisation performed). */ void SyncVar::setData(QByteArray newData) { if(newData.size() != data.size()) throw std::runtime_error("SyncVar::setData(): size do not match."); data = newData; upToDate = true; } /** * @brief Gets if the variable is up-to-date. * @return true if the variable value has been set, false if it has not been set * since the beginning, or the call to setOutOfDate(). */ bool SyncVar::isUpToDate() const { return upToDate; } /** * @brief Sets the variable as out-of-date. * This function is useful when the user need to know when the variable value * has been updated. */ void SyncVar::setOutOfDate() { upToDate = false; } /** * @brief Constructor. */ SyncVarPointerBase::SyncVarPointerBase() { syncVar = nullptr; hriBoard = nullptr; } /** * @brief Associates a SyncVarPointer to a SyncVar. * @param hriBoard the HRI board object. * @param syncVar the SyncVar to associate to the SyncVarPointer. * @remark This method should only be called by HriBoard. */ void SyncVarPointerBase::associate(HriBoard *hriBoard, SyncVar *syncVar) { this->hriBoard = hriBoard; this->syncVar = syncVar; } /** * @brief Gets if the SyncVarPointer can be "dereferenced". * @return true if the pointer has been associated with a SyncVar, false * otherwise. */ bool SyncVarPointerBase::isValid() const { return hriBoard != nullptr; } /** * @brief Gets the SyncVar associated to the SyncVarPointer. * @return a pointer to the associated SyncVar, or nullptr if it was not * associated yet. */ SyncVar *SyncVarPointerBase::getVar() const { return syncVar; } /** * @brief Updates the SyncVar value from the one on the board. * This method requests the HRI board to send the value of the SyncVar. The * local value will be updated later, when the "SyncVar value packet" will be * received. * To check if the value has actually been received, call isUpToDate(). + * @remark This method does nothing if the SyncVar is WRITEONLY. */ void SyncVarPointerBase::readValue() { - syncVar->setOutOfDate(); - hriBoard->readRemoteVar(syncVar); + if(syncVar->getAccess() != WRITEONLY) + { + syncVar->setOutOfDate(); + hriBoard->readRemoteVar(syncVar); + } } /** * @brief Updates the SyncVar value on the board from local value. + * @remark This method does nothing if the SyncVar is READONLY. */ void SyncVarPointerBase::writeValue() { - hriBoard->writeRemoteVar(syncVar); + if(syncVar->getAccess() != READONLY) + hriBoard->writeRemoteVar(syncVar); } diff --git a/CPP/syncvar.h b/CPP/syncvar.h index 489b91a..a47633a 100644 --- a/CPP/syncvar.h +++ b/CPP/syncvar.h @@ -1,125 +1,150 @@ #ifndef SYNCVAR_H #define SYNCVAR_H #include #include #include #include "../Firmware/src/definitions.h" typedef comm_VarType VarType; typedef comm_VarAccess VarAccess; +VarType getType(const bool&); +VarType getType(const uint8_t&); +VarType getType(const int8_t&); +VarType getType(const uint16_t&); +VarType getType(const int16_t&); +VarType getType(const uint32_t&); +VarType getType(const int32_t&); +VarType getType(const uint64_t&); +VarType getType(const int64_t&); +VarType getType(const float&); +VarType getType(const double&); + template class SyncVarPointer; template T& operator*(SyncVarPointer &svp); class HriBoard; /** @defgroup SyncVar SyncVar * @brief Set of classes to handle SyncVar operations conveniently. * * First, get the list of the SyncVars from the HRI board, by creating a * HriBoard object and calling openLink(). The given slot function will be * called when the SyncVars list will be received. Then, it is possible to * create and associate SyncVarPointers to manipulate these SyncVars. * * A local SyncVar can be accessed from a SyncVarPointer, simply using the * * operator. To actually synchronize this value with the one of the board, call * writeRemoteVar() or readRemoteVar(). * * @addtogroup SyncVar * @{ */ /** * @brief Variable that can be synchronized with its HRI board counterpart. */ class SyncVar { template friend T& operator*(SyncVarPointer &svp); public: SyncVar(int index, QString name, VarType type, VarAccess access); int getIndex() const; QString getName() const; int getSize() const; + VarType getType() const; + VarAccess getAccess() const; QByteArray getData() const; void setData(QByteArray newData); bool isUpToDate() const; void setOutOfDate(); private: QByteArray data; ///< SyncVar value, stored as raw bytes. int index; ///< Index of the SyncVar in the list. QString name; ///< Name describing the SyncVar. VarType type; ///< Type of variable. VarAccess access; ///< Access rights of the variable. bool upToDate; ///< Indicates whether the local value is up-to-date or not. }; /** * @brief Generic version of SyncVarPointer. - * @remark This generic version of SyncVarPointer should not be instanced, use + * @remark This generic version of SyncVarPointer cannot be instanced, use * SyncVarPointer instead. */ class SyncVarPointerBase { public: SyncVarPointerBase(); void associate(HriBoard *hriBoard, SyncVar *syncVar); bool isValid() const; SyncVar* getVar() const; void readValue(); void writeValue(); + virtual VarType getType() = 0; + protected: HriBoard* hriBoard; ///< Pointer to the HRI board object. SyncVar* syncVar; ///< Pointer to the associated SyncVar. + }; /** * @brief Typed handle to a SyncVar, for easy value manipulation. * Using the value of the SyncVars is tedious, since their data array must be * casted. SyncVarPointer is a typed object to handle easily a SyncVar value. * * First, create SyncVarPointer of the same type that the SyncVar to be * manipulated. Then, associate it to the desired variable with associate(). * * To access the SyncVar value, just use the * operator, like a pointer. * To synchronize the local SyncVar value with the one on the board, call * readValue() or writeValue(). */ template class SyncVarPointer : public SyncVarPointerBase { - +protected: + /** + * @brief Gets the type of the SyncVarPointer. + * @return the type of the SyncVar that can be associated to this pointer. + */ + VarType getType() + { + return ::getType(**this); + } }; /** * @brief Gets a typed reference to the SyncVar data, from a SyncVarPointer. * @param svp the SyncVarPointer to get the SyncVar value from. * @return a reference to the value of the SyncVar. */ template T& operator*(SyncVarPointer &svp) { if (svp.isValid()) return (T&)*(T*)svp.getVar()->data.data(); else throw std::runtime_error("The SyncVar pointer does not have an associated SyncVar."); } /** * @} */ #endif // SYNCVAR_H