PythonワンライナーでUSBミサイルランチャーを制御
先週の金曜日、弊社の
竹迫、
鶴岡
と共に
弾さんのお宅で行われた
Hackathonに参加しました。
内容に関しては
YappoLogs: YAPC::Asia Hackathon at Dan the hotelが詳しいです。
追記:先ほど公開した上の記事には誤りがありました。 Hackathonで作ったコードは下のような物だったようです。 昨日新入社員歓迎会で飲み過ぎたため、 寝ている間に小人さんがいじったことに気がつかず、 そのまま公開してしまいました。 申し訳ありませんでした。
新人研修にかまけて記事にせずに放置していたところ TokuLog 改め Perl を極めて結婚するブログ - missile on ruby.でリークされてしまいましたが、ワンライナーで作りました。 下のコードを実行すると、動作テストとして三三七拍子を奏でた後、 インタラクティブに操作できるモードになります。
[globals().__setitem__("COMMAND",{'right':[8,0,0,0,0,0,0,0],'d':[2,0,0,0,0,0,0,0],
'f':[16,0,0,0,0,0,0,0],'fire':[16,0,0,0,0,0,0,0],'stop':[0,0,0,0,0,0,0,0],'up':[1,
0,0,0,0,0,0,0],'l':[4,0,0,0,0,0,0,0],'down':[2,0,0,0,0,0,0,0],'s':[0,0,0,0,0,0,0,0
],'r':[8,0,0,0,0,0,0,0],'u':[1,0,0,0,0,0,0,0],'left':[4,0,0,0,0,0,0,0]})]and[(lamb
da f,arg:f(arg,f))((lambda busses,foo:(lambda dev,busses:dev and[globals().__setit
em__("dev",dev[0])or globals().__setitem__("conf",dev[0].configurations[0])or glob
als().__setitem__("intf",conf.interfaces[0][0])or globals().__setitem__("endpoints
",[e.address for e in intf.endpoints])]or foo(busses[1:]))([d for d in busses[0].d
evices if d.idVendor==0x1941 and d.idProduct==0x8021],busses)),__import__("usb").b
usses())]and[globals().__setitem__("handle",dev.open())or handle.setConfiguration(
conf)or handle.claimInterface(intf)or handle.setAltInterface(intf)]and[globals()._
_setitem__("do",lambda command,time=0.0:[COMMAND.has_key(command)and handle.contro
lMsg(0x21,0x09,COMMAND[command],0x2,0x0)]and[time and[__import__("time").sleep(tim
e)or do("stop")]])]and[__name__=="__main__"and(lambda Command:[globals().__setitem
__(cmd,Command(cmd))for cmd in COMMAND]and globals().__setitem__("FIRE",Command("f
ire",6.0)))((lambda cmd,time=0.1:(lambda self:self.__dict__.__setitem__("__repr__"
,lambda:do(cmd,time)and"")or self)(__import__("HTMLParser").HTMLParser())))or[do(x
,3.0/8)or do("s",1.0/8)for x in"uduldudrudududus"]]
どう動くのかは下の動画をご覧下さい。
…音と画像が合っていないですね…。
追記:先ほど公開した上の記事には誤りがありました。 Hackathonで作ったコードは下のような物だったようです。 昨日新入社員歓迎会で飲み過ぎたため、 寝ている間に小人さんがいじったことに気がつかず、 そのまま公開してしまいました。 申し訳ありませんでした。
追記:SourceForge.net: PyUSBとpyusbモジュールについて。を参考にしました。
# -*- coding: cp932 -*-
"""Interface to manupilate MissileLauncher"""
__all__ = ["MissileLauncher"]
from time import sleep
import usb
COMMAND = {}
def make(x):
result = [0] * 8
result[0] = x
return result
COMMAND["left"] = make(0x4)
COMMAND["right"] = make(0x8)
COMMAND["up"] = make(0x1)
COMMAND["down"] = make(0x2)
COMMAND["fire"] = make(0x10)
COMMAND["stop"] = make(0x0)
short = {}
for k in COMMAND:
short[k[0]] = COMMAND[k]
COMMAND.update(short)
class UsbDevice:
def __init__(self, vendor_id, product_id):
busses = usb.busses()
self.handle = None
for bus in busses:
devices = bus.devices
for dev in devices:
if dev.idVendor == vendor_id and dev.idProduct == product_id:
self.dev = dev
self.conf = self.dev.configurations[0]
self.intf = self.conf.interfaces[0][0]
self.endpoints = [e.address for e in self.intf.endpoints]
return
raise RuntimeError('Device not found.')
def open(self):
if self.handle:
self.handle = None
handle = self.dev.open()
handle.setConfiguration(self.conf)
handle.claimInterface(self.intf)
handle.setAltInterface(self.intf)
self.handle = handle
class MissileLauncher:
def __init__(self):
dev = UsbDevice(0x1941, 0x8021)
dev.open()
self.dev = dev
def do(self, command, time = 0.0):
if not COMMAND.has_key(command):
return -1
self.dev.handle.controlMsg(
0x21,
0x09,
COMMAND[command],
0x2,
0x0)
if time:
sleep(time)
self.do("stop")
def putCommandInGlobals():
class Command(object):
def __init__(self, cmd, time = 0.1):
self.cmd = cmd
self.time = time
def __repr__(self):
m.do(self.cmd, self.time)
return ""
for cmd in COMMAND:
globals()[cmd] = Command(cmd)
globals()["FIRE"] = Command("fire", 6.0)
def test():
def do(a, t = 0.5):
m.do(a, t * 0.75)
m.do("stop", t * 0.25)
[do(x) for x in "uduldudrudududus"]
if __name__ == "__main__":
m = MissileLauncher()
putCommandInGlobals()
test()
print "ok."