From df077ebfbe84f63b8c3c6efc750398dce2403da3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Nov 2006 08:10:02 +0000 Subject: [PATCH] Improved packet tracing output (added byte count and packet count) Added support for the df command --- libprs500/__init__.py | 2 +- libprs500/communicate.py | 42 ++++++++++++++++++++----- libprs500/prstypes.py | 68 ++++++++++++++++++++++++++++++++++++++-- prs-500.e3p | 40 +++++++++++++++++------ scripts/prs500.py | 31 ++++++++++++------ 5 files changed, 154 insertions(+), 29 deletions(-) diff --git a/libprs500/__init__.py b/libprs500/__init__.py index cc6b13ca97..10a233edbe 100644 --- a/libprs500/__init__.py +++ b/libprs500/__init__.py @@ -20,6 +20,6 @@ the following rule in C{/etc/udev/rules.d/90-local.rules} :: BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="plugdev" You may have to adjust the GROUP and the location of the rules file to suit your distribution. """ -VERSION = "0.1" +VERSION = "0.1.1" __docformat__ = "epytext" __author__ = "Kovid Goyal " diff --git a/libprs500/communicate.py b/libprs500/communicate.py index 1ee2fe1cba..d0b365e649 100755 --- a/libprs500/communicate.py +++ b/libprs500/communicate.py @@ -47,13 +47,19 @@ The public interface of class L{PRS500Device} defines the methods for performing import usb, sys from array import array -from prstypes import AcknowledgeBulkRead, Answer, Command, DeviceInfo, DirOpen, DirRead, DirClose, \ - FileOpen, FileClose, FileRead, IdAnswer, ListAnswer, \ - ListResponse, LongCommand, FileProperties, PathQuery, Response, \ - ShortCommand, DeviceInfoQuery +from prstypes import * from errors import * MINIMUM_COL_WIDTH = 12 #: Minimum width of columns in ls output +_packet_number = 0 #: Keep track of the packet number of packet tracing + +def _log_packet(packet, header, stream=sys.stderr): + """ Log C{packet} to stream C{stream}. Header should be a small word describing the type of packet. """ + global _packet_number + _packet_number += 1 + print >>stream, header, "(Packet #", str(_packet_number) + ")\n" + print >>stream, packet + print >>stream, "--" class File(object): """ Wrapper that allows easy access to all information about files/directories """ @@ -165,12 +171,12 @@ class PRS500Device(object): @param response_type: an object of type 'type'. The return packet from the device is returned as an object of type response_type. @param timeout: the time to wait for a response from the device, in milliseconds. If there is no response, a L{usb.USBError} is raised. """ - if self._log_packets: print "Command\n%s\n--\n"%command + if self._log_packets: _log_packet(command, "Command") bytes_sent = self.handle.controlMsg(0x40, 0x80, command) if bytes_sent != len(command): raise ControlError(desc="Could not send control request to device\n" + str(query.query)) response = response_type(self.handle.controlMsg(0xc0, 0x81, Response.SIZE, timeout=timeout)) - if self._log_packets: print "Response\n%s\n--\n"%response + if self._log_packets: _log_packet(response, "Response") return response def _send_validated_command(self, command, cnumber=None, response_type=Response, timeout=100): @@ -191,7 +197,7 @@ class PRS500Device(object): @param size: the expected size of the data packet. """ data = data_type(self.handle.bulkRead(PRS500Device.PRS500_BULK_IN_EP, size)) - if self._log_packets: print "Answer\n%s\n--\n"%data + if self._log_packets: _log_packet(data, "Answer d->h") return data def _bulk_read(self, bytes, command_number=0x00, packet_size=4096, data_type=Answer): @@ -364,3 +370,25 @@ class PRS500Device(object): if recurse and file.is_dir and not file.path.startswith(("/dev","/proc")): dirs[len(dirs):] = self.list(file.path, recurse=True) return dirs + + def available_space(self): + """ + Get free space available on the mountpoints: + 1. /Data/ Device memory + 2. a:/ Memory Stick + 3. b:/ SD Card + + @return: A list of tuples. Each tuple has form ("location", free space, total space) + """ + return self._run_session(self._available_space) + + def _available_space(self, args): + """ L{available_space} """ + data = [] + for path in ("/Data/", "a:/", "b:/"): + res = self._send_validated_command(FreeSpaceQuery(path),timeout=5000) # Timeout needs to be increased as it takes time to read card + buffer_size = 16 + res.data[2] + pkt = self._bulk_read(buffer_size, data_type=FreeSpaceAnswer, command_number=FreeSpaceQuery.NUMBER)[0] + data.append( (path, pkt.free_space, pkt.total) ) + return data + diff --git a/libprs500/prstypes.py b/libprs500/prstypes.py index b782530fcb..729ca3fa26 100755 --- a/libprs500/prstypes.py +++ b/libprs500/prstypes.py @@ -85,7 +85,7 @@ class TransferBuffer(list): 0700 0100 0000 0000 0000 0000 0c00 0000 ................ 0200 0000 0400 0000 4461 7461 ........Data """ - ans, ascii = "", "" + ans, ascii = ": ".rjust(10,"0"), "" for i in range(0, len(self), 2): for b in range(2): try: @@ -95,10 +95,10 @@ class TransferBuffer(list): ans = ans + " " if (i+2)%16 == 0: if i+2 < len(self): - ans += " " + ascii + "\n" + ans += " " + ascii + "\n" + (TransferBuffer.phex(i+2)+": ").rjust(10, "0") ascii = "" last_line = ans[ans.rfind("\n")+1:] - padding = 40 - len(last_line) + padding = 50 - len(last_line) ans += "".ljust(padding) + " " + ascii return ans.strip() @@ -271,6 +271,43 @@ class ShortCommand(Command): return property(**locals()) +class FreeSpaceQuery(Command): + """ Query the free space available """ + NUMBER = 0x53 #; Command number + def __init__(self, path): + Command.__init__(self, 20 + len(path)) + self.number=FreeSpaceQuery.NUMBER + self.type=0x01 + self.length = 4 + len(path) + self.path_length = len(path) + self.path = path + + @apply + def path_length(): + doc =\ + """ The length in bytes of the path to follow. C{unsigned int} stored at byte 16. """ + def fget(self): + return self.unpack(start=16, fmt=DWORD)[0] + + def fset(self, val): + self.pack(val, start=16, fmt=DWORD) + + return property(**locals()) + + @apply + def path(): + doc =\ + """ The path. Stored as a string at byte 20. """ + + def fget(self): + return self.unpack(start=20, fmt="<"+str(self.path_length)+"s")[0] + + def fset(self, val): + self.pack(val, start=20, fmt="<"+str(self.path_length)+"s") + + return property(**locals()) + + class DirOpen(Command): """ Open a directory for reading its contents """ @@ -795,6 +832,31 @@ class DeviceInfo(Answer): return src[0:src.find('\x00')] return property(**locals()) +class FreeSpaceAnswer(Answer): + @apply + def total(): + doc =\ + """ The total space in bytes. C{unsigned long long} stored in 8 bytes at byte 24 """ + def fget(self): + return self.unpack(start=24, fmt=DDWORD)[0] + + def fset(self, val): + self.pack(val, start=24, fmt=DDWORD) + + return property(**locals()) + + @apply + def free_space(): + doc =\ + """ The free space in bytes. C{unsigned long long} stored in 8 bytes at byte 32 """ + def fget(self): + return self.unpack(start=32, fmt=DDWORD)[0] + + def fset(self, val): + self.pack(val, start=32, fmt=DDWORD) + + return property(**locals()) + class ListAnswer(Answer): """ Defines the structure of packets that contain items in a list. """ diff --git a/prs-500.e3p b/prs-500.e3p index 92749583b9..fe66e413e6 100644 --- a/prs-500.e3p +++ b/prs-500.e3p @@ -1,7 +1,7 @@ - - + + Python @@ -12,16 +12,31 @@ kovid@kovidgoyal.net - libprs500/communicate.py + libprs500 + communicate.py - libprs500/terminfo.py + libprs500 + terminfo.py - libprs500/prstypes.py + libprs500 + prstypes.py - libprs500/errors.py + libprs500 + errors.py + + + libprs500 + __init__.py + + + setup.py + + + scripts + prs500.py @@ -31,9 +46,16 @@ + + epydoc.conf + + + epydoc-pdf.conf + - libprs500/communicate.py + scripts + prs500.py Subversion @@ -96,9 +118,9 @@ s. - - + + diff --git a/scripts/prs500.py b/scripts/prs500.py index 7ee1edbd48..d55f2df2e0 100755 --- a/scripts/prs500.py +++ b/scripts/prs500.py @@ -16,6 +16,16 @@ from libprs500.errors import ArgumentError MINIMUM_COL_WIDTH = 12 #: Minimum width of columns in ls output +def human_readable(size): + """ Convert a size in bytes into a human readle form """ + if size < 1024: divisor, suffix = 1, "" + elif size < 1024*1024: divisor, suffix = 1024., "K" + elif size < 1024*1024*1024: divisor, suffix = 1024*1024, "M" + elif size < 1024*1024*1024*1024: divisor, suffix = 1024*1024, "G" + size = str(size/divisor) + if size.find(".") > -1: size = size[:size.find(".")+2] + return size + suffix + class FileFormatter(object): def __init__(self, file, term): self.term = term @@ -56,13 +66,7 @@ class FileFormatter(object): def human_readable_size(): doc=""" File size in human readable form """ def fget(self): - if self.size < 1024: divisor, suffix = 1, "" - elif self.size < 1024*1024: divisor, suffix = 1024., "K" - elif self.size < 1024*1024*1024: divisor, suffix = 1024*1024, "M" - elif self.size < 1024*1024*1024*1024: divisor, suffix = 1024*1024, "G" - size = str(self.size/divisor) - if size.find(".") > -1: size = size[:size.find(".")+2] - return size + suffix + human_readable(self.size) return property(**locals()) @apply @@ -161,7 +165,7 @@ def main(): term = TerminalController() cols = term.COLS - parser = OptionParser(usage="usage: %prog command [options] args\n\ncommand is one of: info, ls, cp, cat or rm\n\n"+ + parser = OptionParser(usage="usage: %prog [options] command args\n\ncommand is one of: info, df, ls, cp, cat or rm\n\n"+ "For help on a particular command: %prog command", version="libprs500 version: " + VERSION) parser.add_option("--log-packets", help="print out packet stream to stdout", dest="log_packets", action="store_true", default=False) parser.remove_option("-h") @@ -174,7 +178,16 @@ def main(): command = args[0] args = args[1:] dev = PRS500Device(log_packets=options.log_packets) - if command == "ls": + if command == "df": + dev.open() + data = dev.available_space() + dev.close() + print "Filesystem\tSize \tUsed \tAvail \tUse%" + for datum in data: + total, free, used, percent = human_readable(datum[2]), human_readable(datum[1]), human_readable(datum[2]-datum[1]), \ + str(0 if datum[2]==0 else int(100*(datum[2]-datum[1])/(datum[2]*1.)))+"%" + print "%-10s\t%s\t%s\t%s\t%s"%(datum[0], total, used, free, percent) + elif command == "ls": parser = OptionParser(usage="usage: %prog ls [options] path\n\npath must begin with /,a:/ or b:/") parser.add_option("--color", help="show ls output in color", dest="color", action="store_true", default=False) parser.add_option("-l", help="In addition to the name of each file, print the file type, permissions, and timestamp (the modification time unless other times are selected)", dest="ll", action="store_true", default=False)