diff --git a/bin/captureDevice b/bin/captureDevice new file mode 100644 --- /dev/null +++ b/bin/captureDevice @@ -0,0 +1,137 @@ +#!bin/python +from __future__ import division +from rdflib import URIRef, Literal +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks, Deferred + +import logging +import optparse +import os +import time +import traceback +import treq +import cyclone.web, cyclone.websocket, cyclone.httpclient +from greplin import scales + +from run_local import log +from lib.cycloneerr import PrettyErrorHandler + +from light9.namespaces import L9 +from light9 import networking, showconfig +from light9.rdfdb.syncedgraph import SyncedGraph +from light9.paint.capture import writeCaptureDescription +from light9.greplin_cyclone import StatsForCyclone +from light9.effect.settings import DeviceSettings +from light9.effect.sequencer import sendToCollector + +stats = scales.collection('/webServer', scales.PmfStat('setAttr')) + +class Attrs(PrettyErrorHandler, cyclone.web.RequestHandler): + def put(self): + with stats.setAttr.time(): + client, clientSession, settings, sendTime = parseJsonMessage(self.request.body) + self.set_status(202) + + +class Camera(object): + def __init__(self, imageUrl): + self.imageUrl = imageUrl + + def takePic(self, uri, writePath): + log.info('takePic %s', uri) + return treq.get(self.imageUrl).addCallbacks(lambda r: self._done(writePath, r), log.error) + + @inlineCallbacks + def _done(self, writePath, response): + jpg = yield response.content() + try: + os.makedirs(os.path.dirname(writePath)) + except OSError: + pass + with open(writePath, 'w') as out: + out.write(jpg) + log.info('wrote %s', writePath) + + + +settleTime = .5 + +camera = Camera('http://dash:8200/picamserve/pic?res=480&resize=480&rotation=180&iso=800&redgain=1.6&bluegain=2&shutter=60000') + +def launch(graph): + + def steps(a, b, n): + return [round(a + (b - a) * i / n, 5) for i in range(n)] + + startTime = time.time() + toGather = [] + + row = 0 + for ry in steps(0.85, .92, 6): + xSteps = steps(.24, .45, 12) + if row % 2: + xSteps.reverse() + row += 1 + for rx in xSteps: + toGather.append(DeviceSettings(graph, [ + (L9['device/moving1'], L9['rx'], rx), + (L9['device/moving1'], L9['ry'], ry), + ])) + + numPics = [0] + settingsCache = set() + @inlineCallbacks + def step(): + if not toGather: + reactor.stop() + return + settings = toGather.pop() + + log.info('move to %r', settings) + yield sendToCollector(client='captureDevice', session='main', settings=settings) + + d = Deferred() + reactor.callLater(settleTime, d.callback, None) + yield d + dev = settings.devices()[0] + + devTail = dev.rsplit('/')[-1] + captureId = 'cap%s' % (int(startTime) - 1495170000) + picId = 'pic%s' % numPics[0] + path = '/'.join(['capture', devTail, captureId, picId]) + ctx = URIRef('/'.join([showconfig.showUri(), 'capture', devTail, captureId, 'index'])) + uri = URIRef('/'.join([showconfig.showUri(), 'capture', devTail, captureId, picId])) + + relOutPath = path + '.jpg' + + yield camera.takePic(uri, os.path.join(showconfig.root(), relOutPath)) + numPics[0] += 1 + + writeCaptureDescription(graph, ctx, uri, dev, relOutPath, settingsCache, settings) + + reactor.callLater(0, step) + step().addErrback(log.error) + + reactor.listenTCP(networking.captureDevice.port, + cyclone.web.Application(handlers=[ + (r'/()', cyclone.web.StaticFileHandler, + {"path" : "light9/web", "default_filename" : "captureDevice.html"}), + (r'/stats', StatsForCyclone), + ]), + interface='::') + log.info('serving http on %s', networking.captureDevice.port) + +def main(): + parser = optparse.OptionParser() + parser.add_option("-v", "--verbose", action="store_true", + help="logging.DEBUG") + (options, args) = parser.parse_args() + log.setLevel(logging.DEBUG if options.verbose else logging.INFO) + + graph = SyncedGraph(networking.rdfdb.url, "captureDevice") + + graph.initiallySynced.addCallback(lambda _: launch(graph)).addErrback(log.error) + reactor.run() + +if __name__ == '__main__': + main()