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.

DestijlCommandManager.cs 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. //
  2. // DestijlCommandManager.cs
  3. //
  4. // Author:
  5. // Di MERCURIO Sébastien <dimercur@insa-toulouse.fr>
  6. //
  7. // Copyright (c) 2018 INSA - DGEI
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU General Public License as published by
  11. // the Free Software Foundation, either version 3 of the License, or
  12. // (at your option) any later version.
  13. //
  14. // This program is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. // GNU General Public License for more details.
  18. //
  19. // You should have received a copy of the GNU General Public License
  20. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. using System;
  22. namespace monitor
  23. {
  24. /// <summary>
  25. /// Commands and options parameters used in Destijl project when communicating with server
  26. /// </summary>
  27. public static class DestijlCommandList
  28. {
  29. public const string HeaderMtsComDmb = "COM";
  30. public const string HeaderMtsDmbOrder = "DMB";
  31. public const string HeaderMtsCamera = "CAM";
  32. public const string HeaderMtsMessage = "MSG";
  33. public const string DataComOpen = "o";
  34. public const string DataComClose = "C";
  35. public const string DataCamOpen = "A";
  36. public const string DataCamClose = "I";
  37. public const string DataCamAskArena = "y";
  38. public const string DataCamArenaConfirm = "x";
  39. public const string DataCamInfirm = "z";
  40. public const string DataCamComputePosition = "p";
  41. public const string DataCamStopComputePosition = "s";
  42. public const string HeaderStmAck = "ACK";
  43. public const string HeaderStmNoAck = "NAK";
  44. public const string HeaderStmLostDmb = "LCD";
  45. public const string HeaderStmImage = "IMG";
  46. public const string HeaderStmPos = "POS";
  47. public const string HeaderStmMes = "MSG";
  48. public const string HeaderStmBat = "BAT";
  49. }
  50. /// <summary>
  51. /// Commands used for robot messages
  52. /// </summary>
  53. public static class RobotCommandList
  54. {
  55. public const string RobotPing = "p";
  56. public const string RobotReset = "r";
  57. public const string RobotStartWithoutWatchdog = "u";
  58. public const string RobotStartWithWatchdog = "W";
  59. public const string RobotGetBattery = "v";
  60. public const string RobotGetBusyState = "b";
  61. public const string RobotMove = "M";
  62. public const string RobotTurn = "T";
  63. public const string RobotGetVersion = "V";
  64. public const string RobotPowerOff = "z";
  65. }
  66. /// <summary>
  67. /// Specialization class for command manager, which implemnent destijl protocol between monitor and server
  68. /// </summary>
  69. public class DestijlCommandManager
  70. {
  71. /// <summary>
  72. /// Command Manager object
  73. /// </summary>
  74. private CommandManager commandManager = null;
  75. /// <summary>
  76. /// Part of received message corresponding to command header
  77. /// </summary>
  78. private string receivedHeader = null;
  79. /// <summary>
  80. /// Part of received message corresponding to command data
  81. /// </summary>
  82. private string receivedData = null;
  83. /// <summary>
  84. /// Callback for sending received data to application level
  85. /// </summary>
  86. public delegate void CommandReceivedEvent(string header, string data, byte[] buffer);
  87. public CommandReceivedEvent commandReceivedEvent = null;
  88. /// <summary>
  89. /// Timeout used for command with acknowledge
  90. /// </summary>
  91. public double timeout = 100;
  92. /// <summary>
  93. /// List of available return status
  94. /// </summary>
  95. public enum CommandStatus
  96. {
  97. Success,
  98. Rejected,
  99. InvalidAnswer,
  100. Busy,
  101. CommunicationLostWithRobot,
  102. CommunicationLostWithServer
  103. }
  104. /// <summary>
  105. /// Initializes a new instance of the <see cref="monitor.DestijlCommandManager"/> class.
  106. /// </summary>
  107. /// <param name="callback">Callback reference for reception of data</param>
  108. public DestijlCommandManager(CommandReceivedEvent callback)
  109. {
  110. commandManager = new CommandManager(OnCommandReceived);
  111. this.commandReceivedEvent += callback;
  112. }
  113. /// <summary>
  114. /// Releases unmanaged resources and performs other cleanup operations before the
  115. /// <see cref="monitor.DestijlCommandManager"/> is reclaimed by garbage collection.
  116. /// </summary>
  117. ~DestijlCommandManager()
  118. {
  119. if (commandManager != null) commandManager.Close();
  120. }
  121. /// <summary>
  122. /// Callback used for receiving data from lower layer (CommandManager class)
  123. /// </summary>
  124. /// <param name="msg">String containing received message</param>
  125. /// <param name="buffer">Raw buffer to be used when data are not in ascii format (image for example)</param>
  126. private void OnCommandReceived(string msg, byte[] buffer)
  127. {
  128. // Firstly, split message in (at least) two part : header, and data
  129. string[] msgs = msg.Split(':');
  130. // If it exist at least on element in string array, it should be command header
  131. if (msgs.Length >= 1) receivedHeader = msgs[0];
  132. else receivedHeader = null;
  133. // if msgs array contains at least two elements, second element is normally data
  134. if (msgs.Length >= 2) receivedData = msgs[1];
  135. else receivedData = null;
  136. // when split is done, provide data to application
  137. this.commandReceivedEvent?.Invoke(receivedHeader, receivedData, buffer);
  138. }
  139. /// <summary>
  140. /// Open the specified hostname server, using default port number.
  141. /// </summary>
  142. /// <returns>true if connection succeded, false otherwise</returns>
  143. /// <param name="hostname">Hostname to connect to</param>
  144. public bool Open(string hostname)
  145. {
  146. return this.Open(hostname, Client.defaultPort);
  147. }
  148. /// <summary>
  149. /// Open connection to server "host", with port number "port"
  150. /// </summary>
  151. /// <returns>true if connection succeded, false otherwise</returns>
  152. /// <param name="hostname">Hostname to connect to</param>
  153. /// <param name="port">Port number for connection</param>
  154. public bool Open(string hostname, int port)
  155. {
  156. if (commandManager != null) return commandManager.Open(hostname, port);
  157. else return false;
  158. }
  159. /// <summary>
  160. /// Close connection to server
  161. /// </summary>
  162. public void Close()
  163. {
  164. if (commandManager != null) commandManager.Close();
  165. }
  166. /// <summary>
  167. /// Creates the command to send to server, based on header and data provided
  168. /// </summary>
  169. /// <returns>The command string</returns>
  170. /// <param name="header">Header part of the command</param>
  171. /// <param name="data">Data part of the command</param>
  172. private string CreateCommand(string header, string data)
  173. {
  174. return header + ":" + data;
  175. }
  176. /// <summary>
  177. /// Provide DestijlCommandManager.CommandStatus based on status received by CommandManager.SendCommand and answer string
  178. /// </summary>
  179. /// <returns>Status compatible with DestijlCommandManager.CommandStatus type</returns>
  180. /// <param name="localStatus">Status provided by CommandManager.SendCommand</param>
  181. /// <param name="answer">Answer provided by CommandManager.SendCommand</param>
  182. private CommandStatus DecodeStatus(CommandManager.CommandManagerStatus localStatus, string answer)
  183. {
  184. CommandStatus status = CommandStatus.Success;
  185. // if timeout occures, return CommandStatus.CommunicationLostWithServer
  186. if (localStatus == CommandManager.CommandManagerStatus.Timeout) status = CommandStatus.CommunicationLostWithServer;
  187. // if a command is currently processed, return Busy
  188. else if (localStatus == CommandManager.CommandManagerStatus.Busy) status = CommandStatus.Busy;
  189. else
  190. {
  191. if (answer != null)
  192. {
  193. // if command is not acknowledged, return Rejected
  194. if (answer.ToUpper().Contains(DestijlCommandList.HeaderStmNoAck)) status = CommandStatus.Rejected;
  195. // if communication is lost with robot, return CommunicationLostWithRobot
  196. else if (answer.ToUpper().Contains(DestijlCommandList.HeaderStmLostDmb)) status = CommandStatus.CommunicationLostWithRobot;
  197. // if answer is empty, communication with robot is lost
  198. else if (answer.Length == 0) status = CommandStatus.CommunicationLostWithServer;
  199. //else status = CommandStatus.InvalidAnswer;
  200. }
  201. }
  202. return status;
  203. }
  204. /// <summary>
  205. /// Open communication with robot and wait acknowledge
  206. /// </summary>
  207. /// <returns>Command status (see DecodeStatus)</returns>
  208. public CommandStatus RobotOpenCom()
  209. {
  210. CommandManager.CommandManagerStatus localStatus;
  211. string answer;
  212. localStatus = commandManager.SendCommand(
  213. CreateCommand(DestijlCommandList.HeaderMtsComDmb, DestijlCommandList.DataComOpen),
  214. out answer,
  215. this.timeout);
  216. return DecodeStatus(localStatus, answer);
  217. }
  218. /// <summary>
  219. /// Close communication with robot and wait acknowledge
  220. /// </summary>
  221. /// <returns>Command status (see DecodeStatus)</returns>
  222. public CommandStatus RobotCloseCom()
  223. {
  224. CommandManager.CommandManagerStatus localStatus;
  225. string answer;
  226. localStatus = commandManager.SendCommand(
  227. CreateCommand(DestijlCommandList.HeaderMtsComDmb, DestijlCommandList.DataComClose),
  228. out answer,
  229. this.timeout);
  230. return DecodeStatus(localStatus, answer);
  231. }
  232. /// <summary>
  233. /// Ping the robot.
  234. /// </summary>
  235. /// <returns>Command status (see DecodeStatus)</returns>
  236. public CommandStatus RobotPing()
  237. {
  238. CommandManager.CommandManagerStatus localStatus;
  239. string answer;
  240. localStatus = commandManager.SendCommand(
  241. CreateCommand(DestijlCommandList.HeaderMtsDmbOrder, RobotCommandList.RobotPing),
  242. out answer,
  243. this.timeout);
  244. return DecodeStatus(localStatus, answer);
  245. }
  246. /// <summary>
  247. /// Reset robot and let it in idle mode
  248. /// </summary>
  249. /// <returns>Command status (see DecodeStatus)</returns>
  250. public CommandStatus RobotReset()
  251. {
  252. CommandManager.CommandManagerStatus localStatus;
  253. string answer;
  254. localStatus = commandManager.SendCommand(
  255. CreateCommand(DestijlCommandList.HeaderMtsDmbOrder, RobotCommandList.RobotReset),
  256. out answer,
  257. 0);
  258. return DecodeStatus(localStatus, answer);
  259. }
  260. /// <summary>
  261. /// Start robot, enabling watchdog
  262. /// </summary>
  263. /// <returns>Command status (see DecodeStatus)</returns>
  264. public CommandStatus RobotStartWithWatchdog()
  265. {
  266. CommandManager.CommandManagerStatus localStatus;
  267. string answer;
  268. localStatus = commandManager.SendCommand(
  269. CreateCommand(DestijlCommandList.HeaderMtsDmbOrder, RobotCommandList.RobotStartWithWatchdog),
  270. out answer,
  271. this.timeout);
  272. return DecodeStatus(localStatus, answer);
  273. }
  274. /// <summary>
  275. /// Start robot, without enabling watchdog
  276. /// </summary>
  277. /// <returns>Command status (see DecodeStatus)</returns>
  278. public CommandStatus RobotStartWithoutWatchdog()
  279. {
  280. CommandManager.CommandManagerStatus localStatus;
  281. string answer;
  282. localStatus = commandManager.SendCommand(
  283. CreateCommand(DestijlCommandList.HeaderMtsDmbOrder, RobotCommandList.RobotStartWithoutWatchdog),
  284. out answer,
  285. this.timeout);
  286. return DecodeStatus(localStatus, answer);
  287. }
  288. /// <summary>
  289. /// Move robot forward or backward, for a distance expressed in millimeter
  290. /// </summary>
  291. /// <returns>Command status (see DecodeStatus)</returns>
  292. /// <param name="distance">Distance of mouvment, in millimeter</param>
  293. public CommandStatus RobotMove(int distance)
  294. {
  295. CommandManager.CommandManagerStatus localStatus;
  296. string answer;
  297. localStatus = commandManager.SendCommand(
  298. CreateCommand(DestijlCommandList.HeaderMtsDmbOrder, RobotCommandList.RobotMove + "=" + distance),
  299. out answer,
  300. 0);
  301. return DecodeStatus(localStatus, answer);
  302. }
  303. /// <summary>
  304. /// Make robot turn left or right, for a given angle
  305. /// </summary>
  306. /// <returns>Command status (see DecodeStatus)</returns>
  307. /// <param name="angle">Angle of turn, in degree (negative for left, positive for right)</param>
  308. public CommandStatus RobotTurn(int angle)
  309. {
  310. CommandManager.CommandManagerStatus localStatus;
  311. string answer;
  312. localStatus = commandManager.SendCommand(
  313. CreateCommand(DestijlCommandList.HeaderMtsDmbOrder, RobotCommandList.RobotTurn + "=" + angle),
  314. out answer,
  315. 0);
  316. return DecodeStatus(localStatus, answer);
  317. }
  318. /// <summary>
  319. /// Request robot battery level
  320. /// </summary>
  321. /// <returns>Command status (see DecodeStatus)</returns>
  322. public CommandStatus RobotGetBattery()
  323. {
  324. CommandManager.CommandManagerStatus localStatus;
  325. string answer;
  326. localStatus = commandManager.SendCommand(
  327. CreateCommand(DestijlCommandList.HeaderMtsDmbOrder, RobotCommandList.RobotGetBattery),
  328. out answer,
  329. 0);
  330. return DecodeStatus(localStatus, answer);
  331. }
  332. /// <summary>
  333. /// Request robot firmware version
  334. /// </summary>
  335. /// <returns>Command status (see DecodeStatus)</returns>
  336. /// <param name="version">todo</param>
  337. public CommandStatus RobotGetVersion(out string version)
  338. {
  339. CommandManager.CommandManagerStatus localStatus;
  340. CommandStatus status = CommandStatus.Success;
  341. version = "";
  342. string answer;
  343. localStatus = commandManager.SendCommand(
  344. CreateCommand(DestijlCommandList.HeaderMtsDmbOrder, RobotCommandList.RobotGetVersion),
  345. out answer,
  346. this.timeout);
  347. if (localStatus == CommandManager.CommandManagerStatus.AnswerReceived)
  348. {
  349. string[] msg = answer.Split(':');
  350. if (msg.Length > 1)
  351. {
  352. version = msg[1];
  353. }
  354. }
  355. else if (localStatus == CommandManager.CommandManagerStatus.Timeout)
  356. {
  357. status = CommandStatus.CommunicationLostWithServer;
  358. }
  359. return status;
  360. }
  361. /// <summary>
  362. /// Power off robot
  363. /// </summary>
  364. /// <returns>Command status (see DecodeStatus)</returns>
  365. public CommandStatus RobotPowerOff()
  366. {
  367. CommandManager.CommandManagerStatus localStatus;
  368. string answer;
  369. localStatus = commandManager.SendCommand(
  370. CreateCommand(DestijlCommandList.HeaderMtsDmbOrder, RobotCommandList.RobotPowerOff),
  371. out answer,
  372. 0);
  373. return DecodeStatus(localStatus, answer);
  374. }
  375. /// <summary>
  376. /// Open camera on remote device
  377. /// </summary>
  378. /// <returns>Command status (see DecodeStatus)</returns>
  379. public CommandStatus CameraOpen()
  380. {
  381. CommandManager.CommandManagerStatus localStatus;
  382. string answer;
  383. localStatus = commandManager.SendCommand(
  384. CreateCommand(DestijlCommandList.HeaderMtsCamera, DestijlCommandList.DataCamOpen),
  385. out answer,
  386. this.timeout);
  387. return DecodeStatus(localStatus, answer);
  388. }
  389. /// <summary>
  390. /// Close camera on remote device
  391. /// </summary>
  392. /// <returns>Command status (see DecodeStatus)</returns>
  393. public CommandStatus CameraClose()
  394. {
  395. CommandManager.CommandManagerStatus localStatus;
  396. string answer;
  397. localStatus = commandManager.SendCommand(
  398. CreateCommand(DestijlCommandList.HeaderMtsCamera, DestijlCommandList.DataCamClose),
  399. out answer,
  400. 0);
  401. return DecodeStatus(localStatus, answer);
  402. }
  403. /// <summary>
  404. /// Request still image of detected arena
  405. /// </summary>
  406. /// <returns>Command status (see DecodeStatus)</returns>
  407. public CommandStatus CameraAskArena()
  408. {
  409. CommandManager.CommandManagerStatus localStatus;
  410. string answer;
  411. localStatus = commandManager.SendCommand(
  412. CreateCommand(DestijlCommandList.HeaderMtsCamera, DestijlCommandList.DataCamAskArena),
  413. out answer,
  414. 0);
  415. return DecodeStatus(localStatus, answer);
  416. }
  417. /// <summary>
  418. /// Confirm arena detection (after requesting image of detected arena, using CameraAskArena
  419. /// </summary>
  420. /// <returns>Command status (see DecodeStatus)</returns>
  421. public CommandStatus CameraArenaConfirm()
  422. {
  423. CommandManager.CommandManagerStatus localStatus;
  424. string answer;
  425. localStatus = commandManager.SendCommand(
  426. CreateCommand(DestijlCommandList.HeaderMtsCamera, DestijlCommandList.DataCamArenaConfirm),
  427. out answer,
  428. 0);
  429. return DecodeStatus(localStatus, answer);
  430. }
  431. /// <summary>
  432. /// Reject arena detected (after requesting image of detected arena, using CameraAskArena
  433. /// </summary>
  434. /// <returns>Command status (see DecodeStatus)</returns>
  435. public CommandStatus CameraArenaInfirm()
  436. {
  437. CommandManager.CommandManagerStatus localStatus;
  438. string answer;
  439. localStatus = commandManager.SendCommand(
  440. CreateCommand(DestijlCommandList.HeaderMtsCamera, DestijlCommandList.DataCamInfirm),
  441. out answer,
  442. 0);
  443. return DecodeStatus(localStatus, answer);
  444. }
  445. /// <summary>
  446. /// Request robot position computing
  447. /// </summary>
  448. /// <returns>Command status (see DecodeStatus)</returns>
  449. public CommandStatus CameraComputePosition()
  450. {
  451. CommandManager.CommandManagerStatus localStatus;
  452. string answer;
  453. localStatus = commandManager.SendCommand(
  454. CreateCommand(DestijlCommandList.HeaderMtsCamera, DestijlCommandList.DataCamComputePosition),
  455. out answer,
  456. 0);
  457. return DecodeStatus(localStatus, answer);
  458. }
  459. /// <summary>
  460. /// Stop robot position computing
  461. /// </summary>
  462. /// <returns>Command status (see DecodeStatus)</returns>
  463. public CommandStatus CameraStopComputePosition()
  464. {
  465. CommandManager.CommandManagerStatus localStatus;
  466. string answer;
  467. localStatus = commandManager.SendCommand(
  468. CreateCommand(DestijlCommandList.HeaderMtsCamera, DestijlCommandList.DataCamStopComputePosition),
  469. out answer,
  470. 0);
  471. return DecodeStatus(localStatus, answer);
  472. }
  473. }
  474. }