def gen_sql_code(self, code): """ Дополнение кода информацией из контекста описания объекта. ВНИМАНИЕ! Т.к. одинарные кавычки не должны присутствовать, то необходимо сделать дополнительную предобработку контекста. @param code: Строка блока кода. @return: Полностью заполненная и готовая к выполнению строка блока кода. """ if not code: return None if self.cache_state is None: self.cache_state = self.fill_state() context = self.get_context(self.cache_state) # ВНИМАНИЕ! Т.к. одинарные кавычки не должны присутствовать, # то необходимо сделать дополнительную предобработку контекста. for name, value in context.items(): # context[name] = str(value).replace('\'', '"') if type(value) in (str, unicode): try: context[name] = value.replace('\'', '"') except UnicodeEncodeError: msg = u'Ошибка замены кавычек в значении <%s>' % value log.fatal(msg) journal.write_msg(msg) result = self.gen_code(code, context) return result
def create_src(self, **properties): """ Метод создания объекта источника данных с инициализацией его свойств. @param properties: Словарь свойств источника данных. @return: Объект источника данных или None в случае ошибки. """ # Сначала в любом случае определяем тип источника данных if 'type' in properties: src_typename = properties['type'] src_class = src.DATA_SOURCES.get(src_typename, None) if src_class is None: log.error( u'Ошибка создания объекта источника данных. Тип <%s> не зарегистрирован в системе как источник данных' % src_typename) else: try: log.info(u'Создание объекта источника данных <%s>' % properties.get('name', src_class.__name__)) src_obj = src_class(parent=self, **properties) # Регистрируем новый объект в словаре внутренних объектов self.reg_object(src_obj) return src_obj except: log.fatal(u'Ошибка создания объекта источника данных') else: name = properties.get('name', u'') log.error( u'Ошибка создания объекта источника данных. Не определен тип <%s>' % name) return None
def load_xml_content(xml_filename, is_change_keys=True): """ Загрузить содержимое XML файла в словарно списковую структуру. @param xml_filename: Полное имя XML файла. @param is_change_keys: Произвести автоматическую замену ключей на короткие. @return: Словарно-списковая структура содержания XML файла. Или None в случае ошибки. """ if not os.path.exists(xml_filename): log.warning(u'XML файл <%s> не найден' % xml_filename) return None log.info(u'Загрузка содержимого файла <%s>' % xml_filename) xml_file = None try: xml_file = open(xml_filename, 'r') xml_txt = xml_file.read() xml_file.close() except: if xml_file: xml_file.close() log.fatal(u'Ошибка загрузки содержимого XML файла <%s>' % xml_filename) return None if not xml_txt.strip(): log.error(u'Файл <%s> пустой' % xml_filename) return dict() data = xmltodict.parse(xml_txt) if is_change_keys: data = change_keys_doc(data) return data
def get_http(self, url): """ Получить содержание по URL. @param url: Адрес запроса. @return: Вовращает содержимое XML файла или None в случае ошибки. """ if not (url.startswith('http://') or url.startswith('https://')): log.warning( u'Указан не абсолютный адрес <%s> при получении содержания по адресу' % url) url = self.utm_url + url log.info(u'Преобразуем адрес к абсолютному виду <%s>' % url) try: output_xml_filename = os.path.join(self.output_dir, DEFAULT_OUTPUT_XML_FILENAME) if os.path.exists(output_xml_filename): try: log.info(u'Удаление файла <%s>' % output_xml_filename) os.remove(output_xml_filename) except: msg = u'Ошибка удаления файла <%s>' % output_xml_filename log.fatal(msg) journal.write_msg(msg) cmd = '"%s" --output "%s" -X GET %s' % (self.curl, output_xml_filename, url) execfunc.exec_shell(cmd) content = None if os.path.exists(output_xml_filename): try: content = xmlfunc.load_xml_content(output_xml_filename) except: msg = u'Ошибка XML файла данных <%s>' % output_xml_filename log.error(msg) journal.write_msg(msg) self._backup_error_file(output_xml_filename) raise else: msg = u'Не найден выходной файл УТМ <%s>' % output_xml_filename log.warning(msg) journal.write_msg(msg) return content except: msg = u'Ошибка получения содержания УТМ по адресу <%s>' % url log.fatal(msg) journal.write_msg(msg) return None
def diagnostic(self): """ Простая процедура прверки доступа к источнику данных. @return: True/False. """ connection = None log.info(u'UniReader. Диагностика <%s>.<%s>' % (self.__class__.__name__, self.name)) if self.opc_server is None: msg = u'UniReader. Не определен OPC сервер в <%s>' % self.name log.warning(msg) journal.write_msg(msg) return False if self.topic is None: msg = u'UniReader. Не определен топик в <%s>' % self.name log.warning(msg) journal.write_msg(msg) return False try: # Создание клиента OPC connection = self.create_connection(self.uni_host) if connection is None: msg = u'Не возможно создать объект связи с UniReader. Хост <%s>' % self.uni_host log.error(msg) journal.write_msg(msg) return None # Контроль наличия процедуры чтения значений из OPC сервера rpc_methods = connection.system.listMethods() if 'sources.ReadValueAsString' not in rpc_methods: msg = u'UniReader. Процедура чтения значения из OPC сервера не найдена. Хост <%s>' % self.uni_host log.error(msg) journal.write_msg(msg) return None else: log.info(u'UniReader. Список методов удаленного вызова %s' % str(rpc_methods)) return True except: msg = u'UniReader. Ошибка диагностики <%s>' % self.__class__.__name__ log.fatal(msg) journal.write_msg(msg) return False
def _read(self, *values): """ Чтение данных из источника данных. @param values: Список читаемых переменных. @return: Список прочианных значений. Если переменная не найдена или произошла ошибка чтения, то вместо значения подставляется None с указанием WARNING в журнале сообщений. """ xml_filenames = self.get_xml_filenames() if not values: log.warning(u'Не определены переменные для чтения в <%s>' % self.name) values = self.values log.debug(u'Переменные взяты из описания источника данных: %s' % values) try: result = [list() for i in range(len(values))] for xml_filename in xml_filenames: # Получаем содержимое XML файла xml_content = xmlfunc.load_xml_content(xml_filename) for i, value in enumerate(values): value_path = getattr(self, value) xml_value = xmlfunc.get_xml_content_by_link( xml_content, value_path) result[i].append(xml_value) # Регистрация состояния state = dict([(values[i], val) for i, val in enumerate(result)]) self.reg_state(**state) if self.auto_remove: for xml_filename in xml_filenames: if os.path.exists(xml_filename): try: log.info(u'Удаление файла <%s>' % xml_filename) os.remove(xml_filename) except: msg = u'Ошибка удаления файла <%s>' % xml_filename log.fatal(msg) journal.write_msg(msg) return result except: log.fatal(u'Ошибка чтения данных из файлов %s' % xml_filenames) return None
def parse_key(self, key_txt): """ Распарсить ключ элемента. @param key_txt: Ключ элемента. @return: Имя тега, его атрибуты. """ key_line = re.split(' |\n', key_txt) name = key_line[0] try: attrs = dict([ tuple([re.sub(r'^"|"$', '', s) for s in attr_txt.split('=')]) for attr_txt in key_line[1:] if attr_txt.strip() ]) except: log.fatal(u'Ошибка определения атрибутов XML тега <%s>' % name) attrs = dict() return name, attrs
def doScenario(self, lScenario=None): """ Функция выполнения сценария. @param lScenario: Сценарий-список шагов сценария. Основным элементом визарда является сценарий: Сценарий - это список кортежей: [(Идентификатор, Функция обработки шага сценария, Аргументы, Именованные аргументы), ...] Исполнение сценария запускается по кнопке <OK> на последней странице визарда. """ if lScenario is None: lScenario = self.scenario # Проверить определена ли альтернативная функция обработки сценария if self.do_scenario: return self.do_scenario(lScenario) else: result = True # обработка сценария по шагам for step in lScenario: if step: id = step[0] func = step[1] args = step[2] kwargs = step[3] enable = step[4] if func and enable: try: log.info( u'Выполнение сценария %s args: %s kwargs: %s' % (func.__name__, args, kwargs)) step_result = func(*args, **kwargs) except: log.fatal( u'Ошибка выполнения функции сценария <%s>' % func) step_result = False if config.DEBUG_MODE: raise result = result and step_result return result
def get_value_by_link(self, link): """ Получить значение внутренней переменной по ссылке. @param link: Строковая ссылка в формате: ИМЯ_ОБЪЕКТА.имя_переменной или ИМЯ_ОБЪЕКТА.имя_функции(аргументы функции). @return: Значение внутренней переменной или None если по этой ссылке переменная или объект не найдены. """ if link.startswith(LINK_SIGNATURE): # Убрать сигнатуру из обработки link = link[len(LINK_SIGNATURE):].strip() try: # Разделяем ссылку на имя объекта и имя переменной obj_name, val_name = link.split(LINK_DELIMETER) # Определение объекта obj = self.objects.get(obj_name, None) if obj is None: log.warning(u'Не найден объект <%s> среди зарегистрированных' % obj_name) return None if not self.is_link_func(link): # Определение значения переменной value = obj.state.get(val_name, None) if value is None: log.warning(u'Не найдена переменная <%s> в объекте <%s>' % (val_name, obj_name)) return None else: # Это вызов функции value = None try: value = eval(u'obj.%s' % val_name) except: log.fatal(u'Ошибка вызова метода <%s.%s>' % (obj_name, val_name)) return value except: log.fatal( u'Ошибка получения значения внутренней переменной по ссылке <%s>' % link) return None
def _read_value(self, address): """ Прочитать значение по адресу из RSLinx. @param address: Адрес. Адрес задается явно. @return: Прочитанное значение либо None в случае ошибки. """ opc = None try: # Создание клиента OPC opc = self.create_opc_client(self.opc_host) if opc is None: msg = u'Не возможно создать объект клиента OPC. Хост <%s>' % self.opc_host log.error(msg) journal.write_msg(msg) return None # Список серверов OPC servers = opc.servers() if self.opc_server not in servers: msg = u'Сервер <%s> не найден среди %s' % (self.opc_server, servers) log.warning(msg) journal.write_msg(msg) opc.close() return None # Соедиенение с сервером server = self.opc_server opc.connect(server) # Прочитать из OPC сервера val = opc.read(address) result = self.recode(val[0]) if val and val[1] == 'Good' else None opc.close() log.debug(u'Адрес <%s>. Результат чтения данных %s' % (address, result)) return result except: if opc: opc.close() msg = u'Ошибка чтения значения по адресу <%s> в <%s>' % (address, self.__class__.__name__) log.fatal(msg) journal.write_msg(msg) return None
def del_http(self, url): """ Удалит данные по URL на сервере УТМ. Когда ответ на запрос обработан его необходимо удалить командой вида: curl -X DELETE http://localhost:8080/opt/out/ReplyPartner/407 Регулярное удаление отработанных запросов из списка и сохраненных ответов на эти запросы из списка предотвращает безконтрольный рост размера базы данных УТМ. @param url: Адрес запроса. """ if not (url.startswith('http://') or url.startswith('https://')): log.warning( u'Указан не абсолютный адрес <%s> при получении содержания по адресу' % url) url = self.utm_url + url log.info(u'Преобразуем адрес к абсолютному виду <%s>' % url) try: output_xml_filename = os.path.join(self.output_dir, DEFAULT_OUTPUT_XML_FILENAME) if os.path.exists(output_xml_filename): try: log.info(u'Удаление файла <%s>' % output_xml_filename) os.remove(output_xml_filename) except: msg = u'Ошибка удаления файла <%s>' % output_xml_filename log.fatal(msg) journal.write_msg(msg) cmd = '"%s" --output "%s" -X DELETE %s' % ( self.curl, output_xml_filename, url) execfunc.exec_shell(cmd) content = xmlfunc.load_xml_content(output_xml_filename) if content is not None: # Необходимо проанализировать ошибки content = self.valid_content(content) return content except: msg = u'Ошибка удаления данных УТМ по адресу <%s>' % url log.fatal(msg) journal.write_msg(msg) return None
def clear_all_state_chaches(self, *objects): """ Сбросить все кеши в объектах @param objects: Список обрабатываемых объектов. @return: True/False. """ if not objects: log.warning(u'Не определены объекты для сброса кеша состояния') return False result = True for obj in objects: try: obj.clear_state_cache() result = result and True except: log.fatal(u'Ошибка сброса кеша в объекте <%s>' % obj) result = False return result
def save_xml_content(xml_filename, data, is_rewrite=True): """ Записать словарно списковую структуру в XML файл. @param xml_filename: Полное имя XML файла. @param data: Словарно-списковая структура содержания XML файла. @param is_rewrite: Перезаписать результирующий файл, если необходимо? @return: True/False. """ if os.path.exists(xml_filename) and not is_rewrite: log.warning(u'Запрет на перезапись. Файл <%s> уже существует.' % xml_filename) return False # Сама конвертация словаря в текст if isinstance(data, list): xml_txt = dicttoxml.dicttoxml(data, root=True, custom_root='root', ids=False, attr_type=False) elif isinstance(data, dict): root_key = data[data.keys()[0]] xml_txt = dicttoxml.dicttoxml(data, root=False, custom_root=root_key, ids=False, attr_type=False) else: log.warning(u'Не корректный тип данных <%s> для записи в XML файл' % type(data)) return False xml_file = None try: xml_file = open(xml_filename, 'w') xml_file.write(xml_txt) xml_file.close() return True except: if xml_file: xml_file.close() log.fatal(u'Ошибка записи в файл <%s>' % xml_filename) return False
def getSettingsPath(self): """ Папка сохраненных параметров программы. Находиться в HOME. Функция сразу провеяет если этой папки нет, то создает ее. """ if self.settings_path is None: home_path = utils.getHomeDir() self.settings_path = os.path.join(home_path, config.DEFAULT_WIZ_INI_DIR) if not os.path.exists(self.settings_path): try: log.info(u'Создание директории <%s>' % self.settings_path) os.makedirs(self.settings_path) except: log.fatal(u'Ошибка саздания папки <%s>' % self.settings_path) return self.settings_path
def connect(self, db_url=None): """ Соединение с БД. @param db_url: Конекшн стринг подключения к БД. @return: Объект sqlalchemy движка. """ if db_url is None: db_url = self.get_db_url() if self.connection: self.disconnect() self.connection = None try: # Отображение в консоли выполняемых SQL выражений---------+ # V self.connection = sqlalchemy.create_engine(db_url, echo=False) except: msg = u'Ошибка соединения с БД <%s>' % db_url log.fatal(msg) journal.write_msg(msg) self.connection = None return self.connection
def _read(self, *values): """ Чтение данных из источника данных. @param values: Список читаемых переменных. @return: Список прочианных значений. Если переменная не найдена или произошла ошибка чтения, то вместо значения подставляется None с указанием WARNING в журнале сообщений. """ if not values: log.warning(u'Не определены переменные для чтения в <%s>' % self.name) values = self.values log.debug(u'Переменные взяты из описания источника данных: %s' % values) try: # Прочитать все документы inbox_docs = self.get_inbox_documents() if inbox_docs is None: return False # Регистрация состояния if not self.cache_state: inbox = dict([(doc.get('uuid', str(uuid.uuid4())), doc) for doc in inbox_docs]) state = dict([(val, self.gen_code(getattr(self, val))) for val in values]) # state = self.get_values_as_dict(values) state[INBOX_STATE_NAME] = inbox self.reg_state(**state) self.cache_state = self.state return inbox_docs except: msg = u'Ошибка чтения входящих документов УТМ' log.fatal(msg) journal.write_msg(msg) return None
def _backup_error_file(self, src_filename, err_filename=None): """ Сохранить ошибочный файл в папке. @param src_filename: Имя исходного ошибочного файла. @param err_filename: Имя нового файла. Если не определено, то имя генерируется по времени. @return: True/False. """ if err_filename is None: err_filename = os.path.join( str(self.output_dir), datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S.err')) try: if os.path.exists(err_filename): os.remove(err_filename) shutil.copyfile(src_filename, err_filename) return True except: msg = u'Ошибка сохранения ошибочного файла' log.fatal(msg) journal.write_msg(msg) return False
def _read_value(self, address): """ Прочитать значение по адресу из OPC сервера. @param address: Адрес. Адрес задается явно. @return: Прочитанное значение либо None в случае ошибки. """ connection = None try: # Создание связи connection = self.create_connection(self.uni_host) if connection is None: msg = u'Не возможно создать объект связи с UniReader. Хост <%s>' % self.uni_host log.error(msg) journal.write_msg(msg) return None # Контроль наличия процедуры чтения значений из OPC сервера rpc_methods = connection.system.listMethods() if 'sources.ReadValueAsString' not in rpc_methods: msg = u'UniReader. Процедура чтения значения из OPC сервера не найдена. Хост <%s>' % self.uni_host log.error(msg) journal.write_msg(msg) return None # Прочитать из OPC сервера val = connection.sources.ReadValueAsString('OPC_SERVER_NODE', self.opc_server, address) result = self.recode(val[0]) if val else None log.debug(u'UniReader. Адрес <%s>. Результат чтения данных %s' % (address, result)) return result except: msg = u'UniReader. Ошибка чтения значения по адресу <%s> в <%s>' % ( address, self.__class__.__name__) log.fatal(msg) journal.write_msg(msg) return None
def save_simple_xml_content(xml_filename, data, is_rewrite=True, tag_filter=None): """ Записать словарно списковую структуру в XML файл. Самая простая реализация. @param xml_filename: Полное имя XML файла. @param data: Словарно-списковая структура содержания XML файла. @param is_rewrite: Перезаписать результирующий файл, если необходимо? @param tag_filter: Словарь фильтра тегов, определяющий список и порядок дочерних тегов для каждого тега. Порядок тегов важен для XML, поэтому введен этот фильтр. @return: True/False. """ if os.path.exists(xml_filename) and not is_rewrite: log.warning(u'Запрет на перезапись. Файл <%s> уже существует.' % xml_filename) return False xml_file = None try: # Начать запись xml_file = open(xml_filename, 'wt') xml_writer = simple_dict2xml.icSimpleDict2XmlWriter(data, xml_file) xml_writer.startDocument() xml_writer.startWrite(tag_filter=tag_filter) # Закончить запись xml_writer.endDocument() xml_file.close() return True except: if xml_file: xml_file.close() log.fatal(u'Ошибка записи в файл <%s>' % xml_filename) return False
def _write(self, *values): """ Записать данные в приемник данных. @param values: Список записываемых значений @return: True/False. """ if not self.sql: msg = u'Не определено SQL выражение для записи данных в <%s>' % self.name log.warning(msg) journal.write_msg(msg) return False sql = self.gen_sql_code(self.sql) if sql is None: msg = u'Ошибка запроса SQL' log.warning(msg) journal.write_msg(msg) return False if not sql.strip(): msg = u'Попытка выполнения пустого запроса SQL' log.warning(msg) journal.write_msg(msg) return False try: self.connect() log.info(u'Выполнение SQL: <%s>' % sql) self.connection.execute(sql) self.disconnect() return True except: self.disconnect() msg = u'Ошибка записи данных в <%s>' % self.name log.fatal(msg) journal.write_msg(msg) return False
def _read(self, *values): """ Прочитать данные из источника данных. @param values: Список получаемых значений. @return: Список записей/рекордсет. """ self.recordset = list() if not self.sql: msg = u'Не определено SQL выражение для получения данных в <%s>' % self.name log.warning(msg) journal.write_msg(msg) self.reg_state(recordset=self.recordset) return self.recordset sql = self.gen_sql_code(self.sql) if not sql.strip(): msg = u'Попытка выполнения пустого запроса SQL' log.warning(msg) journal.write_msg(msg) self.reg_state(recordset=self.recordset) return self.recordset try: self.connect() log.info(u'Выполнение SQL: <%s>' % sql) recordset = self.connection.execute(sql) self.disconnect() self.recordset = [dict(rec) for rec in recordset] log.debug(u'Результат запроса: %s' % str(self.recordset)) except: self.disconnect() msg = u'Ошибка получения данных в <%s>' % self.name log.fatal(msg) journal.write_msg(msg) self.recordset = list() self.reg_state(recordset=self.recordset) return self.recordset
def run_tick(self, n_tick=1): """ Запуск всех объектов для выполнения 1 тика. @param n_tick: Номер текущего тика. @return: True/False. """ try: config.set_cfg_var('TICK_DT_START', datetime.datetime.now()) # ВНИМАНИЕ! Необходимо с начале каждого тика надо создавать объекты # чтобы не контролировать актуальность их состояния log.info(u'Создание объектов...') src_objects = list() for properties in config.SOURCES: # Создаем объекты источников данных obj = self.create(**properties) if obj: src_objects.append(obj) dst_objects = list() for properties in config.DESTINATIONS: # Создаем объекты получателей данных obj = self.create(**properties) if obj: dst_objects.append(obj) if not config.QUEUE: log.info(u'Порядок обработки штатный') log.info(u'Начало обработки...') journal.write_msg(u'Начало обработки...') for src_object in src_objects: log.info(u'Чтение данных из <%s>' % src_object.name) journal.write_msg(u'\tЧтение данных из <%s>' % src_object.description) src_object.read_as_dict() for dst_object in dst_objects: log.info(u'Запись данных в <%s>' % dst_object.name) journal.write_msg(u'\tЗапись данных в <%s>' % dst_object.description) dst_object.write_as_dict() log.info(u'...Конец обработки [%d]' % n_tick) journal.write_msg(u'...Конец обработки') else: log.info(u'Порядок обработки задан явно %s' % config.QUEUE) log.info(u'Начало обработки...') journal.write_msg(u'Начало обработки...') for obj_properties in config.QUEUE: obj_name = obj_properties['name'] obj = self.find_object(obj_name) if obj: obj_type = obj_properties['type'] if obj_type in src.DATA_SOURCES.keys(): # Это источник данных log.info(u'Чтение данных из <%s>' % obj.name) journal.write_msg(u'\tЧтение данных из <%s>' % obj.description) obj.read_as_dict() elif obj_type in dst.DATA_DESTINATIONS.keys(): # Это получатель данных log.info(u'Запись данных в <%s>' % obj.name) journal.write_msg(u'\tЗапись данных в <%s>' % obj.description) obj.write_as_dict() else: # Вообще не определенный тип log.warning( u'Не поддерживаемый тип <%s> объекта <%s>' % (obj_type, obj_name)) log.info(u'...Конец обработки [%d]' % n_tick) journal.write_msg(u'...Конец обработки') # Сбросить кеш состояния в конце такта # obj_list = src_objects + dst_objects # self.clear_all_state_chaches(*obj_list) return True except: log.fatal(u'Ошибка выполнения тика [%d]' % n_tick) return False
def _read(self, *values): """ Чтение данных из источника данных. @param values: Список читаемых переменных. @return: Список прочитанных значений. Если переменная не найдена или произошла ошибка чтения, то вместо значения подставляется None с указанием WARNING в журнале сообщений. """ connection = None if not values: log.warning( u'UniReader. Не определены переменные для чтения в <%s>' % self.name) values = self.addresses log.debug( u'UniReader. Переменные взяты из описания источника данных: %s' % values) try: # Создание клиента OPC connection = self.create_connection(self.uni_host) if connection is None: msg = u'Не возможно создать объект связи с UniReader. Хост <%s>' % self.uni_host log.error(msg) journal.write_msg(msg) return None # Контроль наличия процедуры чтения значений из OPC сервера rpc_methods = connection.system.listMethods() if 'sources.ReadValueAsString' not in rpc_methods: msg = u'UniReader. Процедура чтения значения из OPC сервера не найдена. Хост <%s>' % self.uni_host log.error(msg) journal.write_msg(msg) return None # Подготовка переменных для чтения # Адреса всегда задаются строками addresses = self._gen_addresses(*values) log.debug(u'UniReader. Чтение адресов %s' % addresses) # Прочитать из OPC сервера result = list() for address in addresses: value = connection.sources.ReadValueAsString( 'OPC_SERVER_NODE', self.opc_server, address) result.append(self.recode(value) if value else None) # Регистрация состояния state = dict([(values[i], val) for i, val in enumerate(result)]) self.reg_state(**state) log.debug(u'UniReader. Результат чтения данных:') for i, value in enumerate(result): log.debug(u'\t%s\t=\t%s' % (addresses[i], value)) return result except: msg = u'UniReader. Ошибка чтения данных <%s>' % self.__class__.__name__ log.fatal(msg) journal.write_msg(msg) return None
ВНИМАНИЕ! В переменной values в INI файле настроек задаются только адреса читаемые из OPC сервера Другие внутренние переменные могут учавствовать в генерации адресов, но не указываются как values. В противном случае будет exception при чтении данных из OPC сервера. """ from ic.utils import log from ic.utils import journal from ic.utils import txtgen from ic.utils import execfunc from ic import config try: import xmlrpclib except ImportError: log.fatal(u'UniReader OPC Data Source. Import error <xmlrpclib>') from ic import datasrc_proto __version__ = (0, 0, 0, 1) UNI_SERVER_URL_FMT = 'http://%s:%d' DEFAULT_PORT = 8080 class icUniReaderOPCDataSource(datasrc_proto.icDataSourceProto): """ Источник данных UniReader. UniReader используется как шлюз для доступа к OPC серверу. """
ВНИМАНИЕ! В переменной values в INI файле настроек задаются только адреса читаемые из OPC сервера Другие внутренние переменные могут учавствовать в генерации адресов, но не указываются как values. В противном случае будет exception при чтении данных из OPC сервера. """ from ic.utils import log from ic.utils import journal from ic.utils import txtgen from ic.utils import execfunc from ic import config try: import OpenOPC except ImportError: log.fatal(u'RSLinx Data Source. Import error <OpenOPC>') from ic import datasrc_proto __version__ = (0, 0, 5, 1) class icRSLinxDataSource(datasrc_proto.icDataSourceProto): """ Источник данных OPC сервер RSLinx. """ def __init__(self, *args, **kwargs): """ Конструктор. """ datasrc_proto.icDataSourceProto.__init__(self, *args, **kwargs)
def diagnostic(self): """ Простая процедура прверки доступа к источнику данных. @return: True/False. """ opc = None log.info(u'Диагностика <%s>.<%s>' % (self.__class__.__name__, self.name)) if self.opc_server is None: msg = u'Не определен OPC сервер в <%s>' % self.name log.warning(msg) journal.write_msg(msg) return False if self.topic is None: msg = u'Не определен топик в <%s>' % self.name log.warning(msg) journal.write_msg(msg) return False try: # Создание клиента OPC opc = self.create_opc_client(self.opc_host) if opc is None: msg = u'Не возможно создать объект клиента OPC. Хост <%s>' % self.opc_host log.error(msg) journal.write_msg(msg) return None # Список серверов OPC servers = opc.servers() if self.opc_server not in servers: msg = u'Сервер <%s> не найден среди %s' % (self.opc_server, servers) log.warning(msg) journal.write_msg(msg) opc.close() return False server = self.opc_server log.info(u'Диагностика OPC сервера <%s>' % server) # Соедиенение с сервером opc.connect(server) # Вывод состояния сервера log.info(u'Общая информация о OPC сервере') info_list = opc.info() for inf in info_list: log.info(u'\t%s' % str(inf)) # Список интерфейсов servers topics = opc.list() if self.topic not in topics: msg = u'Топик <%s> не найден среди %s' % (self.topic, topics) log.warning(msg) journal.write_msg(msg) opc.close() return False # Режимы одного топика topic = topics[topics.index(self.topic)] log.info(u'Топик/Интерфейс: %s' % topic) modes = opc.list(topic) log.info(u'\tРежимы: %s' % modes) # Переменные for mode in modes: address = topic+u'.'+mode tags = opc.list(address) log.info(u'\t\tТеги <%s>:' % address) for tag in tags: log.info(u'\t\t\t%s' % tag) # Закрытие соединения opc.close() return True except: if opc: opc.close() msg = u'Ошибка диагностики <%s>' % self.__class__.__name__ log.fatal(msg) journal.write_msg(msg) return False
def _read(self, *values): """ Чтение данных из источника данных. @param values: Список читаемых переменных. @return: Список прочитанных значений. Если переменная не найдена или произошла ошибка чтения, то вместо значения подставляется None с указанием WARNING в журнале сообщений. """ opc = None if not values: log.warning(u'Не определены переменные для чтения в <%s>' % self.name) values = self.addresses log.debug(u'Переменные взяты из описания источника данных: %s' % values) try: # Создание клиента OPC opc = self.create_opc_client(self.opc_host) if opc is None: msg = u'Не возможно создать объект клиента OPC. Хост <%s>' % self.opc_host log.error(msg) journal.write_msg(msg) return None # Список серверов OPC servers = opc.servers() if self.opc_server not in servers: msg = u'Сервер <%s> не найден среди %s' % (self.opc_server, servers) log.warning(msg) journal.write_msg(msg) opc.close() return None # Соедиенение с сервером server = self.opc_server opc.connect(server) # Подготовка переменных для чтения # Адреса всегда задаются строками addresses = self._gen_addresses(*values) log.debug(u'Чтение адресов %s' % addresses) # Прочитать из OPC сервера result = [self.recode(val[1]) if val and val[2] == 'Good' else None for val in opc.read(addresses)] # result = [val.encode('') if isinstance(val, unicode) else val for val in result] # result = [val for val in opc.read(addresses)] opc.close() # Регистрация состояния state = dict([(values[i], val) for i, val in enumerate(result)]) self.reg_state(**state) log.debug(u'Результат чтения данных %s' % result) return result except: if opc: opc.close() msg = u'Ошибка чтения данных <%s>' % self.__class__.__name__ log.fatal(msg) journal.write_msg(msg) return None