Added time sync functionality. Now libprs500 automatically syncs the time on the device to local time everytime a connection is opened to the device

Added option to replace existing files to put_file for convenience
This commit is contained in:
Kovid Goyal 2006-12-12 01:31:51 +00:00
parent 3b21f2c4dd
commit fc5b8a22e6
2 changed files with 82 additions and 38 deletions

View File

@ -158,10 +158,10 @@ class PRS500Device(object):
try: try:
if not dev.handle: dev.open() if not dev.handle: dev.open()
res = func(*args, **kwargs) res = func(*args, **kwargs)
except ArgumentError, e: except ArgumentError:
if not kwargs.has_key("end_session") or kwargs["end_session"]: if not kwargs.has_key("end_session") or kwargs["end_session"]:
dev._send_validated_command(EndSession()) dev._send_validated_command(EndSession())
raise e raise
except usb.USBError, e: except usb.USBError, e:
if "No such device" in str(e): if "No such device" in str(e):
raise DeviceError() raise DeviceError()
@ -172,7 +172,7 @@ class PRS500Device(object):
dev.close() dev.close()
raise ProtocolError("There was an unknown error in the protocol. Contact " + AUTHOR) raise ProtocolError("There was an unknown error in the protocol. Contact " + AUTHOR)
dev.close() dev.close()
raise e raise
if not kwargs.has_key("end_session") or kwargs["end_session"]: if not kwargs.has_key("end_session") or kwargs["end_session"]:
dev._send_validated_command(EndSession()) dev._send_validated_command(EndSession())
return res return res
@ -182,7 +182,7 @@ class PRS500Device(object):
def __init__(self, log_packets=False, report_progress=None) : def __init__(self, log_packets=False, report_progress=None) :
""" """
@param log_packets: If true the packet stream to/from the device is logged @param log_packets: If true the packet stream to/from the device is logged
@param: report_progress: Function that is called with a % progress (number between 0 and 100) for various tasks @param report_progress: Function that is called with a % progress (number between 0 and 100) for various tasks
If it is called with -1 that means that the task does not have any progress information If it is called with -1 that means that the task does not have any progress information
""" """
self.device = self.device_descriptor.getDevice() #: The actual device (PyUSB object) self.device = self.device_descriptor.getDevice() #: The actual device (PyUSB object)
@ -237,6 +237,9 @@ class PRS500Device(object):
self._send_validated_command(UnlockDevice(key=0x312d)) self._send_validated_command(UnlockDevice(key=0x312d))
if res.code != 0: if res.code != 0:
raise ProtocolError("Unlocking of device not implemented. Remove locking and retry.") raise ProtocolError("Unlocking of device not implemented. Remove locking and retry.")
res = self._send_validated_command(SetTime())
if res.code != 0:
raise ProtocolError("Could not set time on device")
def close(self): def close(self):
""" Release device interface """ """ Release device interface """
@ -476,8 +479,8 @@ class PRS500Device(object):
try: try:
dest = self.path_properties(path, end_session=False) dest = self.path_properties(path, end_session=False)
except PathError, e: except PathError, e:
if "does not exist" in str(e): return (False, None) if "does not exist" in str(e) or "not mounted" in str(e): return (False, None)
else: raise e else: raise
return (True, dest) return (True, dest)
@safe @safe
@ -497,22 +500,23 @@ class PRS500Device(object):
@safe @safe
def put_file(self, infile, path, end_session=True): def put_file(self, infile, path, replace_file=False, end_session=True):
""" """
Put infile onto the devoce at path Put infile onto the devoce at path
@param infile: An open file object @param infile: An open file object
@param path: The path on the device at which to put infile. It should point to an existing directory. @param path: The path on the device at which to put infile. It should point to an existing directory.
@param replace_file: If True and path points to a file that already exists, it is replaced
""" """
exists, dest = self._exists(path) exists, dest = self._exists(path)
if exists: if exists:
if not dest.is_dir: raise PathError("Cannot write to " + path + " as it already exists") if dest.is_dir:
if not path.endswith("/"): path += "/" if not path.endswith("/"): path += "/"
path += os.path.basename(infile.name) path += os.path.basename(infile.name)
exists, dest = self._exists(path) return self.put_file(infile, path, replace_file=replace_file, end_session=False)
if exists: raise PathError("Cannot write to " + path + " as it already exists") elif not replace_file: raise PathError("Cannot write to " + path + " as it already exists")
res = self._send_validated_command(FileCreate(path)) else:
if res.code != 0: res = self._send_validated_command(FileCreate(path))
raise ProtocolError("There was an error creating device:"+path+". Response code: "+hex(res.code)) if res.code != 0: raise ProtocolError("There was an error creating "+path+" on device. Response code: "+hex(res.code))
chunk_size = 0x8000 chunk_size = 0x8000
data_left = True data_left = True
res = self._send_validated_command(FileOpen(path, mode=FileOpen.WRITE)) res = self._send_validated_command(FileOpen(path, mode=FileOpen.WRITE))
@ -582,7 +586,7 @@ class PRS500Device(object):
def card(self, end_session=True): def card(self, end_session=True):
card = None card = None
if self._exists("a:/")[0]: card = "a:" if self._exists("a:/")[0]: card = "a:"
if self._exists("b:/")[0]: card = "b:" if self._exists("b:/")[0]: card = "b:"
return card return card
@safe @safe
@ -611,7 +615,7 @@ class PRS500Device(object):
return BookList(prefix=prefix, root=root, file=file) return BookList(prefix=prefix, root=root, file=file)
@safe @safe
def add_book(self, infile, name, info, booklists, oncard=False, end_session=True): def add_book(self, infile, name, info, booklists, oncard=False, sync_booklists=False, end_session=True):
""" """
Add a book to the device. If oncard is True then the book is copied to the card rather than main memory. Add a book to the device. If oncard is True then the book is copied to the card rather than main memory.
@ -635,6 +639,24 @@ class PRS500Device(object):
else: name = "books/"+name else: name = "books/"+name
path = prefix + name path = prefix + name
self.put_file(infile, path, end_session=False) self.put_file(infile, path, end_session=False)
if oncard: booklists[1].add_book(info, name, size) bl = booklists[1] if oncard else booklists[0]
else: booklists[0].add_book(info, name, size) bl.add_book(info, name, size)
fix_ids(booklists[0], booklists[1])
if sync_booklists:
self.upload_book_list(booklists[0], end_session=False)
if len(booklists[1]):
self.upload_book_list(booklists[1], end_session=False)
@safe
def upload_book_list(self, booklist, end_session=True):
if not len(booklist): raise ArgumentError("booklist is empty")
path = self.MEDIA_XML
if not booklist.prefix:
card = self.card(end_session=True)
if not card: raise ArgumentError("Cannot upload list to card as card is not present")
path = card + self.CACHE_XML
f = TemporaryFile()
booklist.write(f)
f.seek(0)
self.put_file(f, path, replace_file=True, end_session=False)
f.close()

View File

@ -37,7 +37,7 @@ Responses inherit Command as they share header structure.
Answers are organized as follows: G{classtree Answer} Answers are organized as follows: G{classtree Answer}
""" """
import struct import struct, time
from errors import PacketError from errors import PacketError
DWORD = "<I" #: Unsigned integer little endian encoded in 4 bytes DWORD = "<I" #: Unsigned integer little endian encoded in 4 bytes
@ -204,8 +204,7 @@ class Command(TransferBuffer):
Command numbers are: Command numbers are:
0 GetUsbProtocolVersion 0 GetUsbProtocolVersion
1 ReqEndSession 1 ReqEndSession
10 FskFileOpen 10 FskFileOpen
11 FskFileClose 11 FskFileClose
12 FskGetSize 12 FskGetSize
@ -218,20 +217,16 @@ class Command(TransferBuffer):
19 FskFileSetFileInfo 19 FskFileSetFileInfo
1A FskFileCreate 1A FskFileCreate
1B FskFileDelete 1B FskFileDelete
1C FskFileRename 1C FskFileRename
30 FskFileCreateDirectory 30 FskFileCreateDirectory
31 FskFileDeleteDirectory 31 FskFileDeleteDirectory
32 FskFileRenameDirectory 32 FskFileRenameDirectory
33 FskDirectoryIteratorNew 33 FskDirectoryIteratorNew
34 FskDirectoryIteratorDispose 34 FskDirectoryIteratorDispose
35 FskDirectoryIteratorGetNext 35 FskDirectoryIteratorGetNext
52 FskVolumeGetInfo 52 FskVolumeGetInfo
53 FskVolumeGetInfoFromPath 53 FskVolumeGetInfoFromPath
80 FskFileTerminate
80 FskFileTerminate
100 ConnectDevice 100 ConnectDevice
101 GetProperty 101 GetProperty
102 GetMediaInfo 102 GetMediaInfo
@ -239,13 +234,11 @@ class Command(TransferBuffer):
104 SetTime 104 SetTime
105 DeviceBeginEnd 105 DeviceBeginEnd
106 UnlockDevice 106 UnlockDevice
107 SetBulkSize 107 SetBulkSize
110 GetHttpRequest 110 GetHttpRequest
111 SetHttpRespponse 111 SetHttpRespponse
112 Needregistration 112 Needregistration
114 GetMarlinState 114 GetMarlinState
200 ReqDiwStart 200 ReqDiwStart
201 SetDiwPersonalkey 201 SetDiwPersonalkey
202 GetDiwPersonalkey 202 GetDiwPersonalkey
@ -269,8 +262,7 @@ class Command(TransferBuffer):
213 ReqDiwFactoryinitialize 213 ReqDiwFactoryinitialize
214 GetDiwMacaddress 214 GetDiwMacaddress
215 ReqDiwTest 215 ReqDiwTest
216 ReqDiwDeletekey 216 ReqDiwDeletekey
300 UpdateChangemode 300 UpdateChangemode
301 UpdateDeletePartition 301 UpdateDeletePartition
302 UpdateCreatePartition 302 UpdateCreatePartition
@ -308,7 +300,37 @@ class Command(TransferBuffer):
raise PacketError(str(self.__class__)[7:-2] + " packets must have length atleast 16") raise PacketError(str(self.__class__)[7:-2] + " packets must have length atleast 16")
TransferBuffer.__init__(self, packet) TransferBuffer.__init__(self, packet)
class SetTime(Command):
"""
Set time on device. All fields refer to time in the GMT time zone.
@todo: figure out what the 4 bytes starting at byte 16 are for
"""
NUMBER = 0x104
unknown = field(start=0x10, fmt=DWORD) #: Haven't figured out what this is for. Seems to always be set to 4294966816L
year = field(start=0x14, fmt=DWORD) #: year e.g. 2006
month = field(start=0x18, fmt=DWORD) #: month 1-12
day = field(start=0x1c, fmt=DWORD) #: day 1-31
hour = field(start=0x20, fmt=DWORD) #: hour 0-23
minute = field(start=0x24, fmt=DWORD) #: minute 0-59
second = field(start=0x28, fmt=DWORD) #: second 0-59
def __init__(self, t=None):
""" @param t: time as an epoch """
self.number = SetTime.NUMBER
self.type = 0x01
self.length = 0x1c
self.unknown = 4294966816L
if not t: t = time.time()
t = time.gmtime(t)
self.year = t[0]
self.month = t[1]
self.day = t[2]
self.hour = t[3]
self.minute = t[4]
self.second = t[5] if t[5] < 60 else 59 # Hack you should actually update the entire time tree is second is > 59
class ShortCommand(Command): class ShortCommand(Command):