| [casetta] Files... for previous mail |
[ Thread Index | Date Index | More lists.tuxfamily.org/casetta Archives ]
|
--
Fabien ANDRE aka Xion345 Linux User #418689 -- fabien.andre.g@xxxxxxxxxx -- xion345@xxxxxxxxxxxxx ...Unix, MS-DOS, and Windows NT (also known as the Good, the Bad, and the Ugly). ( Matt Welsh, Not dated ) |
# -*- coding: utf-8 -*-
#
############################################################################
#
# (c) 2007 Xion345 aka Fabien ANDRE <fabien.andre.g@xxxxxxxxxx>
# (c) 2006-2007 Thesa aka Florian Birée <florian.biree@xxxxxxxxxxx>
#
# Website: <URL:http://casetta.tuxfamily.org>
#
# Version 0.3.0dev
#
# changelog:
#
# # 0.3.0 version:
#
# o First release (extract password function is from casetta_gtk code)
# o Add the DOTALL flag in regular expressions
# o Remove the find_password function (replaced by the improved
# find_prgm_metadata function)
#
############################################################################
#
# 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
#
############################################################################
"""Pictures manager for casetta"""
__author__ = "Fabien ANDRE, Florian Birée"
__version__ = "0.3.0dev"
__copyright__ = "Copyright (c) 2006-2007, Fabien ANDRE, Florian Birée"
__license__ = "GPL"
__revision__ = "$Revision: $"
# $Source: $
__date__ = "$Date: $"
import re
def find_prgm_list(rawback):
"""Return a list like [(name1, use_base1, offset1, password1),... ]
Where name1 is the raw name of the first program ;
use_base1 is True if the program use base calculation, False else ;
offset1 is the offset of the first program (for find_prgm_data function) ;
password1 is the raw password of the first program.
"""
names_pattern = re.compile( """
[\x00\xee] # first character (0x00 except if the last prgm have a
# password. In this case, 0xee)
([a-zA-Z0-9\x99\xa9\xb9
\xcd\xce\x89\x20'"~.{}\[\]] # A program name begins by a charater
[a-zA-Z0-9
\x99\xa9\xb9\xcd\xce\x89\x20'"~.{}\[\]\xff]
{7}) # End of program name: 7 of the above charaters or 0xff
.{2} # 2 charaters
[\x00]{2} # 2 0x00 characters
([\x00\x01]) # 0x01 if using base calculation, 0x00 else
(.{2}) # offset of the beginning of the prgm data
# If a password is present:
(?:[\x10] # A 0x10 character (ie a password)
([a-zA-Z0-9\x99\xa9\xb9\xcd\xce\x89\x20'"~.{}\[\]] # Password (same as name)
[a-zA-Z0-9\x99\xa9\xb9\xcd\xce\x89\x20'"~.{}\[\]\xff]{7})
[\x00]{7} # 7 0x00 characters
)?
""", re.VERBOSE | re.DOTALL)
names_list = names_pattern.findall(rawback)
return [(i[0].replace('\xff',''), # Clean the name
bool(ord(i[1])), # Boolean base attribut
i[2], # offset
i[3].replace('\xff', '') # Clean the password
) for i in names_list]
def find_prgm_data(offset, rawback):
"""Return raw data of a program in a backup with its offset"""
#hex1 = hex(ord(offset[1]))
#hex2 = hex(ord(offset[0]))
#ad2 = int(hex1 + hex2.replace('0x',''), 16)
ad2 = ord(offset[1]) * 0x100 + ord(offset[0])
i = ad2
prgm_data = ""
while i > 0:
prgm_data += (rawback[i])
if rawback[i] == '\xff':
break
i -= 1
return prgm_data
# -*- 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: ## Xion345 : I think this can now be wiped/commented ;-)
# 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.Variable:
header = 'VAL'+'\x00'+'VM'+'\x00\x01\x00\x01'
header += data.name
header += '\xff'*7 + 'VariableR' + '\x0a'
header += '\xff'*20
elif data.__class__ == datacls.ViewVariable:
header = 'VAL'+'\x00'+'WD'+'\x00\x01\x00\x01'
header += data.name
header += '\xff'*(8-len(data.name))
header += 'V\x99Win'+str(data.belongs_to_file)
header += '\xff'*2 + 'R\n'
header += '\xff'*20
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.List:
header = 'VAL'+'\x00'+'LT'+'\x00'+'\x01'
header += '\x00'+chr(data.dimension)+data.name
if data.belongs_to_file == None:
header += '\xff'*10
else:
header += '\xff'*2+'File '+str(data.belongs_to_file)+'\xff'*2
header += '\x52'+'\x0a'+'\xff'*20
elif data.__class__ == datacls.SimlEquationMatrix:
header = 'VAL'+'\x00'+'SE'+'\x00'+chr(data.dimensions[0])
header += '\x00'+chr(data.dimensions[1])
header += data.name +'EquationR\n' + '\xff'*20
elif data.__class__ == datacls.PolyEquationMatrix:
header = 'VAL'+'\x00'+'PE'+'\x00'+chr(data.dimensions[0])
header += '\x00'+chr(data.dimensions[1])
header += data.name +'EquationR\n' + '\xff'*20
elif data.__class__ == datacls.FMem:
header = 'TXT' +'\x00'+'FM'
header += '\x00'*3 + chr(data_len)
header += data.name
header += '\xff'*6+'F\x99Mem'+'\xff'*25
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.__class__])
# 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 == "WD": # Variable used to define a view-window
header_type = 'viewvar'
headerlen = 49
elif type_id == "VAL" and sub_id == "LT": # List
header_type = 'list'
headerlen = 49
elif type_id == "VAL" and sub_id == "SE":
header_type = 'simlequation-matrix'
headerlen = 49
elif type_id == "VAL" and sub_id == "PE":
header_type = 'polyequation-matrix'
headerlen = 49
elif type_id == "TXT" and sub_id == "FM":
header_type = 'function-memory'
headerlen = 49
elif type_id == "FNC" and sub_id == "GF":
header_type = "graph-func"
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' or 'simlequation-matrix' or data_type == 'polyequation-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
# 14 bytes for the value itself
# + 1 byte \x3a, transfer inititation
# + 1 byte CRC
data_len=14*ord(header[7])*ord(header[9])
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])
elif data_type == 'var' or data_type == 'viewvar':
data_len = 14
elif data_type == 'function-memory':
data_len = ord(header[9])-2
elif data_type == "graph-func":
data_len = ord(header[9])-2
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 ?
# FIXME : Why not to use the name_rule ?
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
### Matrices ###
elif data.__class__ == datacls.Matrix or data.__class__ == datacls.SimlEquationMatrix or data.__class__ == datacls.PolyEquationMatrix :
# Dimensions
data.dimensions = (ord(header[7]),ord(header[9]))
###Â Lists ###
elif data.__class__ == datacls.List:
# Dimension
data.dimension = ord(header[7])
# Belongs to a FileList ?
if header[23] in [str(i+1) for i in range(6)]:
data.belongs_to_file = int(header[23])
elif data.__class__ == datacls.ViewVariable:
if header[23] in [str(i+1) for i in range(6)]:
data.belongs_to_file = int(header[23])
elif data.__class__ == datacls.GraphFunc:
# F-Type
if header[10] != 'f':
data.ftype = header[10]
else:
data.ftype = 'param'
# Equal_type
data.equal_type = header[17]
# Color
if header[29:31] == 'BL':
data.color = 'blue'
elif header[29:31] == 'OR':
data.color = 'orange'
elif header[29:31] == 'GN':
data.color = 'green'
# Selected ?
if header[32:35] == 'SLD':
data.selected = True
### Pictures ###
elif data.__class__ == datacls.Picture:
data.pallet = PICTURE_PALLET
data.color_byte = 3
### Screen capture ###
elif data.__class__ == datacls.ScreenCapture:
data.name = "Capture"
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
import catdic
import pictures
import backups
import errors
from math import log10
class Data:
"""Data manipulation class"""
name_rule = r'^.'
dType = 'unknown'
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)
class Program(Data):
"""Class for a casio program"""
name_rule = r'^.{1,8}$'
dType = 'program'
def __init__(self, name = '', raw_data = '', date = None, password = "", \
use_base = False):
"""Make an empty program"""
Data.__init__(self, name, raw_data, self.dType, 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'^.'
dType = 'backup'
def __init__(self, name = '', raw_data = '', date = None):
"""Make an empty backup"""
Data.__init__(self, name, raw_data, self.dType, 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_list(self):
"""Find the program list from the backup
Return a list like [(name1, use_base1, offset1, password1),... ]
Where name1 is the raw name of the first program ;
use_base1 is True if the program use base calculation, False else ;
offset1 is the offset of the location of the first program ;
password1 is the raw password of the first program.
"""
return backups.find_prgm_list(self.raw_data)
def get_program_by_name(self, program_name):
"""Return raw data of program from its offset"""
# Get metadata list
metadata_list = self.find_program_list()
# Find metadata for program_name
for (name, use_base, offset, password) in metadata_list:
if name == program_name:
return Program(name = name,
raw_data = backups.find_prgm_data(offset,
self.raw_data),
use_base = use_base,
password = password)
def copy(self):
"""Creat a new Backup object from this object."""
return Backup(self.name, self.raw_data, self.date)
################# Management of various types of VARIABLES ###################
# Casio variables (I mean simple variables like A,B...Z, matrix and lists)
# are stored in a similar way.
### Low level functions ###
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] block 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:
len(string_num)
if (i+2) <= len(string_num):
sli = string_num[i:i+2]
raw_line += chr(int(sli,16))
elif i == len(string_num)-1:
sli = string_num[i]
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
# 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
### Variables and ViewVariables management ###
class Variable(Data):
"""Class for a casio simple var like A,B...Z"""
name_rule = r'^[A-Z]$'
dType = 'variable'
def __init__(self, name='', raw_data='', date=None):
"""Makes an empty variable"""
Data.__init__(self, name, raw_data, self.dType, 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_val(number)
class ViewVariable(Variable):
""" Class for a variable used to define a view-window"""
name_rule = r'^Xmin|Xmax|Xscl|Ymin|Ymax|Yscl|T\xcemin|T\xcemax|T\xceptch$'
dType = 'view-variable'
def __init__(self, name='', raw_data='', date=None, belongs_to_vwin = None ):
"""Makes an empty variable"""
Data.__init__(self, name, raw_data, self.dType, date)
self.belongs_to_vwin = belongs_to_vwin
def copy(self):
""" Create a new ViewVariable object from this object"""
return ViewVariable(self.name, self.raw_data, self.date, self.belongs_to_vwin)
### Matrices and Lists ###
class Matrix(Data):
"""Class for a casio matrix"""
name_rule = r'^Mat [A-Z]$'
dType = 'matrix'
def __init__(self, name='', raw_data='', date = None, dimensions=(0,0)):
"""Makes an empty matrix"""
Data.__init__(self, name, raw_data, self.dType, date)
self.dimensions=dimensions
def set_dimensions(self, dimensions):
if dimensions[0] > 0 and dimensions[0] <= 255 and dimensions[1] > 0 and dimensions[1] <= 255:
self.dimensions = dimensions
else:
raise errors.BadMatrix(dimensions, 'Bad dimensions, maximum size for matrices is 255x255')
def copy(self):
""" Create a new Matrix object from this object """
return Matrix(self.name, self.raw_data, self.date, self.dimensions)
def get_raw_data_list(self):
""" Returns a list of rawdata composed of each value of the
matrix. Useful when sending a matrix
(each value has to be transferred independently,
and has its own checksum"""
raw_data_list=[]
for i in range(len(self.raw_data)/14): # Counts the number of variables inside the matrix
# A variable is 14 octets long
raw_data_list.append(self.raw_data[i*14:(i+1)*14]) # Add each variable to the list
return raw_data_list
def get_val(self):
""" Returns values contained in the matrix in a
human-readable format using a list of list"""
current_line=0
raw_data_list=self.get_raw_data_list()
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):
""" Sets the the matrix content. Takes a python list of list"""
## ***IMPORTANT*** : The checking and the actual remplacement of
## ---------------
## values are done into two different blocks to avoid
## the matrix to be partially modified, and not
## completely if an error appears. Therefore, if
##Â an error is raised during the checking, you can BE SURE
##Â the list was not modifed at all.
## Some tests to check the py_list givent is valid
##Â FIXME : Maybe there's a cleaner way to do that !!!
for line in py_list:
if type(line) == list:
if len(line)==len(py_list[0]):
for el in line:
if type(el) != float and type(el)!=int:
raise errors.BadMatrix( el, 'Matrices must' \
' be composed of numbers only')
else:
raise errors.BadMatrix(line, 'Not all lines have same length')
else:
raise errors.BadMatrix(line, 'You must give a list of lists')
self.raw_data=''
self.set_dimensions((len(py_list), len(py_list[0]))) # Update dimensions before modifing/creatind values
## Actually sets the matrix content
for num_line in range(len(py_list)):
for index in range(len(py_list[num_line])):
coord1=num_line+1 # Add 1 because numbering starts at 0 in python
# compared to 1 on the calculator
coord2=index+1
# Add raw_number (number + coords)
self.raw_data += '\x00'+chr(coord1)+'\x00'+chr(coord2)+set_simple_val(py_list[num_line][index])
class List(Data):
"""Class for a casio list"""
name_rule = r'^List [1-6]$'
dType = 'list'
def __init__(self, name='', raw_data='', date = None, dimension=0, belongs_to_file = None ):
"""Makes an empty list"""
Data.__init__(self, name, raw_data, self.dType, date)
self.dimension = dimension
# This var is used to specify if the list belongs to a file
self.belongs_to_file = belongs_to_file
def set_dimension(self, dimension):
if dimension >= 0 and dimension <= 255:
self.dimension = dimension
else:
raise errors.BadList(dimensions, 'Bad dimensions, maximum size for lists is 255')
def copy(self):
""" Create a new List object from this object """
return List(self.name, self.raw_data, self.date, self.dimension, self.belongs_to_file)
def get_raw_data_list(self):
raw_data_list=[]
for i in range(len(self.raw_data)/14):
raw_data_list.append(self.raw_data[i*14:(i+1)*14])
return raw_data_list
def get_val(self):
raw_data_list=self.get_raw_data_list()
data_list=[]
for i in raw_data_list:
value=get_simple_val(i)
data_list.append(value)
return data_list
def set_val(self,py_list):
## ***IMPORTANT*** : The checking and the actual remplacement of
## ---------------
## values are done into two different blocks to avoid
## the list to be partially modified, and not
## completely if an error appears. Therefore, if
##Â an error is raised during the checking, you can BE SURE
##Â the list was not modifed at all.
## A test to check the list is valid
for el in py_list:
if type(el) != int and type(el) != float:
raise errors.BadList(el, 'The list must be composed of numbers only')
self.raw_data=''
self.set_dimension=len(py_list)
## Actually sets the list content
for index in range(len(py_list)):
self.raw_data += '\x00'+chr(index+1)+'\x00\x01'+set_simple_val(py_list[index])
class SimlEquationMatrix(Matrix):
name_rule = r'^Mat Siml$'
dType = 'simlequation-matrix'
def __init__(self, name='', raw_data='', date = None, dimensions=(2,3)):
Data.__init__(self, name, raw_data, self.dType, date)
self.dimensions = dimensions
def set_dimensions(self,dimensions):
if dimensions in [(int(i)+2,int(i)+3) for i in range(5)]:
self.dimensions = dimensions
else:
raise errors.BadMatrix(dimensions, 'Bad dimensions')
class PolyEquationMatrix(Matrix):
name_rule = r'^Mat Poly$'
dType = 'polyequation-matrix'
def __init__(self, name='', raw_data='', date = None, dimensions=(1,3)):
Data.__init__(self, name, raw_data, self.dType, date)
self.dimensions = dimensions
def set_dimensions(self,dimensions):
if dimensions in [(1,3),(1,4)]:
self.dimensions = dimensions
else:
raise errors.BadMatrix(dimensions, 'Bad dimensions')
############################################################################################
class FMem(Data):
"""Class for a casio F-Mem definition"""
name_rule = r'^f[1-6]$'
dType = 'function-memory'
def __init__(self, name='', raw_data='', date=None):
""" Makes an empty function memory"""
Data.__init__(self, name, raw_data, self.dType, date)
def get_text(self):
""" Returns the content of the F-Mem in an human readable format"""
return catdic.text_encode(self.raw_data)
def set_text(self, text):
""" Sets the content of the F-Mem from a newcat-like string"""
self.raw_data = catdic.text_decode(text)
class GraphFunc(Data):
""" Class for a casio Graph. Function.
Behaviour of this class is different according
to the type of the function : Y,X,Param,r """
name_rule = r'^Y[1-20]|r[1-20]|f[1-20]|X[1-20]'
dType = 'graph-function'
func_types = ('Y','X','r','param')
equal_types = ('=', '>', '<', '\\<=', '\\>=')
colors = ('blue','orange','green')
def __init__(self, name='', raw_data='', date=None, ftype='Y', equal_type='=', color='blue', selected=False ):
""" Makes an empty Graph. Function """
Data.__init__(self, name, raw_data, self.dType, date)
self.set_type(ftype, equal_type)
self.set_color(color)
self.selected = selected
def copy(self):
""" Create a new GraphFunc object from this object """
return GraphFunc(self.name, self.raw_data, self.date, self.ftype, self.equal_type, self.color, self.selected)
def set_color(self, color):
if color in self.colors:
self.color = color
return True
else:
return False
def set_type(self, ftype, equal_type):
if ftype in self.func_types:
self.ftype = ftype
else:
raise errors.BadFunction(ftype, 'Bad function type. Possible types are : '+str(func_types) )
if equal_type != '=':
if ftype == 'Y':
if equal_type in self.equal_types:
self.equal_type = catdic.text_decode(equal_type, True)
else:
raise errors.BadFunction(ftype, 'Bad equality/inequality. Possibilities are '+str(self.equal_types))
else:
raise errors.BadFunction(ftype, 'It is only possible to use inequalities with "Y" functions')
def get_properties(self):
""" Returns metadata about this function (type,equal_type,color, selected)"""
return (self.ftype, catdic.text_encode(self.equal_type), self.color, self.selected)
def get_text(self):
"""Returns the content of the fucntion in an human-readable format"""
return catdic.text_encode(self.raw_data)
def set_text(self, raw_data):
"""Sets the function content from a newcat-like string"""
### FIXME + WARNING : Maybe it would be good to add some tests !! e.g. : verify there are no instructions like \Prog etc... in the text / There are no X in X function types / there are one \xf6 chjaarcter in Param. functions
self.raw_data = catdic.text_decode(raw_data, True)
class Picture(Data):
"""Class for a casio picture"""
name_rule = r'^Picture[1-6]$'
dType = 'picture'
def __init__(self, name = '', raw_data = '', date = None, pallet = None, \
color_byte = None):
"""Make an empty picture"""
Data.__init__(self, name, raw_data, self.dType, 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):
"""Create 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'^.'
dType = 'screencapture'
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)
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)
############## Classes for various types of files ###################
class FileList(Data):
"""File List management class. It can only contains Lists"""
name_rule = r'^File [1-6]$'
dType = 'filelist'
def __init__(self, number):
"""Create a filelist"""
self.data = []
self.number = number
self.name = 'File ' + str(self.number)
def __getitem__(self,index):
""" Same as self.data[index]
This function is supposed to be used
to get a list from the filelist. (eg: filelist[5],
the returns the fifth list of the filelist"""
return self.data[index-1]
def __len__(self,index):
"""Returns the number of lists in the file. Same as len(self.data)"""
return len(self.data)
def add_list(self, lis):
self.data.append(lis)
### Top-level file, able to contain any number of the above defined files
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 __len__(self):
"""Returns the len of self.data, number of items contained in the file_data"""
return len(self.data)
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}
# -*- 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
### Opened files for log :
filereceived=open('/home/fabien_kubuntu/src/casetta/casetta-work/matrix/current-received.hex','w')
filesent=open('/home/fabien_kubuntu/src/casetta/casetta-work/matrix/current-sent.hex','w')
# Default functions
def default_status(data_name, current_size, total_size, is_header = False):
"""Print the status of a transfert on the standard output."""
if total_size > 1: # Xion345 : I added this line to avoid a Zero-division error when receiving data of length 1
percent = ((current_size * 100) / total_size)
else:
percent = 100
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 wants 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=5, # or None
xonxoff=0, #0
rtscts=0) #(o:1)0
self.status = status
self.overwrite = overwrite
self.cancel = False
def check_cancel(self):
"""If the cancel flag is set, unset it and raise TransferAborted"""
if self.cancel:
self.cancel = False
raise errors.TransferAborted()
################## 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.check_cancel()
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():
self.check_cancel()
for data in data_list:
if data.__class__ == datacls.FileList:
for i in data:
self.send_data(i)
else:
self.send_data(data)
###Â Here to manage file list
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"""
self.send_header(data)
response = self.get_byte()
if response == '\x06':
# Calculator accept the header
pass
elif response == '\x24':
raise errors.DataNoManaged([data.__class__])
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:
data.change_pallet(cas.PICTURE_PALLET, ['\x00\x01\x00'] * 4)
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 or data.__class__ == datacls.List or data.__class__ == datacls.SimlEquationMatrix or data.__class__ == datacls.PolyEquationMatrix:
raw_data_list = data.get_raw_data_list()
else:
raw_data_list = [data.raw_data]
for raw_data in raw_data_list:
# CRC calcul
crc = 0
for byte in raw_data:
crc += ord(byte)
#crc = abs(255 - (crc % 256)) + 1
crc = (abs(255 - (crc % 256)) + 1) % 256
#print "old crc=", crc
#crc = 0
#for byte in raw_data:
# crc += ord(byte)
# if crc > 255:
# crc -= 256
#crc = 256 - crc
#print "new crc=", crc
# Now sending the data
filesent.write('\x3a' + raw_data)
self.send_string('\x3a' + raw_data, self.status, data.name, False)
# All data sent, let the calc check the CRC
filesent.write(chr(crc))
self.send_byte(chr(crc))
response = self.get_byte()
if response != '\x06':
raise errors.ChecksumError()
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 receive from the calc."""
file_data = datacls.FileData()
while not self.calc_is_ready_to_send():
self.check_cancel()
# Debut de la réception
while True:
data = self.receive_data()
if data.__class__ == cas.End:
break
if data.__class__ == datacls.List and data.belongs_to_file != None:
#print "Liste appartenant à un fichier " + str(data.belongs_to_file)
if data.name[-1] == '1':
#print "1ère liste détectée"
filelist = datacls.FileList(number = data.belongs_to_file)
#print filelist.__class__
file_data.data.append(filelist) ## FIXME : It would be better to use a method
#print file_data[0].__class__
for i in range(len(file_data)): ##Â FIXME : It would be better to remember index of file_list
if file_data[i].__class__== datacls.FileList and file_data[i].name == 'File '+str(data.belongs_to_file):
print file_data[i]
file_data[i].add_list(data)
else:
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
self.status('unknown', 0, 49, True)
header, data_type, sub_type = self.get_header()
print [data_type, sub_type]
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': # shorter 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 == 'viewvar':
data = datacls.ViewVariable()
elif data_type == 'matrix':
data = datacls.Matrix()
#data.dimensions=(ord(header[7]),ord(header[9])) -> Moved to the function fill_metadata in cas.py
elif data_type == 'list':
data = datacls.List()
#data.dimension=ord(header[7]) -> Moved to the function fill_metadata in cas.py
elif data_type == 'simlequation-matrix':
data = datacls.SimlEquationMatrix()
elif data_type == 'polyequation-matrix':
data = datacls.PolyEquationMatrix()
elif data_type == 'function-memory':
data = datacls.FMem()
elif data_type == 'graph-func':
data = datacls.GraphFunc()
else:
# unknown data, refuse header
self.send_byte("\x00")
raise errors.HeaderError()
cas.fill_metadata(data, header)
print data.__class__
#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")
### Transfer part
## The transfer part is executed only if data_len > 0. You can receive empty list
## when receiving a file list from the calculator.
## In this case only a header is transmitted !
## So, the read byte is not the byte to initiate the transfer
## but the byte to initiate the transfer of the next header but the first byte of the next
## header. Same thing happens when receiving all "Variables".
if data_len > 0:
if not self.get_byte() == '\x3a':
raise errors.BadAnswerFromCalc()
crc = 0
raw_data = ''
#print [data_len]
for index in range(data_len):
self.check_cancel()
byte = self.get_byte()
filereceived.write(byte)
print [byte]
crc = crc + ord(byte)
## List/Matrix management specific block
if ( data_type == 'list' or data_type == 'matrix' or data_type == 'simlequation-matrix' or data_type == 'polyequation-matrix' ) and (index-13)%14==0 and index <= data_len-14:
# Checksum test
calc_crc=self.get_byte()
filereceived.write(calc_crc)
if abs(255 - (crc % 256)) + 1 == ord(calc_crc):
self.send_byte('\x06')
crc=0
resp=self.get_byte()
filereceived.write(resp)
if resp != '\x3a':
raise errors.BadAnswerFromCalc(resp)
else:
raise errors.ChecksumError()
##Â Screencapture management specific block
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
##Â Picture management specific block
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
#self.status(data.name, index, data_len - 1, False)
self.status(data.name, index, data_len-1, False)
## Check CRC
newcrc = ord(self.get_byte())
crc = abs(255 - (crc % 256)) + 1
#print 'new crc=', newcrc, 'crc=', crc
if newcrc != crc and (data_type != 'screencapture' and \
sub_type != 'color') and data_len != 0 :
# Warning: the crc check is not done for color screencapture
# because the crc of color screencapture is never
# valid.
raise errors.ChecksumError()
#if data_type == 8 and data_sub_type == 13:
# print "coincoin (cf.perl)" / Ah ça, c'est vraiment malin !
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
# Everything is OK
self.send_byte('\x06')
return data # Returns the data even if it is empty
def get_header(self):
"""Return [header, header_type]"""
byte = self.get_byte()
if not byte == '\x3a':
#while byte !='':
#print [byte]
# byte=self.get_byte()
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
if h_crc != ord(byte) :
raise errors.ChecksumError()
print [header]
return [header, header_type, sub_type]
# -*- 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 exceptions for the serial transfer tool
#
############################################################################
#
# 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
#
############################################################################
"""Exceptions used in 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: $"
class FormatCantOpen(Exception):
"""Exception raised when a format cannot open a file (write only)"""
def __init__(self, format):
Exception.__init__(self)
self.format = format
def __str__(self):
return repr(self.format.name)
class FormatCantSave(Exception):
"""Exception raised when a format canot save a file (read only)"""
def __init__(self, format):
Exception.__init__(self)
self.format = format
def __str__(self):
return repr(self.format.name)
class ToolNotFound(Exception):
"""Exception raised when a tool cannot be used
(the open function is not found for this tool or the command is not
in the path)"""
def __init__(self, tool):
Exception.__init__(self)
self.tool = tool
def __str__(self):
return repr(self.tool)
class FormatNotFound(Exception):
"""Exception raised when a function cannot select the right format"""
pass
class DataNoManaged(Exception):
"""Exception raised when a format cannot manage some data type"""
def __init__(self, data_type_list):
Exception.__init__(self)
self.data_type_list = data_type_list
def __str__(self):
return repr(self.data_type_list)
class NoCommand(Exception):
"""Exception raised when a command is not found in the PATH"""
def __init__(self, command):
Exception.__init__(self)
self.command = command
def __str__(self):
return repr(self.command)
class ToolNotConfigured(Exception):
"""Exception raised when a mistake arrives using a transfer tool"""
pass
class CalcOutOfMemory(Exception):
"""To be raised when the calculator is out of memory during a transfer"""
pass
class ChecksumError(Exception):
"""To be raised when the calulator answer a CRC error"""
pass
class CannotOverwrite(Exception):
"""To be raised when the calculator refuse to overwrite a data"""
def __init__(self, data_name):
Exception.__init__(self)
self.data_name = data_name
def __str__(self):
return repr(self.data_name)
class BadAnswerFromCalc(Exception):
"""To be raised if an unexpected answer is sended by the calculator."""
def __init__(self, byte):
Exception.__init__(self)
self.byte = byte
def __str__(self):
return repr(chr(ord(self.byte)))
class TransferAborted(Exception):
"""To be raised when a transfer is aborted."""
pass
class HeaderError(Exception):
"""To be raised when the calculator answer an header error or send
a bad/ unknown header."""
pass
class BadMatrix(Exception):
"""To be raised when giving a bad matrix to Matrix.set_val()
(e.g.: a matrix with lines of different lengths,
a matrix containning strings
or a matrix not given under the form of a list of lists)"""
def __init__(self, value, message):
Exception.__init__(self)
self.value = value
self.message = message
def __str__(self):
return self.message +' : '+ repr(self.value)
class BadList(Exception):
"""To be raised when giving a bad list to List.set_val()
(e.g.: a list containning strings
or a list not given under the form of a list)"""
def __init__(self, value, message):
Exception.__init__(self)
self.value = value
self.message = message
def __str__(self):
return self.message +' : '+ repr(self.value)
class BadNumber(Exception):
""" To be raised when the absolute value of the
number is too high/low, especially in data.set_simple_val()"""
def __str__(self):
return "The absolute value of the number is too high/too low and not supported by the calculator"
class BadFunction(Exception):
def __init__(self, value, message):
Exception.__init__(self)
self.value = value
self.message = message
def __str__(self):
return self.message +' : '+ repr(self.value)
| Mail converted by MHonArc 2.6.19+ | http://listengine.tuxfamily.org/ |