#
# pxc_200.py : Driver for the ImageNation PXC-200 frame grabber, using
#             a Linux driver.
#
# Written by akuchlin@mems-exchange.org
#

__revision__ = "$Id: pxc_200.py,v 1.22 2002/03/29 17:03:22 akuchlin Exp $"

import fcntl, os, struct, sys, tempfile, time
import pxc200


from PIL import Image
from mems.instrument import protocols
from mems.instrument.server import util
from mems.instrument.server.configuration import get_configuration

# Starting values of frame grabber parameters
START_BRIGHT = 75
START_CONTRAST = 164
START_HUE = 164
START_SATU = 164
START_SATV = 164

# Global variable holding the camera object
camera = None

# Global variable holding the name of a temporary file that will be used
# for the life of this process.
image_path = tempfile.mktemp(suffix='.jpg')

# Global: desired image size
x_requested, y_requested = (320,240)

class PXC200:
    # Attributes

    def __init__(self):
        # Starting values
        self.image_size = (320, 240)
        self.brightness = START_BRIGHT
        self.contrast = START_CONTRAST
        self.hue = START_HUE
        self.satu = START_SATU
        self.satv = START_SATV
        
        self._camera_fd = -1
        self.open()

        
    def open(self):
        """open()
        Open the camera device.  If it's already open, .close() is
        automatically called first.
        """
        
        config = get_configuration()
        if self.image_size == (640,480):
	    filename = '/dev/pxc0Hbgr'
        else: 
	    filename = '/dev/pxc0bgr' 

        if config.get_debug():
            print 'Opening PXC200 device file=', filename

        old_fd = self._camera_fd 
        if old_fd != -1:
            if config.get_debug():
                print ' Closing previously open fd:', old_fd
            self.close()
            time.sleep(3)
        while 1:
            try:
                self._camera_fd = os.open(filename, os.O_RDONLY)
                if config.get_debug():
                    print ' PXC200 fd=', self._camera_fd
            except os.error, (errno, msg):
                if config.get_debug():
                    print 'PXC-200 open() error', errno,msg ; time.sleep(1)
            else: break
            
    def get_image(self):
        "Grab a viewing-quality image from the camera"
        image = Image.new('RGB', self.image_size)

	f = self._camera_fd

        # Set image parameters
        fcntl.ioctl(f, pxc200.PX_IOCSBRIGHT,
                    struct.pack('i', self.brightness) )
        fcntl.ioctl(f, pxc200.PX_IOCSCONTRAST,
                    struct.pack('i', self.contrast) )
        fcntl.ioctl(f, pxc200.PX_IOCSHUE,
                    struct.pack('i', self.hue) )
        fcntl.ioctl(f, pxc200.PX_IOCSSATU,
                    struct.pack('i', self.satu) )
        fcntl.ioctl(f, pxc200.PX_IOCSSATV,
                    struct.pack('i', self.satv) )

        # Acquire image
        pxc200.snap(f, image.im.id)
        return image

    def set(self, **kw):
        return apply(self._set, (), kw)
        
    def _set(self, **kw):
        "Set camera parameters to new values"
        params_used = []

        if kw.has_key('brightness'):
            self.brightness = kw['brightness']
            params_used.append( 'brightness' )
        if kw.has_key('contrast'):
            self.contrast = kw['contrast']
            params_used.append( 'contrast' )
        if kw.has_key('hue'):
            self.hue = kw['hue']
            params_used.append( 'hue' )
        if kw.has_key('satu'):
            self.satu = kw['satu']
            params_used.append( 'satu' )
        if kw.has_key('satv'):
            self.satv = kw['satv']
            params_used.append( 'satv' )

            
        return params_used
    
    def close(self):
        """close()
        Close down the camera.
        """
        config = get_configuration()
	old_fd = self._camera_fd 
        if config.get_debug():
            print 'Closing PXC-200 fd', old_fd
        if old_fd != -1:
            os.close(old_fd)
            self._camera_fd = -1



def do_snapshot ():
    image = camera.get_image()
    image = image.resize( (x_requested, y_requested) )
    
    # Write the image data to a temporary file
    path = tempfile.mktemp(suffix='.jpg')
    image.save(path)

    # Atomically move the temporary file to the final filename.
    os.rename(path, image_path)
    
    util.send_message("network_server",
                       "image path=%s hires=no" % image_path)

def do_hires ():
    return do_snapshot()


def do_size (x, y):
    # Select the size made available by the camera that's required to
    # support the requested image size.
    global x_requested, y_requested
    x_requested, y_requested = x,y
    
    if 320 <= x or 240 <= y:
        camera.image_size = (640,480)
    else:
        camera.image_size = (320,240)

    camera.open()
        


def loop ():
    "Main loop for the camera process"
    
    config = get_configuration()
    while 1:
        if util.has_input_waiting(sys.stdin):
            line = sys.stdin.readline()
            cmd, args = protocols.parse_message_line(line)
            if config.get_debug():
                print '%s: Received command %s %r' % (__name__, cmd, args)
                
            if cmd == 'quit':
                if config.get_debug():
                    print '%s: exiting' % __name__
                break

            elif cmd == "snapshot":
                do_snapshot(**args)
            elif cmd == "hires":
                do_hires(**args)
            elif cmd == "size":
                do_size(**args)
            else:
                raise RuntimeError, ("Unknown command %r args=%r" % (cmd, args))

    
def main ():
    global camera
    camera = PXC200()
    
    # Run main loop, and be sure to delete the temporary file
    # (XXX this may be a bit too much tidiness; after a 'quit' is sent
    # to the camera process, the network server can then crash because
    # the image file is no longer there.)
    try:
        loop()
    finally:
        try:
            os.unlink(image_path)
        except os.error:
            pass
        
    return 

