artoo 0.5.0 → 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -2
  3. data/.yardopts +1 -1
  4. data/CONTRIBUTORS.md +16 -0
  5. data/Gemfile +10 -9
  6. data/Gemfile.lock +31 -35
  7. data/README.md +110 -19
  8. data/Rakefile +0 -1
  9. data/api/assets/javascripts/artoo/controllers/robot.js.coffee +5 -8
  10. data/api/public/core.js +7 -12
  11. data/api/public/partials/robot-detail.html +1 -1
  12. data/bin/robi +1 -1
  13. data/examples/christmas_roomba.rb +16 -19
  14. data/examples/hello_api_multiple.rb +3 -2
  15. data/examples/notifications.rb +2 -2
  16. data/examples/sphero_messages.rb +9 -3
  17. data/lib/artoo/adaptors/test.rb +28 -0
  18. data/lib/artoo/connection.rb +5 -0
  19. data/lib/artoo/delegator.rb +1 -1
  20. data/lib/artoo/device.rb +13 -0
  21. data/lib/artoo/drivers/counter.rb +28 -0
  22. data/lib/artoo/drivers/pinger.rb +8 -11
  23. data/lib/artoo/drivers/random.rb +27 -0
  24. data/lib/artoo/drivers/test.rb +23 -0
  25. data/lib/artoo/master.rb +4 -2
  26. data/lib/artoo/robot.rb +11 -103
  27. data/lib/artoo/robot_class_methods.rb +147 -0
  28. data/lib/artoo/version.rb +1 -1
  29. data/test/robot_test.rb +3 -3
  30. data/test/test_helper.rb +1 -2
  31. metadata +10 -44
  32. data/bin/sphero.sh +0 -8
  33. data/bin/sphero_linux_bind.sh +0 -25
  34. data/bin/sphero_linux_socat.sh +0 -8
  35. data/examples/sphero2.rb +0 -26
  36. data/lib/artoo/adaptors/ardrone.rb +0 -41
  37. data/lib/artoo/adaptors/ardrone_navigation.rb +0 -33
  38. data/lib/artoo/adaptors/ardrone_video.rb +0 -35
  39. data/lib/artoo/adaptors/firmata.rb +0 -33
  40. data/lib/artoo/adaptors/roomba.rb +0 -51
  41. data/lib/artoo/adaptors/sphero.rb +0 -58
  42. data/lib/artoo/drivers/ardrone.rb +0 -15
  43. data/lib/artoo/drivers/ardrone_navigation.rb +0 -25
  44. data/lib/artoo/drivers/ardrone_video.rb +0 -27
  45. data/lib/artoo/drivers/button.rb +0 -59
  46. data/lib/artoo/drivers/led.rb +0 -48
  47. data/lib/artoo/drivers/motor.rb +0 -69
  48. data/lib/artoo/drivers/pinger2.rb +0 -27
  49. data/lib/artoo/drivers/roomba.rb +0 -179
  50. data/lib/artoo/drivers/servo.rb +0 -59
  51. data/lib/artoo/drivers/sphero.rb +0 -105
  52. data/lib/artoo/drivers/wiichuck.rb +0 -61
  53. data/lib/artoo/drivers/wiiclassic.rb +0 -139
  54. data/lib/artoo/drivers/wiidriver.rb +0 -102
  55. data/test/adaptors/ardrone_test.rb +0 -24
  56. data/test/adaptors/firmata_test.rb +0 -25
  57. data/test/adaptors/sphero_test.rb +0 -24
  58. data/test/drivers/ardrone_navigation_test.rb +0 -11
  59. data/test/drivers/ardrone_test.rb +0 -11
  60. data/test/drivers/ardrone_video_test.rb +0 -11
  61. data/test/drivers/led_test.rb +0 -56
  62. data/test/drivers/motor_test.rb +0 -40
  63. data/test/drivers/roomba_test.rb +0 -87
  64. data/test/drivers/servo_test.rb +0 -45
  65. data/test/drivers/sphero_test.rb +0 -54
  66. data/test/drivers/wiichuck_test.rb +0 -11
  67. data/test/drivers/wiiclassic_test.rb +0 -11
  68. data/test/drivers/wiidriver_test.rb +0 -54
@@ -174,7 +174,6 @@ ngChange:rd,required:dc,ngRequired:dc,ngValue:ud}).directive(lb).directive(ec);a
174
174
 
175
175
  !function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||s.toggleClass("open"),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f<s.length-1&&f++,~f||(f=0),s.eq(f).focus()}};var s=e.fn.dropdown;e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");i||r.data("dropdown",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.dropdown.Constructor=n,e.fn.dropdown.noConflict=function(){return e.fn.dropdown=s,this},e(document).on("click.dropdown.data-api",r).on("click.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on(".dropdown-menu",function(e){e.stopPropagation()}).on("click.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.focus().trigger("shown")}):t.$element.focus().trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(){var e=this;this.$element.hide(),this.backdrop(function(){e.removeBackdrop(),e.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('<div class="modal-backdrop '+r+'" />').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);if(!n.options.delay||!n.options.delay.show)return n.show();clearTimeout(this.timeout),n.hoverState="in",this.timeout=setTimeout(function(){n.hoverState=="in"&&n.show()},n.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?this.render(t.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(e){return~e.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(e){var t=[],n=[],r=[],i;while(i=e.shift())i.toLowerCase().indexOf(this.query.toLowerCase())?~i.indexOf(this.query)?n.push(i):r.push(i):t.push(i);return t.concat(n,r)},highlighter:function(e){var t=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return e.replace(new RegExp("("+t+")","ig"),function(e,t){return"<strong>"+t+"</strong>"})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);
176
176
  (function() {
177
-
178
177
  angular.module("artoo", []).config([
179
178
  "$routeProvider", function($routeProvider) {
180
179
  return $routeProvider.when("/robots", {
@@ -193,6 +192,7 @@ ngChange:rd,required:dc,ngRequired:dc,ngValue:ud}).directive(lb).directive(ec);a
193
192
  restrict: "A",
194
193
  link: function(scope, element, attrs, controller) {
195
194
  var clazz, path;
195
+
196
196
  clazz = attrs.activeLink;
197
197
  path = attrs.$$element.find('a').attr('href');
198
198
  path = path.substring(1);
@@ -211,9 +211,6 @@ ngChange:rd,required:dc,ngRequired:dc,ngValue:ud}).directive(lb).directive(ec);a
211
211
 
212
212
  }).call(this);
213
213
  (function() {
214
-
215
- window.driversWithOutput = ["Pinger", "Pinger2", "ardrone_navigation", "wiiclassic"];
216
-
217
214
  this.RobotIndexCtrl = function($scope, $http, $location, $route) {
218
215
  $http.get('/robots').success(function(data) {
219
216
  return $scope.robots = data;
@@ -225,6 +222,7 @@ ngChange:rd,required:dc,ngRequired:dc,ngValue:ud}).directive(lb).directive(ec);a
225
222
 
226
223
  this.RobotDetailCtrl = function($scope, $http, $routeParams, $location) {
227
224
  var device;
225
+
228
226
  $http.get('/robots/' + $routeParams.robotId).success(function(data) {
229
227
  return $scope.robot = data;
230
228
  });
@@ -236,6 +234,7 @@ ngChange:rd,required:dc,ngRequired:dc,ngValue:ud}).directive(lb).directive(ec);a
236
234
  };
237
235
  $scope.executeCommand = function(deviceId, command) {
238
236
  var params, post_params;
237
+
239
238
  params = $("#appendedDropdownButton").val();
240
239
  post_params = {};
241
240
  if (params !== "") {
@@ -245,21 +244,18 @@ ngChange:rd,required:dc,ngRequired:dc,ngValue:ud}).directive(lb).directive(ec);a
245
244
  return true;
246
245
  });
247
246
  };
248
- $scope.driverHasOutput = function(driverId) {
249
- if ($.inArray(driverId, window.driversWithOutput) !== -1) {
250
- return true;
251
- }
252
- };
253
247
  device = {
254
248
  console: function() {
255
249
  var wspath;
250
+
256
251
  if (window.ws) {
252
+ window.ws.onmessage = null;
257
253
  window.ws.close();
254
+ $(".console code").empty();
258
255
  }
259
256
  wspath = "ws://" + location.host + "/robots/";
260
257
  window.ws = new WebSocket(wspath + $scope.robot.name + "/devices/" + $scope.deviceDetail.name + "/events");
261
- $(".console code").empty();
262
- return ws.onmessage = function(evt) {
258
+ return window.ws.onmessage = function(evt) {
263
259
  return $(".console code").prepend(evt.data + "\n");
264
260
  };
265
261
  }
@@ -275,5 +271,4 @@ ngChange:rd,required:dc,ngRequired:dc,ngValue:ud}).directive(lb).directive(ec);a
275
271
  (function() {
276
272
 
277
273
 
278
-
279
274
  }).call(this);
@@ -62,7 +62,7 @@
62
62
  </b>
63
63
  </div>
64
64
  </div>
65
- <div class="row-fluid content" ng-show="driverHasOutput( deviceDetail.driver )" >
65
+ <div class="row-fluid content">
66
66
  <div class="console">
67
67
  <code></code>
68
68
  </div>
data/bin/robi CHANGED
@@ -26,7 +26,7 @@ command_set = Pry::CommandSet.new do
26
26
  end
27
27
 
28
28
  block_command "stop", "Stop all robots" do
29
- output.puts "Not yet implemented..."
29
+ output.puts "Stopping robots..."
30
30
  Celluloid::Actor[:master].stop_work
31
31
  end
32
32
 
@@ -16,8 +16,8 @@ end
16
16
  def play_jingle_bells
17
17
  roomba.song(JingleBells.song0, 0)
18
18
  roomba.song(JingleBells.song1, 1)
19
- roomba.song(JingleBells.song2, 2)
20
- roomba.song(JingleBells.song3, 3)
19
+ roomba.song(JingleBells.song0, 2)
20
+ roomba.song(JingleBells.song2, 3)
21
21
 
22
22
  roomba.play(0)
23
23
  sleep(7)
@@ -33,31 +33,28 @@ class JingleBells
33
33
 
34
34
  class << self
35
35
  def song0
36
- [[B, QUARTER], [B, QUARTER], [B, HALF],
37
- [B, QUARTER], [B, QUARTER], [B, HALF],
38
- [B, QUARTER], [D, QUARTER], [G, QUARTER], [A, QUARTER],
39
- [B, WHOLE]]
36
+ [n(B), n(B), n(B, HALF),
37
+ n(B), n(B), n(B, HALF),
38
+ n(B), n(D), n(G), n(A),
39
+ n(B, WHOLE)]
40
40
  end
41
41
 
42
42
  def song1
43
- [[C, QUARTER], [C, QUARTER], [C, QUARTER], [C, QUARTER],
44
- [C, QUARTER], [B, QUARTER], [B, HALF],
45
- [B, QUARTER], [A, QUARTER], [A, QUARTER], [B, QUARTER],
46
- [A, HALF], [D, HALF]]
43
+ [n(C), n(C), n(C), n(C),
44
+ n(C), n(B), n(B, HALF),
45
+ n(B), n(A), n(A), n(B),
46
+ n(A, HALF), n(D, HALF)]
47
47
  end
48
48
 
49
49
  def song2
50
- [[B, QUARTER], [B, QUARTER], [B, HALF],
51
- [B, QUARTER], [B, QUARTER], [B, HALF],
52
- [B, QUARTER], [D, QUARTER], [G, QUARTER], [A, QUARTER],
53
- [B, WHOLE]]
50
+ [n(C), n(C), n(C), n(C),
51
+ n(C), n(B), n(B), n(B),
52
+ n(D), n(D), n(C), n(A),
53
+ n(G, WHOLE)]
54
54
  end
55
55
 
56
- def song3
57
- [[C, QUARTER], [C, QUARTER], [C, QUARTER], [C, QUARTER],
58
- [C, QUARTER], [B, QUARTER], [B, QUARTER], [B, QUARTER],
59
- [D, QUARTER], [D, QUARTER], [C, QUARTER], [A, QUARTER],
60
- [G, WHOLE]]
56
+ def n(note, duration=QUARTER)
57
+ [note, duration]
61
58
  end
62
59
  end
63
60
  end
@@ -4,10 +4,11 @@ class HelloRobot < Artoo::Robot
4
4
  connection :loopback1
5
5
  connection :loopback2
6
6
  connection :loopback3
7
+ device :pinger, :driver => :pinger
8
+ device :counter, :driver => :counter
9
+ device :random, :driver => :random
7
10
  device :passthru1
8
11
  device :passthru2
9
- device :pinger, :driver => :pinger
10
- device :pinger2, :driver => :pinger2
11
12
 
12
13
  api :host => '127.0.0.1', :port => '8080'
13
14
 
@@ -1,9 +1,9 @@
1
1
  require 'artoo'
2
2
 
3
3
  connection :loop
4
- device :pinger, :driver => :pinger
4
+ device :counter, :driver => :counter
5
5
 
6
6
  work do
7
7
  puts 'Starting...'
8
- on pinger, :alive => proc {puts 'I am awesome!'}
8
+ on counter, :update => proc {puts 'I am awesome!'}
9
9
  end
@@ -2,12 +2,18 @@ require 'artoo'
2
2
 
3
3
  connection :sphero, :adaptor => :sphero, :port => '127.0.0.1:4560'
4
4
  device :sphero, :driver => :sphero
5
-
5
+
6
+ def contact(*args)
7
+ @contacts ||= 0
8
+ @contacts += 1
9
+ puts "Contact #{@contacts}"
10
+ end
11
+
6
12
  work do
7
- on sphero, :collision => proc { puts "Collision!" }
13
+ on sphero, :collision => :contact
8
14
 
9
15
  every(3.seconds) do
10
16
  puts "Rolling..."
11
17
  sphero.roll 90, rand(360)
12
18
  end
13
- end
19
+ end
@@ -0,0 +1,28 @@
1
+ require 'artoo/adaptors/adaptor'
2
+
3
+ module Artoo
4
+ module Adaptors
5
+ # This class is used for testing
6
+ class Test < Adaptor
7
+ def connect
8
+ super
9
+ Logger.info "Test adapter connected."
10
+ end
11
+
12
+ def disconnect
13
+ super
14
+ Logger.info "Test adapter disconnected."
15
+ end
16
+
17
+ def reconnect
18
+ Logger.info "Test adapter reconnecting..."
19
+ super
20
+ end
21
+
22
+ def method_missing(method_name, *arguments, &block)
23
+ Logger.info "Test adapter called '#{method_name}'."
24
+ true
25
+ end
26
+ end
27
+ end
28
+ end
@@ -92,6 +92,11 @@ module Artoo
92
92
  private
93
93
 
94
94
  def require_adaptor(type)
95
+ if Artoo::Robot.test?
96
+ original_type = type
97
+ type = :test
98
+ end
99
+
95
100
  require "artoo/adaptors/#{type.to_s}"
96
101
  @adaptor = constantize("Artoo::Adaptors::#{classify(type.to_s)}").new(:port => port, :parent => current_instance)
97
102
  end
@@ -2,7 +2,7 @@ module Artoo
2
2
  # Execution context for top-level robots
3
3
  # DSL methods executed on main are delegated to this class like Sinatra
4
4
  class MainRobot < Robot
5
- set :start_work, false
5
+ set :start_work, Proc.new { ! test? }
6
6
  end
7
7
 
8
8
  # Artoo delegation mixin that acts like Sinatra.
@@ -47,6 +47,14 @@ module Artoo
47
47
  "#{parent.safe_name}_#{name}_#{event}"
48
48
  end
49
49
 
50
+ def publish(event, *data)
51
+ if data.first
52
+ driver.publish(event_topic_name(event), *data)
53
+ else
54
+ driver.publish(event_topic_name(event))
55
+ end
56
+ end
57
+
50
58
  # @return [Hash] device
51
59
  def to_hash
52
60
  {
@@ -87,6 +95,11 @@ module Artoo
87
95
  private
88
96
 
89
97
  def require_driver(d)
98
+ if Artoo::Robot.test?
99
+ original_type = d
100
+ d = :test
101
+ end
102
+
90
103
  require "artoo/drivers/#{d.to_s}"
91
104
  @driver = constantize("Artoo::Drivers::#{classify(d.to_s)}").new(:parent => current_instance)
92
105
  end
@@ -0,0 +1,28 @@
1
+ require 'artoo/drivers/driver'
2
+
3
+ module Artoo
4
+ module Drivers
5
+ # Test driver to count up
6
+ class Counter < Driver
7
+
8
+ COMMANDS = [:count].freeze
9
+
10
+ # Publishes events to update and alive event topics
11
+ # with incremental count
12
+ def start_driver
13
+ @count = 0
14
+ every(interval) do
15
+ @count += 1
16
+ publish(event_topic_name("update"), "count", @count)
17
+ publish(event_topic_name("count"), @count)
18
+ end
19
+
20
+ super
21
+ end
22
+
23
+ def count
24
+ @count
25
+ end
26
+ end
27
+ end
28
+ end
@@ -2,26 +2,23 @@ require 'artoo/drivers/driver'
2
2
 
3
3
  module Artoo
4
4
  module Drivers
5
- # Pings itself
5
+ # Test driver that can be pinged itself
6
6
  class Pinger < Driver
7
7
 
8
8
  COMMANDS = [:ping].freeze
9
9
 
10
- # Publishes events to update and alive event topics
11
- # with incremental count
12
10
  def start_driver
13
- @count = 1
14
- every(interval) do
15
- publish(event_topic_name("update"), "alive", @count)
16
- publish(event_topic_name("alive"), @count)
17
- @count += 1
18
- end
19
-
11
+ @count = 0
20
12
  super
21
13
  end
22
14
 
15
+ # Publishes events to update event topic
16
+ # when pinged
23
17
  def ping
24
- "pong"
18
+ @count += 1
19
+ publish(event_topic_name("update"), "ping", @count)
20
+ publish(event_topic_name("ping"), @count)
21
+ "ping #{@count}"
25
22
  end
26
23
  end
27
24
  end
@@ -0,0 +1,27 @@
1
+ require 'artoo/drivers/driver'
2
+
3
+ module Artoo
4
+ module Drivers
5
+ # Test driver to obtain a random number
6
+ class Random < Driver
7
+
8
+ COMMANDS = [:random].freeze
9
+
10
+ # Publishes event to update event topic
11
+ # with random number
12
+ def start_driver
13
+ every(interval) do
14
+ @number = rand(100000)
15
+ publish(event_topic_name("update"), "random", @number)
16
+ publish(event_topic_name("random"), @number)
17
+ end
18
+
19
+ super
20
+ end
21
+
22
+ def random
23
+ @number
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ require 'artoo/drivers/driver'
2
+
3
+ module Artoo
4
+ module Drivers
5
+ # Test driver
6
+ class Test < Driver
7
+ def start_driver
8
+ Logger.info "Test driver starting..."
9
+
10
+ super
11
+ end
12
+
13
+ def command(method_name, *arguments)
14
+ if method_name == "publish"
15
+ super
16
+ else
17
+ Logger.info "Driver command '#{method_name}'"
18
+ true
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -46,6 +46,7 @@ module Artoo
46
46
  # Do asynchronous work for each robot
47
47
  def start_work
48
48
  robots.each {|r| r.async.work} unless Artoo::Robot.is_running?
49
+ Artoo::Robot.running!
49
50
  end
50
51
 
51
52
  # Pause work for each robot
@@ -61,9 +62,10 @@ module Artoo
61
62
  robots.each {|r| r.async.continue_work}
62
63
  end
63
64
 
64
- # @deprecated
65
+ # terminate all robots
65
66
  def stop_work
66
- #robots.each {|r| r.async.stop_work} unless !Artoo::Robot.is_running?
67
+ robots.each {|r| r.terminate} unless !Artoo::Robot.is_running?
68
+ Artoo::Robot.stopped!
67
69
  end
68
70
  end
69
71
  end
@@ -4,6 +4,7 @@ require 'multi_json'
4
4
  require 'artoo/ext/timers'
5
5
  require 'artoo/ext/actor'
6
6
 
7
+ require 'artoo/robot_class_methods'
7
8
  require 'artoo/basic'
8
9
  require 'artoo/connection'
9
10
  require 'artoo/device'
@@ -13,14 +14,16 @@ require 'artoo/master'
13
14
  require 'artoo/port'
14
15
  require 'artoo/utility'
15
16
 
16
-
17
17
  module Artoo
18
18
  # The most important class used by Artoo is Robot. This represents the primary
19
19
  # interface for interacting with a collection of physical computing capabilities.
20
+ #
21
+ # This file contains the instance-level methods used by Artoo::Robot
20
22
  class Robot
21
23
  include Celluloid
22
24
  include Celluloid::Notifications
23
25
  extend Artoo::Basic
26
+ extend Artoo::Robot::ClassMethods
24
27
  include Artoo::Utility
25
28
  include Artoo::Events
26
29
 
@@ -37,108 +40,6 @@ module Artoo
37
40
  initialize_devices(params[:devices] || {})
38
41
  end
39
42
 
40
- class << self
41
- attr_accessor :device_types, :working_code, :running,
42
- :use_api, :api_host, :api_port
43
-
44
- def connection_types
45
- @@connection_types ||= []
46
- end
47
-
48
- # Connection to some hardware that has one or more devices via some specific protocol
49
- # @example connection :arduino, :adaptor => :firmata, :port => '/dev/tty.usbmodemxxxxx'
50
- # @param [String] name
51
- # @param [Hash] params
52
- def connection(name, params = {})
53
- Celluloid::Logger.info "Registering connection '#{name}'..."
54
- self.connection_types << {:name => name}.merge(params)
55
- end
56
-
57
- # Device that uses a connection to communicate
58
- # @example device :collision_detect, :driver => :switch, :pin => 3
59
- # @param [String] name
60
- # @param [Hash] params
61
- def device(name, params = {})
62
- Celluloid::Logger.info "Registering device '#{name}'..."
63
- self.device_types ||= []
64
- self.device_types << {:name => name}.merge(params)
65
- end
66
-
67
- # The work that needs to be performed
68
- # @example
69
- # work do
70
- # every(10.seconds) do
71
- # puts "hello, world"
72
- # end
73
- # end
74
- # @param [block] work
75
- def work(&block)
76
- Celluloid::Logger.info "Preparing work..."
77
- self.working_code = block if block_given?
78
- end
79
-
80
- # Activate RESTful api
81
- # Example:
82
- # api :host => '127.0.0.1', :port => '1234'
83
- def api(params = {})
84
- Celluloid::Logger.info "Registering api..."
85
- self.use_api = true
86
- self.api_host = params[:host] || '127.0.0.1'
87
- self.api_port = params[:port] || '4321'
88
- end
89
-
90
- # Work can be performed by either:
91
- # an existing instance
92
- # an array of existing instances
93
- # or, a new instance can be created
94
- # @param [Robot] robot
95
- def work!(robot=nil)
96
- return if !test? && is_running?
97
- prepare_robots(robot)
98
-
99
- unless cli?
100
- Celluloid::Actor[:api] = Api.new(self.api_host, self.api_port) if self.use_api
101
- master.start_work
102
- self.running = true
103
- sleep # sleep main thread, and let the work commence!
104
- end
105
- end
106
-
107
- # Prepare master robots for work
108
- def prepare_robots(robot=nil)
109
- if robot.respond_to?(:work)
110
- robots = [robot]
111
- elsif robot.kind_of?(Array)
112
- robots = robot
113
- else
114
- robots = [self.new]
115
- end
116
-
117
- Celluloid::Actor[:master] = Master.new(robots)
118
- end
119
-
120
- # Master actor
121
- def master
122
- Celluloid::Actor[:master]
123
- end
124
-
125
- # @return [Boolean] True if test env
126
- def test?
127
- ENV["ARTOO_TEST"] == 'true'
128
- end
129
-
130
- # @return [Boolean] True if cli env
131
- def cli?
132
- ENV["ARTOO_CLI"] == 'true'
133
- end
134
-
135
- # @return [Boolean] True if it's running
136
- def is_running?
137
- self.running ||= false
138
- self.running == true
139
- end
140
- end
141
-
142
43
  # @return [String] Name without spaces and downcased
143
44
  def safe_name
144
45
  name.gsub(' ', '_').downcase
@@ -200,6 +101,13 @@ module Artoo
200
101
  current_class.working_code ||= proc {puts "No work defined."}
201
102
  end
202
103
 
104
+ # @param [Symbol] period
105
+ # @param [Numeric] interval
106
+ # @return [Boolean] True if there is recurring work for the period and interval
107
+ def has_work?(period, interval)
108
+ current_instance.timers.find {|t| t.recurring == (period == :every) && t.interval == interval}
109
+ end
110
+
203
111
  # @return [Hash] robot
204
112
  def to_hash
205
113
  {