spheroncd.ncd 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. process main {
  2. If (@true) {
  3. var("/dev/input/by-id/usb-Logitech_Logitech_Extreme_3D-event-joystick") joy_dev;
  4. value(["X": "0", "Y": "0", "RZ": "0", "THROTTLE": "255"]) joy_axis_min;
  5. value(["X": "1023", "Y": "1023", "RZ": "255", "THROTTLE": "0" ]) joy_axis_max;
  6. var("60") rz_degrees;
  7. var("/dev/rfcomm0") sphero_dev;
  8. var("5") max_sendq_len;
  9. var("20") max_roll_interval;
  10. var(@false) print_joy_events;
  11. var("/run/current-system/sw/bin/stty") stty;
  12. var("/run/current-system/sw/bin/python2.7") python;
  13. } config;
  14. call(@_spheroncd, {^config});
  15. }
  16. template _spheroncd {
  17. objref_arg(_arg0) config;
  18. value([]) joy_positions;
  19. Do {
  20. joy_positions->insert("BTN_TRIGGER", "0");
  21. joy_positions->insert("BTN_THUMB", "0");
  22. Foreach (config.joy_axis_min As axis_name: range) {
  23. joy_positions->insert(@concat("ABS_", axis_name), @num_divide(range, "2"));
  24. };
  25. };
  26. value([]) joy_latch_buttons;
  27. blocker(@true) joy_event_signal;
  28. var("0") last_adjust;
  29. compile_search(" ") search_space;
  30. refhere() global;
  31. process_manager() mgr;
  32. mgr->start(@_spheroncd_joystick_events, {});
  33. mgr->start(@_spheroncd_sphero, {});
  34. }
  35. template _spheroncd_joystick_events {
  36. objref(^_caller.global) global;
  37. log(@notice, "Listening on joystick ", global.config.joy_dev);
  38. sys.evdev(global.config.joy_dev) joy_event;
  39. If (global.config.print_joy_events) {
  40. println(joy_event.code, " ", joy_event.value);
  41. };
  42. global.joy_positions->try_get(joy_event.code) positions_entry;
  43. If (positions_entry.exists) {
  44. global.joy_positions->replace(joy_event.code, joy_event.value);
  45. global.joy_event_signal->up();
  46. joy_event->nextevent();
  47. };
  48. global.joy_latch_buttons->try_get(joy_event.code) buttons_entry;
  49. If (buttons_entry.exists) {
  50. If (@val_equal(joy_event.value, "0")) {
  51. joy_event->nextevent();
  52. };
  53. println(joy_event.code);
  54. global.joy_latch_buttons->replace(joy_event.code, @true);
  55. global.joy_event_signal->up();
  56. joy_event->nextevent();
  57. };
  58. joy_event->nextevent();
  59. }
  60. template _spheroncd_sphero {
  61. objref(^_caller.global) global;
  62. log(@notice, "Using Sphero device ", global.config.sphero_dev);
  63. var(@true) first_time;
  64. backtrack_point() retry_connect;
  65. If (@not(first_time)) {
  66. sleep("5000");
  67. };
  68. first_time->set(@false);
  69. runonce({global.config.stty, "-F", global.config.sphero_dev, "115200", "raw", "-echo"}, ["term_on_deinit":@true, "keep_stderr":@true]) stty_exec;
  70. If (@num_different(stty_exec.exit_status, "0")) {
  71. log(@error, "Failed to stty the Sphero device");
  72. retry_connect->go();
  73. };
  74. sys.connect({@device, global.config.sphero_dev}) connection;
  75. If (connection.is_error) {
  76. log(@error, "Sphero device error");
  77. retry_connect->go();
  78. };
  79. log(@notice, "Opened Sphero device");
  80. value({}) send_queue;
  81. blocker(@true) send_event;
  82. depend_scope() depsc;
  83. refhere() dev;
  84. process_manager() mgr;
  85. mgr->start(@_spheroncd_read_task, {});
  86. mgr->start(@_spheroncd_send_task, {});
  87. mgr->start(@_spheroncd_ping_task, {});
  88. mgr->start(@_spheroncd_control_task, {});
  89. mgr->start(@_spheroncd_calculator_task, {});
  90. call(@_spheroncd_set_back_led, {^dev, "255"});
  91. }
  92. template _spheroncd_read_task {
  93. objref(^_caller.dev) dev;
  94. backtrack_point() read_again;
  95. dev.connection->read() recvd_data;
  96. If (recvd_data.eof) {
  97. log(@error, "Read EOF on Sphero device");
  98. dev.retry_connect->go();
  99. };
  100. read_again->go();
  101. }
  102. template _spheroncd_send_task {
  103. objref(^_caller.dev) dev;
  104. backtrack_point() next_message;
  105. Do {
  106. dev.send_event->use();
  107. If (@val_equal(dev.send_queue.length, "0")) {
  108. if(@false);
  109. };
  110. };
  111. dev.send_queue->get("0") message;
  112. dev.send_queue->remove("0");
  113. dev.connection->write(message);
  114. next_message->go();
  115. }
  116. template _spheroncd_enqueue_packet {
  117. objref_arg(_arg0) dev;
  118. alias(@_arg1) reset_timeout;
  119. alias(@_arg2) want_answer;
  120. alias(@_arg3) device_id;
  121. alias(@_arg4) command_id;
  122. alias(@_arg5) seq_num;
  123. value(_arg6) data;
  124. Do {
  125. If (@num_greater_equal(dev.send_queue.length, dev.global.config.max_sendq_len)) {
  126. log(@warning, "send queue exhausted");
  127. _do->break();
  128. };
  129. value("") message;
  130. message->append(@struct_encode({
  131. {@u8, "255"},
  132. {@u8, @num_add("252", @num_add(@if(reset_timeout, "2", "0"), @if(want_answer, "1", "0")))},
  133. {@u8, device_id},
  134. {@u8, command_id},
  135. {@u8, seq_num},
  136. {@u8, @num_add(data.length, "1")}
  137. }));
  138. message->append(data);
  139. message->substr("2") checksumed_data;
  140. message->append(@struct_encode({
  141. {@u8, @checksum(@inverted_sum_bytes, checksumed_data)}
  142. }));
  143. dev.send_queue->append(message);
  144. dev.send_event->downup();
  145. };
  146. }
  147. template _spheroncd_ping_task {
  148. objref(^_caller.dev) dev;
  149. backtrack_point() again;
  150. call(@_spheroncd_enqueue_packet, {^dev, @true, @true, "0", "1", "0", ""});
  151. sleep("2000");
  152. again->go();
  153. }
  154. template _spheroncd_control_task {
  155. objref(^_caller.dev) dev;
  156. objref(^dev.global) global;
  157. backtrack_point() control_again;
  158. global.joy_event_signal->down();
  159. global.joy_positions->get("ABS_X") pos_x;
  160. global.joy_positions->get("ABS_Y") pos_y;
  161. global.joy_positions->get("ABS_RZ") pos_rz;
  162. global.joy_positions->get("ABS_THROTTLE") pos_throttle;
  163. global.joy_positions->get("BTN_TRIGGER") turbo;
  164. global.joy_positions->get("BTN_THUMB") adjust;
  165. global.config.joy_axis_min->get("X") min_x;
  166. global.config.joy_axis_min->get("Y") min_y;
  167. global.config.joy_axis_min->get("RZ") min_rz;
  168. global.config.joy_axis_min->get("THROTTLE") min_throttle;
  169. global.config.joy_axis_max->get("X") max_x;
  170. global.config.joy_axis_max->get("Y") max_y;
  171. global.config.joy_axis_max->get("RZ") max_rz;
  172. global.config.joy_axis_max->get("THROTTLE") max_throttle;
  173. var(global.last_adjust) old_adjust;
  174. global.last_adjust->set(adjust);
  175. var(@concat(
  176. pos_x, " ", min_x, " ", max_x, " ",
  177. pos_y, " ", min_y, " ", max_y, " ",
  178. pos_rz, " ", min_rz, " ", max_rz, " ", global.config.rz_degrees, " ",
  179. pos_throttle, " ", min_throttle, " ", max_throttle, " ",
  180. turbo, " ", adjust, "\n"
  181. )) calc_request;
  182. call(@_spheroncd_calc_operation, {^dev, calc_request}) calc_op;
  183. global.search_space->explode(calc_op.response) resp_fields;
  184. value(resp_fields) resp_fields;
  185. resp_fields->get("0") calc_heading;
  186. resp_fields->get("1") calc_speed;
  187. If (@and(@num_different(old_adjust, "0"), @num_equal(adjust, "0"))) {
  188. call(@_spheroncd_set_heading, {^dev, calc_heading});
  189. };
  190. call(@_spheroncd_roll, {^dev, calc_speed, calc_heading, "1"});
  191. sleep(global.config.max_roll_interval);
  192. global.joy_event_signal->use();
  193. control_again->go();
  194. }
  195. template _spheroncd_calculator_task {
  196. objref(^_caller.dev) dev;
  197. objref(^dev.global) global;
  198. var(@true) first_time;
  199. backtrack_point() try_again;
  200. If (@not(first_time)) {
  201. sleep("1000");
  202. };
  203. first_time->set(@false);
  204. var({global.config.python, "-B", "calculator.py"}) cmd;
  205. sys.start_process(cmd, "rw", ["keep_stderr": @true]) proc;
  206. If (proc.is_error) {
  207. log(@error, "Process error");
  208. try_again->go();
  209. };
  210. proc->read_pipe() read_pipe;
  211. If (read_pipe.is_error) {
  212. log(@error, "Read pipe error");
  213. try_again->go();
  214. };
  215. proc->write_pipe() write_pipe;
  216. If (write_pipe.is_error) {
  217. log(@error, "Write pipe error");
  218. try_again->go();
  219. };
  220. dev.depsc->provide(@calculator);
  221. }
  222. template _spheroncd_calc_operation {
  223. objref_arg(_arg0) dev;
  224. alias(@_arg1) request;
  225. value("") response;
  226. Do {
  227. dev.depsc->depend({@calculator}) calc;
  228. value("") buffer;
  229. calc.write_pipe->write(request);
  230. backtrack_point() read_again;
  231. calc.read_pipe->read() read_data;
  232. If (@not(read_data.not_eof)) {
  233. log(@error, "Got EOF from calculator");
  234. calc.try_again->go();
  235. };
  236. buffer->append(read_data);
  237. var(@num_subtract(buffer.length, "1")) len_minus_one;
  238. buffer->substr(len_minus_one) last_char;
  239. If (@val_different(last_char, "\n")) {
  240. read_again->go();
  241. };
  242. buffer->substr("0", len_minus_one) without_newline;
  243. response->reset(without_newline);
  244. };
  245. }
  246. template _spheroncd_set_heading {
  247. objref_arg(_arg0) dev;
  248. alias(@_arg1) heading;
  249. call(@_spheroncd_enqueue_packet, {^dev, @true, @false, "2", "1", "0", @struct_encode({
  250. {@u16b, heading}
  251. })});
  252. }
  253. template _spheroncd_roll {
  254. objref_arg(_arg0) dev;
  255. alias(@_arg1) speed;
  256. alias(@_arg2) heading;
  257. alias(@_arg3) state;
  258. call(@_spheroncd_enqueue_packet, {^dev, @true, @false, "2", "48", "0", @struct_encode({
  259. {@u8, speed},
  260. {@u16b, heading},
  261. {@u8, state}
  262. })});
  263. }
  264. template _spheroncd_set_led_output {
  265. objref_arg(_arg0) dev;
  266. alias(@_arg1) red;
  267. alias(@_arg2) green;
  268. alias(@_arg3) blue;
  269. alias(@_arg4) persist;
  270. call(@_spheroncd_enqueue_packet, {^dev, @true, @false, "2", "32", "0", @struct_encode({
  271. {@u8, red},
  272. {@u8, green},
  273. {@u8, blue},
  274. {@u8, @if(persist, "1", "0")}
  275. })});
  276. }
  277. template _spheroncd_set_back_led {
  278. objref_arg(_arg0) dev;
  279. alias(@_arg1) bright;
  280. call(@_spheroncd_enqueue_packet, {^dev, @true, @false, "2", "33", "0", @struct_encode({
  281. {@u8, bright}
  282. })});
  283. }