def _write(self, *values): """ Записать данные в приемник данных. @param values: Список записываемых значений @return: True/False. """ if self.template_fmt is None: msg = u'Не указан шаблон для заполнения форматированного текстового файла.' log.warning(msg) journal.write_msg(msg) return False if self.output_fmt is None: msg = u'Не указан выходной файл для заполнения форматированного текстового файла.' log.warning(msg) journal.write_msg(msg) return False context = self.get_values_as_dict() template = txtgen.gen(self.template_fmt, context) output = txtgen.gen(self.output_fmt, context) # Заполняем шаблон переменными gen_result = txtgen.gen_txt_file(template, output, context) return gen_result
def create_connection(self, host=None): """ Создание объекта связи с UniReader XMLRPC сервером. @param host: Хост OPC сервера для возможности удаленного подключения (через DCOM) к OPC серверу. Если не определен, то считается что OPC сервер находится локально. @return: Объект Uni - сервера. """ if type(host) not in (None.__class__, str, unicode): msg = u'UniReader. Не корректный тип хоста OPC сервера <%s>' % type( host) log.error(msg) journal.write_msg(msg) return None is_local_opc = (host is None) or (type(host) in (str, unicode) and host.lower().strip() in ('localhost', '127.0.0.1')) if is_local_opc: log.info(u'UniReader. OPC сервер находится локально') else: log.info(u'UniReader. OPC сервер находится на <%s>' % host) url = UNI_SERVER_URL_FMT % (host, self.uni_port) log.info(u'UniReader. URL для подключения к Uni-серверу: <%s>' % url) connection = xmlrpclib.ServerProxy(url) return connection
def get_xml_filenames(self): """ Определить список обрабатываемых XML файлов. @return: Cписок обрабатываемых XML файлов. """ if self.cache_xml_filenames: # Работаем с мгновенным слепком списка файлов return self.cache_xml_filenames if self.xml_filename is None: msg = u'Не определены XML файлы источника данных <%s>' % self.__class__.__name__ log.warning(msg) journal.write_msg(msg) return list() xml_filenames = list() if self.is_filename_pattern(self.xml_filename): # Если это шаблон, то получить список файлов xml_filenames = glob.glob(self.xml_filename) log.info(u'Найдены XML файлы в <%s>:' % self.xml_filename) for xml_filename in xml_filenames: log.info(u'\t%s' % xml_filename) # Это указание конкретного файла if os.path.exists(self.xml_filename): self.cache_xml_filenames = [self.xml_filename] return self.cache_xml_filenames # else: # log.warning(u'Не найден XML файл <%s> источника данных' % self.xml_filename) self.cache_xml_filenames = xml_filenames return self.cache_xml_filenames
def parse_inbox_docs(self, doc_uuids=None, str_replaces=None, **content_links): """ Произвести сборку данных входных документов по запрашиваемым данным. @param doc_uuids: Список UUID необходимых входных документов. Если None, то обрабатываются все документы. @param str_replaces: Словарь замены в строковых значениях. Если словарь не определен замены не производятся. @param content_links: Словарь запрашиваемых данных у каждого документа. Например: { 'error': 'A/error', 'transport_uuid': 'A/url' } @return: Список заполненных словарей запрашиваемых данных для каждого документа. """ if doc_uuids is None: doc_uuids = self.get_inbox_doc_uuid() result = list() for doc_uuid in doc_uuids: if not doc_uuid: msg = u'УТМ. Не корректный UUID запрашиваемого документа <%s>' % doc_uuid log.warning(msg) journal.write_msg(msg) continue doc = self.parse_inbox_doc(doc_uuid, str_replaces=str_replaces, **content_links) if doc is not None: result.append(doc) log.debug(u'УТМ. Сборка данных входных документов %s' % result) return result
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 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 find_inbox_document(self, doc_uuid): """ Найти входящий документ по его UUID. @param doc_uuid: UUID документа. @return: Словарь содержимого документа или None если документ не найден или ошибка. """ if self.cache_state and INBOX_STATE_NAME in self.cache_state: if doc_uuid in self.cache_state[INBOX_STATE_NAME]: return self.cache_state[INBOX_STATE_NAME][doc_uuid] else: msg = u'Кеш источника данных <%s>. Документ <%s> не найден во входящих' % ( self.name, doc_uuid) log.warning(msg) journal.write_msg(msg) else: content = self.get_http('/opt/out') if content: document_urls = content['A'].get('url', list()) if type(document_urls) not in (list, tuple): # ВНИМАНИЕ! Может быть только 1 документ # Все равно мы должны обрабатывать список document_urls = [document_urls] if not document_urls: msg = u'Список входящих документов пуст' log.warning(msg) journal.write_msg(msg) for document_url in document_urls: if isinstance( document_url, dict) and document_url['@replyId'] == doc_uuid: document_content = self.get_http(document_url['#text']) # ВНИМАНИЕ! Необходимо кроме содержания запоминать URL и ReplyID документа # иначе нельзя будет идентифицировать документ # return document_content return dict(url=document_url['#text'], uuid=doc_uuid, content=document_content) elif isinstance(document_url, str) or isinstance( document_url, unicode): # Внимание! Если документ имеет родительский документ - то есть и replyId, # если документ первичный - то и нет для него replyId # Т.е. не правильно ого искать по UUID log.warning( u'Поиск. Внешний первичный документ <%s> пропущен' % document_url) continue msg = u'Документ <%s> не найден во входящих' % doc_uuid log.warning(msg) journal.write_msg(msg) else: msg = u'Нет ответа от УТМ' log.warning(msg) journal.write_msg(msg) return None
def valid_content(self, content): """ Анализ содержания на ошибки. @param content: Содержание в текстовом виде. """ if CURL_HTTP_404_ERR in content: msg = u'Http 404' log.error(msg) log.error(content) journal.write_msg(msg) return None elif CURL_HTTP_500_ERR in content: msg = u'Http 500' log.error(msg) log.error(content) journal.write_msg(msg) return None return content
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 disconnect(self, connection=None): """ Разорвать соединение с БД. @param connection: Объект связи с БД. @return: True/False. """ if connection is None: connection = self.connection if connection: connection.dispose() if connection == self.connection: self.connection = None connection = None return True else: msg = u'Не определен объект связи с БД в <%s>' % self.name log.warning(msg) journal.write_msg(msg) return False
def write(self, *values): """ Записать данные в приемник данных. Функция выполняется с пред... и пост... обработкой. @param values: Список записываемых значений @return: True/False. """ if self.all_values: # Необходимо провести проверку на заполнение всех значений if not self.valid_all_values(*values): msg = u'Не полностью заполнены входные значения для генерации файла <%s>' % str(self.output_fmt) log.warning(msg) journal.write_msg(msg) return False # Перед выполнение произвести замену из контекста return execfunc.exec_prev_post_decorate(self._write, self.gen_code(self.prev_cmd), self.gen_code(self.post_cmd), *values)
def create_opc_client(self, opc_host=None): """ Создание объекта OPC клиента. @param opc_host: Хост OPC сервера для возможности удаленного подключения (через DCOM) к OPC серверу. Если не определен, то считается что OPC сервер находится локально. @return: Объект OPC сервера. """ if type(opc_host) not in (None.__class__, str, unicode): msg = u'Не корректный тип хоста OPC сервера <%s>' % type(opc_host) log.error(msg) journal.write_msg(msg) return None is_local_opc = (opc_host is None) or (type(opc_host) in (str, unicode) and opc_host.lower().strip() in ('localhost', '127.0.0.1')) if is_local_opc: log.info(u'OPC сервер находится локально') opc = OpenOPC.client() else: log.info(u'OPC сервер находится на <%s>' % opc_host) opc = OpenOPC.open_client(opc_host) return opc
def get_content_error(self, content): """ Проверка возвращаемого значение/содержания на ошибки. @param content: Возвращаемое значение какой либо операции. @return: Текст ошибки, None - нет ошибки. """ if content is None: msg = u'Не определено содержание для проверки на ошибки' log.warning(msg) journal.write_msg(msg) return None if 'A' not in content: msg = u'Не корректный формат <%s>' % content log.warning(msg) journal.write_msg(msg) return None if 'error' in content['A']: err_txt = content['A']['error'] # Отобразить контент для отладки msg = u'Ошибка содержимого: <%s>' % content log.error(msg) journal.write_msg(msg) return err_txt 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(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 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: Список имен файлов. """ if not self.filename_pattern: msg = u'Не определен шаблон поиска файлов' log.warning(msg) journal.write_msg(msg) return list() filenames = glob.glob(self.filename_pattern) log.info(u'Найденные файлы:') for filename in filenames: log.info(u'\t%s' % filename) self.state = dict([(os.path.basename(filename), os.path.abspath(filename)) for filename in filenames]) return filenames
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 parse_inbox_doc(self, doc_uuid, str_replaces=None, **content_links): """ Произвести сборку данных входных документов по запрашиваемым данным. @param doc_uuid: UUID необходимого входного документа. @param str_replaces: Словарь замены в строковых значениях. Если словарь не определен замены не производятся. @param content_links: Словарь запрашиваемых данных у каждого документа. Например: { 'error': 'A/error', 'transport_uuid': 'A/url' } @return: Список заполненных словарей запрашиваемых данных для документа. """ if doc_uuid is None: msg = u'УТМ. Не определен UUID документа при запросе данных' log.warning(msg) journal.write_msg(msg) return dict([(name, None) for name in content_links.keys()]) doc_content = self.find_inbox_document(doc_uuid) if doc_content: result = dict() for name, link in content_links.items(): value = xmlfunc.get_xml_content_by_link(doc_content, link) if str_replaces and type(value) in (str, unicode): for replace_src, replace_dst in str_replaces.items(): value = value.replace(replace_src, replace_dst) result[name] = value return result else: msg = u'УТМ. Ошибка чтения содержимого документа <%s>' % doc_uuid log.warning(msg) journal.write_msg(msg) return None
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_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 _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 _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 get_inbox_documents(self): """ Запросить входящие документы. """ if self.cache_state and INBOX_STATE_NAME in self.cache_state: return self.cache_state[INBOX_STATE_NAME].values() content = self.get_http('/opt/out') if content is None: msg = u'Ошибка определения данных по адресу </opt/out>' log.warning(msg) journal.write_msg(msg) return None if not self.get_content_error(content): documents = list() if 'A' not in content: # Если не ссылка на документ, то это просто его содержание documents.append( dict(url=u'', uuid=str(uuid.uuid4()), content=content)) else: document_urls = content['A'].get('url', list()) if type(document_urls) not in (list, tuple): # ВНИМАНИЕ! Может быть только 1 документ # Все равно мы должны обрабатывать список document_urls = [document_urls] if not document_urls: msg = u'Список входящих документов ЕГАИС УТМ пуст' log.warning(msg) journal.write_msg(msg) return documents else: # log.debug(u'Обрабатываемые документы. URLs:') for document_url in document_urls: log.debug(u'\t%s' % document_url) for document_url in document_urls: if isinstance(document_url, str) or isinstance( document_url, unicode): log.debug(u'Получаем документ URL (str) <%s>' % document_url) document_content = self.get_http(document_url) documents.append( dict(url=document_url, uuid=str(uuid.uuid4()), content=document_content)) elif isinstance(document_url, dict): log.debug(u'Получаем документ URL (dict) <%s>' % document_url['#text']) document_content = self.get_http(document_url['#text']) documents.append( dict(url=document_url['#text'], uuid=document_url['@replyId'], content=document_content)) else: msg = u'Не обрабатываемый тип адреса документа <%s>' % document_url log.warning(msg) journal.write_msg(msg) return documents else: return None
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
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
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