Changeset 3540

Show
Ignore:
Timestamp:
04/21/10 12:14:25 (12 years ago)
Author:
jerome
Message:

API should be completely there, although partially implemented (no
handling for the terminal's keyboard for now).
TODO : define some additional constants.
TODO : check boundaries when setting values.
TODO : test with the real terminal.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • pykocard/trunk/pykocard/cartadistcrs.py

    r3539 r3540  
    2626import serial # On Debian/Ubuntu : apt-get install python-serial 
    2727 
     28# Some constants : names are mine, not Cartadis. 
     29# 
     30# Write errors 
     31NOERROR = 0 
     32ERRWRITEERROR = -1 
     33ERRNOCARD = -2 
     34ERRCARDBLOCKED = -3 
     35ERRUNKNOWNCARD = -4 
     36ERRINVALID = -5 
     37ERRMAXTRANSACTION = -6 
     38ERRVALUETOOHIGH = -7 
     39ERRGROUPNOTALLOWED = -8 
     40ERRWRITEBEFOREREAD = -9 
     41ERRREADBEFOREWRITE = -10 
     42ERRCOMPARISON = -11 
     43# 
     44# Read errors 
     45ERRREADERROR = -1 
     46# 
     47# Other errors 
     48ERRILLEGALGROUP = -1 
     49ERRNOTADMIN = -9 
     50ERRLISTFULL = -10 
     51ERRADMINNOTALLOWED = -11 
     52 
     53 
    2854class CartadisTCRS : 
    2955    """A class to manage Cartadis TCRS vending card readers. 
     
    3460    """ 
    3561    def __init__(self, device, timeout=5.0, debug=False) : 
    36         """Initializes the connection to the reader.""" 
     62        """Initializes the connection to the TCRS.""" 
    3763        self.device = device 
    3864        self.timeout = timeout 
     
    4066 
    4167        self.lastcommand = None 
    42         self.tcrsprompt = chr(13) + chr(10) + '$' # the prompt 
     68        self.sol = chr(13) + chr(10) # start of line (begins each answer) 
     69        self.sollen = len(self.sol) 
     70        self.prompt = chr(13) + chr(10) + '$' # the prompt 
     71        self.promptlen = len(self.prompt) 
    4372        self.eoc = chr(13) # end of command 
    4473 
    4574        # Each Cartadis vending card contain the following informations : 
    46         # 
    47         # the card can only be read on readers for which this group number 
    48         # was specifically allowed. 
    49         self.group = None 
    50         # the number of credits on the card. 
    51         self.value = None 
    52         # the two following fields allow the card 
    53         # to be assigned to a particular individual. 
    54         # only plastic cards can use such attributes, 
    55         # for throw-away cards, these values should both be set to 0 
    56         self.department = None 
    57         self.account = None 
    58         # transaction number. Max 3000 for plastic cards, else 500. 
    59         self.trnum = None 
    60  
    61         # opens the connection to the reader 
     75        self.cardcontent = { "group" : None, # the card can only be read on a TCRS for which this group number was specifically allowed. 
     76                             "value" : None, # the number of credits on the card. 
     77                             "department" : None, # these two fields can be used to identify the owner of the card 
     78                             "account" : None, 
     79                             "trnum" : None  # Transaction number : Max 3000 for plastic cars, else 500. 
     80                           } 
     81 
     82        # opens the connection to the TCRS 
    6283        self.tcrs = serial.Serial(device, 
    6384                                  baudrate=9600, 
     
    7091 
    7192        # cleans up any data waiting to be read 
    72         self.tcrs.flushInput() 
    73  
    74         # Identifies the terminal 
    75         self.versionNumber = self.version() 
    76         self.serialNumber = self.serial() 
    77         self.logDebug("%s terminal detected on device %s with serial number %s" \ 
    78                           % (self.versionNumber, 
    79                              self.device, 
    80                              self.serialNumber)) 
     93        try : 
     94            self.tcrs.flushInput() 
     95        except serial.serialutil.SerialException, msg : 
     96            self.logError(msg) 
     97            self.close() 
     98        else : 
     99            # Identifies the terminal 
     100            self.versionNumber = self.version() 
     101            self.serialNumber = self.serial() 
     102            self.logDebug("%s terminal detected on device %s with serial number %s" \ 
     103                              % (self.versionNumber, 
     104                                 self.device, 
     105                                 self.serialNumber)) 
     106            self.supportedCommands = self.help() 
     107            self.logDebug("Supported commands : %s" % self.supportedCommands) 
    81108 
    82109    def __del__(self) : 
     
    92119            self.logDebug("Serial link closed.") 
    93120 
     121    def logError(self, message) : 
     122        """Logs an error message.""" 
     123        sys.stderr.write("%s\n" % message) 
     124        sys.stderr.flush() 
     125 
    94126    def logDebug(self, message) : 
    95127        """Logs a debug message.""" 
    96128        if self.debug : 
    97             sys.stderr.write("%s\n" % message) 
    98             sys.stderr.flush() 
     129            self.logError(message) 
    99130 
    100131    def sendCommand(self, cmd, param=None) : 
    101         """Sends a command to the reader.""" 
    102         if param is not None : 
    103             command = "%s %s%s" % (cmd, param, self.eoc) 
    104         else : 
    105             command = "%s%s" % (cmd, self.eoc) 
    106         self.logDebug("Sending %s to reader" % repr(command)) 
    107         self.tcrs.write(command) 
    108         self.tcrs.flush() 
    109         self.lastcommand = command 
    110         # 
    111         # IMPORTANT : the following code doesn't work because currently 
    112         # PySerial doesn't allow an EOL marker to be several chars long. 
    113         # I've just sent a patch for this to PySerial's author, and we'll 
    114         # see what happens. If not accepted, I'll write it another way. 
    115         return = self.tcrs.readline(eol=self.tcrsprompt)[:-len(self.tcrsprompt)] 
     132        """Sends a command to the TCRS.""" 
     133        if self.tcrs is not None : 
     134            if param is not None : 
     135                command = "%s %s%s" % (cmd, param, self.eoc) 
     136            else : 
     137                command = "%s%s" % (cmd, self.eoc) 
     138            self.logDebug("Sending %s to TCRS" % repr(command)) 
     139            self.tcrs.write(command) 
     140            self.tcrs.flush() 
     141            self.lastcommand = command 
     142            # 
     143            # IMPORTANT : the following code doesn't work because currently 
     144            # PySerial doesn't allow an EOL marker to be several chars long. 
     145            # I've just sent a patch for this to PySerial's author, and we'll 
     146            # see what happens. If not accepted, I'll write it another way. 
     147            answer = self.tcrs.readline(eol=self.prompt) 
     148            self.logDebug("TCRS answered %s" % repr(answer)) 
     149            if answer.startswith(self.sol) and answer.endswith(self.prompt) : 
     150                return answer[self.sollen:-self.promptlen] 
     151            else : 
     152                self.logError("Unknown answer %s" % repr(answer)) 
     153                return None 
     154        else : 
     155            self.logError("Device %s is not open" % self.device) 
    116156 
    117157    def help(self) : 
    118158        """Returns the list of commands supported by the TCRS.""" 
    119         result = self.sendCommand("help") 
    120         self.logDebug("Supported commands : %s" % result) 
    121         return result 
     159        return self.sendCommand("help") 
    122160 
    123161    def version(self) : 
     
    128166        """Returns the TCRS' serial number.'""" 
    129167        return self.sendCommand("serial") 
     168 
     169    def read(self) : 
     170        """Reads the card's content to the TCRS. Returns the type of card or an error value.""" 
     171        return int(self.sendCommand("read")) 
     172 
     173    def write(self) : 
     174        """Writes the TCRS values to the card. Returns 0 or error value.""" 
     175        return int(self.sendCommand("write")) 
     176 
     177    def sensor(self) : 
     178        """Returns 0 if there's no card in TCRS, else 1, 2 or 3.""" 
     179        return int(self.sendCommand("sensor")) 
     180 
     181    def eject(self) : 
     182        """Ejects the card from the TCRS.""" 
     183        return self.sendCommand("eject") 
     184 
     185    def trnum(self) : 
     186        """Returns the number of transactions made with this card.""" 
     187        return int(self.sendCommand("trnum")) 
     188 
     189    def value(self, value=None) : 
     190        """Returns the last value read, or sets the new value of the card, but doesn't write it to the card yet.""" 
     191        if value is None : 
     192            return int(self.sendCommand("value")) 
     193        else : 
     194            return self.sendCommand("value", value) 
     195 
     196    def account(self, account) : 
     197        """Returns the last account number read, or sets the account number, but doesn't write it to the card yet.'""" 
     198        if account is None : 
     199            return int(self.sendCommand("account")) 
     200        else : 
     201            return self.sendCommand("account", account) 
     202 
     203    def department(self, department) : 
     204        """Returns the last department number read, or sets the department number, but doesn't write it to the card yet.'""" 
     205        if department is None : 
     206            return int(self.sendCommand("department")) 
     207        else : 
     208            return self.sendCommand("department", department) 
     209 
     210    def group(self, group) : 
     211        """Returns the last group number read, or sets the group number, but doesn't write it to the card yet.'""" 
     212        if group is None : 
     213            return int(self.sendCommand("group")) 
     214        else : 
     215            return self.sendCommand("group", group) 
     216 
     217    def addgrp(self, group=None) : 
     218        """Adds the group to the list of allowed ones. If no group, the one on the admin card is used.""" 
     219        return int(self.sendCommand("addgrp", group)) 
     220 
     221    def listgrp(self) : 
     222        """Returns the list of allowed group numbers.""" 
     223        return self.sendCommand("listgrp") 
     224 
     225    def delgrp(self, group) : 
     226        """Deletes the group from the list of allowed groups.""" 
     227        return int(self.sendCommand("delgrp", group)) 
     228 
     229    def cardtype(self, cardtype) : 
     230        """Returns the type of card, or sets it (not clear in the doc if a write call is needed or not).""" 
     231        return int(self.sendCommand("cardtype", cardtype)) 
     232 
     233    def display(self, text) : 
     234        """Displays a string of text on the TCRS' screen.""" 
     235        return self.sendCommand("display", text) 
     236 
     237    def echo(self, echo) : 
     238        """Changes the type of echo for the TCRS' keyboard.""" 
     239        raise NotImplementedError 
     240 
     241    def key(self, key) : 
     242        """Not really clear what it does...""" 
     243        raise NotImplementedError 
     244 
     245    def getstr(self) : 
     246        """Returns a string from keyboard or -1 if buffer is empty.""" 
     247        raise NotImplementedError 
     248 
     249    def getkey(self) : 
     250        """Returns the value of the key pressed, or -1 if no key was hit.""" 
     251        raise NotImplementedError 
     252 
     253    def prompt1(self, prompt1) : 
     254        """Changes the 'Introduce card' message.""" 
     255        raise NotImplementedError 
     256 
     257    def prompt2(self, prompt2) : 
     258        """Changes the 'Credit:' message.""" 
     259        raise NotImplementedError 
     260 
     261    def prompt3(self, prompt3) : 
     262        """Changes the text displayed after the value of the card (e.g. 'EURO').""" 
     263        raise NotImplementedError 
    130264 
    131265if __name__ == "__main__" :