[casetta] Variables, Matrices, Lists (Receive and read)

[ Thread Index | Date Index | More lists.tuxfamily.org/casetta Archives ]


Hello,

It is almost 12:00 !
I did not succeded in sending a matrix to my calculator, it is a complete failure. I have a checksum error which is very similar to the
one I get when I try to send a picture
and that I mentionned by mail (same problem !, in the send_data function in devices_serial).

However, I wrote the doc on the raw format page in the wiki.
It is possible to receive simple variables, matrices and lists and extract values from them. Build_header works only with matrices.
Inserting values is only supported for matrices and simple variables (When I have some free time I will add it : that would be very easy).

I have a little problem when receiving multiple variables (With Link > Transmit > sel > Variables), for the last variable, instead of sending its content the calculator sends a header where the content of the variable would be (cafix, has also problems when recieving multiple vairables. So I found a bad solution : if the transfert tool detects the beginning of the header inside a variable's content, it stops. So everything is OK on the computer's side but you have to manually interrupt the com. on the calc. Values are correctly saved in the computer.

So has you may have noticed nothing is finnished.... :-)

(I am not at my home next week, I will quit my Normandy, so I will not play with casetta).

remote-ctrl is for xmms because I don't understand dcop :-).

Cheers, 

--
Fabien ANDRE aka Xion345
Linux User #418689 -- fabien.andre.g@xxxxxxxxxx -- xion345@xxxxxxxxxxxxx
Note that nobody reads every post in linux-kernel. In fact, nobody who expects to have time left over to actually do any real kernel work will read even half. Except Alan Cox, but he's actually not human, but about a thousand gnomes working in under-ground caves in Swansea. None of the individual gnomes read all the postings either, they just work together really well. ( Linus Torvalds, 2000 )
# -*- coding: utf-8 -*-
#
############################################################################
#
# (c) 2007 Florian Birée aka Thesa <florian.biree@xxxxxxxxxxx>
#
# Website: <URL:http://casetta.tuxfamily.org>
#
# Version 0.3.0dev
#
# changelog:
#
# # 0.3.0 version:
#
#   o First release
#     This code is hardly inspired by cafix softwares (cafix.sourceforge.com)
#     A lot of thanks for cafix coders.
#
############################################################################
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
# MA 02110-1301, USA.
#
# http://www.gnu.org/copyleft/gpl.html
#
############################################################################
"""Internal serial tansfer tool"""

__author__ = "Florian Birée"
__version__ = "0.3.0dev"
__copyright__ = "Copyright (c) 2007, Florian Birée"
__license__ = "GPL"
__revision__ = "$Revision: $"
# $Source: $
__date__ = "$Date: $"

import serial
import time
import sys
import cas
import errors
import data as datacls

# Default functions
def default_status(data_name, current_size, total_size, is_header = False):
    """Print the status of a transfert on the standard output."""
    percent = ((current_size * 100) / total_size)
    if is_header:
        progress_line = 'Header   '
    else:
        progress_line = data_name + ' ' * (9 - len(data_name))
        progress_line += '[' + '=' * (percent / 2)
        progress_line += ' ' * (50 - (percent / 2)) + '] '
    progress_line += '(%i%%) %i/%i' % (percent, current_size, total_size)
    if current_size != 0:
        progress_line = '\r' + progress_line
    sys.stdout.write(progress_line)
    sys.stdout.flush()
    if current_size >= total_size:
        sys.stdout.write('\n')

def default_overwrite(data_name):
    """Ask the user if he want to overwrite data_name on the calculator."""
    user_rep = raw_input('Overwrite %s (Y/N): ' % data_name)
    return user_rep.lower().startswith('y')


# Serial management classes
class Connexion:
    """Casetta serial transfer tool."""
    
    name = 'serial'
    managed_data = [datacls.Program, datacls.Backup, datacls.Picture, \
                    datacls.ScreenCapture]
    def __init__(self, serial_port = 0, status = default_status, \
                 overwrite = default_overwrite):
        """Initial configuration of the serial port
        
        status is a function which is runned as status(current_data_name, 
        current_size, total_size, is_header) each time the status change.
        overwrite is a function which is runned as overwrite(data_name) and 
        which must return True or False.
        """
        if type(serial_port) == str and serial_port.isdigit():
            serial_port = int(serial_port)
        self.serial = serial.Serial(
            port=serial_port,
            baudrate=9600,
            bytesize=serial.EIGHTBITS,
            parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_ONE,
            timeout=None,
            xonxoff=0, #0
            rtscts=1) #1
        self.status = status
        self.overwrite = overwrite

    # low level serial functions
    def get_byte(self):
        """Read a single byte"""
        byte = self.serial.read()
        return byte

    def send_byte(self, byte):
        """Send a single byte"""
        self.serial.write(byte)
        
    def send_string(self, string, status = None, data_name = '', \
                    is_header = False):
        """Send a string of bytes"""
        for index in range(len(string)):
            self.send_byte(string[index])
            time.sleep(0.005)
            if status != None:
                status(data_name, index, len(string) - 1, is_header)
    
    # send system
    def send(self, data_list):
        """Send all data in data_list
        
        Warning: a backup must be the only data to send
        """
        while not self.calc_is_ready_to_receive():
            pass
        for data in data_list:
            self.send_data(data)
        if data_list[-1].__class__ != datacls.Backup:
            # Send the END header
            self.send_header(cas.End())
    
    def calc_is_ready_to_receive(self):
        """Return if the calculator is ready to receive some data"""
        interactive = False
        if interactive:
            code = '\x06'
        else:
            code = '\x16'
        self.send_byte(code)
        byte_rep = self.get_byte()
        if interactive and byte_rep == '\x06':
            return True
        elif not interactive and byte_rep == '\x13':
            return True
        else:
            return False
    
    def send_data(self, data):
        """Send a data to the calc"""
        file3=open('sums-sent.hex','w')
        self.send_header(data)
        response = self.get_byte()
        if response == '\x06':
            # Calculator accepted the header
            pass
        elif response == '\x24':
            raise errors.DataNoManaged()
        elif response == '\x2b':
            raise errors.ChecksumError()
        elif response == '\x21':
            #File already exist in the calculator.
            if self.overwrite(data.name) :
                self.send_byte('\x06')
                sec_resp = self.get_byte()
                if sec_resp != '\x06':
                    raise errors.CannotOverwrite(data.name)
                # Calculator accept to overwrite.
            else:
                # send abort code
                self.send_byte('\x15')
                sec_resp = self.get_byte()
                if sec_resp != '\x06':
                    raise errors.BadAnswerFromCalc(sec_resp)
                raise errors.TransferAborted()
        elif response == '\x51':
            raise errors.HeaderError()
        else:
            raise errors.BadAnswerFromCalc(response)
        # Make the data list to be transfered
        if data.__class__ == datacls.Picture:
            raw_data_list = [data.raw_data[0:1028],
                             data.raw_data[1028:2056],
                             data.raw_data[2056:3084],
                             data.raw_data[3084:4112]]
        elif data.__class__ == datacls.Matrix:
            raw_data_list = data.get_raw_data_list()[:-1]
        else:
            raw_data_list = [data.raw_data]
        for raw_data in raw_data_list:
            # CRC calculation
            crc = 0
            for byte in raw_data:
                crc += ord(byte)
            crc = abs(255 - (crc % 256)) + 1
            #crc = (abs(255 - (crc % 256)) + 1) % 256
            #crc = abs(255 - (crc % 256)) + 1
            # Now sending the data
            self.send_string('\x3a' + raw_data, self.status, data.name, False)
            # All data sent, let the calc check the CRC
            file3.write(chr(crc)+'\xff')
            self.send_byte(chr(crc))
            response = self.get_byte()
            if response != '\x06':
                raise errors.ChecksumError()
        if data.__class__ == datacls.Matrix:
            self.send_string('\x3a'+data.get_raw_data_list()[-1])
        file3.close()
        return
    
    def send_header(self, data):
        """Send the header corresponding to data"""
        header = cas.build_header(data)
        if data.__class__ == cas.End:
            self.send_string('\x3a' + header, self.status, 'End', False)
        else:
            self.send_string('\x3a' + header, self.status, data.name, True)
        return

    #Receive system
    def receive(self):
        """Return a file_data with all data received from the calc."""
        file_data = datacls.FileData()
        while not self.calc_is_ready_to_send():
            pass
        # Debut de la réception
        while True:
            data = self.receive_data()
            if data.__class__ == cas.End:
                break
            file_data.data.append(data)
            if data.__class__ == datacls.ScreenCapture or \
                    data.__class__ == datacls.Backup:
                break
        return file_data
    
    def calc_is_ready_to_send(self):
        """Waiting for receiving data"""
        byte = self.get_byte()
        if byte == '\x15':
            # Received request for interactive handshake (0x15)
            # -- responding with 0x13
            self.send_byte("\x13")
            return True
        elif byte == '\x16':
            # Received request for noninteractive handshake (0x16)
            # -- responding with 0x13
            self.send_byte("\x13")
            return True
        else:
            return False

    def receive_data(self):
        """Receive a data"""
        # Get header
        #file=open('/home/fabien_kubuntu/src/casetta/current-content.hex','w')
        #log=open('/home/fabien_kubuntu/src/casetta/current-trans.log','w')
        file2=open('/home/fabien_kubuntu/src/casetta/sums.hex','w')
        self.status('unknown', 0, 49, True)
        header, data_type, sub_type = self.get_header()
        self.status('unknown', 49, 49, True)
        if data_type == 'end':
            # End of the transfer : no data
            return cas.End()
        # Get data informations
        data_len = cas.get_data_len(header, data_type, sub_type)
        # Make the data
        if data_type == 'program':          # Xion345 : It is possible to 
            data = datacls.Program()        # replace all this code by something
        elif data_type == 'backup':         # smaller like : getattr('datacls.'\
            data = datacls.Backup()         # + data_type[0].upper() + data_type
        elif data_type == 'picture':        # [1:]+'()')
            data = datacls.Picture()
        elif data_type == 'screencapture':
            data = datacls.ScreenCapture()
        elif data_type == 'var':
            data = datacls.Variable()
        elif data_type == 'matrix':
            data = datacls.Matrix()
            data.dimensions=(ord(header[7]),ord(header[9]))
        elif data_type == 'list':
            data = datacls.List()
            data.dimension=ord(header[7])
        else:
            # unknown data, refuse header
            self.send_byte("\x00")
            raise errors.HeaderError()
        cas.fill_metadata(data, header)
        #if data_len == 0:
        #    #datalen is 0 -- sending 0x00
        #    self.send_byte('\x00')       

        # we accepted the header -- so we send 0x06 to the calc
        self.send_byte("\x06")
        if not self.get_byte() == '\x3a':
            raise errors.BadAnswerFromCalc()
        crc = 0
        raw_data = ''
        for index in range(data_len):
            #
            byte = self.get_byte()
            #file.write(byte)
            crc = crc + ord(byte)
            
            
            if (data_type == 'list' or data_type == 'matrix') and (index-13)%14==0:# and index >=12:
                # Checksum test
                calc_crc=self.get_byte()
                file2.write(calc_crc+'\xff')
                #log.write(str(abs(255 - (crc % 256)) + 1)+'/'+str(ord(calc_crc)))
                if abs(255 - (crc % 256)) + 1 == ord(calc_crc) or index >= data_len-50:
                    self.send_byte('\x06')
                    crc=0
                    if index<=data_len-50:
                        resp=self.get_byte()
                        if resp != '\x3a':
                            raise errors.BadAnswerFromCalc(resp)
                    
                else:
                    raise errors.ChecksumError()
            #
            if data_type == 'screencapture' and sub_type == 'color' \
                    and (index == 1024 or index == 2048):
                resp2 = self.get_byte()
                self.send_byte('\x06')
                resp2 = self.get_byte()
                crc = 0
            if data_type == 'picture' and (index + 1) % 1028 == 0 \
                    and index != 0 and index + 1 < data_len:
                # IMAGE / color screencapture
                newcrc = ord(self.get_byte())
                crc = abs(255 - (crc % 256)) + 1
                if not newcrc == crc :
                    raise errors.ChecksumError()
                crc = 0
                self.send_byte('\x06')
                resp2 = self.get_byte()
                if not resp2 == '\x3a':
                    raise errors.BadAnswerFromCalc(resp2)
            #if data_type == 1 and bytesproc + 1 == 62684:
            #    print 'byte 62684 of a backup : send 0x06'
            #    send_byte('\x06')
            raw_data += byte
            #bytectrl += 1
            self.status(data.name, index, data_len - 1, False)
        newcrc = ord(self.get_byte())
        crc = abs(255 - (crc % 256)) + 1
        if "VAL\x00VM" in raw_data: # This works around a bug while receiving 
            #print "End of var trans."
            #for i in range(49):
            #   self.get_byte()
            return cas.End()                 # Many variables from the calc.
                         # It sends two variables which have no values
        #if newcrc != crc and (data_type != 'screencapture' and \
                #sub_type != 'color'):
            
            # Warning: the crc check is not done for color screencapture
            #          because the crc of color screencapture is never
            #          valid.
            # raise errors.ChecksumError()
        self.send_byte('\x06')
        #if data_type == 8 and data_sub_type == 13:
        #    print "coincoin (cf.perl)"
        if data_type == 'screencapture' and sub_type == 'color':
            data.raw_data = cas.color_screencapture_to_raw(raw_data)
        elif data_type == 'screencapture' and sub_type == 'mono':
            data.raw_data = cas.mono_screencapture_to_raw(raw_data)
        else:
            data.raw_data = raw_data
        file2.close()
        #log.close()
        return data

    def get_header(self):
        """Return [header, header_type]"""
        file2=open('/home/fabien_kubuntu/src/casetta/current-header.hex','w')
        byte = self.get_byte()
        if not byte == '\x3a':
            raise errors.HeaderError()
        header = ''
        total_bytes = 100
        cur_byte = 0
        h_crc = 0
        while cur_byte < total_bytes:
            byte = self.get_byte()
            header += byte
            if cur_byte == 7:
                header_len, header_type, sub_type = \
                                cas.get_header_format(header)
                total_bytes = header_len
            cur_byte += 1
            if cur_byte < total_bytes:
                h_crc = (h_crc + ord(byte)) % 256
        h_crc = (0 - h_crc) % 256
        file2.write(header)
        file2.close()
        if h_crc != ord(byte) :
            raise errors.ChecksumError()

        return [header, header_type, sub_type]
# -*- coding: utf-8 -*-
#
############################################################################
#
# (c) 2007 Florian Birée aka Thesa <florian.biree@xxxxxxxxxxx>
#
# Website: <URL:http://casetta.tuxfamily.org>
#
# Version 0.3.0dev
#
# changelog:
#
# # 0.3.0 version:
#
#   o First release
#     This code is hardly inspired by cafix softwares (cafix.sourceforge.com)
#     A lot of thanks for cafix coders.
#
############################################################################
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
# MA 02110-1301, USA.
#
# http://www.gnu.org/copyleft/gpl.html
#
############################################################################
"""cas (CASIOLINK format) management module"""

__author__ = "Florian Birée"
__version__ = "0.3.0dev"
__copyright__ = "Copyright (c) 2007, Florian Birée"
__license__ = "GPL"
__revision__ = "$Revision: $"
# $Source: $
__date__ = "$Date: $"

import datetime
import errors
import data as datacls

# Constant
PICTURE_PALLET = ['o', 'b', 'g', 'w']
SCREEN_PALLET = ['b', 'g', 'w', 'o']

# Special Data class
class End(datacls.Data):
    """Special data to end a transfer."""
    def __init__(self):
        """New end data"""
        datacls.Data.__init__(self)
        self.dType = 'end'

# Cas format functions
def build_header(data):
    """Return a cas header built from data"""
    if data.__class__ == End:
        header = 'END' + '\xff' * 45
    else:
        #header_len = 49
        data_len = len(data.raw_data) + 2
        #header = '\xff' * header_len
        if data.__class__ == datacls.Program:
            header = 'TXT\x00PG'
            header += chr(data_len / (256 ** 3))
            header += chr((data_len % 256 ** 3) / (256 ** 2))
            header += chr((data_len % 256 ** 2) / 256)
            header += chr(data_len % 256) # 0 -> 9
            header += data.name[:8] + '\xff' * (8 - len(data.name[:8])) #10->17
            header += '\xff' * 8
            header += data.password[:8] + '\xff' * (8 - len(data.password[:8]))
            if data.use_base:
                header += 'BN'
            else:
                header += 'NL'
            header += '\xff' * 12
        elif data.__class__ == datacls.Backup:
            header = data.raw_data[2048: 2048 + 48]
            header = header[:33] + '\x00\x10\x00\x00\x00\x00' + header[39:]
        #elif data.dType == 'variable' and False:
            # TO BE CHECKED
            #header += 'VAL\x00'
            #for (my $a = 0; $a < length($self->{dataname}); $a++) {
            #	last if ($a > 8);
            #	my $bar = sprintf "0x%lx",
            #	ord(substr($self->{dataname}, $a, 1));
            #	$foo[10+$a] = $bar;
            #}
            #$foo[18] = "0x56"; # V
            #$foo[19] = "0x61"; # a
            #$foo[20] = "0x72"; # r
            #$foo[21] = "0x69"; # i
            #$foo[22] = "0x61"; # a
            #$foo[23] = "0x62"; # b
            #$foo[24] = "0x6c"; # l
            #$foo[25] = "0x65"; # e
            #$foo[26] = "0x52"; # R
            #$foo[27] = "0x0a"; # LF
            #if ($self->{subtype} == 13) {
            #	$foo[4] = "0x56"; # V
            #	$foo[5] = "0x4d"; # M
            #	$foo[6] = "0x00"; # NUL
            #	$foo[7] = "0x01"; # SOH
            #	$foo[8] = "0x00"; # NUL
            #	$foo[9] = "0x01"; # SOH
            #}
        elif data.__class__== datacls.Matrix:
            header = 'VAL'+'\x00'+'MT'+'\x00'+chr(data.dimensions[0])
            header += '\x00'+chr(data.dimensions[1])+data.name
            header += '\xff'*11 + '\x52'+'\x0a'
            header += '\xff'*20
        elif data.__class__ == datacls.Picture:
            header = 'IMG\x00PC'
            header += '\x00\x40\x00\x80' # len ?
            header += data.name
            header += '\xff' * 8
            header += 'DRUWF'
            header += '\x00\x04\x00\x01' # len ?
            header += '\xff' * 13
        else:
            raise errors.DataNoManaged([data.dType])
    # h_crc (header checksum) calcul
    h_crc = 0
    for byte in header:
        h_crc = (h_crc + ord(byte)) % 256
    h_crc = (0 - h_crc) % 256
    header += chr(h_crc)
    return header

def get_header_format(header):
    """Return [header_len, header_type, sub_type]."""
    sub_type = None
    type_id = header[0:3]
    sub_id = header[4:6]
    if type_id == "MEM" and sub_id == "BU":
        header_type = 'backup'
        headerlen = 49
    elif type_id == "DD@":
        header_type = 'screencapture'
        headerlen = 39
        sub_type = 'mono'
    elif type_id == "DC@":
        header_type = 'screencapture'
        headerlen = 39
        sub_type = 'color'
#     elif type_id == "FNC":
#         datatype = 4
#         headerlen = 49
    elif type_id == "IMG" and sub_id == "PC":
        header_type = 'picture'
        headerlen = 49
    elif type_id == "TXT" and sub_id == "PG":
        header_type = 'program'
        headerlen = 49
    elif type_id == "VAL" and sub_id == "MT": # Matrix
        header_type = 'matrix'
        headerlen = 49
    elif type_id == "VAL" and sub_id == "VM": # Simple variable like A,B,...,Z
        header_type = 'var'                   # For cafix : format=8
        headerlen = 49                        # datatype=13 size=14   
    elif type_id == "VAL" and sub_id == "LT":
        header_type = 'list'
        headerlen = 49
#     elif type_id == "VAL":
#         datatype = 8
#         headerlen = 49
#     elif type_id == "REQ":
#         datatype = 7
#         headerlen = 49
    elif type_id == "END":
        header_type = 'end'
        headerlen = 49
    else:
        header_type = 'unknown'
        headerlen = 49
    return [headerlen, header_type, sub_type]

def get_data_len(header, data_type, sub_type):
    """Return data len from the header"""
    std_len = ord(header[6]) * (256 ** 3) + ord(header[7]) * (256 ** 2) + \
              ord(header[8]) * 256 + ord(header[9]) - 2
    if data_type == 'program':
        data_len = std_len
    elif data_type == 'backup': # bck
        data_len = std_len
    elif data_type == 'end':
        data_len = 0
#     elif data_type == 'val' :
#         data_len = stdlen
#         if data_sub_type == 13:
#             data_len = 14
#         elif data_sub_type == 6:
#             data_len = 2 * (ord(header[8]) + ord(header[9])) - 2
    elif data_type == 'program':
        data_len = std_len
    elif data_type == 'matrix':
        # A matrix is a table with 2 dimensions
        # header[7] is the first dimension
        # header[9] is the second dimension
        # 16 bytes per value transfered
        # 49 byte for a sort of footer
        data_len=14*ord(header[7])*ord(header[9])+47
    elif data_type == 'list':
        # A list is a table with 1 dimension
        # header[7] is the first dimension
        # 16 bytes per value transfered
        # 49 byte for a sort of footer (so it is very similar to matrixes)
        data_len = 14*ord(header[7])+47
    elif data_type == 'var':
        data_len = 14
    elif data_type == 'screencapture' and sub_type == 'color': 
        #screencapture color
        data_len = 3075
    elif data_type == 'screencapture' and sub_type == 'mono':
        #screencapture mono
        data_len = 1024
    elif data_type == 'picture': #image
        data_len = 4112
    else:
        data_len = std_len
    return data_len

def fill_metadata(data, header, add_date = True):
    """Fill a data object with metadata from header"""
    ### All data ###
    # Name:
    # lazy code : what append if \xff are followed by non ff characters ?
    data.name = header[10:18].replace('\xff', '')
    # Date:
    if add_date:
        data.date = datetime.date.today()
    ### Programs ###
    if data.__class__ == datacls.Program:
        # Password:
        data.password = header[26:34].replace('\xff', '')
        # Base:
        if header[34:36] == 'BN':
            data.use_base = True
        else:
            data.use_base = False
    ### Pictures ###
    elif data.__class__ == datacls.Picture:
        data.pallet = PICTURE_PALLET
        data.color_byte = 3
    elif data.__class__ == datacls.ScreenCapture:
        data.pallet = SCREEN_PALLET
        data.color_byte = 0

def color_screencapture_to_raw(screen_capture):
    """Return picture raw data from raw color screen capture."""
    return screen_capture[:0x400 * 2 + 2] +\
           '\x03' + '\x00' * 0x400 +\
           screen_capture[0x400 * 2 + 2:]
           
def mono_screencapture_to_raw(screen_capture):
    """Return picture raw data from raw mono screen capture."""
    # Convert screencapture (invert up and down)
    columns = [''] * 16
    for index in range(len(screen_capture)):
        col = index / 64
        columns[col] += screen_capture[index]
    for col in range(len(columns)):
        byte_list = list(columns[col])
        byte_list.reverse()
        columns[col] = ''.join(byte_list)
    black_sheet = ''.join(columns)
    return '\x01' + black_sheet +\
           '\x02' + '\x00' * 0x400 +\
           '\x03' + '\x00' * 0x400 +\
           '\x04' + '\x00' * 0x400

# File format management classes
class CasFile:
    """Cas (Casiolink) file format manager."""
    
    name = 'cas'
    managed_data = [datacls.Program, datacls.Backup, datacls.Picture, \
                    datacls.ScreenCapture]
    read = True
    write = False
    list_ext = ['cas']
    def __init__(self, filename = None):
        """Make a cas file object with filename"""
        self.filename = filename
        
    def open_file(self):
        """Open a file in the cas Format"""
        # Build the FileData
        file_data = datacls.FileData()
        cas_file = open(self.filename, 'r')
        ctn = cas_file.read()
        cas_file.close()
        # Init var
        index = 0
        is_header = True
        header = ""
        header_len = 100
        data = None
        data_len = 100
        raw_data = ""
        # Loop on bytes in ctn
        while index < len(ctn):
            if is_header and ctn[index] != ':' and len(header) < header_len:
                header += ctn[index]
                if len(header) == 7:
                    # Get metadata
                    header_len, data_type, sub_type = \
                                get_header_format(header)
            elif is_header and len(header) == header_len:
                # Crc byte, init the data record
                is_header = False
                if data_type == 'end':
                    break
                data_len = get_data_len(header, data_type, sub_type)
                # Make the data
                if data_type == 'program':
                    data = file_data.new_record(datacls.Program)
                elif data_type == 'backup':
                    data = file_data.new_record(datacls.Backup)
                elif data_type == 'picture':
                    data = file_data.new_record(datacls.Picture)
                elif data_type == 'ScreenCapture':
                    data = file_data.new_record(datacls.ScreenCapture)
                else:
                    data = file_data.new_record(datacls.Data)
                fill_metadata(data, header, False)
            elif not is_header and len(raw_data) == 0 and ctn[index] == ':':
                # Not header, first :
                pass
            elif not is_header and len(raw_data) < data_len:
                # Raw_data
                if data_type == 'screencapture' and sub_type == 'color' \
                        and (len(raw_data) == 1024 or len(raw_data) == 2048):
                    # Jump two bytes (checksum and :)
                    index += 2
                if data_type == 'picture' and (len(raw_data) + 1) % 1028 == 0 \
                        and len(raw_data) != 0 and len(raw_data) + 1 < data_len:
                    # Jump two bytes (checksum and :)
                    index += 2
                raw_data += ctn[index]
            elif not is_header and len(raw_data) == data_len:
                # Data Crc, save raw_data (and make some convertions)
                if data_type == 'screencapture' and sub_type == 'color':
                    data.raw_data = color_screencapture_to_raw(raw_data)
                elif data_type == 'screencapture' and sub_type == 'mono':
                    data.raw_data = mono_screencapture_to_raw(raw_data)
                else:
                    data.raw_data = raw_data
                # Init vars
                is_header = True
                header = ""
                header_len = 100
                data = None
                raw_data = ""
            index += 1
        return file_data
# -*- coding: utf-8 -*-
#
############################################################################
#
# (c) 2006-2007 Florian Birée aka Thesa <florian.biree@xxxxxxxxxxx>
# (c) 2006 Achraf Cherti aka Asher256 <achrafcherti@xxxxxxxxx>
#
# Website: <URL:http://casetta.tuxfamily.org>
#
# Version 0.3.0dev
#
# changelog:
#
# # 0.3.0 version:
#
#   o First release (code from __init__.py)
#   o Add picture-specific data properties
#   o Add new classes for each data type
#   o Change methods in FileData to have a better behaviour
#
############################################################################
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
# MA 02110-1301, USA.
#
# http://www.gnu.org/copyleft/gpl.html
#
############################################################################
"""Data management classes for casetta"""

__author__ = "Florian Birée, Achraf Cherti"
__version__ = "0.3.0dev"
__copyright__ = "Copyright (c) 2006-2007, Florian Birée, Achraf Cherti"
__license__ = "GPL"
__revision__ = "$Revision: $"
# $Source: $
__date__ = "$Date: $"

import re
from math import log10

import catdic
import pictures
import backups
import errors

class Data:
    """Data manipulation class"""
    name_rule = r'^.'
    def __init__(self, name = '', raw_data = '', data_type = 'unknown', \
            date = None):
        """Make an empty data"""
        self.name = name
        self.raw_data = raw_data
        self.dType = data_type
        self.date = date

    def __repr__(self):
        """Descript this data"""
        return "<Data '%s', type: '%s'>" % (self.name, self.dType)
    
    def copy(self):
        """Creat a new Data object from this object."""
        return Data(self.name, self.raw_data, self.dType, self.date)
    
    def get_name(self):
        """Return the name in an human readable format"""
        return catdic.text_encode(self.name, True)
        
    def set_name(self, new_name):
        """Set the name from a newcat-like string."""
        self.name = catdic.text_decode(new_name, True)

### Management of various types of variables ###
### IMPORTANT ###
# Casio variables (I mean simple variables like A,B...Z, matrix and lists)
# are stored in a similar way.
def get_simple_val(raw_line):
    """Returns the value of the variable in *decimal* format"""
    val=''
    # raw_line[4:10] is where figures are stored
    for i in raw_line[4:10]:
        if ord(i) >= 10:
            val += hex(ord(i)).replace('0x','')
        else:
            val += '0'+hex(ord(i)).replace('0x','')
    val=val[0]+'.'+val[1:]
    # raw_line[12]=special code // raw_line[13]=power
    # raw_line[12]==\x01 -> positive number + positive power
    # raw_line[12]==\x00 -> positive number + negative power
    # raw_line[12]==\x51 -> negative number + positive power
    # raw_line[12]==\x50 -> negative number + negative power
    
    if raw_line[12]=='\x01' or raw_line[12]=='\x51': 
        #Then value > 0 data[13] is positive power
        power= int(hex(ord(raw_line[13])).replace('0x',''))+1
    else: # Then value < 0 data[13] is a negative power
        power=-100+int(hex(ord(raw_line[13])).replace('0x',''))+1
    
    if raw_line[12]=='\x50' or raw_line[12]=='\x51':
        # Then we have a negative number
        return float(val)*(10**power)*-1
    else: # Then we have a positive number
        return float(val)*(10**power)
 
def set_simple_val(nbre):
    if abs(nbre) >= 9.99999999*(10**99) or abs(nbre) <= 9.99999999*(10**-99) and nbre != 0:
        return "Absolute value of number is too high/low and not supported by the calculator" 
    elif nbre==0:
        return '\x00\x01\x00\x01'+'\x00'*8+'\x01'+'\x00'
    raw_line = ''
    string_num=str(nbre)
    if 'e' in string_num:
        string_num=string_num.split('e')[0]
    string_num=string_num.replace('-','').replace('.','')
    string_num=re.sub('^0*','',string_num)
    #print '*'+string_num+'*'
    
    # Numbers are stored this way : 
    # [0|0] [0|1] [0|0] [0|1] [0|1stfig] [2ndFig|3rdFig] [4thFig|5thFig]
    # [6thFig|7thFig] [8thFig|9thFig] [10thFig|0] [0|0] [0|0] [special code]
    # [power]  
    # Each [x|x] is one byte  
    
    # Each xxFig is one figure of the number you entered
    # So you can see that except for 5th and the 10th byte,
    # 2 Fig are are stored in each byte
        
        
    # Stores the first figure in the first byte
    raw_line += chr(int(string_num[0],16))
    i=1
        
    # Stores figures 2-8 in the corresponding bytes
    # or \x00 if figures are not present
    while i <= 7:
        #print "--"
        len(string_num)
        if (i+2) <= len(string_num):
            sli = string_num[i:i+2]
            #print sli
            raw_line += chr(int(sli,16))
        elif i == len(string_num)-1:
            sli = string_num[i]
            #print sli
            raw_line += chr(int(sli+'0',16))
        else:
            raw_line += '\x00'
        i +=2
    # Stores the last figure or \x00
    if len(string_num)>=10:
        raw_line += chr(int(string_num[9]+'0',16))
    else:
        raw_line += '\x00'
    
    raw_line=raw_line+'\x00\x00'
    if abs(nbre)>=1 or string_num=='1':
        power=int(log10(abs(nbre)))
    else:
        power=int(log10(abs(nbre)))-1
    # print power
    # Sets the correct code and power
    # See get_val for more info.
    if nbre >= 0 and power >=0 :    
        raw_line += '\x01'+ chr(int(str(power),16))
    elif nbre >= 0 and power < 0:
        raw_line += '\x00'+ chr(int(str(100+power),16))
    elif nbre < 0 and power >=0:
        raw_line += '\x51'+ chr(int(str(power),16))
    elif nbre < 0 and power <=0:
        raw_line += '\x50'+ chr(int(str(100+power),16))
    return raw_line

class Variable(Data):
    """Class for a casio simple var like A,B...Z"""
    name_rule = r'^[A-Z]$'
    def __init__(self, name='', raw_data='', date=None):
        """Makes an empty variable"""
        Data.__init__(self, name, raw_data, 'variable', date)
    def get_val(self):
        """ Returns the value of raw_data under the form of a *decimal*
        floatting point number"""
        return get_simple_val(self.raw_data)
    def set_val(self, number):
        """Takes a decimal integer or floatting point number and puts
        its value in raw_data"""
        self.raw_data='\x00\x01\x00\x01'+set_simple_var(number)
        
class Matrix(Data):
    """Class for a casio matrix"""
    name_rule = r'^Mat [A-Z]$'
    def __init__(self, name='', raw_data='', date = None, dimensions=(1,1) ):
        """Makes an empty matrix"""
        Data.__init__(self, name, raw_data, 'matrix', date)
        self.dimensions=dimensions
    
    def get_raw_data_list(self):
         
        raw_data_list=[]
        
        #loh.write(self.raw_data)
        for i in range(abs(len(self.raw_data[:-47])/14)):
            raw_data_list.append(self.raw_data[i*14:(i+1)*14]) 
        raw_data_list.append(self.raw_data[-47:])
        
        return raw_data_list
                    
    def get_val(self):
        
        current_line=0 
        
        raw_data_list=self.get_raw_data_list()[:-1]
        
        data_list=[[]]
        
        for i in raw_data_list:
            value=get_simple_val(i)
            if ord(i[1]) == (current_line + 2):
                data_list.append([])
                current_line += 1
            data_list[current_line].append(value)
        return data_list
    
    def set_val(self, py_list):
        self.raw_data=''
        
        for line in py_list:
            ## FIXME Test here if line is a list otherwise raise error
            ## FIXME Test here if all lines have same length otherwise raise error
            crc = 0
            #log.write(str(line))
            for i in line:
                #log.write(str(i))
                ## FIXME Test here if i is an integer otherwise raise error
                # Calculation of the coords
                coord1=py_list.index(line)+1
                coord2=py_list[coord1-1].index(i)+1
                # Number in raw format 
                raw_nbre='\x00'+chr(coord1)+'\x00'+chr(coord2)+set_simple_val(i)                
                # Checksum calculation -> moved to transfert tool
                ## 
                #log.write(raw_nbre+'\xff')
                #crc = 0
                #for i in raw_nbre:
                #    crc += ord(i)
                #crc = abs(255 - (crc % 256)) + 1
                
            # Add raw_number (number + coords)
                self.raw_data += (raw_nbre)
            #log.write(raw_nbre+'\xff')
        ### Add footer
        ### FIXME : Add this in the transfer tool part ?
        self.raw_data += 'END'+'\xff'*43+'V'
        #log.write(self.raw_data)
        #log.close()
                    
class List(Data):
    """Class for a casio list"""
    name_rule = r'^List [1-6]$'
    def __init__(self, name='', raw_data='', date = None, dimension=1 ):
        """Makes an empty list"""
        Data.__init__(self, name, raw_data, 'list', date)
        self.dimension=dimension
    def get_raw_data_list(self):
        raw_data_list=[]
        for i in range(abs(len(self.raw_data[:-47])/14)):
            raw_data_list.append(self.raw_data[i*14:(i+1)*14]) 
        raw_data_list.append(self.raw_data[-47:])
        return raw_data_list
    def get_val(self):
        raw_data_list=self.get_raw_data_list()[:-1]
        data_list=[]
        for i in raw_data_list:
            value=get_simple_val(i)
            data_list.append(value)
        return data_list
      
###################################    
class Program(Data):
    """Class for a casio program"""
    name_rule = r'^.{1,8}$'
    def __init__(self, name = '', raw_data = '', date = None, password = "", \
            use_base = False):
        """Make an empty program"""
        Data.__init__(self, name, raw_data, 'program', date)
        # Program specific properties
        self.password = password
        self.use_base = use_base
    
    def copy(self):
        """Creat a new Program object from this object."""
        return Program(self.name, self.raw_data, self.date, \
                       self.password, self.use_base)

    def get_text(self):
        """Return the program in an human readable format"""
        return catdic.text_encode(self.raw_data)

    def set_text(self, text):
        """Set the program from a newcat-like string"""
        self.raw_data = catdic.text_decode(text)
    
    def get_password(self):
        """Return the password in an human readable format"""
        return catdic.text_encode(self.password, True)
        
    def set_password(self, new_password):
        """Set the password from a newcat-like string."""
        self.password = catdic.text_decode(new_password, True)

class Backup(Data):
    """Class for a casio backup"""
    name_rule = r'^.'
    def __init__(self, name = '', raw_data = '', date = None):
        """Make an empty backup"""
        Data.__init__(self, name, raw_data, 'backup', date)
    
    def find_password(self, program_name):
        """Return a list of possible passwords for a program_name"""
        return backups.find_password(self.raw_data, program_name)
    
    def find_program_names(self):
        """Find the program list from the backup
        
        Return a list of list like ['Program Name', 'offset'].
        """
        return backups.find_prgm_names(self.raw_data)
    
    def find_program_data(self, offset):
        """Return raw data of program from its offset"""
        return backups.find_prgm_data(offset, self.raw_data)

    def copy(self):
        """Creat a new Backup object from this object."""
        return Backup(self.name, self.raw_data, self.date)
    
class Picture(Data):
    """Class for a casio picture"""
    name_rule = r'^Picture[1-6]$'
    def __init__(self, name = '', raw_data = '', date = None, pallet = None, \
            color_byte = None):
        """Make an empty picture"""
        Data.__init__(self, name, raw_data, 'picture', date)
        # self.pallet must be a list of characters as ['o','g','b','w']
        self.pallet = pallet
        self.color_byte = color_byte
    
    def copy(self):
        """Creat a new Picture object from this object."""
        return Picture(self.name, self.raw_data, self.date, self.pallet, \
                       self.color_byte)
    
    def get_picture(self):
        """Return a list of pixels from the picture"""
        return pictures.raw_to_data(self.raw_data, \
                                    self.color_byte, \
                                    self.pallet)
    
    def set_picture(self, pixels_list):
        """Set the picture from a list of pixels"""
        self.raw_data = pictures.data_to_raw(pixels_list, \
                                             wanted_pallet = self.pallet)
    
    def change_pallet(self, new_pallet, headers = None):
        """Convert the picture from the current pallet to new_pallet.
        
        You can add a header using headers (like pictures.sheets_to_raw).
        """
        self.raw_data = pictures.sheets_to_raw(\
                            pictures.change_sheets_pallet(\
                                    pictures.raw_to_sheets( \
                                        self.raw_data, \
                                        self.color_byte, \
                                        len(self.pallet)), \
                                    self.pallet, new_pallet), \
                            headers)
        self.pallet = new_pallet
        if headers != None:
            # Change the color_byte
            self.color_byte = len(headers[0])

    def get_program(self):
        """Return a program in newcat-like format to draw the picture."""
        return pictures.img_to_basic(self.get_picture)

class ScreenCapture(Picture):
    """Class for a screen capture"""
    name_rule = r'^.'
    def __init__(self, name = '', raw_data = '', date = None, pallet = None, \
            color_byte = None):
        """Make an empty screen capture"""
        Picture.__init__(self, name, raw_data, date, pallet, color_byte)
        self.dType = 'screencapture'
    
    def copy(self):
        """Creat a new ScreenCapture object from this object."""
        return ScreenCapture(self.name, self.raw_data, self.date, self.pallet, \
                       self.color_byte)

class FileData:
    """Data file manipulation class"""
    def __init__(self, filename = None, format = None):
        """Make a FileData.
        
        If filename is filled, the file given is opened.
        If format is None, the format is autodetected."""
        self.data = []
        self.export = []
        if filename != None:
            self.import_file(filename, format)

    def __repr__(self):
        """Descript this data"""
        out = "<File of %s casio data:\n" % str(len(self.data))
        for data_index in range(len(self.data)) :
            out += "#%s: %s\n" % (str(data_index), \
                                  self.data[data_index].__repr__())
        out += '>'
        return out

    def __getitem__(self, index):
        """The same of self.data[index]"""
        return self.data[index]

    def export_list(self):
        """Return the list of data to be exported."""
        out_list = []
        for index in self.export:
            out_list.append(self.data[index])
        return out_list

    def remove(self, data):
        """Remove the data record."""
        self.data.remove(data)
        # clear the export list because index list has changed
        self.export = []
    
    def new_record(self, data_class, arg_list = [], arg_dic = {}):
        """Make a new data from the data_class (Program, Backup, etc)
        
        The new data object is append to the file data, and the
        the data object is returned.
        arg_list and arg_dic can be used to give arguments in the class
        initialization.
        The name of the returned data should be checked.
        """
        new_data = data_class(*arg_list, **arg_dic)
        self.data.append(new_data)
        return new_data

    def save(self, filename, format=None, ignore_warnings=False, \
             export=False):
        """Save in (a) file(s) the data.

        If all data aren't managed by this format, raise DataNoManaged
        with the list of data type not managed (['all'] for all ;-) )
        Use ignore_warnings = True to save managed data in this format.
        """
        if format == None:
            from formats import choose_format
            format = choose_format(filename)
        if not format.write:
            raise errors.FormatCantSave(format)
        if not ignore_warnings :
            no_managed_data = self.check_no_managed_data(format, export)
            if no_managed_data != []:
                raise errors.DataNoManaged(no_managed_data)
        afile = format(filename)
        if export:
            afile.save(self.export_list())
        else:
            afile.save(self.data)
        
    def add_records(self, file_data):
        """Add records from another file_data.
        
        Return the list of new records.
        The name of returned data should be checked.
        """
        self.data += file_data.data
        return file_data.data
    
    def import_file(self, filename, format = None):
        """Import all records from filename.
        
        Return the list of new records.
        The name of returned data should be checked.
        """
        if format == None:
            from formats import choose_format
            format = choose_format(filename)
        if not format.read:
            raise errors.FormatCantOpen(format)
        imported_file = format(filename).open_file()
        return self.add_records(imported_file)

    def list_data_types(self, export=False):
        """Return the list of data types used"""
        ret = []
        if export:
            data_list = self.export_list()
        else :
            data_list = self.data
        for data in data_list:
            if not data.__class__ in ret:
                ret.append(data.__class__)
        return ret

    def check_name(self, checked_data):
        """Return 1 if the name of this data is correct
        
        Return 0 if the name syntax is bad
        Return -1 if the name is already used"""
        # check the syntax of the name
        if re.search(checked_data.name_rule, checked_data.name) == None:
            return 0
        # check if already employed
        for data in self.data:
            if data is not checked_data and \
                    data.__class__ == checked_data.__class__ and \
                    data.name == checked_data.name:
                return -1
        return 1

    def copy_record(self, data):
        """Copy data. Return the copy.
        
        The name of the returned data must be changed.
        """
        copy = data.copy()
        self.data.append(copy)
        return copy

    def split_by_data_type(self):
        """Split this file_data by data type.

        Return a dictionary of file data with data classes as keys.
        
        """
        ret = {}
        for data in self.data:
            if not data.__class__ in ret.keys():
                ret[data.__class__] = FileData()
            ret[data.__class__].data += data.copy()
        return ret

    def check_no_managed_data(self, format, export = False):
        """ Return the list of data classes no managed in format.

        Return [] if all data type are managed, ['all'] if nothing is managed
    
        """
        ret = []
        for data_class in self.list_data_types(export) :
            if not data_class in format.managed_data:
                ret.append(data_class)
        ret.sort()
        fd_list = self.list_data_types()
        fd_list.sort()
        if fd_list == ret :
            ret = ['all']
        return ret
    
    def send(self, tool, export = False):
        """Send data using tool."""
        if export:
            tool.send(self.export_list())
        else:
            tool.send(self.data)

    # Deprecated methods (here for compatibility reason):
    def del_data(self, delete_index):
        """Del the data with the index i
        
        Deprecated: please use remove instead
        """
        del(self.data[delete_index])
        # clear the export list because index list has changed
        self.export = []
    
    def add_data(self, file_data):
        """Add data from another file_data

        Return the list of new index
        (You should check the name of those datas)
        
        Deprecated: please use add_records instead
        """
        length = len(self.data)
        self.data += file_data.data
        return range(length, len(self.data))

#     def add(self, filename, format = 'unknown'):
#         """Open datas from filename and add them in the file .

#         Return the list of new index
#         (You should check the name of those datas)
#         
#         Deprecated: please use import instead
#         """
#         if format == 'unknown' :
#             format = formats.choose_format(filename)
#             if format == 'unknown':
#                 raise errors.FormatNotFound
#         return self.add_data(formats.openFunc[format](filename))

    def copy_data(self, data_index):
        """Copy data from dataIndex.

        Return the new index
        (You should check the name of the copy)
        
        Deprecated: use copy_record instead
        """
        new_index = self.new_data()
        self.data[new_index].name = self.data[data_index].name
        self.data[new_index].raw_data = self.data[data_index].raw_data
        self.data[new_index].dType = self.data[data_index].dType
        self.data[new_index].date = self.data[data_index].date
        self.data[new_index].password = self.data[data_index].password
        return new_index

    def new_data(self):
        """Make a new empty data, and return it index.
        
        Deprecated, use new_record instead
        """
        self.data.append(Data())
        return len(self.data) - 1

# Static data:
data_type_list = [Data, Program, Backup, Picture, ScreenCapture]
data_type_dic = {'unknown' : Data,
                 'program' : Program,
                 'backup' : Backup,
                 'picture' : Picture,
                 'screencapture' : ScreenCapture}
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import casetta
connect=casetta.devices_serial.Connexion()
touche=0
while touche != 47:
    while not connect.calc_is_ready_to_send():
        pass 
    #num2=hex(ord(data[5])).replace('0x','')
    #num1=hex(ord(data[4])).replace('0x','')
    #touche=int(num1)*10+int(num2)/10
    var=connect.receive_data()
    touche=int(var.get_val())
    print touche
    if touche==79:
        os.popen('xmms --play-pause')
    elif touche==69:
        os.popen('xmms --stop')
    elif touche==28:
        os.popen('dcop kmix Mixer0 increaseVolume 0')
    elif touche==37:
        os.popen('dcop kmix Mixer0 decreaseVolume 0')
    elif touche==38:
        os.popen('xmms --rew')
    elif touche==27:
        os.popen('xmms --fwd')
    elif touche==68:
        os.popen('xmms --toggle-repeat') 

%Header Record
Format:TXT
Data Type:PG
File Name:XMMSCTRL
Date:20070420
%Data Record
\While 1
0\->\theta
\While \theta=0
\Getkey\->\theta
\WhileEnd
\Send(\theta)
\If \theta=79
\Then "Play-Pause"
\IfEnd
\If \theta=69
\Then "Stop"
\IfEnd
\If \theta=28
\Then "Vol +"
\IfEnd
\If \theta=37
\Then "Vol -"
\IfEnd
\If \theta=38
\Then "Rew"
\IfEnd
\If \theta=27
\Then "Fwd"
\IfEnd
\If \theta=68
\Then "Toggle Repeat"
\IfEnd
\WhileEnd
%End


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/