#!/usr/bin/python
#
# arch-tag: analog and digital i/o classes
# Time-stamp: <2005-06-04 16:15:34 mike>
#
from NPCData import adc_offset, dio_offset

__version__ = '1.0'

class IOP(object):
    """
    Class to represent an I/O port on the Node Power Controller (NPC).
    The initializer expects the following keyword arguments:

        board - I/O board number (0-3)
        port  - I/O port number (0-2)
        bit   - bit number (0-7)

    Output ports require an additional argument:

        dsrc  - data source, an XML-RPC ServerProxy object
    
    """
    def __init__(self, **kwds):
        self.__dict__.update(kwds)
        if type(self.port) == type(''):
            self.port = ord(self.port.upper()) - ord('A')
        byte,self.elem = dio_offset([self.board, self.port, self.bit])

    def set(self):
        """Set the output line"""
        self.dsrc.iopSet(self.board, self.port, 1<<self.bit)

    def clear(self):
        """Clear the output line"""
        self.dsrc.iopClear(self.board, self.port, 1<<self.bit)

    def read(self, rec):
        """Read the bit state from the data record"""
        return (rec[self.elem] >> self.bit) & 1

class ADC(object):
    """
    Class to represent an A/D channel on the Node Power Controller (NPC).
    The initializer expects the following keyword arguments:

        board - I/O board number (0-3)
        chan  - channel number (0-31)
        
    Objects representing current-sensor A/D channels require an additional
    argument:

        dsrc  - data source, an XML-RPC ServerProxy object
    
    """
    def __init__(self, **kwds):
        self.__dict__.update(kwds)
        byte,self.elem = adc_offset([self.board, self.chan])
        self.mask = 1L << self.chan
        
    def read(self, rec, counts=0):
        """Read the A/D voltage from the data record"""
        c = rec[self.elem]
        if counts:
            return c
        return c*5./32768.
    
    def error(self):
        """Check the error status for the A/D channel"""
        errs = self.dsrc.getADerrors()
        try:
            return (errs[self.board] & self.mask)
        except TypeError:
            return 0

    def clear_error(self):
        """Clear the error bit for an A/D channel"""
        self.dsrc.clearADerror(self.board, self.chan)

    def set_limit(self, v):
        """Set the maximum allowed value (in volts)"""
        counts = int(v*32768./5.)
        self.dsrc.setADmax(self.board, self.chan, counts)

    def get_limit(self):
        """Return the maximum allowed value (in volts)"""
        counts = self.dsrc.getADmax(self.board, self.chan)
        return counts*5./32768.
    
    def link_sw(self, sw):
        """Link an A/D channel to an output line"""
        self.dsrc.setADswitch(self.board, self.chan,
                              sw.board, sw.port, sw.bit)
        
