from logitech.g19 import *
|
|
from logitech.g19_keys import Key
|
|
from logitech.g19_receivers import *
|
|
from logitech.runnable import Runnable
|
|
|
|
import multiprocessing
|
|
import os
|
|
import tempfile
|
|
import threading
|
|
import time
|
|
|
|
|
|
class EarthImageCreator(Runnable):
|
|
'''Thread for calling xplanet for specific angles.'''
|
|
|
|
def __init__(self, lg19, angleStart, angleStop, slot, dataStore):
|
|
'''Creates images for angles [angleStart, angleStop) and stores a the
|
|
list of frames via dataStore.store(slot, frames).
|
|
|
|
After completion of each frame, dataStore.signal_frame_done() will be
|
|
called.
|
|
|
|
'''
|
|
Runnable.__init__(self)
|
|
self.__angleStart = angleStart
|
|
self.__angleStop = angleStop
|
|
self.__dataStore = dataStore
|
|
self.__lg19 = lg19
|
|
self.__slot = slot
|
|
|
|
def run(self):
|
|
frames = []
|
|
try:
|
|
handle, filename = tempfile.mkstemp('.bmp')
|
|
os.close(handle)
|
|
|
|
for i in range(self.__angleStart, self.__angleStop):
|
|
if self.is_about_to_stop():
|
|
break
|
|
cmdline = "xplanet -geometry 320x240 -output "
|
|
cmdline += filename
|
|
cmdline += " -num_times 1 -latitude 40 -longitude "
|
|
cmdline += str(i)
|
|
os.system(cmdline)
|
|
frames.append( self.__lg19.convert_image_to_frame(filename) );
|
|
self.__dataStore.signal_frame_done()
|
|
finally:
|
|
os.remove(filename)
|
|
self.__dataStore.store(self.__slot, frames)
|
|
|
|
|
|
class DataStore(object):
|
|
'''Maintains all xplanet generated frames.'''
|
|
|
|
def __init__(self, lg19):
|
|
self.__allFrames = []
|
|
self.__creators = []
|
|
self.__numThreads = multiprocessing.cpu_count()
|
|
self.__lg19 = lg19
|
|
self.__data = [[]] * self.__numThreads
|
|
self.__lock = threading.Lock()
|
|
self.__framesDone = 0
|
|
|
|
def abort_update(self):
|
|
self.__lock.acquire()
|
|
for creator in self.__creators:
|
|
creator.stop()
|
|
self.__lock.release()
|
|
|
|
def get_data(self):
|
|
'''Returns all currently available data.
|
|
|
|
@return List of all frames. If no frames are calculated, an empty list
|
|
will be returned.
|
|
|
|
'''
|
|
self.__lock.acquire()
|
|
frames = self.__allFrames
|
|
self.__lock.release()
|
|
return frames
|
|
|
|
def signal_frame_done(self):
|
|
self.__lock.acquire()
|
|
self.__framesDone += 1
|
|
print "frames done: {0}".format(self.__framesDone)
|
|
self.__lock.release()
|
|
|
|
def update(self):
|
|
'''Regenerates all data.'''
|
|
self.__lock.acquire()
|
|
self.__framesDone = 0
|
|
self.__data = [[]] * self.__numThreads
|
|
self.__lock.release()
|
|
threads = []
|
|
|
|
for i in range(self.__numThreads):
|
|
perCpu = 360 / self.__numThreads
|
|
angleStart = i * perCpu
|
|
if i == self.__numThreads - 1:
|
|
angleStop = 360
|
|
else:
|
|
angleStop = angleStart + perCpu
|
|
c = EarthImageCreator(self.__lg19, angleStart, angleStop, i, self)
|
|
self.__lock.acquire()
|
|
self.__creators.append(c)
|
|
self.__lock.release()
|
|
c.start()
|
|
t = threading.Thread(target=c.run)
|
|
threads.append(t)
|
|
t.start()
|
|
|
|
for t in threads:
|
|
t.join()
|
|
|
|
self.__lock.acquire()
|
|
self.__creators = []
|
|
self.__allFrames = []
|
|
for frame in self.__data:
|
|
self.__allFrames += frame
|
|
self.__lock.release()
|
|
|
|
def store(self, slot, frames):
|
|
self.__lock.acquire()
|
|
print "committing {0}".format(slot)
|
|
self.__data[slot] = frames
|
|
self.__lock.release()
|
|
|
|
|
|
class XplanetRenderer(Runnable):
|
|
'''Renderer which renderes current data from DataStore.'''
|
|
|
|
def __init__(self, lg19, dataStore):
|
|
Runnable.__init__(self)
|
|
self.__dataStore = dataStore
|
|
self.__fps = 25
|
|
self.__lastTime = time.clock()
|
|
self.__lg19 = lg19
|
|
|
|
def execute(self):
|
|
frames = reversed(self.__dataStore.get_data())
|
|
if not frames:
|
|
time.sleep(1)
|
|
counter = 0
|
|
for frame in frames:
|
|
counter += 1
|
|
if counter > self.__fps:
|
|
counter = 0
|
|
if self.is_about_to_stop():
|
|
break
|
|
now = time.clock()
|
|
diff = self.__lastTime - now + (1.0 / self.__fps)
|
|
if diff > 0:
|
|
time.sleep(diff)
|
|
self.__lastTime = time.clock()
|
|
self.__lg19.send_frame(frame)
|
|
|
|
|
|
class XplanetInputProcessor(InputProcessor):
|
|
|
|
def __init__(self, xplanet):
|
|
self.__xplanet = xplanet
|
|
|
|
def process_input(self, inputEvent):
|
|
processed = False
|
|
if Key.PLAY in inputEvent.keysDown:
|
|
self.__xplanet.start()
|
|
processed = True
|
|
if Key.STOP in inputEvent.keysDown:
|
|
self.__xplanet.stop()
|
|
processed = True
|
|
return processed
|
|
|
|
|
|
class Xplanet(object):
|
|
|
|
def __init__(self, lg19):
|
|
self.__dataStore = DataStore(lg19)
|
|
self.__lg19 = lg19
|
|
self.__renderer = XplanetRenderer(lg19, self.__dataStore)
|
|
self.__inputProcessor = XplanetInputProcessor(self)
|
|
|
|
def get_input_processor(self):
|
|
return self.__inputProcessor
|
|
|
|
def start(self):
|
|
t = threading.Thread(target=self.__dataStore.update)
|
|
t.start()
|
|
t = threading.Thread(target=self.__renderer.run)
|
|
self.__renderer.start()
|
|
t.start()
|
|
|
|
def stop(self):
|
|
self.__renderer.stop()
|
|
self.__dataStore.abort_update()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
lg19 = G19()
|
|
xplanet = Xplanet(lg19)
|
|
xplanet.start()
|
|
try:
|
|
while True:
|
|
time.sleep(10)
|
|
finally:
|
|
xplanet.stop()
|