No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

comrobot.cpp 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. /*
  2. * Copyright (C) 2018 dimercur
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include "comrobot.h"
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <unistd.h>
  21. #include <fcntl.h>
  22. #include <termios.h>
  23. #include <string>
  24. #include <stdexcept>
  25. #ifdef __FOR_PC__
  26. #define USART_FILENAME "/dev/ttyUSB0"
  27. #else
  28. #define USART_FILENAME "/dev/ttyS0"
  29. #endif /* __FOR_PC__ */
  30. /*
  31. * Constants to be used for communicating with robot. Contains command tag
  32. */
  33. const char LABEL_ROBOT_PING = 'p';
  34. const char LABEL_ROBOT_RESET = 'r';
  35. const char LABEL_ROBOT_START_WITH_WD = 'W';
  36. const char LABEL_ROBOT_START_WITHOUT_WD = 'u';
  37. const char LABEL_ROBOT_RELOAD_WD = 'w';
  38. const char LABEL_ROBOT_MOVE = 'M';
  39. const char LABEL_ROBOT_TURN = 'T';
  40. const char LABEL_ROBOT_GET_BATTERY = 'v';
  41. const char LABEL_ROBOT_GET_STATE = 'b';
  42. const char LABEL_ROBOT_POWEROFF = 'z';
  43. const char LABEL_ROBOT_OK = 'O';
  44. const char LABEL_ROBOT_ERROR = 'E';
  45. const char LABEL_ROBOT_UNKNOWN_COMMAND = 'C';
  46. const char LABEL_ROBOT_SEPARATOR_CHAR = '=';
  47. const char LABEL_ROBOT_ENDING_CHAR = 0x0D; // carriage return (\\r)
  48. /**
  49. * Open serial link with robot
  50. * @return File descriptor
  51. * @throw std::runtime_error if it fails
  52. */
  53. int ComRobot::Open() {
  54. return this->Open(USART_FILENAME);
  55. }
  56. /**
  57. * Open serial link with robot
  58. * @param usart Filename of usart to open
  59. * @return File descriptor
  60. * @throw std::runtime_error if it fails
  61. */
  62. int ComRobot::Open(string usart) {
  63. struct termios options;
  64. fd = open(usart.c_str(), O_RDWR | O_NOCTTY /*| O_NDELAY*/); //Open in blocking read/write mode
  65. if (fd == -1) {
  66. cerr<<"["<<__PRETTY_FUNCTION__<<"] Unable to open UART ("<<usart<<"). Ensure it is not in use by another application"<<endl<<flush;
  67. throw std::runtime_error{"Unable to open UART"};
  68. exit(EXIT_FAILURE);
  69. }
  70. else
  71. {
  72. fcntl(fd, F_SETFL, 0);
  73. tcgetattr(fd, &options);
  74. options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
  75. cfsetospeed (&options, B9600);
  76. cfsetispeed (&options, B9600);
  77. options.c_cc[VMIN]=0;
  78. options.c_cc[VTIME]=1; /* Timeout of 100 ms per character*/
  79. tcsetattr(fd, TCSANOW, &options);
  80. }
  81. return fd;
  82. }
  83. /**
  84. * Close serial link
  85. * @return Success if above 0, failure if below 0
  86. */
  87. int ComRobot::Close() {
  88. return close(fd);
  89. }
  90. /**
  91. * Send a message to robot
  92. * @param msg Message to send to robot
  93. * @return 1 if success, 0 otherwise
  94. * @attention Message is destroyed (delete) after being sent. You do not need to delete it yourself
  95. * @attention Write is blocking until message is written into buffer (linux side)
  96. * @warning Write is not thread save : check that multiple tasks can't access this method simultaneously
  97. */
  98. Message *ComRobot::Write(Message* msg) {
  99. Message *msgAnswer;
  100. string s;
  101. if (this->fd != -1) {
  102. Write_Pre();
  103. s=MessageToString(msg);
  104. AddChecksum(s);
  105. //cout << "[" <<__PRETTY_FUNCTION__<<"] Send command: "<<s<<endl<<flush;
  106. int count = write(this->fd, s.c_str(), s.length()); //Filestream, bytes to write, number of bytes to write
  107. if (count < 0) {
  108. cerr << "[" << __PRETTY_FUNCTION__ << "] UART TX error (" << to_string(count) << ")" << endl << flush;
  109. msgAnswer = new Message(MESSAGE_ANSWER_COM_ERROR);
  110. } else { /* write successfull, read answer from robot */
  111. try {
  112. s = Read();
  113. cout << "Answer = "<<s<<endl<<flush;
  114. if (VerifyChecksum(s)) {
  115. msgAnswer = StringToMessage(s);
  116. } else msgAnswer = new Message(MESSAGE_ANSWER_ROBOT_UNKNOWN_COMMAND);
  117. } catch (std::runtime_error &e) {
  118. s = string(e.what());
  119. if (s.find("imeout")) { // timeout detecté
  120. msgAnswer = new Message(MESSAGE_ANSWER_ROBOT_TIMEOUT);
  121. } else {
  122. msgAnswer = new Message(MESSAGE_ANSWER_COM_ERROR);
  123. }
  124. }
  125. }
  126. } else {
  127. cerr << __PRETTY_FUNCTION__ << ": Com port not open" << endl << flush;
  128. throw std::runtime_error{"Com port not open"};
  129. }
  130. // deallocation of msg
  131. delete(msg);
  132. return msgAnswer;
  133. }
  134. /**
  135. * Get a message from robot
  136. * @return Message currently received
  137. * @attention A message object is created (new) when receiving data from robot. You MUST remember to destroy is (delete) after use
  138. * @attention Read method is blocking until a message is received
  139. * @warning Read is not thread safe : Do not call it in multiple tasks simultaneously
  140. */
  141. string ComRobot::Read() {
  142. string s;
  143. int rxLength;
  144. unsigned char receivedChar;
  145. do {
  146. rxLength = read(this->fd, (void*) &receivedChar, 1); //Filestream, buffer to store in, number of bytes to read (max)
  147. if (rxLength ==0) { // timeout
  148. // try again
  149. rxLength = read(this->fd, (void*) &receivedChar, 1); //Filestream, buffer to store in, number of bytes to read (max)
  150. if (rxLength ==0) { // re-timeout: it sucks !
  151. throw std::runtime_error {"ComRobot::Read: Timeout when reading from com port"};
  152. }
  153. } else if (rxLength <0) { // big pb !
  154. throw std::runtime_error {"ComRobot::Read: Unknown problem when reading from com port"};
  155. } else { // everything ok
  156. if ((receivedChar != '\r') && (receivedChar != '\n')) s += receivedChar;
  157. }
  158. } while ((receivedChar != '\r') && (receivedChar != '\n'));
  159. return s;
  160. }
  161. Message *ComRobot::SendCommand(Message* msg, MessageID answerID, int maxRetries) {
  162. int counter = maxRetries;
  163. Message *msgSend;
  164. Message *msgRcv;
  165. Message *msgTmp;
  166. do {
  167. msgSend = msg->Copy();
  168. cout << "S => " << msgSend->ToString() << endl << flush;
  169. msgTmp = Write(msgSend);
  170. cout << "R <= " << msgTmp->ToString() << endl << flush;
  171. if (msgTmp->CompareID(answerID)) counter = 0;
  172. else counter--;
  173. if (counter == 0) msgRcv=msgTmp->Copy();
  174. delete(msgTmp);
  175. } while (counter);
  176. delete (msg);
  177. return msgRcv;
  178. }
  179. /**
  180. * Convert an array of char to its message representation (when receiving data from stm32)
  181. * @param bytes Array of char
  182. * @return Message corresponding to received array of char
  183. */
  184. Message* ComRobot::StringToMessage(string s) {
  185. Message *msg;
  186. switch (s[0]) {
  187. case LABEL_ROBOT_OK:
  188. msg=new Message(MESSAGE_ANSWER_ACK);
  189. break;
  190. case LABEL_ROBOT_ERROR:
  191. msg=new Message(MESSAGE_ANSWER_ROBOT_ERROR);
  192. break;
  193. case LABEL_ROBOT_UNKNOWN_COMMAND:
  194. msg=new Message(MESSAGE_ANSWER_ROBOT_UNKNOWN_COMMAND);
  195. break;
  196. case '0':
  197. msg=new MessageBattery(MESSAGE_ROBOT_BATTERY_LEVEL, BATTERY_EMPTY);
  198. break;
  199. case '1':
  200. msg=new MessageBattery(MESSAGE_ROBOT_BATTERY_LEVEL, BATTERY_LOW);
  201. break;
  202. case '2':
  203. msg=new MessageBattery(MESSAGE_ROBOT_BATTERY_LEVEL, BATTERY_FULL);
  204. break;
  205. default:
  206. msg=new Message(MESSAGE_ANSWER_ROBOT_ERROR);
  207. cerr<<"["<<__PRETTY_FUNCTION__<<"] Unknown message received from robot (" << s <<")"<<endl<<flush;
  208. }
  209. return msg;
  210. }
  211. /**
  212. * Convert a message to its array of char representation (for sending command to stm32)
  213. * @param msg Message to be sent to robot
  214. * @param buffer Array of char, image of message to send
  215. */
  216. string ComRobot::MessageToString(Message *msg) {
  217. string s;
  218. float val_f;
  219. int val_i;
  220. unsigned char *b;
  221. switch (msg->GetID()) {
  222. case MESSAGE_ROBOT_PING:
  223. s+=LABEL_ROBOT_PING;
  224. break;
  225. case MESSAGE_ROBOT_RESET:
  226. s+=LABEL_ROBOT_RESET;
  227. break;
  228. case MESSAGE_ROBOT_POWEROFF:
  229. s+=LABEL_ROBOT_POWEROFF;
  230. break;
  231. case MESSAGE_ROBOT_START_WITHOUT_WD:
  232. s+=LABEL_ROBOT_START_WITHOUT_WD;
  233. break;
  234. case MESSAGE_ROBOT_START_WITH_WD:
  235. s+=LABEL_ROBOT_START_WITH_WD;
  236. break;
  237. case MESSAGE_ROBOT_RELOAD_WD:
  238. s+=LABEL_ROBOT_RELOAD_WD;
  239. break;
  240. case MESSAGE_ROBOT_BATTERY_GET:
  241. s+=LABEL_ROBOT_GET_BATTERY;
  242. break;
  243. case MESSAGE_ROBOT_STATE_GET:
  244. s+=LABEL_ROBOT_GET_STATE;
  245. break;
  246. case MESSAGE_ROBOT_GO_FORWARD:
  247. s+=LABEL_ROBOT_MOVE;
  248. s+=LABEL_ROBOT_SEPARATOR_CHAR;
  249. s.append(to_string(500000));
  250. break;
  251. case MESSAGE_ROBOT_GO_BACKWARD:
  252. s+=LABEL_ROBOT_MOVE;
  253. s+=LABEL_ROBOT_SEPARATOR_CHAR;
  254. s.append(to_string(-500000));
  255. break;
  256. case MESSAGE_ROBOT_GO_LEFT:
  257. s+=LABEL_ROBOT_TURN;
  258. s+=LABEL_ROBOT_SEPARATOR_CHAR;
  259. s.append(to_string(90));
  260. break;
  261. case MESSAGE_ROBOT_GO_RIGHT:
  262. s+=LABEL_ROBOT_TURN;
  263. s+=LABEL_ROBOT_SEPARATOR_CHAR;
  264. s.append(to_string(-90));
  265. break;
  266. case MESSAGE_ROBOT_MOVE:
  267. s+=LABEL_ROBOT_MOVE;
  268. s+=LABEL_ROBOT_SEPARATOR_CHAR;
  269. s.append(to_string(((MessageInt*)msg)->GetValue()));
  270. break;
  271. case MESSAGE_ROBOT_TURN:
  272. s+=LABEL_ROBOT_TURN;
  273. s+=LABEL_ROBOT_SEPARATOR_CHAR;
  274. s.append(to_string(((MessageInt*)msg)->GetValue()));
  275. break;
  276. default:
  277. cerr<<"["<<__PRETTY_FUNCTION__<<"] Invalid message for robot ("<<msg->ToString()<<")"<<endl<<flush;
  278. throw std::runtime_error {"Invalid message"};
  279. }
  280. return s;
  281. }
  282. /**
  283. * Add a checksum and carriage return to a command string
  284. * @param[in,out] s String containing command for robot, without ending char (carriage return)
  285. */
  286. void ComRobot::AddChecksum(string &s) {
  287. unsigned char checksum=0;
  288. for (string::iterator it=s.begin(); it!=s.end(); ++it) {
  289. checksum ^= (unsigned char)*it;
  290. }
  291. s+=(char)checksum; // Add calculated checksum
  292. s+=(char)LABEL_ROBOT_ENDING_CHAR;
  293. }
  294. /**
  295. * Verify if checksum of an incoming answer from robot is valid,
  296. * then remove checksum from incoming answer (if checksum is ok)
  297. * @param[in,out] s String containing incoming answer from robot
  298. * @return true is checksum is valid, false otherwise.
  299. */
  300. bool ComRobot::VerifyChecksum(string &s) {
  301. unsigned char checksum=0;
  302. for (string::iterator it=s.begin(); it!=s.end(); ++it) {
  303. checksum ^= (unsigned char)*it;
  304. }
  305. if (checksum==0) { // checksum is ok, remove last char of string (checksum)
  306. s.pop_back(); // remove last char
  307. return true;
  308. }
  309. else return false;
  310. }