artoo 0.5.0 → 1.0.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  {