OverviewFilesCommitsIssuesDocsReleasesPeers

cthuloid / cuemgr / prinboo.py

174 lines · python ref:
import socket, os, errno, ast, paramiko, time
from socketsthread import *

class Motors(SocketOwner):
  """ Serial connection to arduino controlling Prinboo wheelchair motors"""

  def __init__(self, address):
    SocketOwner.__init__(self, address, 1339)
    self.speed = 0  # denotes number of L/Rs we will send to the arduino
    self.STOP = ' '
    self.direction = self.STOP

  def readForWriting(self):
    self.sendSpeed()


  def sendSpeed(self):
    s = self.STOP # initial character must reset the uc's internal counter to zero

    if self.direction != self.STOP:
      # append a number of l and r's
      for i in range(self.speed): s += self.direction
    self.write(s)

  def incSpeed(self):
    self.speed += 1
    self.sendSpeed()

  def decSpeed(self):
    self.speed -= 1
    self.sendSpeed()

  def stop(self):
    self.direction = self.STOP
    self.sendSpeed()

  def turnLeft(self):
    self.direction = 'L'
    self.sendSpeed()

  def turnRight(self):
    self.direction = 'R'
    self.sendSpeed()

  def forward(self):
    self.direction = 'LR'
    self.sendSpeed()

  def backward(self):
    self.direction = 'lr'
    self.sendSpeed()


class LimbServos(LinedSocketOwner):
    """ Serial connection to arduino controlling Prinboo limb and head servos"""

    def __init__(self, address):
        LinedSocketOwner.__init__(self, address, 1338)
        self.anglesDict = {}

    def readyForWriting(self):
        super().readyForWriting()
        self.readServos() # request current servo angles
        self.socket.send(b'r\n')

    #def dataReceived(self, data): pass

    def getAngle(self, id): return self.anglesDict[id]

    def setAngle(self, idOrDict, angle=None, sendUpdate=True):
      if isinstance(idOrDict, int):
        self.anglesDict[idOrDict] = angle
      elif isinstance(idOrDict, dict) and angle == None:
        for id,a in idOrDict.items(): self.anglesDict[id] = a
      elif isinstance(idOrDict, list) and angle == None:
        id = 1
        for a in idOrDict:
          self.anglesDict[id] = a
          id += 1
      else: raise TypeError('bad argument to Servos.setAngle')

      if sendUpdate: self.sendServoPos()

    def __str__(self): return str(self.anglesDict)

    # argument is a dictionary of id:angle
    # angles are 0-1023; center is 512; safe angle range is 200-824
    def sendServoPos(self):
        #print(self.anglesDict)
        #if not self.valid(): return

        # text protocol of id:angle pairs
        cmd = 's'
        for id,angle in self.anglesDict.items():
            if id == 5: continue # TODO servo 5 broke
            cmd += ' ' + str(id) + ':' + str(angle)

        cmd += '\n'
        #print(cmd)
        self.write(cmd)

    def handleLine(self, line):
        # read the positions of all servos, which is given in a json/python dict format
        preamble ='Servo Readings:'
        if line.startswith(preamble):
            readings_text = line[len(preamble):].strip()
            readings = ast.literal_eval(readings_text)
            #print(readings_text)
            if not isinstance(readings, dict):
                print('error reading servos')
                return
            self.setAngle(readings, sendUpdate=False)

        #else: print(line)

    def readServos(self):
        self.write('r\n')

class Screen:
    '''Play videos on Prinboo's raspi via SSH connection.
    '''
    def __init__(self, address):
      #try:
        ssh = paramiko.SSHClient()
        self.ssh = ssh
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(address, username='pi', password='raspberry')
        # start playing immediately
        self.play('~/cthuloid/ghettopticon/blender/prinboo-talking.mp4')
      #except paramiko.ssh_exception.NoValidConnectionsError:
      #  self.ssh = None

    def play(self, filename):
      # remove special characters and append it to the video player name
      special = '$\\#!|<>;'
      for c in special: filename.replace(c, ' ')

      # play video file continuously and hang onto stdin so we can control playback
      filename = 'omxplayer --loop --orientation 180 ' + filename
      self.stdin, stdout, stderr = self.ssh.exec_command(filename)

    def togglePlayback(self):
      self.stdin.write('i')
      time.sleep(.2)
      self.stdin.write(' ')

    def exit(self):
      self.stdin.write('q')
      #ssh.close()

class PrinbooClass:
  def __init__(self, address):
    self.limbs = LimbServos(address)
    self.motors = Motors(address)
    self.screen = Screen(address)
    self.thread = SocketsThread((self.limbs, self.motors), self.motors)
    self.thread.start()

    # keep only one of these threads running at a time
    self.limbsThread = None

  def exit(self):
    self.screen.exit()
    self.thread.exit()
    if self.limbsThread: self.limbsThread.exit()

Prinboo = PrinbooClass('10.0.0.23')

if __name__ == '__main__':
  addr = 'localhost'
  #p = Prinboo(addr, '')
  s = Screen(addr)
  s.play('vlc ~/circusautomatic/cthuloid/ghettopticon/blender/prinboo0001-1000.mp4')