Example #1
0
	def serial_login(self):
		"""
		Attempt to log into the meter over the C12.18 protocol.  Returns
		True on success, False on a failure.  This can be called by modules
		in order to login with a username and password configured within
		the framework instance.
		"""
		username = self.options['USERNAME']
		userid = self.options['USERID']
		password = self.options['PASSWORD']
		if self.options['PASSWORDHEX']:
			hex_regex = re.compile('^([0-9a-fA-F]{2})+$')
			if hex_regex.match(password) == None:
				self.print_error('Invalid characters in password')
				raise FrameworkConfigurationError('invalid characters in password')
			password = unhexlify(password)
		if len(username) > 10:
			self.print_error('Username cannot be longer than 10 characters')
			raise FrameworkConfigurationError('username cannot be longer than 10 characters')
		if not (0 <= userid <= 0xffff):
			self.print_error('User id must be between 0 and 0xffff')
			raise FrameworkConfigurationError('user id must be between 0 and 0xffff')
		if len(password) > 20:
			self.print_error('Password cannot be longer than 20 characters')
			raise FrameworkConfigurationError('password cannot be longer than 20 characters')
		
		if not self.serial_connection.start():
			return False
		if not self.serial_connection.login(username, userid, password):
			return False
		return True
Example #2
0
    def serial_connect(self):
        """
		Connect to the serial device and then verifies that the meter is
		responding.  Once the serial device is opened, this function attempts
		to retreive the contents of table #0 (GEN_CONFIG_TBL) to configure
		the endianess it will use.  Returns True on success.
		"""
        username = self.options['USERNAME']
        userid = self.options['USERID']
        if len(username) > 10:
            self.logger.error('username cannot be longer than 10 characters')
            raise FrameworkConfigurationError(
                'username cannot be longer than 10 characters')
        if not (0 <= userid <= 0xffff):
            self.logger.error('user id must be between 0 and 0xffff')
            raise FrameworkConfigurationError(
                'user id must be between 0 and 0xffff')

        self.serial_get()
        try:
            self.serial_connection.start()
            if not self.serial_connection.login(username, userid):
                self.logger.error(
                    'the meter has rejected the username and userid')
                raise FrameworkConfigurationError(
                    'the meter has rejected the username and userid')
        except C1218IOError as error:
            self.logger.error(
                'serial connection has been opened but the meter is unresponsive'
            )
            raise error

        try:
            general_config_table = self.serial_connection.get_table_data(0)
        except C1218ReadTableError as error:
            self.logger.error(
                'serial connection as been opened but the general configuration table (table #0) could not be read'
            )
            raise error

        if (ord(general_config_table[0]) & 1):
            self.logger.info(
                'setting the connection to use big-endian for C1219 data')
            self.serial_connection.c1219_endian = '>'
        else:
            self.logger.info(
                'setting the connection to use little-endian for C1219 data')
            self.serial_connection.c1219_endian = '<'

        try:
            self.serial_connection.stop()
        except C1218IOError as error:
            self.logger.error(
                'serial connection has been opened but the meter is unresponsive'
            )
            raise error

        self.__serial_connected__ = True
        self.logger.warning('the serial interface has been connected')
        return True
Example #3
0
	def __init__(self, stdout = None):
		self.modules = { }
		self.__package__ = '.'.join(self.__module__.split('.')[:-1])
		package_path = __import__(self.__package__, None, None, ['__path__']).__path__[0]	# that's some python black magic trickery for you
		if stdout == None:
			stdout = sys.stdout
		self.stdout = stdout
		
		self.directories = Namespace()
		self.directories.user_data = os.path.expanduser('~') + os.sep + '.termineter' + os.sep
		self.directories.modules_path = package_path + os.sep + 'modules' + os.sep
		self.directories.data_path = package_path + os.sep + 'data' + os.sep
		if not os.path.isdir(self.directories.data_path):
			self.logger.critical('path to data not found')
			raise FrameworkConfigurationError('path to data not found')
		if not os.path.isdir(self.directories.user_data):
			os.mkdir(self.directories.user_data)
		
		self.serial_connection = None
		self.__serial_connected__ = False
		
		# setup logging stuff
		self.logger = logging.getLogger(self.__package__ + '.' + self.__class__.__name__.lower())
		main_file_handler = logging.handlers.RotatingFileHandler(self.directories.user_data + self.__package__ + '.log', maxBytes = 262144, backupCount = 5)
		main_file_handler.setLevel(logging.DEBUG)
		main_file_handler.setFormatter(logging.Formatter("%(asctime)s %(name)-50s %(levelname)-10s %(message)s"))
		logging.getLogger('').addHandler(main_file_handler)
		
		# setup and configure options
		# Whether or not these are 'required' is really enforced by the individual
		# modules get_missing_options method and by which options they require based
		# on their respective types.  See framework/templates.py for more info.
		self.options = Options(self.directories)
		self.options.addBoolean('USECOLOR', 'enable color on the console interface', default = False)
		self.options.addString('CONNECTION', 'serial connection string')
		self.options.addString('USERNAME', 'serial username', default = '0000')
		self.options.addInteger('USERID', 'serial userid', default = 0)
		self.options.addString('PASSWORD', 'serial c12.18 password', default = '00000000000000000000')
		self.options.addBoolean('PASSWORDHEX', 'if the password is in hex', default = True)
		self.advanced_options = AdvancedOptions(self.directories)
		self.advanced_options.addInteger('BAUDRATE', 'serial connection baud rate', default = 9600)
		self.advanced_options.addInteger('BYTESIZE', 'serial connection byte size', default = serial.EIGHTBITS)
		self.advanced_options.addBoolean('CACHETBLS', 'cache certain read-only tables', default = True)
		self.advanced_options.setCallback('CACHETBLS', self.__optCallbackSetTableCachePolicy__)
		self.advanced_options.addInteger('STOPBITS', 'serial connection stop bits', default = serial.STOPBITS_ONE)
		self.advanced_options.addInteger('NBRPKTS', 'c12.18 maximum packets for reassembly', default = 2)
		self.advanced_options.addInteger('PKTSIZE', 'c12.18 maximum packet size', default = 512)
		if sys.platform.startswith('linux'):
			self.options.setOption('USECOLOR', 'True')
		
		# check and configure rfcat stuff
		self.rfcat_available = False
		try:
			import rflib
			self.logger.info('the rfcat library is available')
			self.rfcat_available = True
		except ImportError:
			self.logger.info('the rfcat library is not available, it can be found at https://code.google.com/p/rfcat/')
			pass
		if self.rfcat_available:
			# init the values to be used
			self.rfcat_connection = None
			self.__rfcat_connected__ = False
			self.is_rfcat_connected = lambda: self.__rfcat_connected__
			# self.options.addInteger('RFCATIDX', 'the rfcat device to use', default = 0)
		
		# start loading modules
		modules_path = self.directories.modules_path
		self.logger.debug('searching for modules in: ' + modules_path)
		self.current_module = None
		if not os.path.isdir(modules_path):
			self.logger.critical('path to modules not found')
			raise FrameworkConfigurationError('path to modules not found')
		for module_path in FileWalker(modules_path, absolute_path = True, skip_dirs = True):
			module_path = module_path.replace(os.path.sep, '/')
			if not module_path.endswith('.py'):
				continue
			module_path = module_path[len(modules_path):-3]
			module_name = module_path.split(os.path.sep)[-1]
			if module_name.startswith('__'):
				continue
			if module_name.lower() != module_name:
				continue
			if module_path.startswith('rfcat') and not self.rfcat_available:
				self.logger.debug('skipping module: ' + module_path + ' because rfcat is not available')
				continue
			# looks good, proceed to load
			self.logger.debug('loading module: ' + module_path)
			try:
				module_instance = self.import_module(module_path)
			except FrameworkRuntimeError:
				continue
			if not isinstance(module_instance, module_template):
				self.logger.error('module: ' + module_path + ' is not derived from the module_template class')
				continue
			# if isinstance(module_instance, rfcat_module_template) and not self.rfcat_available:
			# 	self.logger.debug('skipping module: ' + module_path + ' because rfcat is not available')
			#	continue
			if not hasattr(module_instance, 'run'):
				self.logger.critical('module: ' + module_path + ' has no run() method')
				raise FrameworkRuntimeError('module: ' + module_path + ' has no run() method')
			if not isinstance(module_instance.options, Options) or not isinstance(module_instance.advanced_options, Options):
				self.logger.critical('module: ' + module_path + ' options and advanced_options must be Options instances')
				raise FrameworkRuntimeError('options and advanced_options must be Options instances')
			module_instance.name = module_name
			module_instance.path = module_path
			self.modules[module_path] = module_instance
		self.logger.info('successfully loaded ' + str(len(self.modules)) + ' modules into the framework')
		return
Example #4
0
	def serial_connect(self):
		"""
		Connect to the serial device and then verifies that the meter is
		responding.  Once the serial device is opened, this function attempts
		to retreive the contents of table #0 (GEN_CONFIG_TBL) to configure
		the endianess it will use.  Returns True on success.
		"""
		username = self.options['USERNAME']
		userid = self.options['USERID']
		if len(username) > 10:
			self.logger.error('username cannot be longer than 10 characters')
			raise FrameworkConfigurationError('username cannot be longer than 10 characters')
		if not (0 <= userid <= 0xffff):
			self.logger.error('user id must be between 0 and 0xffff')
			raise FrameworkConfigurationError('user id must be between 0 and 0xffff')
		
		frmwk_c1218_settings = {
			'nbrpkts': self.advanced_options['NBRPKTS'],
			'pktsize': self.advanced_options['PKTSIZE']
		}
		
		frmwk_serial_settings = GetDefaultSerialSettings()
		frmwk_serial_settings['baudrate'] = self.advanced_options['BAUDRATE']
		frmwk_serial_settings['bytesize'] = self.advanced_options['BYTESIZE']
		frmwk_serial_settings['stopbits'] = self.advanced_options['STOPBITS']
		
		self.logger.info('opening serial device: ' + self.options['CONNECTION'])
		
		try:
			self.serial_connection = Connection(self.options['CONNECTION'], c1218_settings = frmwk_c1218_settings, serial_settings = frmwk_serial_settings, enable_cache = self.advanced_options['CACHETBLS'])
		except Exception as error:
			self.logger.error('could not open the serial device')
			raise error
		
		try:
			self.serial_connection.start()
			if not self.serial_connection.login(username, userid):
				self.logger.error('the meter has rejected the username and userid')
				raise FrameworkConfigurationError('the meter has rejected the username and userid')
		except C1218IOError as error:
			self.logger.error('serial connection has been opened but the meter is unresponsive')
			raise error
		
		try:
			general_config_table = self.serial_connection.get_table_data(0)
		except C1218ReadTableError as error:
			self.logger.error('serial connection as been opened but the general configuration table (table #0) could not be read')
			raise error
		
		if (ord(general_config_table[0]) & 1):
			self.logger.info('setting the connection to use big-endian for C1219 data')
			self.serial_connection.c1219_endian = '>'
		else:
			self.logger.info('setting the connection to use little-endian for C1219 data')
			self.serial_connection.c1219_endian = '<'
		
		try:
			self.serial_connection.stop()
		except C1218IOError as error:
			self.logger.error('serial connection has been opened but the meter is unresponsive')
			raise error
		
		self.__serial_connected__ = True
		self.logger.warning('the serial interface has been connected')
		return True