Beispiel #1
0
class RenkeYgcJyz12VW2(SitModbusDevice):

    # CONSTANTS

    DEFAULT_SLAVE_ADDRESS = 1
    DEFAULT_MODBUS_PORT = '/dev/ttyUSB0'
    DEFAULT_TARGET_MODE = SitModbusDevice.TARGET_MODE_RTU
    PARSER_DESCRIPTION = 'Actions with renke_ygc-jyz-12V-W2 from ali irradiance sensor' + SitConstants.DEFAULT_HELP_LICENSE_NOTICE

    # CLASS ATTRIBUTES

    _byte_order = Endian.Big
    _word_order = Endian.Big
    _substract_one_to_register_index = False
    _rtu_baudrate = 9600
    _rtu_parity = 'N'
    _rtu_timeout = 15  #seconds

    # FUNCTIONS DEFINITION
    """
		Initialize
	"""
    def __init__(self,
                 a_slave_address=DEFAULT_SLAVE_ADDRESS,
                 a_port=DEFAULT_MODBUS_PORT,
                 an_ip_address=None):
        assert self.valid_slave_address(
            a_slave_address), 'invalid a_slave_address:{}'.format(
                a_slave_address)
        try:
            self.init_arg_parse()
            assert self.valid_slave_address(
                a_slave_address
            ), 'a_slave_address parameter invalid:{}'.format(l_slave_address)
            l_slave_address = a_slave_address
            l_usb_port = self.DEFAULT_MODBUS_PORT
            if __name__ == '__main__':
                if (hasattr(self._args, 'slave_address')
                        and self._args.slave_address):
                    l_slave_address = self._args.slave_address
                if (hasattr(self._args, 'usb_port') and self._args.usb_port):
                    l_usb_port = self._args.usb_port
            super().__init__(l_slave_address,
                             self.DEFAULT_TARGET_MODE,
                             a_port=l_usb_port,
                             an_ip_address=self._args.host_ip)
            self._logger = SitLogger().new_logger(self.__class__.__name__,
                                                  self._args.host_mac)
            self._init_sit_modbus_registers(l_slave_address)

            self.invariants()
            #self._logger.debug('init->' + self.out())
        except OSError as l_e:
            self._logger.warning(
                "init-> OSError, probably rollingfileAppender" % (l_e))
            if e.errno != errno.ENOENT:
                raise l_e
        except Exception as l_e:
            print('Error in init: %s' % (l_e))
            raise l_e
            #exit(1)

    def _init_sit_modbus_registers(self, a_slave_address):
        """
			Initializes self._sit_modbus_registers
		"""
        assert self.valid_slave_address(
            a_slave_address), 'invalid a_slave_address:{}'.format(
                a_slave_address)

        l_reg_list = OrderedDict()

        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16u(
                'GHI',
                'Total irradiation on the external irradiation sensor/pyranometer (W/m2)',
                0x00,
                a_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'Int16u',
                an_is_metadata=False))
        #SitUtils.od_extend(l_reg_list, RegisterTypeInt16u('GHIDev', 'Solar radiation deviation (0~1800)', 0x52, a_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Int16u', an_is_metadata=False))
        #SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('WDigIo', 'Active power setpoint Digital I/O', 31235, l_slave_address, SitModbusRegister.ACCESS_MODE_R, '%', an_is_metadata=False, a_post_set_value_call=self.sma_fix2))

        self.append_modbus_registers(l_reg_list)

        #		self.add_cc_only_sit_modbus_registers(1)
        #		self.add_common_sit_modbus_registers(2)

        self.invariants()

    def _header_rows(self):
        return [['#Mn', 'renke'], ['#Md', 'ygc-jyz-12V-W2']]

    def sma_fix2(self, a_sit_modbus_register):
        """
		"""
        l_new_val = a_sit_modbus_register.value / 100
        self._logger.debug(
            'sma_fix2->Setting new value-> old:{} new:{}'.format(
                a_sit_modbus_register.value, l_new_val))
        a_sit_modbus_register.value = l_new_val

    def _W_event(self, a_sit_modbus_register):
        """
		Called by modbus_device.call_sit_modbus_registers_events()
		"""
        self._logger.debug('_W_event-> register:{}'.format(
            a_sit_modbus_register.out_short()))
        l_short_desc = 'W'
        l_min_val = self.MIN_W_FOR_RAISE_EVENT_GENERATION
        l_val = a_sit_modbus_register.value
        l_sit_dt = SitDateTime()

        if a_sit_modbus_register.short_description == l_short_desc:
            l_start_time = time(8, 30)
            l_end_time = time(16, 30)
            l_is_day, l_valid_time = l_sit_dt.time_is_between(
                datetime.now().time(), l_start_time, l_end_time)
            #			l_is_between_sunrise_sunset = l_sit_dt.now_is_into_sunrise_sunset_from_conf(self._sit_json_conf)
            #
            #			self._logger.debug('read_sit_modbus_register-> l_is_day:{} l_valid_time:{} l_is_between_sunrise_sunset:{}'.format(l_is_day, l_valid_time, l_is_between_sunrise_sunset))
            l_msg = '_W_event-> register_index:{} value ({}) '.format(
                a_sit_modbus_register.register_index, l_val)
            l_msg += ' between {} and {}'.format(l_start_time, l_end_time)
            self._logger.info(
                '_W_event-> DEVICE IS GENERATING {} kW'.format(l_val))
            if (l_valid_time and l_val <= l_min_val):
                l_msg = '_W_event-> register_index:{} value ({} <= {}), valid_time:{}'.format(
                    a_sit_modbus_register.register_index, l_val, l_min_val,
                    l_valid_time)
                self._logger.warning(l_msg)
                # Create Dir
                l_dir = '/tmp/solarity_events'
                if not os.path.exists(l_dir):
                    os.makedirs(l_dir)
                l_file = self.__class__.__name__ + '_event_{}_register_{}_slave_{}'.format(
                    datetime.now().strftime("%Y%m%d_%H"),
                    a_sit_modbus_register.register_index,
                    a_sit_modbus_register.slave_address)
                l_file_abs_path = l_dir + '/' + l_file
                if not os.path.exists(l_file_abs_path):
                    self._logger.info(
                        '_W_event-> Event not sent, sending email file:{}'.
                        format(l_file_abs_path))
                    # SEND MAIL
                    l_subject = 'event with failure on $(hostname) slave:' + str(
                        a_sit_modbus_register.slave_address
                    ) + ' $(date +%Y%m%d_%H%M%S) W val:(' + str(
                        l_val) + ' <= ' + str(l_min_val) + ')W '
                    l_body = [
                        'event in slave:{} with failure on $(hostname) $(date +%Y%m%d_%H%M%S) review file {}'
                        .format(
                            a_sit_modbus_register.slave_address,
                            self.csv_file_path(
                                a_sit_modbus_register.slave_address))
                    ]
                    l_body.append(' Between {} and {}'.format(
                        l_start_time, l_end_time))
                    l_body.append('Register->out:{}'.format(
                        a_sit_modbus_register.out_human_readable(
                            a_with_description=self._args.long)))
                    l_subject, l_body = self._setted_parts(l_subject, l_body)
                    SitUtils.send_mail(
                        self.events_mail_receivers(), l_subject, l_body, [
                            self.csv_file_path(
                                a_sit_modbus_register.slave_address)
                        ])

                    os.mknod(l_file_abs_path)
                else:
                    self._logger.warning(
                        '_W_event-> Event already sent, not sending email file:{}'
                        .format(l_file_abs_path))

            else:
                l_msg = '_W_event-> Event not raised register_index:{} value ({} > {}), valid_time:{}'.format(
                    a_sit_modbus_register.register_index, l_val, l_min_val,
                    l_valid_time)
                self._logger.debug(l_msg)

# ACCESS

# IMPLEMENTATION

# EXECUTE ARGS

    """
		Parsing arguments and calling corresponding functions
	"""
    def execute_corresponding_args(self):
        try:
            self.connect()
            if self._args.verbose:
                self._logger.setLevel(logging.DEBUG)
            else:
                self._logger.setLevel(logging.INFO)
            if self._args.store_values or self._args.display_all or self._args.raise_event:
                assert self.valid_slave_address(
                    self._slave_address), 'Invalid slave address {}'.format(
                        self._slave_address)
                self.read_all_sit_modbus_registers()
                if self._args.store_values:
                    self.store_values_into_csv(self._sit_modbus_registers,
                                               self._slave_address)
                if self._args.display_all:
                    print(
                        self.out_human_readable(
                            a_with_description=self._args.long))
                if self._args.raise_event:
                    assert len(self._sit_modbus_registers
                               ) > 0, 'modbus_registers_not_empty'
                    self.call_sit_modbus_registers_events()
            if self._args.test:
                self.test()


#			if self._args.manual_restart:
#				self.manual_restart()
        except Exception as l_e:
            self._logger.exception(
                "execute_corresponding_args-> Exception occured: %s" % (l_e))
            print('Error: %s' % (l_e))
            raise l_e
        finally:
            if self.is_connected():
                self.disconnect()
        self.invariants()

    def add_arg_parse_modbus_device(self):
        super().add_arg_parse()

    def add_arg_parse(self):
        """
		Override method
		"""
        self.add_arg_parse_modbus_device()
        self._parser.add_argument(
            '-e',
            '--raise_event',
            help='Raises the corresponding event if setted',
            action="store_true")
        #self._parser.add_argument('-r', '--manual_restart', help='Sends a manual restart to inverter manager', action="store_true")
        self._parser.add_argument('-c',
                                  '--slave_address',
                                  help='Slave address of modbus device',
                                  nargs='?')
        self._parser.add_argument('-p',
                                  '--usb_port',
                                  help='USB port',
                                  nargs='?')

    def add_required_named(self, a_required_named):
        pass

    def test(self):
        """
			Test function
		"""
        try:
            self._logger.info("################# BEGIN #################")
            #			l_sit_dt = SitDateTime()
            #			l_is_between_sunrise_sunset = l_sit_dt.now_is_into_sunrise_sunset_from_conf(self._sit_json_conf)
            #self.call_sit_modbus_registers_events(l_slave)
            #			self._logger.info("--> ************* device models *************: %s" % (l_d.models))	 #Lists properties to be loaded with l_d.<property>.read() and then access them
            #			self._logger.info("-->inverter ************* l_d.inverter.points *************: %s" % (l_d.inverter.points))	#Gives the inverter available properties
            #			self._logger.info("-->inverter ************* common *************: %s" % (l_d.common))
            #			self._logger.info("-->inverter ************* common Serial Number *************: %s" % (l_d.common.SN))
            #l_res = self.register_value(0x0, 1, 1)
            l_reg_index = 0
            l_slave = 1
            l_val = 1
            #l_write_res = self.write_register_value(l_reg_index, l_slave, l_val)
            l_res = self.register_values_int_16_u(0x0, 1)
            self._logger.info("Register value:{}".format(l_res))
            self._logger.info("################# END #################")
        except Exception as l_e:
            self._logger.exception("Exception occured: %s" % (l_e))
            print('Error: %s' % (l_e))
            self._logger.error('Error: %s' % (l_e))
            raise l_e

    def events_mail_receivers(self):
        return [
            '*****@*****.**',
            '*****@*****.**'
        ]
        #return ['*****@*****.**']
        #return ['*****@*****.**', '*****@*****.**']

    def invariants_modbus_device(self):
        super().invariants()

    def invariants(self):
        self.invariants_modbus_device()
class SitJsonConf(object):

    # CONSTANTS

    # VARIABLES
    _logger = None
    _config_dir = None  #Setted by init
    _config_file = None  #File name only
    _config_data = None  # Where json.dump are put

    # FUNCTIONS DEFINITION

    def __init__(
            self,
            a_config_file_name=SitConstants.SIT_JSON_DEFAULT_CONFIG_FILE_NAME):
        """
			Initialize
		"""
        self._config_dir = SitConstants.DEFAULT_CONF_DIR
        self._config_file = a_config_file_name
        if not os.path.isdir(self._config_dir):
            raise Exception(
                'init->Config dir {} does not exist, sudo mkdir -m 777 {}'.
                format(self._config_dir, self._config_dir))
        try:
            self._logger = SitLogger().new_logger(__name__)
            if __name__ == '__main__':
                self.init_arg_parse()
            self.read_config(self.configuration_file_path())
        except OSError as l_e:
            self._logger.warning(
                "init-> OSError, probably rollingfileAppender:{}".format(l_e))
            if l_e.errno != errno.ENOENT:
                raise l_e
        except Exception as l_e:
            self._logger.error('Error in init: {}'.format(l_e))
            raise l_e

    def configuration_file_path(self):
        """
		"""
        l_res = os.path.join(self._config_dir, self._config_file)
        return l_res

    def read_config(self, a_config_file_path=None):
        """
			puts into self._config_data json data
		"""
        if a_config_file_path is None:
            l_config_file_path = self.configuration_file_path()
        else:
            l_config_file_path = a_config_file_path
        with open(l_config_file_path, "r") as l_file:
            self._config_data = json.load(l_file)

    def item(self, a_key):
        """
		returns value of given key exception if not found
		"""
        l_res = None
        return l_res

# SCRIPT ARGUMENTS

    def init_arg_parse(self):
        """
			Parsing arguments
		"""
        self._logger.debug('init_arg_parse-> begin')
        self._parser = argparse.ArgumentParser(
            description='Actions with sunspec through TCP')
        self._add_arguments()
        l_args = self._parser.parse_args()
        self._args = l_args

    def _add_arguments(self):
        """
		Add arguments to parser (called by init_arg_parse())
		"""
        self._parser.add_argument('-v',
                                  '--verbose',
                                  help='increase output verbosity',
                                  action="store_true")
        self._parser.add_argument('-u',
                                  '--update_leds_status',
                                  help='Updates led status according to spec',
                                  action="store_true")
        self._parser.add_argument('-t',
                                  '--test',
                                  help='Runs test method',
                                  action="store_true")

        #self._parser.add_argument('-u', '--base_url', help='NOT_IMPLEMENTED:Gives the base URL for requests actions', nargs='?', default=self.DEFAULT_BASE_URL)
        l_required_named = self._parser.add_argument_group(
            'required named arguments')
        #		l_required_named.add_argument('-i', '--host_ip', help='Host IP', nargs='?', required=True)
        #		l_required_named.add_argument('-u', '--slave_address', help='Slave address of modbus device', nargs='?', required=True)
        l_required_named.add_argument('-m',
                                      '--host_mac',
                                      help='Host MAC',
                                      nargs='?',
                                      required=True)
#		l_required_named.add_argument('-l', '--longitude', help='Longitude coordinate (beware timezone is set to Chile)', nargs='?', required=True)
#		l_required_named.add_argument('-a', '--lattitude', help='Lattitude coordinate (beware timezone is set to Chile)', nargs='?', required=True)
#		l_required_named.add_argument('-d', '--device_type', help='Device Type:' + ('|'.join(str(l) for l in self.DEVICE_TYPES_ARRAY)), nargs='?', required=True)

    def execute_corresponding_args(self):
        """
			Parsing arguments and calling corresponding functions
		"""
        if self._args.verbose:
            self._logger.setLevel(logging.DEBUG)
        else:
            self._logger.setLevel(logging.INFO)
        if self._args.test:
            self.test()
        #if self._args.store_values:

# TEST

    def test(self):
        """
			Test function
		"""
        try:
            self._logger.info("################# BEGIN #################")
            self._logger.info(
                "--> ************* device models *************: {}".format(
                    l_d.models)
            )  #Lists properties to be loaded with l_d.<property>.read() and then access them
        except Exception as l_e:
            self._logger.exception("Exception occured:  {}".format(l_e))
            print('Error: {}'.format(l_e))
            self._logger.error('Error: {}'.format(l_e))
            sys.exit(1)
class SitModbusRegister(ABC):

    # CONSTANTS
    LOG_FILE_PATH = '/var/log/solarity'
    DEFAULT_LOGGING_LEVEL = logging.INFO  #For console overrided by --verbose
    DEFAULT_FILE_LOGGING_LEVEL = logging.DEBUG  #For file
    DEFAULT_CSV_FILE_LOCATION = '/var/solarity'  #without ending slash

    DEFAULT_ACCESS_MODE = 'R'
    ACCESS_MODE_RW = 'RW'
    ACCESS_MODE_R = 'R'

    # VARIABLES

    _logger = None

    _short_description = None
    _description = None
    _register_index = None
    _value = None
    _value_unit = None
    _access_mode = None
    _scale_factor_register_index = None
    _is_metadata = False
    _slave_address = None  # Can be none, in that case the default slave_address is taken

    _words_count = None

    _event = None
    _post_set_value_call = None

    # FUNCTIONS DEFINITION
    """
		Initialize
	"""
    def __init__(self,
                 a_short_description,
                 a_description,
                 a_register_index,
                 a_slave_address,
                 an_access_mode=DEFAULT_ACCESS_MODE,
                 a_value_unit=None,
                 a_scale_factor_register_index=None,
                 an_event=None,
                 an_is_metadata=False,
                 a_post_set_value_call=None):
        #require
        assert self.valid_access_mode(
            an_access_mode), 'invalid an_access_mode:{}'.format(an_access_mode)
        assert self.valid_register_index(
            a_register_index), 'invalid a_register_index:{}'.format(
                a_register_index)
        from sit_modbus_device import SitModbusDevice
        assert SitModbusDevice.valid_slave_address(
            a_slave_address), 'invalid a_slave_address:{}'.format(
                a_slave_address)

        self._short_description = a_short_description
        self._description = a_description
        self._register_index = a_register_index
        self._access_mode = an_access_mode
        self._value_unit = a_value_unit
        self._scale_factor_register_index = a_scale_factor_register_index
        self._event = an_event
        self._is_metadata = an_is_metadata
        self._slave_address = a_slave_address
        self._post_set_value_call = a_post_set_value_call

        self._logger = SitLogger().new_logger(self.__class__.__name__)
        self._logger.debug('init-> created SitModbusRegister:%s' %
                           (self.to_ordered_dict()))

        #ensure
        self.invariants()

    """
		Parsing arguments
	"""

    def init_arg_parse(self):
        """App help"""
        self._parser = argparse.ArgumentParser(
            description='Actions with sunspec through TCP')
        self._parser.add_argument('-v',
                                  '--verbose',
                                  help='increase output verbosity',
                                  action="store_true")
        self._parser.add_argument('-t',
                                  '--test',
                                  help='Runs test method',
                                  action="store_true")

        #self._parser.add_argument('-u', '--base_url', help='NOT_IMPLEMENTED:Gives the base URL for requests actions', nargs='?', default=self.DEFAULT_BASE_URL)
        l_required_named = self._parser.add_argument_group(
            'required named arguments')
        #		l_required_named.add_argument('-i', '--host_ip', help='Host IP', nargs='?', required=True)
        #		l_required_named.add_argument('-u', '--slave_address', help='Slave address of modbus device', nargs='?', required=True)
        #		l_required_named.add_argument('-l', '--longitude', help='Longitude coordinate (beware timezone is set to Chile)', nargs='?', required=True)
        #		l_required_named.add_argument('-a', '--lattitude', help='Lattitude coordinate (beware timezone is set to Chile)', nargs='?', required=True)
        #		l_required_named.add_argument('-d', '--device_type', help='Device Type:' + ('|'.join(str(l) for l in self.DEVICE_TYPES_ARRAY)), nargs='?', required=True)
        args = self._parser.parse_args()
        self._args = args

    """
		Parsing arguments and calling corresponding functions
	"""

    def execute_corresponding_args(self):
        self.init_arg_parse()
        if self._args.verbose:
            self._logger.setLevel(logging.DEBUG)
            self._console_handler.setLevel(logging.DEBUG)
            self._file_handler.setLevel(logging.DEBUG)
        else:
            self._logger.setLevel(logging.DEBUG)
            self._console_handler.setLevel(logging.ERROR)
            self._file_handler.setLevel(logging.DEBUG)
        if self._args.test:
            self.test()

        #if self._args.store_values:

# VALIDATION

    def valid_access_mode(self, v):
        """
		Is  access mode valid
		"""
        return v == self.ACCESS_MODE_R or v == self.ACCESS_MODE_RW

    def valid_register_index(self, v):
        """
		Value is int and > 0
		"""
        try:
            l_is_int = int(v)
            l_res = l_is_int >= 0
        except Exception as l_e:
            l_res = False

        return l_res

# GETTERS AND SETTERS

    @property
    def short_description(self):
        return self._short_description

    @property
    def description(self):
        return self._description

    @property
    def register_index(self):
        return self._register_index

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, v):
        self._value = v

    def is_access_mode_rw(self):
        return self._access_mode == self.ACCESS_MODE_RW

    @property
    def value_unit(self):
        return self._value_unit

    @property
    def scale_factor_register_index(self):
        return self._scale_factor_register_index

    @property
    def is_metadata(self):
        return self._is_metadata

    @property
    def words_count(self):
        return self._words_count

    @property
    def slave_address(self):
        return self._slave_address

# STATUS REPORT

    def has_event(self):
        return self._event is not None

    def has_post_set_value_call(self):
        return self._post_set_value_call is not None

    def has_slave_address(self):
        from sit_modbus_device import SitModbusDevice
        return SitModbusDevice.valid_slave_address(self._slave_address)

# STATUS SETTING

    @abstractmethod
    def set_value_with_raw(self, v):
        # OR raise NotImplementedError
        pass

# EVENT

    def call_event(self):
        """
			calls sit_event.call_method_to_call(self)
		"""
        assert self.has_event(), 'event not setted, check it before call'
        assert isinstance(
            self._event,
            SitModbusRegisterEvent), 'self._event is not a SitModbusRegister'

        self._logger.debug(
            'call_event-> has_event:{}, register short_out:{}'.format(
                self.has_event(), self.out_short()))
        l_optional_args = None
        self._event.modbus_register = self
        #self._event.call_method_to_call([self, a_slave_address], l_optional_args)
        self._event.call_method_to_call([self], l_optional_args)

    def call_post_set_value(self):
        """
			calls _post_set_value_call function
		"""
        assert self._post_set_value_call is not None, '_post_set_value_call is None'
        self._post_set_value_call(self)

# CONVERSION

# OUTPUT

    def to_ordered_dict(self):
        l_res = OrderedDict()

        l_res['short_description'] = self._short_description
        l_res['value'] = self._value
        l_res['value_unit'] = self._value_unit
        l_res['description'] = self._description
        l_res['register_index'] = self._register_index
        l_res['access_mode'] = self._access_mode
        l_res[
            'scale_factor_register_index'] = self._scale_factor_register_index
        l_res['has_event'] = self.has_event()
        l_res['slave_address'] = self._slave_address

        return l_res

    def out(self, a_sep='|', an_item_prefix='', a_with_description=False):
        """
			returns a string with values of to_ordered_dict()	
		"""
        l_res = ''
        for l_key, l_val in self.to_ordered_dict().items():
            l_res += an_item_prefix + l_key + ' = ' + str(l_val) + a_sep

        return l_res

    def out_short(self, a_sep='|'):
        """
			returns a string with values of to_ordered_dict()	
		"""
        l_res = ''

        l_res = l_res + 'index:' + str(self._register_index) + a_sep
        l_res = l_res + 'short:' + self._short_description + a_sep
        l_res = l_res + 'desc:' + self._description + a_sep
        l_res = l_res + 'val:' + str(self._value) + a_sep
        l_res = l_res + 'u:' + str(self._value_unit) + a_sep
        l_res = l_res + 'has_event:' + str(self.has_event()) + a_sep
        l_res = l_res + 'slave:' + str(self._slave_address)

        return l_res

    def out_human_readable(self, an_item_prefix='', a_with_description=False):
        l_res = ''
        assert self.short_description is not None
        assert self.value_unit is not None
        l_val = self.value
        if l_val is None:
            l_val = 'None'
        elif isinstance(l_val, int) or isinstance(l_val, float):
            from sit_utils import SitUtils
            l_val = SitUtils.format_number_human_readable(l_val, 2)
        if a_with_description:
            l_tmp = '({}/{}) '.format(self.register_index, self.slave_address)
            l_tmp += self.description
            l_desc = '     {:<55}'.format(l_tmp)
        else:
            l_desc = ''
        l_res += an_item_prefix + '{:<23}'.format(
            self.short_description) + '{:<5}'.format('=') + '{:<25}'.format(
                l_val) + '{:<10}'.format(self.value_unit) + l_desc

        return l_res

# TEST FUNCTION

    def test(self):
        """
			Test function
		"""
        try:
            print("################# BEGIN #################")
            self._logger.info(
                "--> ************* device models *************: %s" %
                (l_d.models)
            )  #Lists properties to be loaded with l_d.<property>.read() and then access them
            self._logger.info(
                "-->inverter ************* l_d.inverter.points *************: %s"
                % (l_d.inverter.points)
            )  #Gives the inverter available properties
            self._logger.info(
                "-->inverter ************* common *************: %s" %
                (l_d.common))
            self._logger.info(
                "-->inverter ************* common Serial Number *************: %s"
                % (l_d.common.SN))
        except Exception as l_e:
            self._logger.exception("Exception occured: %s" % (l_e))
            print('Error: %s' % (l_e))
            self._logger.error('Error: %s' % (l_e))
            sys.exit(1)


# INVARIANTS

    def invariants(self):
        assert self.valid_access_mode(
            self._access_mode), 'invalid access mode:{}'.format(
                self._access_mode)
        assert self.valid_register_index(
            self._register_index), 'invalid register index:{}'.format(
                self._register_index)
class SitDateTime(object):

    # CONSTANTS

    DAY, NIGHT = 1, 2

    # VARIABLES

    _logger = None

    # SETTERS AND GETTERS

    # FUNCTIONS DEFINITION

    def __init__(self):
        self._logger = SitLogger().new_logger(__name__)

    def local_offset_hours(self):
        """
		returns local offset in hours
		ex. Chile -3 or -4
		"""
        l_res = 0
        l_cmd = 'date +%z'
        try:
            l_code, l_stdout, l_stderr = SitUtils.system_call(l_cmd)
            l_res = int(l_stdout)
            l_res = l_res / 100
        except Exception as l_e:
            self._logger.error('localOffsetHours error'.format(l_e))
            raise l_e
        assert l_res <= -3, 'local_offset_hours->invalid_l_res:{}'.format(
            l_res)
        self._logger.debug('localOffsetHours-> cmd_res:{} res:{}'.format(
            l_stdout, l_res))

        return l_res

        return l_res

    def time_is_between(self, a_time_to_check, an_on_time, an_off_time):
        """
			https://stackoverflow.com/a/20518510/2118777

			l_on_time = datetime.time(23,30)
			l_off_time = datetime.time(4,15)
			l_timenow = datetime.datetime.now().time()
			l_current_time = datetime.datetime.now().time()

			l_is_day, l_matching = check_time(l_current_time, l_on_time, l_off_time)
			if l_matching:
				if l_is_day == NIGHT:
					print('Night Time detected.')
				elif l_is_day == DAY:
					print('Day Time detected.')
		"""
        if an_on_time > an_off_time:
            if a_time_to_check > an_on_time or a_time_to_check < an_off_time:
                return self.NIGHT, True
        elif an_on_time < an_off_time:
            if a_time_to_check > an_on_time and a_time_to_check < an_off_time:
                return self.DAY, True
        elif a_time_to_check == an_on_time:
            return None, True
        return None, False

    def time_is_into_sunrise_sunset(self,
                                    a_date_time,
                                    a_longitude,
                                    a_latitude,
                                    a_local_offset=-3,
                                    a_sunrise_interval_min=0,
                                    a_sunset_interval_min=0):
        """
			is given a_date_time into sunrise sunset range?
			return boolean
		"""
        l_ss = SunriseSunset(datetime.now(),
                             latitude=a_latitude,
                             longitude=a_longitude,
                             localOffset=a_local_offset)
        l_sunrise_time, l_sunset_time = l_ss.calculate()
        l_res = (
            l_sunrise_time + timedelta(seconds=a_sunrise_interval_min * 60) <
            datetime.now()
            and l_sunset_time - timedelta(seconds=a_sunset_interval_min * 60) >
            datetime.now())
        self._logger.debug(
            'time_is_into_sunrise_sunset-> sunrise_time:{} sunset_time:{} datetime:{} result (sun is up?):{}'
            .format(l_sunrise_time, l_sunset_time, a_date_time, l_result))

        return l_res

    def now_is_into_sunrise_sunset_from_conf(self, a_sit_json_conf):
        """
		"""
        assert isinstance(a_sit_json_conf, SitJsonConf)

        l_longitude = self._sit_json_conf.item('longitude')
        l_latitude = self._sit_json_conf.item('latitude')
        l_local_offset = self._sit_json_conf.item('local_offset')
        l_sunrise_interval_minutes = self._sit_json_conf.item(
            'sunrise_interval_minutes')
        l_sunset_interval_minutes = self._sit_json_conf.item(
            'l_sunset_interval_minutes')
        l_res = time_is_into_sunrise_sunset(datetime.now(), l_longitude,
                                            l_latitude, l_local_offset,
                                            l_sunrise_interval_minutes,
                                            l_sunset_interval_minutes)

        return l_res
class InverterManager(SitModbusDevice):

    # CONSTANTS

    DEFAULT_SLAVE_ADDRESS = 125
    DEFAULT_MODBUS_PORT = 502
    DEFAULT_TARGET_MODE = SitModbusDevice.TARGET_MODE_TCP
    MIN_W_FOR_RAISE_EVENT_GENERATION = 50
    PARSER_DESCRIPTION = 'Actions with sma inverter manager. ' + SitConstants.DEFAULT_HELP_LICENSE_NOTICE

    # CLASS ATTRIBUTES

    _byte_order = Endian.Big
    _word_order = Endian.Big
    _substract_one_to_register_index = True
    _slave_addresses_list = None

    # FUNCTIONS DEFINITION
    """
		Initialize
	"""
    def __init__(self,
                 a_slave_address=DEFAULT_SLAVE_ADDRESS,
                 a_port=DEFAULT_MODBUS_PORT,
                 an_ip_address=None):
        assert self.valid_slave_address(
            a_slave_address), 'init invalid slave address'
        try:
            self.init_arg_parse()
            l_slave_address = a_slave_address
            if (hasattr(self._args, 'slave_address')
                    and self._args.slave_address):
                self._slave_addresses_list = SitUtils.args_to_list(
                    self._args.slave_address)
            if self._slave_addresses_list is None:
                self._slave_addresses_list = [a_slave_address]

            assert self.valid_slave_address_list(
                self._slave_addresses_list
            ), 'Given script arguments are not valid, or could not be parsed:{}'.format(
                self._slave_addresses_list)
            assert self.valid_ip(
                self._args.host_ip), 'valid ip address:{}'.format(
                    self._args.host_ip)

            super().__init__(l_slave_address,
                             self.DEFAULT_TARGET_MODE,
                             a_port=self.DEFAULT_MODBUS_PORT,
                             an_ip_address=self._args.host_ip)
            self._logger = SitLogger().new_logger(self.__class__.__name__,
                                                  self._args.host_mac)

            self.invariants()
            #self._logger.debug('init->' + self.out())
        except OSError as l_e:
            self._logger.warning(
                "init-> OSError, probably rollingfileAppender" % (l_e))
            if e.errno != errno.ENOENT:
                raise l_e
        except Exception as l_e:
            print('Error in init: %s' % (l_e))
            raise l_e
            #exit(1)

    def _init_sit_modbus_registers(self, a_slave_address):
        """
			Initializes self._sit_modbus_registers
		"""
        l_reg_list = OrderedDict()
        l_slave_address = a_slave_address

        self._add_common_registers(l_reg_list, l_slave_address)

        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16u('ID',
                               'Model ID (ID): 120 = Sunspec nameplate model',
                               40238,
                               l_slave_address,
                               SitModbusRegister.ACCESS_MODE_R,
                               'uint16',
                               an_is_metadata=True))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor('AC_A',
                                          'AC Current sum of all inverters',
                                          40188,
                                          l_slave_address,
                                          SitModbusRegister.ACCESS_MODE_R,
                                          'A',
                                          a_scale_factor=40192))

        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16u(
                'VArPct_Mod',
                'Mode of the percentile reactive power limitation: 1 = in % of WMax',
                40365, l_slave_address, SitModbusRegister.ACCESS_MODE_R,
                'enum16'))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16u(
                'VArPct_Ena',
                'Control of the percentile reactive power limitation,(SMA: Qext): 1 = activated',
                40365, l_slave_address, SitModbusRegister.ACCESS_MODE_RW,
                'enum16'))

        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16u('WMaxLim_Ena',
                               'Limiting (0 Deactivate, 1 activated):', 40353,
                               l_slave_address,
                               SitModbusRegister.ACCESS_MODE_RW, 'enum16'))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor(
                'WMaxLimPct',
                'Set power to default value, in % of WMax-WMaxLimPct_SF',
                40349,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_RW,
                'uint16',
                a_scale_factor=40367))

        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor(
                'VRef', 'Voltage at the PCC (VRef), in V VRef_SF (40289)',
                40269, l_slave_address, SitModbusRegister.ACCESS_MODE_RW,
                'uint16', 40289))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor(
                'VMax',
                'Set value for maximum voltage (VMax), in V VMinMax_SF',
                40271,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'uint16',
                a_scale_factor=40291))

        self.append_modbus_registers(l_reg_list)

        self.invariants()

    def _add_common_registers(self, a_reg_list, a_slave_address):
        """
		adds to a_reg_list
		"""
        l_reg_list = a_reg_list
        l_slave_address = a_slave_address

        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeString16('Mn',
                                 'Manufacturer',
                                 40005,
                                 l_slave_address,
                                 SitModbusRegister.ACCESS_MODE_R,
                                 'String16',
                                 an_is_metadata=True))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeString16('Md',
                                 'Model (Md): SMA Inverter Manager',
                                 40021,
                                 l_slave_address,
                                 SitModbusRegister.ACCESS_MODE_R,
                                 'String16',
                                 an_is_metadata=True))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeString8('Opt',
                                'Options (Opt): Inverter Manager name',
                                40037,
                                l_slave_address,
                                SitModbusRegister.ACCESS_MODE_R,
                                'String8',
                                an_is_metadata=True))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeString8(
                'Vr',
                'Version (Vr): Version number of the installed firmware',
                40045,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'String8',
                an_is_metadata=True))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeString16(
                'SN',
                'Serial number (SN) of the device that uses the Modbus unit ID',
                40053,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'String16',
                an_is_metadata=True))

        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor(
                'PPVphAB',
                'Voltage, line conductor L1 to L2, in V V_SF (40199) : average value of all inverters',
                40193,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'V',
                a_scale_factor=40199))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor(
                'PPVphBC',
                'Voltage, line conductor L2 to L3, in V V_SF (40199) : average value of all inverters',
                40194,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'V',
                a_scale_factor=40199))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor(
                'PPVphCA',
                'Voltage, line conductor L3 to L1, in V V_SF (40199) : average value of all inverters',
                40195,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'V',
                a_scale_factor=40199))

        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor(
                'PPVphA',
                'Voltage, line conductor L1 to N (PPVphA), in V-V_SF (40199): average value of all inverters',
                40196,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'V',
                a_scale_factor=40199))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor(
                'PPVphB',
                'Voltage, line conductor L1 to N (PPVphB), in V-V_SF (40199): average value of all inverters',
                40197,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'V',
                a_scale_factor=40199))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor(
                'PPVphC',
                'Voltage, line conductor L1 to N (PPVphC), in V-V_SF (40199): average value of all inverters',
                40198,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'V',
                a_scale_factor=40199))

        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor(
                'W',
                'Active power (W), in W-W_SF (40201): sum of all inverters',
                40200,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'W',
                a_scale_factor=40192,
                an_event=SitModbusRegisterEvent(self._W_event),
                a_post_set_value_call=self.w_fix))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt32uScaleFactor(
                'WH',
                'Total yield (WH), in Wh WH_SF (40212): sum of all inverters',
                40210,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'WH',
                a_scale_factor=40212))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16uScaleFactor(
                'TmpCab',
                'Internal temperature, in °C Tmp_SF (40223): average value of all inverters',
                40219,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                '°C',
                a_scale_factor=40223))

    def _W_event(self, a_sit_modbus_register):
        """
		Called by modbus_device.call_sit_modbus_registers_events()
		"""
        self._logger.debug('_W_event-> register:{}'.format(
            a_sit_modbus_register.out_short()))
        l_short_desc = 'W'
        l_min_val = self.MIN_W_FOR_RAISE_EVENT_GENERATION
        l_val = a_sit_modbus_register.value
        l_sit_dt = SitDateTime()

        if a_sit_modbus_register.short_description == l_short_desc:
            l_start_time = time(8, 30)
            l_end_time = time(16, 30)
            l_is_day, l_valid_time = l_sit_dt.time_is_between(
                datetime.now().time(), l_start_time, l_end_time)
            #			l_is_between_sunrise_sunset = l_sit_dt.now_is_into_sunrise_sunset_from_conf(self._sit_json_conf)
            #
            #			self._logger.debug('read_sit_modbus_register-> l_is_day:{} l_valid_time:{} l_is_between_sunrise_sunset:{}'.format(l_is_day, l_valid_time, l_is_between_sunrise_sunset))
            l_msg = '_W_event-> register_index:{} value ({}) '.format(
                a_sit_modbus_register.register_index, l_val)
            l_msg += ' between {} and {}'.format(l_start_time, l_end_time)
            self._logger.info(
                '_W_event-> DEVICE IS GENERATING {} kW'.format(l_val))
            if (l_valid_time and l_val <= l_min_val):
                l_msg = '_W_event-> register_index:{} value ({} <= {}), valid_time:{}'.format(
                    a_sit_modbus_register.register_index, l_val, l_min_val,
                    l_valid_time)
                self._logger.warning(l_msg)
                # Create Dir
                l_dir = '/tmp/solarity_events'
                if not os.path.exists(l_dir):
                    os.makedirs(l_dir)
                l_file = self.__class__.__name__ + '_event_{}_register_{}_slave_{}'.format(
                    datetime.now().strftime("%Y%m%d_%H"),
                    a_sit_modbus_register.register_index,
                    a_sit_modbus_register.slave_address)
                l_file_abs_path = l_dir + '/' + l_file
                if not os.path.exists(l_file_abs_path):
                    self._logger.info(
                        '_W_event-> Event not sent, sending email file:{}'.
                        format(l_file_abs_path))
                    # SEND MAIL
                    l_subject = 'event with failure on $(hostname) ' + str(
                        a_sit_modbus_register.slave_address
                    ) + ' $(date +%Y%m%d_%H%M%S) W val:(' + str(
                        l_val) + ' <= ' + str(l_min_val) + ')kW '
                    l_body = [
                        'event in slave:{} with failure on $(hostname) $(date +%Y%m%d_%H%M%S) review file {}'
                        .format(
                            a_sit_modbus_register.slave_address,
                            self.csv_file_path(
                                a_sit_modbus_register.slave_address))
                    ]
                    l_body.append(' Between {} and {}'.format(
                        l_start_time, l_end_time))
                    l_body.append('Register->out:{}'.format(
                        a_sit_modbus_register.out_human_readable(
                            a_with_description=self._args.long)))
                    SitUtils.send_mail(
                        self.events_mail_receivers(), l_subject, l_body, [
                            self.csv_file_path(
                                a_sit_modbus_register.slave_address)
                        ])

                    os.mknod(l_file_abs_path)
                else:
                    self._logger.warning(
                        '_W_event-> Event already sent, not sending email file:{}'
                        .format(l_file_abs_path))

            else:
                l_msg = '_W_event-> Event not raised register_index:{} value ({} > {}), valid_time:{}'.format(
                    a_sit_modbus_register.register_index, l_val, l_min_val,
                    l_valid_time)
                self._logger.debug(l_msg)

    def w_fix(self, a_sit_modbus_register):
        """
		"""
        l_new_val = a_sit_modbus_register.value * 100
        self._logger.debug('w_fix->Setting new value-> old:{} new:{}'.format(
            a_sit_modbus_register.value, l_new_val))
        a_sit_modbus_register.value = l_new_val

    def manual_restart(self):
        """
		Manual restart 
		documented on p.45 of doc
		"""
        l_res = 'test_res'
        self._logger.info('manual_restart-> NOW')
        #a_register_index, a_slave_address, a_value):
        l_res = self.write_register_value(0, 201, 1)
        self._logger.info('manual_restart-> result:{}'.format(l_res))

        return l_res


# ACCESS

# IMPLEMENTATION

# EXECUTE ARGS

    """
		Parsing arguments and calling corresponding functions
	"""
    def execute_corresponding_args(self):
        try:
            self.connect()
            if self._args.verbose:
                self._logger.setLevel(logging.DEBUG)
            else:
                self._logger.setLevel(logging.INFO)
            if self._args.store_values or self._args.display_all or self._args.test or self._args.raise_event:
                assert self.valid_slave_address_list(
                    self._slave_addresses_list
                ), 'Slave addresses list is invalid:{}'.format(
                    self._slave_addresses_list)
                # FOR EACH SLAVE
                for l_slave in self._slave_addresses_list:
                    self._sit_modbus_registers = OrderedDict()
                    try:
                        self._init_sit_modbus_registers(l_slave)
                        self.read_all_sit_modbus_registers()
                        if self._args.store_values:
                            self.store_values_into_csv(
                                self._sit_modbus_registers, l_slave)
                        if self._args.display_all:
                            print(
                                self.out_human_readable(
                                    a_with_description=self._args.long))
                        if self._args.raise_event:
                            assert len(self._sit_modbus_registers
                                       ) > 0, 'modbus_registers_not_empty'
                            self.call_sit_modbus_registers_events()
                        if self._args.test:
                            self.test()
                    except ModbusException as l_e:
                        self._logger.error(
                            'Modbus error on slave {}, msg:{}'.format(
                                l_slave, l_e))
                    except Exception as l_e:
                        self._logger.error(
                            'Exception on slave {}, msg:{}'.format(
                                l_slave, l_e))
                        raise l_e
            if self._args.manual_restart:
                self.manual_restart()
        except Exception as l_e:
            self._logger.exception(
                "execute_corresponding_args-> Exception occured: %s" % (l_e))
            print('Error: %s' % (l_e))
            raise l_e
        finally:
            if self.is_connected():
                self.disconnect()
        self.invariants()

    def add_arg_parse_modbus_device(self):
        super().add_arg_parse()

    def add_arg_parse(self):
        """
		Override method
		"""
        self.add_arg_parse_modbus_device()
        self._parser.add_argument(
            '-e',
            '--raise_event',
            help='Raises the corresponding event if setted',
            action="store_true")
        self._parser.add_argument(
            '-r',
            '--manual_restart',
            help='Sends a manual restart to inverter manager',
            action="store_true")
        self._parser.add_argument('-c',
                                  '--slave_address',
                                  help='Slave address of modbus device',
                                  nargs='?')

    def add_required_named(self, a_required_named):
        pass

    def test(self):
        """
			Test function
		"""
        try:
            self._logger.info("################# BEGIN #################")
            #			l_sit_dt = SitDateTime()
            #			l_is_between_sunrise_sunset = l_sit_dt.now_is_into_sunrise_sunset_from_conf(self._sit_json_conf)
            #self.call_sit_modbus_registers_events(l_slave)
            #			self._logger.info("--> ************* device models *************: %s" % (l_d.models))	 #Lists properties to be loaded with l_d.<property>.read() and then access them
            #			self._logger.info("-->inverter ************* l_d.inverter.points *************: %s" % (l_d.inverter.points))	#Gives the inverter available properties
            #			self._logger.info("-->inverter ************* common *************: %s" % (l_d.common))
            #			self._logger.info("-->inverter ************* common Serial Number *************: %s" % (l_d.common.SN))
            self._logger.info("################# END #################")
        except Exception as l_e:
            self._logger.exception("Exception occured: %s" % (l_e))
            print('Error: %s' % (l_e))
            self._logger.error('Error: %s' % (l_e))
            raise l_e

    def events_mail_receivers(self):
        return [
            '*****@*****.**',
            '*****@*****.**'
        ]
        #return ['*****@*****.**']
        #return ['*****@*****.**', '*****@*****.**']

    def invariants_modbus_device(self):
        super().invariants()

    def invariants(self):
        self.invariants_modbus_device()
        assert isinstance(
            self._slave_addresses_list, list
        ) or self._slave_addresses_list is None, 'self._slave_address is list or None {}'.format(
            self._slave_addresses_list)
        for l_slave in self._slave_addresses_list:
            assert l_slave >= 3, 'l_slave >=3 not the case:{}'.format(l_slave)
Beispiel #6
0
class DataManagerInverter(ClusterController):

    # CONSTANTS

    DEFAULT_SLAVE_ADDRESS = 3
    MIN_W_FOR_RAISE_EVENT_GENERATION = 2000
    PARSER_DESCRIPTION = 'Actions with sma data manager inverters .  ' + SitConstants.DEFAULT_HELP_LICENSE_NOTICE

    # CLASS ATTRIBUTES

    _byte_order = Endian.Big
    _word_order = Endian.Big
    _substract_one_to_register_index = False

    _slave_addresses_list = None
    _current_read_device_class = None
    _last_read_slave_address = None
    _last_read_serial_number = None

    # INITIALIZE
    """
		Initialize
	"""
    def __init__(self,
                 a_slave_address=DEFAULT_SLAVE_ADDRESS,
                 a_port=ClusterController.DEFAULT_MODBUS_PORT,
                 an_ip_address=None):
        """
		slave_address priority to commandline arguments
		"""
        assert self.valid_slave_address(
            a_slave_address), 'init invalid slave address'
        try:
            self.init_arg_parse()
            l_slave_address = a_slave_address
            if (hasattr(self._args, 'slave_address')
                    and self._args.slave_address):
                self._slave_addresses_list = SitUtils.args_to_list(
                    self._args.slave_address)

            assert self.valid_slave_address_list(
                self._slave_addresses_list
            ), 'Given script arguments are not valid, or could not be parsed'
            assert self.valid_ip(
                self._args.host_ip), 'valid ip address:{}'.format(
                    self._args.host_ip)

            super().__init__(l_slave_address,
                             a_port=a_port,
                             an_ip_address=self._args.host_ip)
            self._logger = SitLogger().new_logger(self.__class__.__name__,
                                                  self._args.host_mac)

            self.invariants()
            #self._logger.debug('init->' + self.out())
        except OSError as l_e:
            self._logger.warning(
                "init-> OSError, probably rollingfileAppender" % (l_e))
            if e.errno != errno.ENOENT:
                raise l_e
        except Exception as l_e:
            print('Error in init: %s' % (l_e))
            raise l_e
            #exit(1)

    def _init_sit_modbus_registers(self, a_slave_address):
        """
			Initializes self._sit_modbus_registers
		"""
        self.add_common_sit_modbus_registers(a_slave_address)
        #self.add_cc_only_sit_modbus_registers(a_slave_address)

        self.invariants()

    def add_common_sit_modbus_registers(self, a_slave_address):
        """
		COMMON REGISTERS to ClusterController and Inverters
		"""
        super().add_common_sit_modbus_registers(a_slave_address)
#		l_reg_list = OrderedDict()
#
#		#PARAMETERS UNIT_ID = 2 (p.26 of doc)
#		SitUtils.od_extend(l_reg_list, RegisterTypeInt64u('Wh2', 'Total energy fed in across all line conductors, in Wh (accumulated values of the inverters) System param', 30513, SitModbusRegister.ACCESS_MODE_R, 'Wh', an_is_metadata=False, a_slave_address=2))
#
#		self.append_modbus_registers(l_reg_list)

# MODBUS READING

    def read_all_sit_modbus_registers(self):
        """
			Reads all registers and print result as debug
		"""
        self._logger.debug(
            'read_all_sit_modbus_registers-> registers to read count({}) start --------------------------------------------------'
            .format(len(self._sit_modbus_registers)))

        for l_short_desc, l_sit_reg in self._sit_modbus_registers.items():
            self.read_sit_modbus_register(l_sit_reg)
            # Setting slave address if changed
            if self._last_read_slave_address != l_sit_reg.slave_address:
                self._current_read_device_class = None
                self._last_read_serial_number = None
                self._last_read_slave_address = l_sit_reg.slave_address
            #Setting device class
            if l_sit_reg.short_description == 'DeviceClass':
                self._current_read_device_class = l_sit_reg.value
            elif l_sit_reg.short_description == 'SN':
                self._last_read_serial_number = l_sit_reg.value
            if l_sit_reg.has_post_set_value_call():
                l_sit_reg.call_post_set_value()
            #self._logger.debug('read_all_registers-> sit_register.out():%s' % (l_sit_reg.out()))
            self._logger.debug(
                'read_all_registers-> sit_register.out_short():%s' %
                (l_sit_reg.out_short()))

# EVENTS

    def _W_event(self, a_sit_modbus_register):
        """
		REDEFINE

		Called by modbus_device.call_sit_modbus_registers_events()
		"""

        if 'PV inverter' in self._current_read_device_class:
            super()._W_event(a_sit_modbus_register)

    def _setted_parts(self, a_subject, a_body):
        """
		return subject and body
		"""
        l_sub = a_subject + ' SN:{}'.format(self._last_read_serial_number)
        l_body = a_body
        return l_sub, l_body


# IMPLEMENTATION

# EXECUTE ARGS

    """
		Parsing arguments and calling corresponding functions
	"""
    def execute_corresponding_args(self):
        try:
            self.connect()
            if self._args.verbose:
                self._logger.setLevel(logging.DEBUG)
            else:
                self._logger.setLevel(logging.INFO)
            if self._args.store_values or self._args.display_all or self._args.test or self._args.raise_event:
                assert self.valid_slave_address_list(
                    self._slave_addresses_list
                ), 'Slave addresses list is invalid:{}'.format(
                    self._slave_addresses_list)
                self._logger.debug(
                    'execute_corresponding_args-> _slave_addresses_list:{}'.
                    format(self._slave_addresses_list))
                # FOR EACH SLAVE
                for l_slave in self._slave_addresses_list:
                    self._sit_modbus_registers = OrderedDict()
                    try:
                        self._init_sit_modbus_registers(l_slave)
                        self.read_all_sit_modbus_registers()
                        if self._args.store_values:
                            self.store_values_into_csv(
                                self._sit_modbus_registers, l_slave)
                        if self._args.display_all:
                            print(
                                self.out_human_readable(
                                    a_with_description=self._args.long))
                        if self._args.raise_event:
                            assert len(self._sit_modbus_registers
                                       ) > 0, 'modbus_registers_not_empty'
                            self.call_sit_modbus_registers_events()
                        if self._args.test:
                            self.test()
                    except ModbusException as l_e:
                        self._logger.error(
                            'Modbus error on slave {}, msg:{}'.format(
                                l_slave, l_e))
                    except Exception as l_e:
                        self._logger.error(
                            'Exception on slave {}, msg:{}'.format(
                                l_slave, l_e))
                        raise l_e

        except Exception as l_e:
            self._logger.exception(
                "execute_corresponding_args-> Exception occured: %s" % (l_e))
            print('Error: %s' % (l_e))
            raise l_e
        finally:
            if self.is_connected():
                self.disconnect()
        self.invariants()

    def add_arg_parse(self):
        """
		Override method
		"""
        self.add_arg_parse_modbus_device()
        self._parser.add_argument(
            '-e',
            '--raise_event',
            help='Raises the corresponding event if setted',
            action="store_true")
        self._parser.add_argument('-c',
                                  '--slave_address',
                                  help='Slave address of modbus device',
                                  nargs='?',
                                  required=True)

    def add_required_named(self, a_required_named):
        pass

    def test(self):
        """
			Test function
		"""
        try:
            self._logger.info("################# BEGIN #################")
            #			l_sit_dt = SitDateTime()
            #			l_is_between_sunrise_sunset = l_sit_dt.now_is_into_sunrise_sunset_from_conf(self._sit_json_conf)
            #self.call_sit_modbus_registers_events(l_slave)
            #			self._logger.info("--> ************* device models *************: %s" % (l_d.models))	 #Lists properties to be loaded with l_d.<property>.read() and then access them
            #			self._logger.info("-->inverter ************* l_d.inverter.points *************: %s" % (l_d.inverter.points))	#Gives the inverter available properties
            #			self._logger.info("-->inverter ************* common *************: %s" % (l_d.common))
            #			self._logger.info("-->inverter ************* common Serial Number *************: %s" % (l_d.common.SN))
            self._logger.info("################# END #################")
        except Exception as l_e:
            self._logger.exception("Exception occured: %s" % (l_e))
            print('Error: %s' % (l_e))
            self._logger.error('Error: %s' % (l_e))
            raise l_e

    def invariants(self):
        self.invariants_modbus_device()
        assert isinstance(
            self._slave_addresses_list, list
        ) or self._slave_addresses_list is None, 'self._slave_address is list or None {}'.format(
            self._slave_addresses_list)
        for l_slave in self._slave_addresses_list:
            assert l_slave >= 3, 'l_slave >=3 not the case:{}'.format(l_slave)
Beispiel #7
0
class SmartLogger1000aInverter(SitModbusDevice):

# CONSTANTS

	DEFAULT_SLAVE_ADDRESS = 1
	DEFAULT_MODBUS_PORT = 502
	DEFAULT_TARGET_MODE = SitModbusDevice.TARGET_MODE_TCP
	MIN_W_FOR_RAISE_EVENT_GENERATION = 50
	PARSER_DESCRIPTION = 'Actions with Huawei smart logger 1000a device. ' + SitConstants.DEFAULT_HELP_LICENSE_NOTICE

# CLASS ATTRIBUTES

	_byte_order = Endian.Big
	_word_order = Endian.Big
	_substract_one_to_register_index = False

	_inverter_indexes_list = None

# FUNCTIONS DEFINITION 

	"""
		Initialize
	"""
	def __init__(self, a_slave_address=DEFAULT_SLAVE_ADDRESS, a_port=DEFAULT_MODBUS_PORT, an_ip_address=None):
		assert self.valid_slave_address(a_slave_address), 'invalid a_slave_address:{}'.format(a_slave_address)
		try:
			self.init_arg_parse()
			assert self.valid_slave_address(a_slave_address), 'a_slave_address parameter invalid:{}'.format(l_slave_address)
			l_slave_address = a_slave_address
			if __name__ == '__main__':
				if (hasattr(self._args, 'slave_address') and self._args.slave_address):
					l_slave_address = self._args.slave_address
			super().__init__(l_slave_address, self.DEFAULT_TARGET_MODE, a_port=self.DEFAULT_MODBUS_PORT, an_ip_address=self._args.host_ip) 
			self._inverter_indexes_list = SitUtils.args_to_list(self._args.inverter_index)
			self._logger = SitLogger().new_logger(self.__class__.__name__, self._args.host_mac)

			self.invariants()
			#self._logger.debug('init->' + self.out())
		except OSError as l_e:
			self._logger.warning("init-> OSError, probably rollingfileAppender" % (l_e))
			if e.errno != errno.ENOENT:
				raise l_e
		except Exception as l_e:
			print('Error in init: %s' % (l_e))
			raise l_e
			#exit(1)

	def _init_sit_modbus_registers(self, a_slave_address, an_inverter_index):
		"""
			Initializes self._sit_modbus_registers
		"""
		assert self.valid_slave_address(a_slave_address), 'invalid a_slave_address:{}'.format(a_slave_address)
		assert self.valid_inverter_index (an_inverter_index), 'valid inverter index:{}'.format(an_inverter_index)
		self.add_inverter_modbus_registers(1, an_inverter_index)

		self.invariants()

	def add_inverter_modbus_registers(self, a_slave_address, an_inverter_index):
		"""
			INVERTERS see p.20 of documentation
		"""
		assert an_inverter_index >= 1, 'inverter index >= 1: {}'.format(an_inverter_index)
		l_initial_register_address = 51000
		l_base_address = l_initial_register_address + (25 * (an_inverter_index - 1))

		l_reg_list = OrderedDict()
		l_slave_address = a_slave_address

		self._logger.info('add_inverter_modbus_registers-> Base address: {}'.format(l_base_address))

		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s(SitConstants.SS_REG_SHORT_ABB_AC_POWER, 'Active power for inverter nr: {}'.format(an_inverter_index), l_base_address, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'W', an_is_metadata=False, an_event=SitModbusRegisterEvent(self._W_event)))
		SitUtils.od_extend(l_reg_list, RegisterTypeInt16u(SitConstants.SS_REG_SHORT_ABB_STATUS_OPERATING_STATE, 'Status for inverter nr: {}'.format(an_inverter_index), l_base_address + 9, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'deg celcius', an_is_metadata=False))
		SitUtils.od_extend(l_reg_list, RegisterTypeInt16s(SitConstants.SS_REG_SHORT_ABB_TEMP_CAB, 'Cabinet temperatore for inverter nr: {}'.format(an_inverter_index), l_base_address + 11, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'deg celcius', an_is_metadata=False))

		self.append_modbus_registers(l_reg_list)


	def _W_event(self, a_sit_modbus_register):
		"""
		Called by modbus_device.call_sit_modbus_registers_events()
		"""
		self._logger.debug('_W_event-> register:{}'.format(a_sit_modbus_register.out_short()))
		l_short_desc = 'W'
		l_min_val = self.MIN_W_FOR_RAISE_EVENT_GENERATION
		l_val = a_sit_modbus_register.value 
		l_sit_dt = SitDateTime()

		if a_sit_modbus_register.short_description == l_short_desc:
			l_start_time = time(8, 30)
			l_end_time = time(16, 30)
			l_is_day, l_valid_time = l_sit_dt.time_is_between(datetime.now().time(), l_start_time, l_end_time)
#			l_is_between_sunrise_sunset = l_sit_dt.now_is_into_sunrise_sunset_from_conf(self._sit_json_conf)
#
#			self._logger.debug('read_sit_modbus_register-> l_is_day:{} l_valid_time:{} l_is_between_sunrise_sunset:{}'.format(l_is_day, l_valid_time, l_is_between_sunrise_sunset))
			l_msg = '_W_event-> register_index:{} value ({}) '.format(a_sit_modbus_register.register_index, l_val)
			l_msg += ' between {} and {}'.format(l_start_time, l_end_time)
			self._logger.info('_W_event-> DEVICE IS GENERATING {} kW'.format(l_val))
			if ( l_valid_time and
					l_val <= l_min_val):
				l_msg = '_W_event-> register_index:{} value ({} <= {}), valid_time:{}'.format(a_sit_modbus_register.register_index, l_val, l_min_val, l_valid_time)
				self._logger.warning(l_msg)
				# Create Dir
				l_dir = '/tmp/solarity_events'
				if not os.path.exists(l_dir):
					os.makedirs(l_dir)
				l_file = self.__class__.__name__ + '_event_{}_register_{}_slave_{}'.format(datetime.now().strftime("%Y%m%d_%H"), a_sit_modbus_register.register_index, a_sit_modbus_register.slave_address)
				l_file_abs_path = l_dir + '/' + l_file
				if not os.path.exists(l_file_abs_path):
					self._logger.info('_W_event-> Event not sent, sending email file:{}'.format(l_file_abs_path))
					# SEND MAIL
					l_subject = 'event with failure on $(hostname) slave:' + str(a_sit_modbus_register.slave_address) + ' $(date +%Y%m%d_%H%M%S) W val:(' + str(l_val) + ' <= ' + str(l_min_val) + ')W '
					l_body = ['event in slave:{} with failure on $(hostname) $(date +%Y%m%d_%H%M%S) review file {}'.format(a_sit_modbus_register.slave_address, self.csv_file_path(a_sit_modbus_register.slave_address))]
					l_body.append(' Between {} and {}'.format(l_start_time, l_end_time))
					l_body.append('Register->out:{}'.format(a_sit_modbus_register.out_human_readable(a_with_description=self._args.long)))
					l_subject, l_body = self._setted_parts(l_subject, l_body)
					SitUtils.send_mail(self.events_mail_receivers(), l_subject, l_body, [self.csv_file_path(a_sit_modbus_register.slave_address)])

					os.mknod(l_file_abs_path)
				else:
					self._logger.warning('_W_event-> Event already sent, not sending email file:{}'.format(l_file_abs_path))

			else:
				l_msg = '_W_event-> Event not raised register_index:{} value ({} > {}), valid_time:{}'.format(a_sit_modbus_register.register_index, l_val, l_min_val, l_valid_time)
				self._logger.debug(l_msg)

	def _setted_parts(self, a_subject, a_body):
		return a_subject, a_body


# ACCESS


# IMPLEMENTATION


# EXECUTE ARGS

	"""
		Parsing arguments and calling corresponding functions
	"""
	def execute_corresponding_args(self):
		try:
			self.connect()
			if self._args.verbose:
				self._logger.setLevel(logging.DEBUG)
			else:
				self._logger.setLevel(logging.INFO)
			if self._args.store_values or self._args.display_all or self._args.test or self._args.raise_event:
				assert self.valid_slave_address(self._slave_address), 'Invalid slave address {}'.format(self._slave_address)
				for l_inverter_index in self._inverter_indexes_list:
					assert self.valid_inverter_index(l_inverter_index), 'execute_corresponding_args->valid inverter index:{}'.format(l_inverter_index)
					self._init_sit_modbus_registers(self._slave_address, l_inverter_index)
					self.read_all_sit_modbus_registers()
					if self._args.store_values:
						self.store_values_into_csv(self._sit_modbus_registers, self._slave_address)
					if self._args.display_all:
						print(self.out_human_readable(a_with_description=self._args.long))
					if self._args.raise_event:
						assert len(self._sit_modbus_registers) > 0, 'modbus_registers_not_empty'
						self.call_sit_modbus_registers_events()
					if self._args.test:
						self.test()
#			if self._args.manual_restart:
#				self.manual_restart()
		except Exception as l_e:
			self._logger.exception("execute_corresponding_args-> Exception occured: %s" % (l_e))
			print('Error: %s' % (l_e))
			raise l_e
		finally:
			if self.is_connected():
				self.disconnect()
		self.invariants()

	def add_arg_parse_modbus_device(self):
		super().add_arg_parse()

	def add_arg_parse(self):
		"""
		Override method
		"""
		self.add_arg_parse_modbus_device()
		self._parser.add_argument('-e', '--raise_event', help='Raises the corresponding event if setted', action="store_true")
		#self._parser.add_argument('-r', '--manual_restart', help='Sends a manual restart to inverter manager', action="store_true")
		self._parser.add_argument('-c', '--slave_address', help='Slave address of modbus device', nargs='?')

	def add_required_named(self, a_required_named):
		"""
		"""
		a_required_named.add_argument('-x', '--inverter_index', help='Inverter index >= 1, can be 1-n or 1,2,5', nargs='?', required=True)

	def test(self):
		"""
			Test function
		"""
		try:
			self._logger.info ("################# BEGIN #################")
#			l_sit_dt = SitDateTime()
#			l_is_between_sunrise_sunset = l_sit_dt.now_is_into_sunrise_sunset_from_conf(self._sit_json_conf)
			#self.call_sit_modbus_registers_events(l_slave)
#			self._logger.info("--> ************* device models *************: %s" % (l_d.models))	 #Lists properties to be loaded with l_d.<property>.read() and then access them
#			self._logger.info("-->inverter ************* l_d.inverter.points *************: %s" % (l_d.inverter.points))	#Gives the inverter available properties
#			self._logger.info("-->inverter ************* common *************: %s" % (l_d.common))	
#			self._logger.info("-->inverter ************* common Serial Number *************: %s" % (l_d.common.SN))	
			self._logger.info ("################# END #################")
		except Exception as l_e:
			self._logger.exception("Exception occured: %s" % (l_e))
			print('Error: %s' % (l_e))
			self._logger.error('Error: %s' % (l_e))
			raise l_e

	def events_mail_receivers(self):
		return ['*****@*****.**', '*****@*****.**']
		#return ['*****@*****.**']
		#return ['*****@*****.**', '*****@*****.**']

	def valid_inverter_index(self, an_inverter_index):
		return an_inverter_index >= 1

	def invariants_modbus_device(self):
		super().invariants()

	def invariants(self):
		self.invariants_modbus_device()
Beispiel #8
0
class ClusterController(SitModbusDevice):

# CONSTANTS

	DEFAULT_SLAVE_ADDRESS = 1
	DEFAULT_MODBUS_PORT = 502
	DEFAULT_TARGET_MODE = SitModbusDevice.TARGET_MODE_TCP
	MIN_W_FOR_RAISE_EVENT_GENERATION = 2000
	PARSER_DESCRIPTION = 'Actions with sma cluster controller device. ' + SitConstants.DEFAULT_HELP_LICENSE_NOTICE

# CLASS ATTRIBUTES

	_byte_order = Endian.Big
	_word_order = Endian.Big
	_substract_one_to_register_index = False

# FUNCTIONS DEFINITION 

	"""
		Initialize
	"""
	def __init__(self, a_slave_address=DEFAULT_SLAVE_ADDRESS, a_port=DEFAULT_MODBUS_PORT, an_ip_address=None):
		assert self.valid_slave_address(a_slave_address), 'invalid a_slave_address:{}'.format(a_slave_address)
		try:
			self.init_arg_parse()
			assert self.valid_slave_address(a_slave_address), 'a_slave_address parameter invalid:{}'.format(l_slave_address)
			l_slave_address = a_slave_address
			if __name__ == '__main__':
				if (hasattr(self._args, 'slave_address') and self._args.slave_address):
					l_slave_address = self._args.slave_address
			super().__init__(l_slave_address, self.DEFAULT_TARGET_MODE, a_port=self.DEFAULT_MODBUS_PORT, an_ip_address=self._args.host_ip) 
			self._logger = SitLogger().new_logger(self.__class__.__name__, self._args.host_mac)
			self._init_sit_modbus_registers(l_slave_address)

			self.invariants()
			#self._logger.debug('init->' + self.out())
		except OSError as l_e:
			self._logger.warning("init-> OSError, probably rollingfileAppender" % (l_e))
			if e.errno != errno.ENOENT:
				raise l_e
		except Exception as l_e:
			print('Error in init: %s' % (l_e))
			raise l_e
			#exit(1)

	def _init_sit_modbus_registers(self, a_slave_address):
		"""
			Initializes self._sit_modbus_registers
		"""
		assert self.valid_slave_address(a_slave_address), 'invalid a_slave_address:{}'.format(a_slave_address)
		self.add_common_sit_modbus_registers(1)
		self.add_cc_only_sit_modbus_registers(2)

		self.invariants()


	def add_common_sit_modbus_registers(self, a_slave_address):
		"""
		Common devices registers
		"""
		assert self.valid_slave_address(a_slave_address), 'invalid a_slave_address:{}'.format(a_slave_address)
		assert a_slave_address == 1 or a_slave_address >= 3, 'Dont ask for slave_address 2, the add_cc_only_sit_modbus_registers is done for that! addr:{}'.format(a_slave_address)

		l_reg_list = OrderedDict()
		l_slave_address = a_slave_address

		# CLUSTER AND INVERTERS
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('Vr', 'Version number of the SMA Modbus profile', 30001, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=True))
		
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('ID', 'SUSy ID (of the Cluster Controller)', 30003, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=True))
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('SN', 'Serial number (of the Cluster Controller)', 30005, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=True))
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('NewData', 'Modbus data change: meter value is increased by the Cluster Controller if new data is available.', 30007, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=False))
		SitUtils.od_extend(l_reg_list, RegisterTypeSmaCCDeviceClass('DeviceClass', 'Device Class', 30051, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Enum', an_is_metadata=True))

		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('W', 'Current active power on all line conductors (W), accumulated values of the inverters', 30775, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'W', an_is_metadata=False, an_event=SitModbusRegisterEvent(self._W_event)))
		SitUtils.od_extend(l_reg_list, RegisterTypeInt64u('Wh', 'Total energy fed in across all line conductors, in Wh (accumulated values of the inverters) System param', 30513, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Wh', an_is_metadata=False))
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('VAr', 'Reactive power on all line conductors (var), accumulated values of the inverters', 30805, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'VAr', an_is_metadata=False))
		SitUtils.od_extend(l_reg_list, RegisterTypeInt64u('TotWhDay', 'Energy fed in on current day across all line conductors, in Wh (accumulated values of the inverters)', 30517, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Wh', an_is_metadata=False))

		self.append_modbus_registers(l_reg_list)

	def add_cc_only_sit_modbus_registers(self, a_slave_address):
		"""
		Registers particular to cluster controller
		"""
		assert self.valid_slave_address(a_slave_address), 'invalid a_slave_address:{}'.format(a_slave_address)
		assert a_slave_address == 2, 'add_cc_only_sit_modbus_registers->for this part slave_address should be =2 and is:{}'.format(a_slave_address)
		
		l_reg_list = OrderedDict()
		l_slave_address = a_slave_address

		#PARAMETERS UNIT_ID = 2 (p.26 of doc)
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('WDigIo', 'Active power setpoint Digital I/O', 31235, l_slave_address, SitModbusRegister.ACCESS_MODE_R, '%', an_is_metadata=False, a_post_set_value_call=self.sma_fix2))
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('WAnalog', 'Active power setpoint Analog', 31237, l_slave_address, SitModbusRegister.ACCESS_MODE_R, '%', an_is_metadata=False, a_post_set_value_call=self.sma_fix2))
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('WSetPoint', 'Active power setpoint in %s', 31239, l_slave_address, SitModbusRegister.ACCESS_MODE_R, '%', an_is_metadata=False, a_post_set_value_call=self.sma_fix2))
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('WSetPointDirMar', 'Active power setpoint in %s Specification Modbus Direct marketing', 31241, l_slave_address, SitModbusRegister.ACCESS_MODE_R, '%', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('ResSetPoint', 'Resulting setpoint (minimum value definition of all specifications)', 31243, l_slave_address, SitModbusRegister.ACCESS_MODE_R, '%', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 
		#Strange
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('WExport', 'Current utility grid export active power P in W (actual value of the active power fed in at the grid-connection point; measured with an external measuring device).', 31249, l_slave_address, SitModbusRegister.ACCESS_MODE_R, '%', an_is_metadata=False)) 

		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('VArExport', 'Current utility grid export reactive power Q in VAr (actual value of the reactive power fed in at the grid- connection point; measured with an external measuring device).', 31251, l_slave_address, SitModbusRegister.ACCESS_MODE_R, '%', an_is_metadata=False)) 

		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('AC_1', 'Analog current input 1 (mA)', 34637, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'mA', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('AC_2', 'Analog current input 2 (mA)', 34639, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'mA', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('AC_3', 'Analog current input 3 (mA)', 34641, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'mA', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('AC_4', 'Analog current input 4 (mA)', 34643, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'mA', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 

		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('InDCV_1', 'Analog voltage input 1 (V)', 34645, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'V', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('InDCV_2', 'Analog voltage input 2 (V)', 34647, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'V', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('InDCV_3', 'Analog voltage input 3 (V)', 34649, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'V', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('InDCV_4', 'Analog voltage input 4 (V)', 34651, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'V', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 
		SitUtils.od_extend(l_reg_list, RegisterTypeInt16s('WSetPointDirTotal', 'Direct marketer: Active power setpoint P, in % of the maximum active power (PMAX) of the PV plant. -100-0=Load|0=No active power|0-100 generator', 40493, l_slave_address, SitModbusRegister.ACCESS_MODE_R, '%', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('WSetPointMan', 'Active power setpoint (manual specification)', 41167, l_slave_address, SitModbusRegister.ACCESS_MODE_R, '%', an_is_metadata=False, a_post_set_value_call=self.sma_fix2)) 
		# IRRADIATIONS
		# not working on sanbe, getting max_int, SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('IrradiationSurfaceTot', 'Total irradiation on the sensor surface (W/m2)', 34613, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'W/m2', an_is_metadata=False))
		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('GHI', 'Total irradiation on the external irradiation sensor/pyranometer (W/m2)', 34623, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'W/m2', an_is_metadata=False))

		self.append_modbus_registers(l_reg_list)


	def sma_fix2(self, a_sit_modbus_register):
		"""
		"""
		l_new_val = a_sit_modbus_register.value / 100
		self._logger.debug('sma_fix2->Setting new value-> old:{} new:{}'.format(a_sit_modbus_register.value, l_new_val))
		a_sit_modbus_register.value = l_new_val

	def _W_event(self, a_sit_modbus_register):
		"""
		Called by modbus_device.call_sit_modbus_registers_events()
		"""
		self._logger.debug('_W_event-> register:{}'.format(a_sit_modbus_register.out_short()))
		l_short_desc = 'W'
		l_min_val = self.MIN_W_FOR_RAISE_EVENT_GENERATION
		l_val = a_sit_modbus_register.value 
		l_sit_dt = SitDateTime()

		if a_sit_modbus_register.short_description == l_short_desc:
			l_start_time = time(8, 30)
			l_end_time = time(16, 30)
			l_is_day, l_valid_time = l_sit_dt.time_is_between(datetime.now().time(), l_start_time, l_end_time)
#			l_is_between_sunrise_sunset = l_sit_dt.now_is_into_sunrise_sunset_from_conf(self._sit_json_conf)
#
#			self._logger.debug('read_sit_modbus_register-> l_is_day:{} l_valid_time:{} l_is_between_sunrise_sunset:{}'.format(l_is_day, l_valid_time, l_is_between_sunrise_sunset))
			l_msg = '_W_event-> register_index:{} value ({}) '.format(a_sit_modbus_register.register_index, l_val)
			l_msg += ' between {} and {}'.format(l_start_time, l_end_time)
			self._logger.info('_W_event-> DEVICE IS GENERATING {} kW'.format(l_val))
			if ( l_valid_time and
					l_val <= l_min_val):
				l_msg = '_W_event-> register_index:{} value ({} <= {}), valid_time:{}'.format(a_sit_modbus_register.register_index, l_val, l_min_val, l_valid_time)
				self._logger.warning(l_msg)
				# Create Dir
				l_dir = '/tmp/solarity_events'
				if not os.path.exists(l_dir):
					os.makedirs(l_dir)
				l_file = self.__class__.__name__ + '_event_{}_register_{}_slave_{}'.format(datetime.now().strftime("%Y%m%d_%H"), a_sit_modbus_register.register_index, a_sit_modbus_register.slave_address)
				l_file_abs_path = l_dir + '/' + l_file
				if not os.path.exists(l_file_abs_path):
					self._logger.info('_W_event-> Event not sent, sending email file:{}'.format(l_file_abs_path))
					# SEND MAIL
					l_subject = 'event with failure on $(hostname) slave:' + str(a_sit_modbus_register.slave_address) + ' $(date +%Y%m%d_%H%M%S) W val:(' + str(l_val) + ' <= ' + str(l_min_val) + ')W '
					l_body = ['event in slave:{} with failure on $(hostname) $(date +%Y%m%d_%H%M%S) review file {}'.format(a_sit_modbus_register.slave_address, self.csv_file_path(a_sit_modbus_register.slave_address))]
					l_body.append(' Between {} and {}'.format(l_start_time, l_end_time))
					l_body.append('Register->out:{}'.format(a_sit_modbus_register.out_human_readable(a_with_description=self._args.long)))
					l_subject, l_body = self._setted_parts(l_subject, l_body)
					SitUtils.send_mail(self.events_mail_receivers(), l_subject, l_body, [self.csv_file_path(a_sit_modbus_register.slave_address)])

					os.mknod(l_file_abs_path)
				else:
					self._logger.warning('_W_event-> Event already sent, not sending email file:{}'.format(l_file_abs_path))

			else:
				l_msg = '_W_event-> Event not raised register_index:{} value ({} > {}), valid_time:{}'.format(a_sit_modbus_register.register_index, l_val, l_min_val, l_valid_time)
				self._logger.debug(l_msg)

	def _setted_parts(self, a_subject, a_body):
		return a_subject, a_body

	def manual_restart(self):
		"""
		Manual restart 
		documented on p.45 of doc
		"""
		l_res = 'test_res'
		self._logger.info('manual_restart-> NOW')
		#a_register_index, a_slave_address, a_value):
		l_res = self.write_register_value(0, 201, 1)
		self._logger.info('manual_restart-> result:{}'.format(l_res))

		return l_res

	def read_all_sit_modbus_registers(self): 
		"""
		Read inverters data
		"""
		super().read_all_sit_modbus_registers()

#		l_reg_index = 42109
#		l_slave_address = 3
#		self.read_inverter_data(l_slave_address)
	
	def read_inverter_data(self, a_slave_address):
		"""
		Was for test but not working
		"""
		assert False, 'deprecated'
		l_reg = RegisterTypeInt64u('Wh2', 'Total energy fed in across all line conductors, in Wh (accumulated values of the inverters) System param', 30513, SitModbusRegister.ACCESS_MODE_R, 'Wh', an_is_metadata=False, a_slave_address=a_slave_address)
		self.read_inverter_data_register(l_reg, a_slave_address)
		print (l_reg.out_human_readable(a_with_description=True))

#		l_reg = RegisterTypeInt32u('SN', 'Serial Number', a_reg_index + 1, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=True)
#		self.read_inverter_data_register(l_reg, a_slave_address)
#
#		l_reg = RegisterTypeInt16u('UnitID', 'Unit ID', a_reg_index + 3, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=True)
#		self.read_inverter_data_register(l_reg, a_slave_address)


	def read_inverter_data_register(self, a_register, a_slave_address):
		"""
		Reads given inverter data
		Was for test but not working
		"""
		assert False, 'deprecated'
		try:
			self.read_sit_modbus_register(a_register, a_slave_address)
			if self._args.store_values:
				pass
	#			self.store_values_into_csv([l_reg], l_slave)
			if self._args.display_all:
				print('***************** INVERTER slave:{} ******************'.format(a_slave_address))
				print(a_register.out_human_readable(a_with_description=self._args.long))
		except ModbusException as l_e:
			self._logger.error('read_inverter_data-> error reading register {}'.format(l_e))
		except Exception as l_e:
			raise l_e

# ACCESS


# IMPLEMENTATION


# EXECUTE ARGS

	"""
		Parsing arguments and calling corresponding functions
	"""
	def execute_corresponding_args(self):
		try:
			self.connect()
			if self._args.verbose:
				self._logger.setLevel(logging.DEBUG)
			else:
				self._logger.setLevel(logging.INFO)
			if self._args.store_values or self._args.display_all or self._args.test or self._args.raise_event:
				assert self.valid_slave_address(self._slave_address), 'Invalid slave address {}'.format(self._slave_address)
				self.read_all_sit_modbus_registers()
				if self._args.store_values:
					self.store_values_into_csv(self._sit_modbus_registers, self._slave_address)
				if self._args.display_all:
					print(self.out_human_readable(a_with_description=self._args.long))
				if self._args.raise_event:
					assert len(self._sit_modbus_registers) > 0, 'modbus_registers_not_empty'
					self.call_sit_modbus_registers_events()
				if self._args.test:
					self.test()
#			if self._args.manual_restart:
#				self.manual_restart()
		except Exception as l_e:
			self._logger.exception("execute_corresponding_args-> Exception occured: %s" % (l_e))
			print('Error: %s' % (l_e))
			raise l_e
		finally:
			if self.is_connected():
				self.disconnect()
		self.invariants()

	def add_arg_parse_modbus_device(self):
		super().add_arg_parse()

	def add_arg_parse(self):
		"""
		Override method
		"""
		self.add_arg_parse_modbus_device()
		self._parser.add_argument('-e', '--raise_event', help='Raises the corresponding event if setted', action="store_true")
		#self._parser.add_argument('-r', '--manual_restart', help='Sends a manual restart to inverter manager', action="store_true")
		self._parser.add_argument('-c', '--slave_address', help='Slave address of modbus device', nargs='?')

	def add_required_named(self, a_required_named):
		pass

	def test(self):
		"""
			Test function
		"""
		try:
			self._logger.info ("################# BEGIN #################")
#			l_sit_dt = SitDateTime()
#			l_is_between_sunrise_sunset = l_sit_dt.now_is_into_sunrise_sunset_from_conf(self._sit_json_conf)
			#self.call_sit_modbus_registers_events(l_slave)
#			self._logger.info("--> ************* device models *************: %s" % (l_d.models))	 #Lists properties to be loaded with l_d.<property>.read() and then access them
#			self._logger.info("-->inverter ************* l_d.inverter.points *************: %s" % (l_d.inverter.points))	#Gives the inverter available properties
#			self._logger.info("-->inverter ************* common *************: %s" % (l_d.common))	
#			self._logger.info("-->inverter ************* common Serial Number *************: %s" % (l_d.common.SN))	
			self._logger.info ("################# END #################")
		except Exception as l_e:
			self._logger.exception("Exception occured: %s" % (l_e))
			print('Error: %s' % (l_e))
			self._logger.error('Error: %s' % (l_e))
			raise l_e

	def events_mail_receivers(self):
		return ['*****@*****.**', '*****@*****.**']
		#return ['*****@*****.**']
		#return ['*****@*****.**', '*****@*****.**']

	def invariants_modbus_device(self):
		super().invariants()

	def invariants(self):
		self.invariants_modbus_device()
class SitModbusDevice(object):

    # CONSTANTS
    DEFAULT_LOGGING_LEVEL = logging.DEBUG  #For console overrided by --verbose
    DEFAULT_FILE_LOGGING_LEVEL = logging.DEBUG  #For file
    DEFAULT_BASE_URL = "http://localhost:9999/"  # with ending slash
    DEFAULT_PORT = "/dev/ttyUSB0"  #https://unix.stackexchange.com/a/144735/47775 to get it
    TARGET_MODE_TCP = 'tcp'
    TARGET_MODE_RTU = 'rtu'
    DEFAULT_TARGET_MODE = TARGET_MODE_TCP  # or rtu
    DEFAULT_TARGET_PORT = 502
    MAX_CONNECT_RETRIES_COUNT = 3
    MAX_MODBUS_REGISTER_RETRIES_COUNT = 3

    LOG_FILE_PATH = '/var/log/solarity'
    DEFAULT_CSV_FILE_LOCATION = '/var/solarity'  #without ending slash
    PARSER_DESCRIPTION = 'Actions with modbus device. ' + SitConstants.DEFAULT_HELP_LICENSE_NOTICE

    # VARIABLES
    _logger = None
    _args = None
    _console_handler = None
    _file_handler = None

    _sit_json_conf = None

    _modbus_client = None
    _is_connected = False
    _base_url = DEFAULT_BASE_URL

    _target_ip = None
    _target_port = DEFAULT_TARGET_PORT
    _target_mode = DEFAULT_TARGET_MODE
    _slave_address = None
    _client_connect_retries = 3
    _rtu_timeout = 1  #seconds
    _rtu_stopbits = 1
    _rtu_bytesize = 8
    _rtu_parity = 'E'
    _rtu_baudrate = 19200  #9600
    _byte_order = Endian.Big
    _word_order = Endian.Big
    _substract_one_to_register_index = False

    _sit_modbus_registers = OrderedDict()  # OrderedDict

    # FUNCTIONS DEFINITION

    def __init__(self,
                 a_slave_address,
                 a_target_mode=DEFAULT_TARGET_MODE,
                 a_port=DEFAULT_PORT,
                 an_ip_address=None):
        """
		Initialize
		@param a_slave_address
		@param a_target_mode
		@param a_port
		@param an_ip_address
		"""
        assert self.valid_slave_address(
            a_slave_address), 'invalid a_slave_address:{}'.format(
                a_slave_address)

        self._target_port = a_port
        self._slave_address = a_slave_address
        self._target_ip = an_ip_address

        self._logger = SitLogger().new_logger(self.__class__.__name__)
        self._sit_json_conf = SitJsonConf(__name__)
        self._target_mode = a_target_mode

        if self._target_mode == self.TARGET_MODE_TCP:
            assert self.valid_ip(self._target_ip), 'valid ip address'
        self.invariants()

    def process_script_arguments(self):
        """
		reads script arguments and executes corresponding argument
		"""
        try:
            self.init_arg_parse()
            self.connect()
            self.execute_corresponding_args()
        except Exception as l_e:
            self._logger.exception('process_script_arguments->Exception:%s' %
                                   (l_e))
            if self.is_connected():
                self.disconnect()

    def connect(self):
        """
		sets self._modbus_client and connects to it
		"""
        if self._target_mode == self.TARGET_MODE_TCP:
            assert self.valid_ip(self._target_ip), 'Target ip is None'
        assert not self.is_connected()

        l_retries_count = 0
        self._logger.debug(
            'connect-> with args target_mode:{} port:{} rtu_timeout:{} baudrate:{}'
            .format(self._target_mode, self._target_port, self._rtu_timeout,
                    self._rtu_baudrate))
        while l_retries_count < self.MAX_CONNECT_RETRIES_COUNT and not self._is_connected:
            try:
                if self._target_mode == self.TARGET_MODE_RTU:
                    assert os.geteuid() == 0, 'user must be root for RTU mode'
                    # DOC: https://github.com/riptideio/pymodbus/blob/8ef32997ee1da1cd465f2e19ff3b54b93d38728c/pymodbus/repl/main.py
                    self._modbus_client = ModbusSerialClient(
                        method=self._target_mode,
                        port=str(self._target_port),
                        timeout=self._rtu_timeout,
                        stopbits=self._rtu_stopbits,
                        bytesize=self._rtu_bytesize,
                        parity=self._rtu_parity,
                        baudrate=self._rtu_baudrate)
                    self._modbus_client.debug_enabled = True
                    self._logger.debug(
                        'connect->target:{} port:{} timeout:{} stopbit:{} bytesize:{} parity:{} baudrate:{}'
                        .format(self._target_mode, str(self._target_port),
                                self._rtu_timeout, self._rtu_stopbits,
                                self._rtu_bytesize, self._rtu_parity,
                                self._rtu_baudrate))
                    self._logger.info('connect->RTU Client Mode:{}'.format(
                        self._target_mode))
                else:
                    assert self._target_mode == self.TARGET_MODE_TCP
                    self._modbus_client = ModbusTcpClient(
                        self._target_ip,
                        port=str(self._target_port),
                        retries=self._client_connect_retries,
                        retry_on_empty=True)
                    self._logger.info('connect->TCP Client Mode:{}'.format(
                        self._target_mode))

                #Connect to the serial modbus server
                connection = self._modbus_client.connect()
                if self._modbus_client.connect:
                    #self._logger.debug("Client is connected")
                    self._is_connected = True
                    self._logger.info('connect -> Connection success')
                else:
                    self._is_connected = False
                    raise ConnectionException(
                        "connect->Could not connect to _modbus_client")
            except ConnectionException as l_e:
                l_retries_count += 1
                self._logger.exception(
                    "connect->ConnectionException occured during connection, retrying:{}"
                    .format(l_e))
                time.sleep(1)
            except Exception as l_e:
                self._logger.exception(
                    "connect->Exception occured during connection:{}".format(
                        l_e))
                raise l_e

# HIGH LEVEL FUNCTIONS

    def _header_rows(self):
        #return [['#Mn', 'some_manufacturer'], ['#Md', 'some_model']]
        return []

    def add_modbus_register_from_values(
            self,
            a_short_description,
            a_description,
            a_register_index,
            a_register_type,
            a_slave_address,
            an_access_mode=SitModbusRegister.ACCESS_MODE_R,
            a_value_unit=None,
            a_scale_factor_register_index=None,
            an_event=None,
            an_is_metadata=False):
        """ 
		adds to self._sit_modbus_registers
		"""
        assert not a_short_description in self._sit_modbus_registers.keys(
        ), 'Already has key ' + a_short_description
        self._sit_modbus_registers[a_short_description] = SitModbusRegister(
            a_short_description,
            a_description,
            a_register_index,
            a_register_type,
            an_access_mode,
            a_value_unit,
            a_scale_factor_register_index=a_scale_factor_register_index,
            an_event=an_event,
            an_is_metadata=an_is_metadata)

    def add_modbus_register(self, a_modbus_register):
        assert isinstance(
            a_modbus_register,
            SitModbusRegister), 'arg is not a SitModbusRegister but {}'.format(
                a_modbus_register.__class__.__name__)
        assert a_modbus_register.has_slave_address(
        ), 'a_modbus_register has no slave_address'
        self._sit_modbus_registers[
            a_modbus_register.short_description] = a_modbus_register

    def append_modbus_registers(self, an_ordered_dict):
        """
		add_modbus_register with each item of given an_ordered_dict
		"""
        assert isinstance(an_ordered_dict,
                          OrderedDict), 'param is not an OrderedDict'
        for l_short_desc, l_reg in an_ordered_dict.items():
            self.add_modbus_register(l_reg)

    def read_register_from_short_description(self, a_short_description,
                                             a_slave_address):
        """
		returns a read register
		@param: a_short_description
		"""
        l_reg = self._sit_modbus_registers[a_short_description]
        self.read_sit_modbus_register(l_reg, a_slave_address)

    def read_sit_modbus_register(self, a_sit_modbus_register):
        """
		setting value of given modbus register
		@a_sit_modbus_register
		"""
        assert isinstance(
            a_sit_modbus_register,
            SitModbusRegister), 'given argument must be a SitModbusRegister'
        assert self.is_connected(), 'Not connected'

        l_val = self.register_value(a_sit_modbus_register.register_index,
                                    a_sit_modbus_register.words_count,
                                    a_sit_modbus_register.slave_address)

        a_sit_modbus_register.set_value_with_raw(l_val)

        self.set_value_with_scale_factor(a_sit_modbus_register)

    def set_value_with_scale_factor(self, a_sit_modbus_register):
        """
		set a_sit_modbus_register value with read scale_factor read from scale_factor_register_index
		"""
        if a_sit_modbus_register.scale_factor_register_index is not None:
            l_scale_factor = self.register_values_int_16_s(
                a_sit_modbus_register.scale_factor_register_index,
                a_sit_modbus_register.slave_address)
            l_val = a_sit_modbus_register.value
            l_val = l_val * 10**l_scale_factor
            self._logger.debug(
                'set_value_with_scale_factor-> old_val:%s scale_factor:%s new_val:%s'
                % (a_sit_modbus_register.value, l_scale_factor, l_val))
            a_sit_modbus_register.value = l_val
#		else:
#			self._logger.debug('read_sit_modbus_register-> scale_factor_index should be None:%s' % (a_sit_modbus_register.scale_factor_register_index))

    def read_all_sit_modbus_registers(self):
        """
			Reads all registers and print result as debug
		"""
        self._logger.debug(
            'read_all_sit_modbus_registers-> registers to read count({}) start --------------------------------------------------'
            .format(len(self._sit_modbus_registers)))

        for l_short_desc, l_sit_reg in self._sit_modbus_registers.items():
            self.read_sit_modbus_register(l_sit_reg)
            if l_sit_reg.has_post_set_value_call():
                l_sit_reg.call_post_set_value()
            #self._logger.debug('read_all_registers-> sit_register.out():%s' % (l_sit_reg.out()))
            self._logger.debug(
                'read_all_registers-> sit_register.out_short():%s' %
                (l_sit_reg.out_short()))

# LOW LEVEL FUNCTIONS READ

    def register_value(self, a_register_index, a_register_length,
                       a_slave_address):
        """
		Returns a given register value
		@a_register_length: 1 register is 16 bits (2 bytes = 1 word)
		"""
        assert self.is_connected(), 'register_value->device is not connected'
        assert isinstance(a_register_index,
                          int), 'register_value->Slave address is not an int'
        assert self.valid_slave_address(
            a_slave_address
        ), 'register_value->Slave address is not valid:' + str(a_slave_address)

        #self._logger.debug('register_value-> _substract_one_to_register_index:%s' % (self._substract_one_to_register_index))
        if self._substract_one_to_register_index:
            l_register_index = a_register_index - 1
            l_register_index_s_debug = str(a_register_index) + '-1'
        else:
            l_register_index = a_register_index
            l_register_index_s_debug = str(l_register_index)
        l_retries_count = 0
        while l_retries_count < self.MAX_MODBUS_REGISTER_RETRIES_COUNT:
            try:
                #Starting add, num of reg to read, slave unit.
                self._logger.debug(
                    'register_value-> index:{} length:{} unit:{} _substract_one_to_register_index:{}'
                    .format(l_register_index, a_register_length,
                            a_slave_address,
                            self._substract_one_to_register_index))
                l_result = self._modbus_client.read_holding_registers(
                    l_register_index, a_register_length,
                    unit=a_slave_address)  # Average current
                if l_result is not None:
                    if (hasattr(l_result, 'function_code')
                            and l_result.function_code < 0xFFFFFFFF):
                        self._logger.debug(
                            "register_value-> read register index:%s (%s) length:%s slave_address:%s"
                            % (l_register_index, l_register_index_s_debug,
                               a_register_length, a_slave_address))
                        #self._logger.debug(l_result)
                        #self._logger.debug("register_value->register 0 value:%s" % l_result.getRegister(1))
                        #self._logger.debug("register_value-> 0 type:%s" % type(l_result.getRegister(0)))
                        #self._logger.debug(l_result._str_())
                    else:
                        self._logger.error(
                            "register_value-> returned code is invalid: {}".
                            format(l_result))
                else:
                    l_msg = "register_value-> No register received, l_result is None"
                    self._logger.error(l_msg)
                    raise ModbusException(l_msg)

                if not hasattr(l_result, 'registers'):
                    l_msg = 'register_value-> read register has no registers attribute, slave:{} reading register:{} length:{}'.format(
                        a_slave_address, l_register_index, a_register_length)
                    self._logger.error(l_msg)
                    raise ModbusException(l_msg)

                return l_result
            except KeyboardInterrupt:
                self._logger.exception(
                    "register_value-> Keyboard interruption")
            except ModbusException as l_e:
                l_retries_count += 1
                if l_retries_count >= self.MAX_MODBUS_REGISTER_RETRIES_COUNT:
                    self._logger.error(
                        'register_value-> error with ModbusException not retrying but raising'
                    )
                    raise l_e
                else:
                    self._logger.error(
                        'register_value-> error with ModbusException retrying {}'
                        .format(l_retries_count))
                    self.disconnect
                    time.sleep(0.2)
                    self.connect
            except Exception as l_e:
                self._logger.exception(
                    "register_value-> Exception occured, msg:%s" % l_e)
                raise l_e

    def _int_from_register(self, a_register, a_start_index, a_bits_count):
        """
		"""
        l_tmp_binary = "{0:b}".format(a_register)
        l_tmp_binary = l_tmp_binary.zfill(16)
        l_end_index = len(l_tmp_binary) - a_start_index
        l_start_index = len(l_tmp_binary) - a_start_index - a_bits_count
        l_tmp = l_tmp_binary[
            l_start_index:l_end_index +
            1]  # second is where ends exclusive, first is inclusive
        l_result = int(l_tmp, 2)
        self._logger.debug(
            "************************************* _int_from_register-> register(%s) binary (%s) %s:%s => %s result(%s)"
            % (a_register, l_tmp_binary, l_start_index, l_end_index, l_tmp,
               l_result))
        return l_result

    def register_values_date_time(self, a_register_index, a_slave_address):
        """
		from spec returns a string formated 
		@a_register_index: a register index
		"""
        assert False, 'Deprecated'
        try:
            l_register_res = self.register_value_invalid_int(
                a_register_index, 4, a_slave_address)
            self._logger.debug(
                "******* BEGIN ****************************** register_values_date_time->a_register_index:%s"
                % (a_register_index))
            l_year = self._int_from_register(l_register_res.registers[0], 0,
                                             6) + 2000
            l_day = self._int_from_register(l_register_res.registers[1], 0, 4)
            l_month = self._int_from_register(l_register_res.registers[1], 8,
                                              4)
            l_min = self._int_from_register(l_register_res.registers[2], 0, 5)
            l_hour = self._int_from_register(l_register_res.registers[2], 8, 5)
            l_sec = int(int(l_register_res.registers[3]) / 1000)

            l_result = str(l_year) + '-' + str(l_month).zfill(2) + '-' + str(
                l_day).zfill(2) + 'T' + str(l_hour).zfill(2) + ':' + str(
                    l_min).zfill(2) + ':' + str(l_sec).zfill(
                        2) + 'Z'  #Definitive result as expecte
        except Exception as l_e:
            self._logger.exception("register_values_date_time exception: %s" %
                                   l_e)
            raise l_e

        l_result = (';'.join(str(l) for l in l_register_res.registers)
                    )  # only decimals
        self._logger.debug(
            "******** END ***************************** register_values_date_time->registers:%s- result:%s-"
            % (l_register_res.registers, l_result))
        return l_result

    def register_values_pm5500_date_time(self, a_register_index,
                                         a_slave_address):
        """
		from spec returns a string formated with datetime
		@a_register_index: a register index start index, the followings will be read
		"""
        l_i = 0
        l_register_res = self.register_value_invalid_int(
            a_register_index + l_i, 1, a_slave_address)
        l_year = l_register_res.registers[0]
        l_i = l_i + 1
        l_register_res = self.register_value_invalid_int(
            a_register_index + l_i, 1, a_slave_address)
        l_month = l_register_res.registers[0]
        l_i = l_i + 1
        l_register_res = self.register_value_invalid_int(
            a_register_index + l_i, 1, a_slave_address)
        l_day = l_register_res.registers[0]
        l_i = l_i + 1
        l_register_res = self.register_value_invalid_int(
            a_register_index + l_i, 1, a_slave_address)
        l_hour = l_register_res.registers[0]
        l_i = l_i + 1
        l_register_res = self.register_value_invalid_int(
            a_register_index + l_i, 1, a_slave_address)
        l_min = l_register_res.registers[0]
        l_i = l_i + 1
        l_register_res = self.register_value_invalid_int(
            a_register_index + l_i, 1, a_slave_address)
        l_sec = l_register_res.registers[0]
        l_i = l_i + 1

        l_result = str(l_year) + str(l_month).zfill(2) + str(l_day).zfill(
            2) + 'T' + str(l_hour).zfill(2) + str(l_min).zfill(2) + str(
                l_sec).zfill(2) + 'Z'

        self._logger.debug("register_values_pm5500_date_time->result:%s" %
                           l_result)
        return l_result

    def register_values_float_32(self, a_register_index, _slave_address):
        """
		from spec returns a float from given register index
		@a_register_index: a register index
		"""
        l_register_res = self.register_value_invalid_int(
            a_register_index, 2, a_slave_address)
        #self._logger.debug("register_values_u_long->before decoder:%s" % l_register_res.registers)
        decoder = BinaryPayloadDecoder.fromRegisters(
            l_register_res.registers,
            byteorder=self._byte_order,
            wordorder=self._word_order
        )  #https://pymodbus.readthedocs.io/en/latest/source/example/modbus_payload.html
        #https://pymodbus.readthedocs.io/en/v1.3.2/library/payload.html?highlight=binarypayloaddecoder#pymodbus.payload.BinaryPayloadDecoder
        l_result = decoder.decode_32bit_float()
        if not instance(l_result, float):
            self._logger.error(
                "register_values_float_32-> result of decode_32bit_float is not a float but:'%s'"
                % l_result)
            l_result = 0
        self._logger.debug("register_values_float_32->after decoder:%s" %
                           l_result)
        return l_result

    def register_values_float_32_pf(self, a_register_index, _slave_address):
        """
		from spec returns a float from given register index for power factor
		@a_register_index: a register index
			********* FROM DOC ***********************
			Power factor values are specially encoded floating point values.


			Pseudo code to decode PF Value
			if (rigVal > 1)
			{
			 PF_Val = 2 - regVal;
			  PF is leading
			  }
			  else if (regVal < -1)
			  {
			   PF_Val = -2-regVal
				PF is leading
				}
				else if ( abs(regVal) equals 1 )
				{
				 PF_Val = regVal
				  PF is at unity
				  }
				  else
				  {
				   PF_Val = regVal
					PF is lagging
					}

		"""
        l_register_res = self.register_value(a_register_index, 2,
                                             a_slave_address)
        #self._logger.debug("register_values_u_long->before decoder:%s" % l_register_res.registers)
        decoder = BinaryPayloadDecoder.fromRegisters(
            l_register_res.registers,
            byteorder=Endian.Big,
            wordorder=Endian.Big
        )  #https://pymodbus.readthedocs.io/en/latest/source/example/modbus_payload.html
        #https://pymodbus.readthedocs.io/en/v1.3.2/library/payload.html?highlight=binarypayloaddecoder#pymodbus.payload.BinaryPayloadDecoder
        l_result = decoder.decode_32bit_float()
        if l_result > 1:
            l_result = 2 - l_result
        elif l_result < -1:
            l_result = -2 - l_result
        else:
            pass
        self._logger.debug("register_values_float_32_pf->after decoder:%s" %
                           l_result)
        return l_result

    def register_values_string_8(self, a_register_index, _slave_address):
        """
		from spec returns a string
		@a_register_index: a register index
		"""
        l_register_res = self.register_value(a_register_index, 8,
                                             a_slave_address)
        #self._logger.debug("register_values_u_long->before decoder:%s" % l_register_res.registers)
        decoder = BinaryPayloadDecoder.fromRegisters(
            l_register_res.registers,
            byteorder=Endian.Big,
            wordorder=Endian.Big
        )  #https://pymodbus.readthedocs.io/en/latest/source/example/modbus_payload.html
        #https://pymodbus.readthedocs.io/en/v1.3.2/library/payload.html?highlight=binarypayloaddecoder#pymodbus.payload.BinaryPayloadDecoder
        l_result = decoder.decode_string(8)
        if len(l_result) > 0 and str(l_result[0]) == '0':
            l_result = ''
        else:
            l_result = l_result.decode('utf-8', errors='replace')
        #self._logger.debug("register_values_string16->after decoder:%s" % l_result)

        assert isinstance(
            l_result, str), 'result is no str but' + l_result.__class.__name__
        return l_result

    def register_values_string_16(self, a_register_index, a_slave_address):
        """
		from spec returns a word from given register index
		@a_register_index: a register index
		"""
        l_register_res = self.register_value(a_register_index, 16,
                                             a_slave_address)
        #self._logger.debug("register_values_u_long->before decoder:%s" % l_register_res.registers)
        decoder = BinaryPayloadDecoder.fromRegisters(
            l_register_res.registers,
            byteorder=Endian.Big,
            wordorder=Endian.Big
        )  #https://pymodbus.readthedocs.io/en/latest/source/example/modbus_payload.html
        #https://pymodbus.readthedocs.io/en/v1.3.2/library/payload.html?highlight=binarypayloaddecoder#pymodbus.payload.BinaryPayloadDecoder
        l_result = decoder.decode_string(16)
        if len(l_result) > 0 and str(l_result[0]) == '0':
            l_result = ''
        else:
            l_result = l_result.decode('utf-8', errors='replace')
        #self._logger.debug("register_values_string16->after decoder:%s" % l_result)

        assert isinstance(
            l_result, str), 'result is no str but' + l_result.__class.__name__
        return l_result

    def register_values_int_16_s(self, a_register_index, a_slave_address):
        """
		from spec returns a word from given register index
		@a_register_index: a register index
		"""
        l_register_res = self.register_value(a_register_index, 1,
                                             a_slave_address)
        #self._logger.debug("register_values_16_s->before decoder:%s" % l_register_res.registers)
        decoder = BinaryPayloadDecoder.fromRegisters(
            l_register_res.registers,
            byteorder=Endian.Big,
            wordorder=Endian.Big
        )  #https://pymodbus.readthedocs.io/en/latest/source/example/modbus_payload.html
        #https://pymodbus.readthedocs.io/en/v1.3.2/library/payload.html?highlight=binarypayloaddecoder#pymodbus.payload.BinaryPayloadDecoder
        l_result = decoder.decode_16bit_int()
        #self._logger.debug("register_values_u_word->after decoder:%s" % l_result)
        assert isinstance(
            l_result, int), 'result is no int but' + l_result.__class.__name__
        return l_result

    def register_values_int_16_u(self, a_register_index, a_slave_address):
        """
		from spec returns an int
		@a_register_index: a register index
		"""
        l_register_res = self.register_value(a_register_index, 1,
                                             a_slave_address)
        #self._logger.debug("register_values_int_16_u->before decoder:%s" % l_register_res.registers)
        decoder = BinaryPayloadDecoder.fromRegisters(
            l_register_res.registers,
            byteorder=Endian.Big,
            wordorder=Endian.Big
        )  #https://pymodbus.readthedocs.io/en/latest/source/example/modbus_payload.html
        #https://pymodbus.readthedocs.io/en/v1.3.2/library/payload.html?highlight=binarypayloaddecoder#pymodbus.payload.BinaryPayloadDecoder
        l_result = decoder.decode_16bit_uint()
        #self._logger.debug("register_values_int16_u->after decoder:%s" % l_result)
        return l_result

    def register_values_int_32_u(self, a_register_index, a_slave_address):
        """
		from spec returns a long from given register index
		@a_register_index: a register index
		"""
        l_register_res = self.register_value(a_register_index, 2,
                                             a_slave_address)
        #self._logger.debug("register_values_u_long->before decoder:%s" % l_register_res.registers)
        decoder = BinaryPayloadDecoder.fromRegisters(
            l_register_res.registers,
            byteorder=Endian.Big,
            wordorder=Endian.Big
        )  #https://pymodbus.readthedocs.io/en/latest/source/example/modbus_payload.html
        #https://pymodbus.readthedocs.io/en/v1.3.2/library/payload.html?highlight=binarypayloaddecoder#pymodbus.payload.BinaryPayloadDecoder
        l_result = decoder.decode_32bit_uint()
        #self._logger.debug("register_values_int_32_u->after decoder:%s" % l_result)
        return l_result

    def register_values_int_32_s(self, a_register_index, a_slave_address):
        """
		from spec returns a long from given register index
		@a_register_index: a register index
		"""
        l_register_res = self.register_value(a_register_index, 2,
                                             a_slave_address)
        #self._logger.debug("register_values_u_long->before decoder:%s" % l_register_res.registers)
        decoder = BinaryPayloadDecoder.fromRegisters(
            l_register_res.registers,
            byteorder=Endian.Big,
            wordorder=Endian.Big
        )  #https://pymodbus.readthedocs.io/en/latest/source/example/modbus_payload.html
        #https://pymodbus.readthedocs.io/en/v1.3.2/library/payload.html?highlight=binarypayloaddecoder#pymodbus.payload.BinaryPayloadDecoder
        l_result = decoder.decode_32bit_int()
        #self._logger.debug("register_values_int_32_u->after decoder:%s" % l_result)
        return l_result

    def register_values_int_64_u(self, a_register_index, a_slave_address):
        """
		from spec returns a long from given register index
		@a_register_index: a register index
		"""
        l_register_res = self.register_value(a_register_index, 4,
                                             a_slave_address)
        #self._logger.debug("register_values_u_long->before decoder:%s" % l_register_res.registers)
        decoder = BinaryPayloadDecoder.fromRegisters(
            l_register_res.registers,
            byteorder=Endian.Big,
            wordorder=Endian.Big
        )  #https://pymodbus.readthedocs.io/en/latest/source/example/modbus_payload.html
        #https://pymodbus.readthedocs.io/en/v1.3.2/library/payload.html?highlight=binarypayloaddecoder#pymodbus.payload.BinaryPayloadDecoder
        l_result = decoder.decode_64bit_uint()
        #self._logger.debug("register_values_int_32_u->after decoder:%s" % l_result)
        return l_result

    def register_values_int_64_s(self, a_register_index, a_slave_address):
        """
		from spec returns a long from given register index
		@a_register_index: a register index
		"""
        l_register_res = self.register_value(a_register_index, 4,
                                             a_slave_address)
        #self._logger.debug("register_values_u_long->before decoder:%s" % l_register_res.registers)
        decoder = BinaryPayloadDecoder.fromRegisters(
            l_register_res.registers,
            byteorder=Endian.Big,
            wordorder=Endian.Big
        )  #https://pymodbus.readthedocs.io/en/latest/source/example/modbus_payload.html
        #https://pymodbus.readthedocs.io/en/v1.3.2/library/payload.html?highlight=binarypayloaddecoder#pymodbus.payload.BinaryPayloadDecoder
        l_result = decoder.decode_64bit_int()
        #self._logger.debug("register_values_int_32_u->after decoder:%s" % l_result)
        return l_result

# FILE OUTPUT

    def store_values_into_csv(self, an_ordered_dict, a_slave_address):
        """
		Stores values into CSV  into self.get_csv_file_path()
		@param a_row_dict: an OrderedDict
		"""
        assert isinstance(an_ordered_dict,
                          OrderedDict), 'param is not an OrderedDict'
        assert isinstance(a_slave_address, int), 'param is not an int'
        try:
            l_f_name = self.csv_file_path(a_slave_address)
            l_file_exists = os.path.isfile(l_f_name)
            self._logger.info(
                "store_values_into_csv->Writting into file %s exists:%s" %
                (l_f_name, l_file_exists))
            with open(l_f_name, mode='a+') as l_csv_file:
                l_csv_writter = csv.writer(l_csv_file,
                                           delimiter=',',
                                           quotechar='"',
                                           quoting=csv.QUOTE_MINIMAL)
                if not l_file_exists:
                    for l_header_row in self._header_rows():
                        self._logger.info(
                            "store_values_into_csv->writting header:{}".format(
                                l_header_row))
                        l_csv_writter.writerow(l_header_row)
                # Metadata and registers
                l_header_list = []
                l_values_dict = []
                l_values_dict.append(
                    datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'))
                for l_reg in an_ordered_dict.values():
                    if l_reg.is_metadata:
                        l_header_line = []
                        l_header_line.append('#' + l_reg.short_description)
                        l_header_line.append(l_reg.value)
                        if not l_file_exists:
                            self._logger.info(
                                "store_values_into_csv->Writting METADATA row: %s"
                                % (';'.join(str(l) for l in l_header_line)))
                            l_csv_writter.writerow(l_header_line)
                    else:
                        l_header_list.append(l_reg.short_description)
                        l_val = l_reg.value
                        l_values_dict.append(l_val)
                # Header
                l_header_list.insert(0, 'Timestamp')

                assert len(l_header_list) == len(
                    l_values_dict
                ), 'header row {} doesnt have the same length as data row{}, hit file {}'.format(
                    len(l_header_list), len(l_values_dict), l_f_name)

                if not l_file_exists:
                    self._logger.info(
                        "store_values_into_csv->Writting HEADER row: %s" %
                        (';'.join(str(l) for l in l_header_list)))
                    l_csv_writter.writerow(l_header_list)
                #Registers no metadata
                self._logger.info(
                    "store_values_into_csv->HEADER (not written) row: %s" %
                    ('|'.join(str(l) for l in l_header_list)))
                self._logger.info(
                    "store_values_into_csv->Writting row: %s" %
                    ('|'.join(str(l.value) for l in an_ordered_dict.values())))
                l_csv_writter.writerow(l_values_dict)
        except Exception as l_e:
            self._logger.error('store_values_into_csv->Error: %s' % l_e)
            raise l_e

    def csv_file_path(self, a_slave_address):
        """
		Returns the csv file path
			if test adds tests.csv at the end of file_name
		"""
        assert self._target_ip, "host ip is empty"
        assert isinstance(a_slave_address, int), "slave_address is not an int"
        assert self._args.host_mac, "host mac is empty"
        #		if __debug__:
        #			try:
        #				socket.inet_aton(self._args.host_ip)
        #			except socket.error as l_e:
        #				assert False, "Host ip address is invalid"
        #				raise l_e

        l_dir = self.DEFAULT_CSV_FILE_LOCATION + '/' + str(
            datetime.today().year) + '/' + '{:02d}'.format(
                datetime.today().month)
        l_result = (l_dir + '/' + datetime.today().strftime('%Y%m%d') + '_' +
                    self._args.host_mac.replace(':', '-') + '_' +
                    self._target_ip + '_' + str(a_slave_address) + '_' +
                    os.path.basename(__file__) + '_' +
                    self.__class__.__name__ + '.csv')
        if self._args.test:
            l_result += 'test.csv'

        try:
            os.makedirs(l_dir)
        except OSError as l_e:
            if l_e.errno == errno.EEXIST:
                pass
            else:
                self._logger.error('get_csv_file_path Error: %s' % (l_e))
                raise l_e

        return l_result

# LOW LEVEL FUNCTIONS READ

    def write_register_value(self, a_register_index, a_slave_address, a_value):
        """
		Returns a given register value
		@a_register_index
		@a_register_length: 1 register is 16 bits (2 bytes = 1 word)
		@a_slave_address
		@a_value
		"""
        assert self.is_connected(), 'register_value->device is not connected'
        assert self.valid_slave_address(
            self._slave_address), 'register_value->Slave address is None'

        #self._logger.debug('register_value-> _substract_one_to_register_index:%s' % (self._substract_one_to_register_index))
        if self._substract_one_to_register_index:
            l_register_index = a_register_index - 1
            l_register_index_s_debug = str(a_register_index) + '-1'
        else:
            l_register_index = a_register_index
            l_register_index_s_debug = str(l_register_index)
        try:
            #Starting add, num of reg to read, slave unit.
            l_result = self._modbus_client.write_register(
                l_register_index, a_value,
                unit=a_slave_address)  # Average current
            if l_result is not None:
                if (hasattr(l_result, 'function_code')
                        and l_result.function_code < 0xFFFFFFFF):
                    self._logger.debug(
                        "register_value-> read register index:%s (%s) value:(%s) length:%s slave_address:%s"
                        %
                        (l_register_index, l_register_index_s_debug, '|'.join(
                            str(l_elt) for l_elt in l_result.registers),
                         a_register_length, self._slave_address))
                    #self._logger.debug(l_result)
                    #self._logger.debug("register_value->register 0 value:%s" % l_result.getRegister(1))
                    #self._logger.debug("register_value-> 0 type:%s" % type(l_result.getRegister(0)))
                    #self._logger.debug(l_result._str_())
                else:
                    self._logger.error(
                        "register_value-> returned code is invalid: {}".format(
                            l_result))
            else:
                self._logger.error(
                    "register_value-> No register received, l_result is None")

            if not hasattr(l_result, 'registers'):
                raise Exception(
                    'register_value-> read register has no registers attribute, reading register:'
                    + str(l_register_index))  # TODO CORRECTME HERE

            return l_result
        except KeyboardInterrupt:
            self._logger.exception("register_value-> Keyboard interruption")
        except Exception as l_e:
            self._logger.exception(
                "register_value-> Exception occured, msg:%s" % l_e)
            raise l_e

# CONNECTION

    def is_connected(self):
        """
		Self explaining
		"""
        #self._logger.debug("is_connected-> %s, modbusclient:%s" % (self._is_connected, self._modbus_client))
        return self._modbus_client is not None and self._is_connected

    def disconnect(self):
        """
		Disconnects modbus client
		"""
        assert self.is_connected()
        assert self._modbus_client is not None

        try:
            self._modbus_client.close()
            self._is_connected = False
            self._logger.info('disconnect -> Disconnection success')
        except Exception as l_e:
            self._logger.exception("disconnect->Exception occured msg:" +
                                   l_e.message)
            raise l_e

    def init_arg_parse(self):
        """
		Parsing arguments
		override add_arg_parse if necessary
		"""
        """App help"""
        self._parser = argparse.ArgumentParser(
            description=self.PARSER_DESCRIPTION)
        self.add_arg_parse()
        l_args = self._parser.parse_args()
        self._args = l_args

    def add_arg_parse(self):
        """
		Adding arguments to parser
		"""
        # OPTIONALS
        self._parser.add_argument('-v',
                                  '--verbose',
                                  help='increase output verbosity',
                                  action="store_true")
        self._parser.add_argument(
            '-u',
            '--base_url',
            help='NOT_IMPLEMENTED:Gives the base URL for requests actions',
            nargs='?',
            default=self.DEFAULT_BASE_URL)
        self._parser.add_argument(
            '-s',
            '--store_values',
            help='Stores values into csv file located into ' +
            self.DEFAULT_CSV_FILE_LOCATION,
            action="store_true")
        self._parser.add_argument('-t',
                                  '--test',
                                  help='Runs test method',
                                  action="store_true")
        self._parser.add_argument(
            '-a',
            '--display_all',
            help='Displays all register after reading them',
            action='store_true')
        self._parser.add_argument(
            '-d',
            '--long',
            help=
            'Displays all register after reading them, with long version and description',
            action='store_true')

        # REQUIRED
        l_required_named = self._parser.add_argument_group(
            'required named arguments')
        l_required_named.add_argument('-i',
                                      '--host_ip',
                                      help='Host IP',
                                      nargs='?',
                                      required=True)
        l_required_named.add_argument('-m',
                                      '--host_mac',
                                      help='Host MAC',
                                      nargs='?',
                                      required=True)
        self.add_required_named(l_required_named)

    def add_required_named(self, a_required_named):
        a_required_named.add_argument('-c',
                                      '--slave_address',
                                      help='Slave address of modbus device',
                                      nargs='?',
                                      required=True)

    def execute_corresponding_args(self):
        """
		Calls the corresponding function to given script argument
		"""
        assert isinstance(self._slave_address,
                          list), 'self._slave_address is list'

        if self._args.verbose:
            self._logger.setLevel(logging.DEBUG)
#			self._console_handler.setLevel(logging.DEBUG)
#			self._file_handler.setLevel(logging.DEBUG)
        else:
            self._logger.setLevel(logging.DEBUG)
#			self._console_handler.setLevel(logging.ERROR)
#			self._file_handler.setLevel(logging.DEBUG)
        if self._args.base_url:
            self._logger.debug(
                "execute_corresponding_args->given argument was '%s'" %
                self._args.base_url)
            self._base_url = self._args.base_url
        if self._args.display_all or self._args.store_values or self._args.test:
            for l_slave in self._slave_address:
                assert self.valid_slave_address(
                    l_slave
                ), 'given slave address should be int but' + str(l_slave)
                self.read_all_sit_modbus_registers(l_slave)
                if self._args.display_all():
                    l_long = False
                    if hasattr(self._args, 'long'):
                        l_long = self._args.long
                    print(self.out_human_readable(l_long))
                if self._args.store_values:
                    self.store_values_into_csv(self._sit_modbus_registers,
                                               l_slave)
                if self._args.test:
                    self.test()

# EVENTS

    def call_sit_modbus_registers_events(self):
        l_index = 0
        for l_short_desc, l_sit_reg in self._sit_modbus_registers.items():
            if l_sit_reg.has_event():
                self._logger.debug(
                    'call_sit_modbus_registers_events-> Calling event for register_short:{}'
                    .format(l_sit_reg.out_short()))
                self._logger.debug(
                    'call_sit_modbus_registers_events-> counter:{}/{}'.format(
                        l_index, len(self._sit_modbus_registers.items())))
                l_sit_reg.call_event()
            #self._logger.debug('read_all_registers-> sit_register.out():%s' % (l_sit_reg.out()))
            l_index += 1

# VALIDATION

    @staticmethod
    def valid_slave_address_list(a_list):
        """ List """
        assert isinstance(a_list, list), 'a_list is not a list'

        l_res = False
        if isinstance(a_list, list):
            for l_elt in a_list:
                l_res = SitModbusDevice.valid_slave_address(l_elt)
                if not l_res:
                    break

        return l_res

    @staticmethod
    def valid_slave_address(an_int):
        l_res = False
        l_res = isinstance(an_int, int)
        if l_res:
            l_res = (an_int is not None) and an_int > 0 and an_int <= 253

        return l_res

    def valid_ip(self, v):
        if v is None:
            return False
        else:
            try:
                socket.inet_aton(v)
                # legal
                return True
            except socket.error:
                # Not legal
                return False

# OUTPUT

    def out_without_registers(self, a_sep='\n', an_item_prefix=''):
        l_res = ''

        l_res = l_res + an_item_prefix + 'port:' + str(
            self._target_port) + a_sep
        l_res = l_res + an_item_prefix + 'target_ip:' + self._target_ip + a_sep
        l_res = l_res + an_item_prefix + 'target_mode:' + self._target_mode + a_sep
        l_res = l_res + an_item_prefix + 'slave_address:' + str(
            self._slave_address) + a_sep
        l_res = l_res + an_item_prefix + 'client_connect_retries:' + str(
            self._client_connect_retries) + a_sep
        l_res = l_res + an_item_prefix + 'rtu_timeout:' + str(
            self._rtu_timeout) + a_sep
        l_res = l_res + an_item_prefix + 'rtu_stopbits:' + str(
            self._rtu_stopbits) + a_sep
        l_res = l_res + an_item_prefix + 'rtu_parity:' + self._rtu_parity + a_sep
        l_res = l_res + an_item_prefix + 'rtu_baudrate:' + str(
            self._rtu_baudrate) + a_sep
        l_res = l_res + an_item_prefix + 'byte_order:' + self._byte_order + a_sep
        l_res = l_res + an_item_prefix + 'word_order:' + self._word_order

        return l_res

    def out(self, a_sep='\n', an_item_prefix='\t', a_with_description=False):
        l_res = self.__class__.__name__ + a_sep
        l_res += self.out_without_registers(a_sep, an_item_prefix) + a_sep

        l_res += 'Registers:' + a_sep
        for l_short_desc, l_sit_reg in self._sit_modbus_registers.items():
            l_res += l_sit_reg.out_human_readable(
                an_item_prefix, a_with_description=a_with_description) + a_sep

        return l_res

    def out_human_readable(self, a_with_description=False):
        l_res = ''

        l_res += self.out('\n', '\t', a_with_description)

        return l_res

# INVARIANT

    def invariants(self):
        """
		Raise exception if not complying
		"""
        assert self.valid_slave_address(
            self._slave_address), 'valid slave address'


# TEST

    def test(self):
        """
		Test method
		"""
        self._logger.info("Test method->")
Beispiel #10
0
class SmartLogger1000a(SitModbusDevice):

    # CONSTANTS

    DEFAULT_SLAVE_ADDRESS = 1
    DEFAULT_MODBUS_PORT = 502
    DEFAULT_TARGET_MODE = SitModbusDevice.TARGET_MODE_TCP
    MIN_W_FOR_RAISE_EVENT_GENERATION = 5000
    PARSER_DESCRIPTION = 'Actions with Huawei smart logger 1000a device. ' + SitConstants.DEFAULT_HELP_LICENSE_NOTICE

    # CLASS ATTRIBUTES

    _byte_order = Endian.Big
    _word_order = Endian.Big
    _substract_one_to_register_index = False

    # FUNCTIONS DEFINITION
    """
		Initialize
	"""
    def __init__(self,
                 a_slave_address=DEFAULT_SLAVE_ADDRESS,
                 a_port=DEFAULT_MODBUS_PORT,
                 an_ip_address=None):
        assert self.valid_slave_address(
            a_slave_address), 'invalid a_slave_address:{}'.format(
                a_slave_address)
        try:
            self.init_arg_parse()
            assert self.valid_slave_address(
                a_slave_address
            ), 'a_slave_address parameter invalid:{}'.format(l_slave_address)
            l_slave_address = a_slave_address
            if __name__ == '__main__':
                if (hasattr(self._args, 'slave_address')
                        and self._args.slave_address):
                    l_slave_address = self._args.slave_address
            super().__init__(l_slave_address,
                             self.DEFAULT_TARGET_MODE,
                             a_port=self.DEFAULT_MODBUS_PORT,
                             an_ip_address=self._args.host_ip)
            self._logger = SitLogger().new_logger(self.__class__.__name__,
                                                  self._args.host_mac)
            self._init_sit_modbus_registers(l_slave_address)

            self.invariants()
            #self._logger.debug('init->' + self.out())
        except OSError as l_e:
            self._logger.warning(
                "init-> OSError, probably rollingfileAppender" % (l_e))
            if e.errno != errno.ENOENT:
                raise l_e
        except Exception as l_e:
            print('Error in init: %s' % (l_e))
            raise l_e
            #exit(1)

    def _init_sit_modbus_registers(self, a_slave_address):
        """
			Initializes self._sit_modbus_registers
		"""
        assert self.valid_slave_address(
            a_slave_address), 'invalid a_slave_address:{}'.format(
                a_slave_address)
        self.add_common_sit_modbus_registers(1)

        self.invariants()

    def add_common_sit_modbus_registers(self, a_slave_address):
        """
		Common devices registers
		"""
        assert self.valid_slave_address(
            a_slave_address), 'invalid a_slave_address:{}'.format(
                a_slave_address)
        assert a_slave_address == 1 or a_slave_address >= 3, 'Dont ask for slave_address 2, the add_cc_only_sit_modbus_registers is done for that! addr:{}'.format(
            a_slave_address)

        l_reg_list = OrderedDict()
        l_slave_address = a_slave_address
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeStringVar(SitConstants.SS_REG_SHORT_ABB_SERIAL_NUMBER,
                                  'ESN',
                                  40713,
                                  10,
                                  l_slave_address,
                                  SitModbusRegister.ACCESS_MODE_R,
                                  'W',
                                  an_is_metadata=True))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt32s(SitConstants.SS_REG_SHORT_ABB_AC_POWER,
                               'Total active output power of all inverters',
                               40525,
                               l_slave_address,
                               SitModbusRegister.ACCESS_MODE_R,
                               'W',
                               an_is_metadata=False,
                               an_event=SitModbusRegisterEvent(self._W_event)))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt32s(
                SitConstants.SS_REG_SHORT_ABB_AC_S_REACTIVE_POWER,
                'Reactive power',
                40544,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'kVar',
                an_is_metadata=False))

        # Active power control
        #	SitUtils.od_extend(l_reg_list, RegisterTypeInt16u(SitConstants.SS_REG_SHORT_ABB_STATUS_OPERATING_STATE, 'Plant Status 1=Unlimited/2Limited/3Idle/4Fault/5Communication_interrupt', 40543, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Enum', an_is_metadata=False)) # Not working on tranque sante and maristas santamaria
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16u('PlantSt2',
                               'Plant Status 2 0=ildle/1=on-grid/...',
                               40566,
                               l_slave_address,
                               SitModbusRegister.ACCESS_MODE_R,
                               'Enum',
                               an_is_metadata=False))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16u('ActPwrCtlMode',
                               'Active power control mode 0=no limit/other...',
                               40737,
                               l_slave_address,
                               SitModbusRegister.ACCESS_MODE_R,
                               'Enum',
                               an_is_metadata=False))
        #Meter
        # UNABLE TO READ IT SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('WMeter', 'Active power of meter', 32278, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'W', an_is_metadata=False))

        #Huawei specials
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt32s(
                SitConstants.SS_REG_SHORT_EXTRA_HUAWEI_ACT_POWER_ADJ,
                'Active Power adjustment',
                40426,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'Int',
                an_is_metadata=False))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt16u(
                SitConstants.SS_REG_SHORT_EXTRA_HUAWEI_ACT_POWER_ADJ_PCT,
                'Active Power adjustment percentage',
                40428,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                '%',
                an_is_metadata=False))

        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt32u(
                'LifeTimeKWHOut',
                'Equals the total energy yield generatedby all inverters.',
                40560,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'UInt',
                an_is_metadata=False))
        SitUtils.od_extend(
            l_reg_list,
            RegisterTypeInt32u(
                'TodaykWhOutput',
                'Equals daily energy yield generated byall inverters.',
                40562,
                l_slave_address,
                SitModbusRegister.ACCESS_MODE_R,
                'UInt',
                an_is_metadata=False))

        #SitUtils.od_extend(l_reg_list, RegisterTypeStrVar('Mn', 'Model', 30000, 15, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'String15', an_is_metadata=True))

        # CLUSTER AND INVERTERS
        #		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('Vr', 'Version number of the SMA Modbus profile', 30001, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=True))
        #
        #		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('ID', 'SUSy ID (of the Cluster Controller)', 30003, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=True))
        #		SitUtils.od_extend(l_reg_list, RegisterTypeInt32u('SN', 'Serial number (of the Cluster Controller)', 30005, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=True))
        #		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('NewData', 'Modbus data change: meter value is increased by the Cluster Controller if new data is available.', 30007, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=False))
        #		SitUtils.od_extend(l_reg_list, RegisterTypeSmaCCDeviceClass('DeviceClass', 'Device Class', 30051, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Enum', an_is_metadata=True))
        #
        #		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('W', 'Current active power on all line conductors (W), accumulated values of the inverters', 30775, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'W', an_is_metadata=False, an_event=SitModbusRegisterEvent(self._W_event)))
        #		SitUtils.od_extend(l_reg_list, RegisterTypeInt64u('Wh', 'Total energy fed in across all line conductors, in Wh (accumulated values of the inverters) System param', 30513, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Wh', an_is_metadata=False))
        #		SitUtils.od_extend(l_reg_list, RegisterTypeInt32s('VAr', 'Reactive power on all line conductors (var), accumulated values of the inverters', 30805, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'VAr', an_is_metadata=False))
        #		SitUtils.od_extend(l_reg_list, RegisterTypeInt64u('TotWhDay', 'Energy fed in on current day across all line conductors, in Wh (accumulated values of the inverters)', 30517, l_slave_address, SitModbusRegister.ACCESS_MODE_R, 'Wh', an_is_metadata=False))
        #
        self.append_modbus_registers(l_reg_list)

    def _W_event(self, a_sit_modbus_register):
        """
		Called by modbus_device.call_sit_modbus_registers_events()
		"""
        self._logger.debug('_W_event-> register:{}'.format(
            a_sit_modbus_register.out_short()))
        l_short_desc = 'W'
        l_min_val = self.MIN_W_FOR_RAISE_EVENT_GENERATION
        l_val = a_sit_modbus_register.value
        l_sit_dt = SitDateTime()

        if a_sit_modbus_register.short_description == l_short_desc:
            l_start_time = time(8, 30)
            l_end_time = time(16, 30)
            l_is_day, l_valid_time = l_sit_dt.time_is_between(
                datetime.now().time(), l_start_time, l_end_time)
            #			l_is_between_sunrise_sunset = l_sit_dt.now_is_into_sunrise_sunset_from_conf(self._sit_json_conf)
            #
            #			self._logger.debug('read_sit_modbus_register-> l_is_day:{} l_valid_time:{} l_is_between_sunrise_sunset:{}'.format(l_is_day, l_valid_time, l_is_between_sunrise_sunset))
            l_msg = '_W_event-> register_index:{} value ({}) '.format(
                a_sit_modbus_register.register_index, l_val)
            l_msg += ' between {} and {}'.format(l_start_time, l_end_time)
            self._logger.info(
                '_W_event-> DEVICE IS GENERATING {} kW'.format(l_val))
            if (l_valid_time and l_val <= l_min_val):
                l_msg = '_W_event-> register_index:{} value ({} <= {}), valid_time:{}'.format(
                    a_sit_modbus_register.register_index, l_val, l_min_val,
                    l_valid_time)
                self._logger.warning(l_msg)
                # Create Dir
                l_dir = '/tmp/solarity_events'
                if not os.path.exists(l_dir):
                    os.makedirs(l_dir)
                l_file = self.__class__.__name__ + '_event_{}_register_{}_slave_{}'.format(
                    datetime.now().strftime("%Y%m%d_%H"),
                    a_sit_modbus_register.register_index,
                    a_sit_modbus_register.slave_address)
                l_file_abs_path = l_dir + '/' + l_file
                if not os.path.exists(l_file_abs_path):
                    self._logger.info(
                        '_W_event-> Event not sent, sending email file:{}'.
                        format(l_file_abs_path))
                    # SEND MAIL
                    l_subject = 'event with failure on $(hostname) slave:' + str(
                        a_sit_modbus_register.slave_address
                    ) + ' $(date +%Y%m%d_%H%M%S) W val:(' + str(
                        l_val) + ' <= ' + str(l_min_val) + ')W '
                    l_body = [
                        'event in slave:{} with failure on $(hostname) $(date +%Y%m%d_%H%M%S) review file {}'
                        .format(
                            a_sit_modbus_register.slave_address,
                            self.csv_file_path(
                                a_sit_modbus_register.slave_address))
                    ]
                    l_body.append(' Between {} and {}'.format(
                        l_start_time, l_end_time))
                    l_body.append('Register->out:{}'.format(
                        a_sit_modbus_register.out_human_readable(
                            a_with_description=self._args.long)))
                    l_subject, l_body = self._setted_parts(l_subject, l_body)
                    SitUtils.send_mail(
                        self.events_mail_receivers(), l_subject, l_body, [
                            self.csv_file_path(
                                a_sit_modbus_register.slave_address)
                        ])

                    os.mknod(l_file_abs_path)
                else:
                    self._logger.warning(
                        '_W_event-> Event already sent, not sending email file:{}'
                        .format(l_file_abs_path))

            else:
                l_msg = '_W_event-> Event not raised register_index:{} value ({} > {}), valid_time:{}'.format(
                    a_sit_modbus_register.register_index, l_val, l_min_val,
                    l_valid_time)
                self._logger.debug(l_msg)

    def _setted_parts(self, a_subject, a_body):
        return a_subject, a_body

    def manual_restart(self):
        """
		Manual restart 
		documented on p.45 of doc
		"""
        l_res = 'test_res'
        self._logger.info('manual_restart-> NOW')
        #a_register_index, a_slave_address, a_value):
        l_res = self.write_register_value(0, 201, 1)
        self._logger.info('manual_restart-> result:{}'.format(l_res))

        return l_res

    def read_all_sit_modbus_registers(self):
        """
		Read inverters data
		"""
        super().read_all_sit_modbus_registers()

#		l_reg_index = 42109
#		l_slave_address = 3
#		self.read_inverter_data(l_slave_address)

    def read_inverter_data(self, a_slave_address):
        """
		Was for test but not working
		"""
        assert False, 'deprecated'
        l_reg = RegisterTypeInt64u(
            'Wh2',
            'Total energy fed in across all line conductors, in Wh (accumulated values of the inverters) System param',
            30513,
            SitModbusRegister.ACCESS_MODE_R,
            'Wh',
            an_is_metadata=False,
            a_slave_address=a_slave_address)
        self.read_inverter_data_register(l_reg, a_slave_address)
        print(l_reg.out_human_readable(a_with_description=True))

#		l_reg = RegisterTypeInt32u('SN', 'Serial Number', a_reg_index + 1, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=True)
#		self.read_inverter_data_register(l_reg, a_slave_address)
#
#		l_reg = RegisterTypeInt16u('UnitID', 'Unit ID', a_reg_index + 3, SitModbusRegister.ACCESS_MODE_R, 'Int32u', an_is_metadata=True)
#		self.read_inverter_data_register(l_reg, a_slave_address)

    def read_inverter_data_register(self, a_register, a_slave_address):
        """
		Reads given inverter data
		Was for test but not working
		"""
        assert False, 'deprecated'
        try:
            self.read_sit_modbus_register(a_register, a_slave_address)
            if self._args.store_values:
                pass

    #			self.store_values_into_csv([l_reg], l_slave)
            if self._args.display_all:
                print('***************** INVERTER slave:{} ******************'.
                      format(a_slave_address))
                print(
                    a_register.out_human_readable(
                        a_with_description=self._args.long))
        except ModbusException as l_e:
            self._logger.error(
                'read_inverter_data-> error reading register {}'.format(l_e))
        except Exception as l_e:
            raise l_e

# ACCESS

# IMPLEMENTATION

# EXECUTE ARGS
    """
		Parsing arguments and calling corresponding functions
	"""
    def execute_corresponding_args(self):
        try:
            self.connect()
            if self._args.verbose:
                self._logger.setLevel(logging.DEBUG)
            else:
                self._logger.setLevel(logging.INFO)
            if self._args.store_values or self._args.display_all or self._args.test or self._args.raise_event:
                assert self.valid_slave_address(
                    self._slave_address), 'Invalid slave address {}'.format(
                        self._slave_address)
                self.read_all_sit_modbus_registers()
                if self._args.store_values:
                    self.store_values_into_csv(self._sit_modbus_registers,
                                               self._slave_address)
                if self._args.display_all:
                    print(
                        self.out_human_readable(
                            a_with_description=self._args.long))
                if self._args.raise_event:
                    assert len(self._sit_modbus_registers
                               ) > 0, 'modbus_registers_not_empty'
                    self.call_sit_modbus_registers_events()
                if self._args.test:
                    self.test()


#			if self._args.manual_restart:
#				self.manual_restart()
        except Exception as l_e:
            self._logger.exception(
                "execute_corresponding_args-> Exception occured: %s" % (l_e))
            print('Error: %s' % (l_e))
            raise l_e
        finally:
            if self.is_connected():
                self.disconnect()
        self.invariants()

    def add_arg_parse_modbus_device(self):
        super().add_arg_parse()

    def add_arg_parse(self):
        """
		Override method
		"""
        self.add_arg_parse_modbus_device()
        self._parser.add_argument(
            '-e',
            '--raise_event',
            help='Raises the corresponding event if setted',
            action="store_true")
        #self._parser.add_argument('-r', '--manual_restart', help='Sends a manual restart to inverter manager', action="store_true")
        self._parser.add_argument('-c',
                                  '--slave_address',
                                  help='Slave address of modbus device',
                                  nargs='?')

    def add_required_named(self, a_required_named):
        pass

    def test(self):
        """
			Test function
		"""
        try:
            self._logger.info("################# BEGIN #################")
            #			l_sit_dt = SitDateTime()
            #			l_is_between_sunrise_sunset = l_sit_dt.now_is_into_sunrise_sunset_from_conf(self._sit_json_conf)
            #self.call_sit_modbus_registers_events(l_slave)
            #			self._logger.info("--> ************* device models *************: %s" % (l_d.models))	 #Lists properties to be loaded with l_d.<property>.read() and then access them
            #			self._logger.info("-->inverter ************* l_d.inverter.points *************: %s" % (l_d.inverter.points))	#Gives the inverter available properties
            #			self._logger.info("-->inverter ************* common *************: %s" % (l_d.common))
            #			self._logger.info("-->inverter ************* common Serial Number *************: %s" % (l_d.common.SN))
            self._logger.info("################# END #################")
        except Exception as l_e:
            self._logger.exception("Exception occured: %s" % (l_e))
            print('Error: %s' % (l_e))
            self._logger.error('Error: %s' % (l_e))
            raise l_e

    def events_mail_receivers(self):
        return [
            '*****@*****.**',
            '*****@*****.**'
        ]
        #return ['*****@*****.**']
        #return ['*****@*****.**', '*****@*****.**']

    def invariants_modbus_device(self):
        super().invariants()

    def invariants(self):
        self.invariants_modbus_device()
Beispiel #11
0
class RegisterTypeInt16u(SitModbusRegister):

# CONSTANTS


# VARIABLES
	_logger = None
	_words_count = 1
	_byte_order = Endian.Big
	_word_order = Endian.Big

# SETTERS AND GETTERS


# INITIALIZATION

	def __init__(
			self, 
			a_short_description, 
			a_description, 
			a_register_index, 
			a_slave_address, 
			an_access_mode=SitModbusRegister.DEFAULT_ACCESS_MODE, 
			a_value_unit=None, 
			a_scale_factor_register_index=None, 
			an_event=None, 
			an_is_metadata=False, 
			a_post_set_value_call=None
		):
		"""
			Initialize
		"""
		try:
			#*** Logger
			self._logger = SitLogger().new_logger(__name__)
			super().__init__(a_short_description, 
				a_description, 
				a_register_index, 
				a_slave_address, 
				an_access_mode, 
				a_value_unit, 
				a_scale_factor_register_index, 
				an_event, 
				an_is_metadata, 
				a_post_set_value_call)
			self.invariants()
		except OSError as l_e:
			self._logger.warning("init-> OSError, probably rollingfileAppender:{}".format(l_e))
			if e.errno != errno.ENOENT:
				raise l_e
		except Exception as l_e:
			self._logger.error('Error in init: {}'.format(l_e))
			raise l_e
			#exit(1)

# STATUS SETTING

	def set_value_with_raw(self, a_register_read_res):
		self._logger.debug("set_value_with_raw, u_int_16->before decoder:{}".format(a_register_read_res.registers))
		decoder = BinaryPayloadDecoder.fromRegisters(a_register_read_res.registers, byteorder=self._byte_order, wordorder=self._word_order) #https://pymodbus.readthedocs.io/en/latest/source/example/modbus_payload.html
		#https://pymodbus.readthedocs.io/en/v1.3.2/library/payload.html?highlight=binarypayloaddecoder#pymodbus.payload.BinaryPayloadDecoder
		l_v = decoder.decode_16bit_uint()
		self._logger.debug("set_value_with_raw->after decoder:{} raw_int:{} raw_register_hexa:0x{}".format(l_v, a_register_read_res.registers[0], '{:02X}'.format(a_register_read_res.registers[0])))
		self._value = l_v