#!/usr/bin/env python # # Digita Operating Environment- Command Device Protocol # # $Id$ # Created on: 11/11/1999 # Author : Sai Lai Lo # # Copyright (C) 1999 AT&T Laboratories Cambridge # # The python module is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free # Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA import string import struct import array import os import time if __name__ == "__main__": import sys import regsub # Camera Error code kCHNoErr = 0 kCHUnimplemented = 1 kCHUnsupportedVersion = 2 kCHAppTimeout = 3 kCHInternalError = 4 kCHParamError = 5 kCHFileSystemFull = 6 kCHFileNotFound = 7 kCHDataSectionNotFound = 8 kCHInvalidFileType = 9 kCHUnknownDrive = 10 kCHDriveNotMounted = 11 kCHSystemBusy = 12 kCHBatteryLow = 13 # Host Error code hResponseMismatch = 1 hUnExpectedDataType = 2 hInvalidParam = 3 hNotSupportedYet = 4 #################################################################### # Exception class # Base class of all exceptions raised by members of this module class Error: pass # Exception raised because the camera reports an error, error code # is one of the camera error code. # class CamError(Error): def __init__(self,errorcode): self.errorcode = errorcode # Exception raised because the host detects an error, error code # is one of the host error code # class HostError(Error): def __init__(self,errorcode): self.errorcode = errorcode ##################################################################### class Cam: """ Digita Command Device Protocol """ def __init__ (self, fileobj): "Create a Cdp instance and use the file object to talk to the camera." self.fh = fileobj def GetProductInfo(self,pname): """ Requests specific information about the product by parameter. You can request information by parameter, or you can request all product information by passing an empty string as pname. The return value is a list of PNameTypeValue. """ kGetProductInfo = 0x0001 if (pname != 0): bodyfmt = 'L' else: bodyfmt = '' chdr = CmdHeader(kGetProductInfo,bodyfmt) # send header cpkt = chdr.pack() self.fh.write(cpkt) # send body if (pname != 0): cpkt = struct.pack('>L',pname) self.fh.write(cpkt) rhdr = ResHeader(kGetProductInfo) rpkt = self.fh.read(ResHeader.hdrsize) (result,bodysize) = rhdr.unpack(rpkt) if (result != kCHNoErr): raise CamError(result) rpkt = self.fh.read(bodysize) tvl = UnpackPNameTypeValueList(rpkt) return tvl def GetFileList(self,order,filespec): """ Return a list of all the files in the selected by and . """ kGetFileList = 0x0040 if (order < 0 or order > 2): raise HostError(hInvalidParam) bodyfmt = 'll32s16s' chdr = CmdHeader(kGetFileList,bodyfmt) cpkt = chdr.pack() + \ struct.pack('>'+bodyfmt, order, filespec.DriveNo, filespec.PathName, filespec.DOSName) self.fh.write(cpkt) rhdr = ResHeader(kGetFileList) rpkt = self.fh.read(ResHeader.hdrsize) (result,bodysize) = rhdr.unpack(rpkt) if (result != kCHNoErr): raise CamError(result) rpkt = self.fh.read(bodysize) rfl = UnpackResFileList(rpkt) return rfl def GetFileData(self,filespec,dataselect,fileobj = None): """ Retrieve a thumbnail, or an entire capture file. """ kGetFileData = 0x0042 if (dataselect == 0): fl = self.GetFileList(0,filespec) filesize = fl[0].FileLength elif (dataselect == 1): raise HostError(hNotSupportedYet) else: raise HostError(hInvalidParam) bodyfmt = 'l32s16sL3L' chdr = CmdHeader(kGetFileData,bodyfmt) offset = 0 data = array.array('c') while( offset != filesize): cpkt = chdr.pack() + \ struct.pack('>'+bodyfmt, filespec.DriveNo, filespec.PathName, filespec.DOSName, dataselect, offset, filesize-offset, filesize) self.fh.write(cpkt) self.fh.flush() # Use unbuffered I/O to read data from the camera. # The camera seem to have problems coping with read request # issued by python's buffered I/O. # fd = self.fh.fileno() rpkt = os.read(fd,8192) rhdr = ResHeader(kGetFileData) if (len(rpkt) < 24): raise HostError(hResponseMismatch) (result,bodysize) = rhdr.unpack(rpkt[0:12]) if (result != kCHNoErr): raise CamError(result) (roff,rlen,rfsize) = struct.unpack('>3L',rpkt[12:24]) if (roff != offset): raise HostError(hResponseMismatch) offset = offset + rlen filesize = rfsize data.fromstring(rpkt[24:]) bodysize = bodysize - (len(rpkt) - 24) while (bodysize > 0): if (bodysize > 8192): rs = 8192 else: rs = bodysize rpkt = os.read(fd,rs) data.fromstring(rpkt) bodysize = bodysize - rs if (fileobj == None): return data.tostring() else: data.tofile(fileobj) return None def SetCameraState(self,ptv): "" kSetCameraState = 0x0012 bodydata = [ ptv.name[0], ptv.name[1], ptv.name[2], ptv.name[3] ] if (ptv.type == 1 or ptv.type == 3 or ptv.type == 4 or ptv.type == 5): bodyfmt = '4cL' bodydata.append(ptv.data) elif (ptv.type == 2): bodyfmt = '4cl' bodydata.append(ptv.data) elif (ptv.type == 6): bodyfmt = '4c4c' bodydata.extend([ptv.data[0],ptv.data[1],ptv.data[2],ptv.data[3]]) elif (ptv.type == 7): bodyfmt = '4c16s' bodydata.append(ptv.data) elif (ptv.type == 8): bodyfmt = '4c32s' bodydata.append(ptv.data) else: raise hUnExpectedDataType() chdr = CmdHeader(kSetCameraState,bodyfmt) cpkt = chdr.pack() + \ apply(struct.pack,tuple(['>'+bodyfmt]+bodydata)) self.fh.write(cpkt) rhdr = ResHeader(kSetCameraState) rpkt = self.fh.read(ResHeader.hdrsize) (result,bodysize) = rhdr.unpack(rpkt) if (result != kCHNoErr): raise CamError(result) return ##################################################################### class CmdHeader: """ __init__(cmdcode,bodyfmt) - create a command header instance pack() - return a packed string of the header """ hdrformat = '>LB3BHH' # header format string for struct.pack version = 0 def __init__ (self,cmdcode,bodyfmt): "Create a CmdHeader instance." self.format = CmdHeader.hdrformat + bodyfmt self.length = struct.calcsize(self.format) - 4 self.command = cmdcode def pack(self): "return a packed string of the header" b = struct.pack(self.hdrformat, self.length, # fLength 0, # fVersion 0, # fReserved[0] 0, # fReserved[1] 0, # fReserved[2] self.command, # fCommand 0) # fResult return b ##################################################################### class ResHeader: """ hdrsize - size of result header __init___(cmdcode) - create a result header instance unpack(hdr) - unpack the argument string, return result code and size of body """ hdrformat = ">LB3BHH" # header format string for struct.pack hdrsize = 12 # size of header def __init__ (self,cmdcode): "Create a ResHeader instance" self.command = cmdcode def unpack(self,hdr): "Unpack the header" (length, version, reserved0, reserved1, reserved2, command, result) = struct.unpack(ResHeader.hdrformat,hdr) if ((0xffff + ~self.command + 1) != command): raise HostError(Cam.hResponseMismatch) length = length - (ResHeader.hdrsize - 4) return result, length #################################################################### class PNameTypeValueList: """ value attributes: name - list of 4 char type - unsigned integer (1-8) data - one of the 8 types as determined by type """ def __init__(self): self.name = [0,0,0,0] self.type = 1 self.data = 0 def dump(self): print self.name, self.type, self.data ##################################################################### class FileNameStruct: """ value attributes: DriveNo - integer, drive number PathName - string, path for this image including '/' DOSName - string, DOS name of file """ def __init__(self): self.DriveNo = -1 self.PathName = "" self.DOSName = "" def dump(self): print 'D: %d P: %s N: %s' % \ (self.DriveNo,self.PathName,self.DOSName) ##################################################################### class ResFileItem: """ value attributes: DriveNo - integer, drive number PathName - string, path for this image including '/' DOSName - string, DOS name of file FileLength - integer, length of file in bytes. -1 = unknown FileStatus - unsigned integer, file status bitflags ipcm - bit 0, 1=image processing completed """ def __init__(self): self.DriveNo = -1 self.PathName = "" self.DOSName = "" self.FileLength = -1 self.FileStatus = 0 def dump(self): print 'D: %d P: %s N: %s L: %d F:%d' % \ (self.DriveNo,self.PathName,self.DOSName,self.FileLength,\ self.FileStatus & 0x1) ##################################################################### def UnpackPNameTypeValueList(pkt): "" (total,) = struct.unpack(">L",pkt[0:4]) start = 4 ptvl = [] for i in range(total): ptv = PNameTypeValueList() # A PNameTypeValue is a fixed size record of the following format: # Name - 4 bytes # Type - 4 bytes (unsigned integer (1-8)) # Data - 32 bytes ptv.name = [0,0,0,0] (ptv.name[0],ptv.name[1],ptv.name[2], ptv.name[3],ptv.type) = struct.unpack(">4cL",pkt[start:start+8]) start = start + 8 if (ptv.type == 1): # Unsigned Integer (32bits) (ptv.data,) = struct.unpack(">L",pkt[start:start+4]) elif (ptv.type == 2): # Signed Integer (32bits) (ptv.data,) = struct.unpack(">l",pkt[start:start+4]) elif (ptv.type == 3): # Fixed (32 bits) (ptv.data,) = struct.unpack(">L",pkt[start:start+4]) elif (ptv.type == 4): # Boolean (32bits) (ptv.data,) = struct.unpack(">L",pkt[start:start+4]) elif (ptv.type == 5): # Bitflags (32bits) (ptv.data,) = struct.unpack(">L",pkt[start:start+4]) elif (ptv.type == 6): ptv.data = [0,0,0,0] (ptv.data[0],ptv.data[1], ptv.data[2],ptv.data[3]) = struct.unpack(">4c",pkt[start:start+4]) elif (ptv.type == 7 or ptv.type == 8): end = string.index(pkt[start:],'\0') ptv.data = pkt[start:start+end] else: raise hUnExpectedDataType() start = start + 32 ptvl.append(ptv) return ptvl ##################################################################### def UnpackResFileList(pkt): "" (total,) = struct.unpack('>L',pkt[0:4]) start = 4 rfl = [] for i in range(total): rfi = ResFileItem() (rfi.DriveNo,) = struct.unpack('>l',pkt[start:start+4]) start = start + 4 end = string.index(pkt[start:],'\0') rfi.PathName = pkt[start:start+end] start = start + 32 end = string.index(pkt[start:],'\0') rfi.DOSName = pkt[start:start+end] start = start + 16 (rfi.FileLength,rfi.FileStatus) = struct.unpack('>lL', pkt[start:start+8]) start = start + 8 rfl.append(rfi) return rfl def main(): f = open('/dev/kodak','r+') cam = Cam(f) # Uncomment the following to increase the maximum host buffer size on # the camera # # hbs = PNameTypeValueList() # hbs.name = [ 'm','h','b','s' ] # hbs.type = 1 # hbs.data = 32768 # cam.SetCameraState(hbs) if (len(sys.argv) < 2 ) : filespec = FileNameStruct() rfl = cam.GetFileList(1,filespec) print 'List image files on camera:' for rfi in rfl: print '%d:%s%s' % (rfi.DriveNo,rfi.PathName,rfi.DOSName) print 'To download a file: ./cdp.py :/' else: fn = FileNameStruct() try: (dn,pn) = regsub.split(sys.argv[1],':') fn.DriveNo = string.atoi(dn) (fn.PathName,fn.DOSName) = regsub.split(pn,'/') fn.PathName = fn.PathName + '/' except: print 'Wrong file name format. Should be :PathName/FileName' sys.exit(1) try: os.mkdir(fn.PathName) except: pass print 'Save file to ' + fn.PathName + fn.DOSName + '...' output = open(fn.PathName+fn.DOSName,'w') cam.GetFileData(fn,0,output) output.close() f.close() if __name__ == "__main__": main()