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 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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 __SIMULATION__
  26. #include <sys/socket.h>
  27. #include <netinet/in.h>
  28. #include <arpa/inet.h>
  29. int sock = 0;
  30. #define PORT 6699
  31. #endif
  32. #ifdef __FOR_PC__
  33. #define USART_FILENAME "/dev/ttyUSB0"
  34. #else
  35. #define USART_FILENAME "/dev/ttyS0"
  36. #endif /* __FOR_PC__ */
  37. /*
  38. * Constants to be used for communicating with robot. Contains command tag
  39. */
  40. const char LABEL_ROBOT_PING = 'p';
  41. const char LABEL_ROBOT_RESET = 'r';
  42. const char LABEL_ROBOT_START_WITH_WD = 'W';
  43. const char LABEL_ROBOT_START_WITHOUT_WD = 'u';
  44. const char LABEL_ROBOT_RELOAD_WD = 'w';
  45. const char LABEL_ROBOT_MOVE = 'M';
  46. const char LABEL_ROBOT_TURN = 'T';
  47. const char LABEL_ROBOT_GET_BATTERY = 'v';
  48. const char LABEL_ROBOT_GET_STATE = 'b';
  49. const char LABEL_ROBOT_POWEROFF = 'z';
  50. const char LABEL_ROBOT_OK = 'O';
  51. const char LABEL_ROBOT_ERROR = 'E';
  52. const char LABEL_ROBOT_UNKNOWN_COMMAND = 'C';
  53. const char LABEL_ROBOT_SEPARATOR_CHAR = '=';
  54. const char LABEL_ROBOT_ENDING_CHAR = 0x0D; // carriage return (\\r)
  55. /**
  56. * Open serial link with robot
  57. * @return File descriptor
  58. * @throw std::runtime_error if it fails
  59. */
  60. int ComRobot::Open() {
  61. return this->Open(USART_FILENAME);
  62. }
  63. /**
  64. * Open serial link with robot
  65. * @param usart Filename of usart to open
  66. * @return File descriptor
  67. * @throw std::runtime_error if it fails
  68. */
  69. int ComRobot::Open(string usart) {
  70. struct termios options;
  71. #ifdef __SIMULATION__
  72. struct sockaddr_in serv_addr;
  73. if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  74. printf("\n Socket creation error \n");
  75. return -1;
  76. }
  77. serv_addr.sin_family = AF_INET;
  78. serv_addr.sin_port = htons(PORT);
  79. // Convert IPv4 and IPv6 addresses from text to binary form
  80. if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
  81. printf("\nInvalid address/ Address not supported \n");
  82. return -1;
  83. }
  84. if (connect(sock, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
  85. return -1;
  86. }
  87. /*send(sock , hello , strlen(hello) , 0 );
  88. printf("Hello message sent\n");
  89. valread = read( sock , buffer, 1024);
  90. printf("%s\n",buffer ); */
  91. return 1;
  92. #else
  93. fd = open(usart.c_str(), O_RDWR | O_NOCTTY /*| O_NDELAY*/); //Open in blocking read/write mode
  94. if (fd == -1) {
  95. cerr << "[" << __PRETTY_FUNCTION__ << "] Unable to open UART (" << usart << "). Ensure it is not in use by another application" << endl << flush;
  96. throw std::runtime_error{"Unable to open UART"};
  97. exit(EXIT_FAILURE);
  98. } else {
  99. fcntl(fd, F_SETFL, 0);
  100. tcgetattr(fd, &options);
  101. options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
  102. cfsetospeed(&options, B9600);
  103. cfsetispeed(&options, B9600);
  104. options.c_cc[VMIN] = 0;
  105. options.c_cc[VTIME] = 1; /* Timeout of 100 ms per character */
  106. tcsetattr(fd, TCSANOW, &options);
  107. }
  108. return fd;
  109. #endif
  110. }
  111. /**
  112. * Close serial link
  113. * @return Success if above 0, failure if below 0
  114. */
  115. int ComRobot::Close() {
  116. return close(fd);
  117. }
  118. /**
  119. * Send a message to robot
  120. * @param msg Message to send to robot
  121. * @return 1 if success, 0 otherwise
  122. * @attention Message is destroyed (delete) after being sent. You do not need to delete it yourself
  123. * @attention Write is blocking until message is written into buffer (linux side)
  124. * @warning Write is not thread save : check that multiple tasks can't access this method simultaneously
  125. */
  126. Message *ComRobot::Write(Message* msg) {
  127. Message *msgAnswer;
  128. string s;
  129. if (this->fd != -1) {
  130. Write_Pre();
  131. s = MessageToString(msg);
  132. #ifdef __SIMULATION__
  133. char buffer[1024] = {0};
  134. cout << "[" <<__PRETTY_FUNCTION__<<"] Send command: "<<s<<endl<<flush;
  135. send(sock, s.c_str(), s.length(), 0);
  136. int valread = read(sock, buffer, 1024);
  137. msgAnswer = new Message(MESSAGE_ANSWER_ACK);
  138. printf("%s\n", buffer);
  139. #else
  140. AddChecksum(s);
  141. //cout << "[" <<__PRETTY_FUNCTION__<<"] Send command: "<<s<<endl<<flush;
  142. int count = write(this->fd, s.c_str(), s.length()); //Filestream, bytes to write, number of bytes to write
  143. if (count < 0) {
  144. cerr << "[" << __PRETTY_FUNCTION__ << "] UART TX error (" << to_string(count) << ")" << endl << flush;
  145. msgAnswer = new Message(MESSAGE_ANSWER_COM_ERROR);
  146. } else { /* write successfull, read answer from robot */
  147. try {
  148. s = Read();
  149. //cout << "Answer = "<<s<<endl<<flush;
  150. if (VerifyChecksum(s)) {
  151. msgAnswer = StringToMessage(s);
  152. } else msgAnswer = new Message(MESSAGE_ANSWER_ROBOT_UNKNOWN_COMMAND);
  153. } catch (std::runtime_error &e) {
  154. s = string(e.what());
  155. if (s.find("imeout")) { // timeout detecté
  156. msgAnswer = new Message(MESSAGE_ANSWER_ROBOT_TIMEOUT);
  157. } else {
  158. msgAnswer = new Message(MESSAGE_ANSWER_COM_ERROR);
  159. }
  160. }
  161. }
  162. #endif
  163. } else {
  164. cerr << __PRETTY_FUNCTION__ << ": Com port not open" << endl << flush;
  165. throw std::runtime_error{"Com port not open"};
  166. }
  167. // deallocation of msg
  168. delete(msg);
  169. return msgAnswer;
  170. }
  171. /**
  172. * Get a message from robot
  173. * @return Message currently received
  174. * @attention A message object is created (new) when receiving data from robot. You MUST remember to destroy is (delete) after use
  175. * @attention Read method is blocking until a message is received
  176. * @warning Read is not thread safe : Do not call it in multiple tasks simultaneously
  177. */
  178. string ComRobot::Read() {
  179. string s;
  180. int rxLength;
  181. unsigned char receivedChar;
  182. do {
  183. rxLength = read(this->fd, (void*) &receivedChar, 1); //Filestream, buffer to store in, number of bytes to read (max)
  184. if (rxLength == 0) { // timeout
  185. // try again
  186. rxLength = read(this->fd, (void*) &receivedChar, 1); //Filestream, buffer to store in, number of bytes to read (max)
  187. if (rxLength == 0) { // re-timeout: it sucks !
  188. throw std::runtime_error{"ComRobot::Read: Timeout when reading from com port"};
  189. }
  190. } else if (rxLength < 0) { // big pb !
  191. throw std::runtime_error{"ComRobot::Read: Unknown problem when reading from com port"};
  192. } else { // everything ok
  193. if ((receivedChar != '\r') && (receivedChar != '\n')) s += receivedChar;
  194. }
  195. } while ((receivedChar != '\r') && (receivedChar != '\n'));
  196. return s;
  197. }
  198. Message *ComRobot::SendCommand(Message* msg, MessageID answerID, int maxRetries) {
  199. int counter = maxRetries;
  200. Message *msgSend;
  201. Message *msgRcv;
  202. Message *msgTmp;
  203. do {
  204. msgSend = msg->Copy();
  205. cout << "S => " << msgSend->ToString() << endl << flush;
  206. msgTmp = Write(msgSend);
  207. cout << "R <= " << msgTmp->ToString() << endl << flush;
  208. if (msgTmp->CompareID(answerID)) counter = 0;
  209. else counter--;
  210. if (counter == 0) msgRcv = msgTmp->Copy();
  211. delete(msgTmp);
  212. } while (counter);
  213. delete (msg);
  214. return msgRcv;
  215. }
  216. /**
  217. * Convert an array of char to its message representation (when receiving data from stm32)
  218. * @param bytes Array of char
  219. * @return Message corresponding to received array of char
  220. */
  221. Message* ComRobot::StringToMessage(string s) {
  222. Message *msg;
  223. switch (s[0]) {
  224. case LABEL_ROBOT_OK:
  225. msg = new Message(MESSAGE_ANSWER_ACK);
  226. break;
  227. case LABEL_ROBOT_ERROR:
  228. msg = new Message(MESSAGE_ANSWER_ROBOT_ERROR);
  229. break;
  230. case LABEL_ROBOT_UNKNOWN_COMMAND:
  231. msg = new Message(MESSAGE_ANSWER_ROBOT_UNKNOWN_COMMAND);
  232. break;
  233. case '0':
  234. msg = new MessageBattery(MESSAGE_ROBOT_BATTERY_LEVEL, BATTERY_EMPTY);
  235. break;
  236. case '1':
  237. msg = new MessageBattery(MESSAGE_ROBOT_BATTERY_LEVEL, BATTERY_LOW);
  238. break;
  239. case '2':
  240. msg = new MessageBattery(MESSAGE_ROBOT_BATTERY_LEVEL, BATTERY_FULL);
  241. break;
  242. default:
  243. msg = new Message(MESSAGE_ANSWER_ROBOT_ERROR);
  244. cerr << "[" << __PRETTY_FUNCTION__ << "] Unknown message received from robot (" << s << ")" << endl << flush;
  245. }
  246. return msg;
  247. }
  248. /**
  249. * Convert a message to its array of char representation (for sending command to stm32)
  250. * @param msg Message to be sent to robot
  251. * @param buffer Array of char, image of message to send
  252. */
  253. string ComRobot::MessageToString(Message *msg) {
  254. string s;
  255. float val_f;
  256. int val_i;
  257. unsigned char *b;
  258. switch (msg->GetID()) {
  259. case MESSAGE_ROBOT_PING:
  260. s += LABEL_ROBOT_PING;
  261. break;
  262. case MESSAGE_ROBOT_RESET:
  263. s += LABEL_ROBOT_RESET;
  264. break;
  265. case MESSAGE_ROBOT_POWEROFF:
  266. s += LABEL_ROBOT_POWEROFF;
  267. break;
  268. case MESSAGE_ROBOT_START_WITHOUT_WD:
  269. s += LABEL_ROBOT_START_WITHOUT_WD;
  270. break;
  271. case MESSAGE_ROBOT_START_WITH_WD:
  272. s += LABEL_ROBOT_START_WITH_WD;
  273. break;
  274. case MESSAGE_ROBOT_RELOAD_WD:
  275. s += LABEL_ROBOT_RELOAD_WD;
  276. break;
  277. case MESSAGE_ROBOT_BATTERY_GET:
  278. s += LABEL_ROBOT_GET_BATTERY;
  279. break;
  280. case MESSAGE_ROBOT_STATE_GET:
  281. s += LABEL_ROBOT_GET_STATE;
  282. break;
  283. case MESSAGE_ROBOT_GO_FORWARD:
  284. s += LABEL_ROBOT_MOVE;
  285. s += LABEL_ROBOT_SEPARATOR_CHAR;
  286. s.append(to_string(500000));
  287. break;
  288. case MESSAGE_ROBOT_GO_BACKWARD:
  289. s += LABEL_ROBOT_MOVE;
  290. s += LABEL_ROBOT_SEPARATOR_CHAR;
  291. s.append(to_string(-500000));
  292. break;
  293. case MESSAGE_ROBOT_GO_LEFT:
  294. s += LABEL_ROBOT_TURN;
  295. s += LABEL_ROBOT_SEPARATOR_CHAR;
  296. s.append(to_string(-500000));
  297. break;
  298. case MESSAGE_ROBOT_GO_RIGHT:
  299. s += LABEL_ROBOT_TURN;
  300. s += LABEL_ROBOT_SEPARATOR_CHAR;
  301. s.append(to_string(500000));
  302. break;
  303. case MESSAGE_ROBOT_STOP:
  304. s += LABEL_ROBOT_MOVE;
  305. s += LABEL_ROBOT_SEPARATOR_CHAR;
  306. s.append(to_string(0));
  307. break;
  308. case MESSAGE_ROBOT_MOVE:
  309. s += LABEL_ROBOT_MOVE;
  310. s += LABEL_ROBOT_SEPARATOR_CHAR;
  311. s.append(to_string(((MessageInt*) msg)->GetValue()));
  312. break;
  313. case MESSAGE_ROBOT_TURN:
  314. s += LABEL_ROBOT_TURN;
  315. s += LABEL_ROBOT_SEPARATOR_CHAR;
  316. s.append(to_string(((MessageInt*) msg)->GetValue()));
  317. break;
  318. default:
  319. cerr << "[" << __PRETTY_FUNCTION__ << "] Invalid message for robot (" << msg->ToString() << ")" << endl << flush;
  320. throw std::runtime_error{"Invalid message"};
  321. }
  322. return s;
  323. }
  324. /**
  325. * Add a checksum and carriage return to a command string
  326. * @param[in,out] s String containing command for robot, without ending char (carriage return)
  327. */
  328. void ComRobot::AddChecksum(string &s) {
  329. unsigned char checksum = 0;
  330. for (string::iterator it = s.begin(); it != s.end(); ++it) {
  331. checksum ^= (unsigned char) *it;
  332. }
  333. s += (char) checksum; // Add calculated checksum
  334. s += (char) LABEL_ROBOT_ENDING_CHAR;
  335. }
  336. /**
  337. * Verify if checksum of an incoming answer from robot is valid,
  338. * then remove checksum from incoming answer (if checksum is ok)
  339. * @param[in,out] s String containing incoming answer from robot
  340. * @return true is checksum is valid, false otherwise.
  341. */
  342. bool ComRobot::VerifyChecksum(string &s) {
  343. unsigned char checksum = 0;
  344. for (string::iterator it = s.begin(); it != s.end(); ++it) {
  345. checksum ^= (unsigned char) *it;
  346. }
  347. if (checksum == 0) { // checksum is ok, remove last char of string (checksum)
  348. s.pop_back(); // remove last char
  349. return true;
  350. } else return false;
  351. }