Snippets

Ilnur Ibragimov Разбор SOAP протокола BN-Complex

Created by Ilnur Ibragimov
# -*- coding: utf-8 -*-
from mx.DateTime import DateTimeFrom
from functools import partial
from receiver.protocols import generic
from core.models import Mark
from utils.web import HTTPConnection, Application, RequestHandler
from tornado import template
from core.logger import get_logger
from core.constants import MAX_SENSOR_INDEX

import xml.etree.cElementTree as ET

ignore_exceptions = []


class ProtocolHandler(generic.ProtocolHandler):
    '''
    BN-Complex protocol handler
    '''

    def __init__(self, stream, address=None, redis_cl=None, DEBUG=False, receiver=None, stream_close_callback=None, device_dict=None, **kwargs):
        self.stream = stream
        self.io_loop = stream.io_loop
        self.address = address
        self.receiver = receiver
        self.device_list = receiver.device_list
        self.close_callback = stream_close_callback
        self.device_dict = device_dict
        self.redis_cl = redis_cl
        self.DEBUG = DEBUG
        self.in_a = {}
        self.in_d = {}
        self.device_id = None
        self.logger = get_logger(__name__, ignore_exceptions)

    def start(self):
        self.http_connection = HTTPConnection(self.stream, self.address, application,
            extra={'receiver': self.receiver, 'handler': self}, xheaders=True)

    def stop(self):
        self.stream.close()
        return


def datetime_to_kml(source):
    dt = source
    if dt.year <= 1900:
        return '1970-01-01T00:00:00Z'
    return dt.strftime('%Y-%m-%dT%H:%M:%SZ')

def parse_field(field, mark_dict):
    tag = field.tag
    attrs = field.attrib
    text = field.text

    if tag == 'ObjectID':
        mark_dict['device_id'] = int(text)
        mark_dict['device_id_original'] = text
    elif tag == 'Coord':
        mark_dict['datetime'] = int(DateTimeFrom(attrs['time']).gmticks())
        mark_dict['datetime_original'] = attrs['time']
        mark_dict['point'] = {
            'latitude': float(attrs['lat']),
            'longitude': float(attrs['lon'])
        }
        mark_dict['azimuth'] = float(attrs['dir'])
        mark_dict['is_valid'] = bool(int(attrs['valid']))
        mark_dict['speed'] = float(attrs['speed'])
        mark_dict['hdop'] = int(attrs.get('hdop', 1))
        mark_dict['satcount'] = int(attrs.get('satcount', 10))
        mark_dict['altitude'] = int(float(attrs.get('alt', 0)))
    elif tag == 'DigI':
        inpnum = attrs.get('inpnum')
        try:
            inpnum = int(inpnum)
        except:
            inpnum = None

        if inpnum is not None:
            mark_dict['in_d'][inpnum] = 1

    elif tag == 'AnalogI':
        num = attrs.get('num')
        val = attrs.get('val')

        try:
            num = int(num)
        except:
            num = None

        try:
            val = float(val)
        except:
            val = None

        if num is not None:
            mark_dict['in_a'][num] = val


response_tmpl = template.Template(
'''<?xml version="1.0" encoding="windows-1251"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope" xmlns:ws="http://ws">
<soapenv:Header/>
<soapenv:Body>
    {% for m in marks %}
    <ws:PutCoordResponce>
    <ObjectID>{{ m['device_id_original'] }}</ObjectID>
    <Coord time="{{ m['datetime_original'] }}"/>
    {% for i in m['in_d'] %}
    <DigI inpnum="{{ i }}"/>
    {% end %}
    {% for i in m['in_a'] %}
    <AnalogI num="{{ i }}" val="{{ m['in_a'][i] }}"/>
    {% end %}
    </ws:PutCoordResponce>
    {% end %}
</soapenv:Body>
</soapenv:Envelope>
''')

# Envelope.Body.Fault.Reason.Text
error_tmpl = template.Template(
'''<?xml version="1.0" encoding="windows-1251"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope" xmlns:ws="http://ws">
<soapenv:Header/>
<soapenv:Body>
    <Fault>
        <Reason>
            <Text>{{ error_text }}</Text>
        </Reason>
    </Fault>
</soapenv:Body>
</soapenv:Envelope>
''')


class MainHandler(RequestHandler):

    def prepare(self):
        self.redis = self.application._extra['receiver'].redis
        self.handler = self.application._extra['handler']

    def on_finish(self):
        self.handler.close_callback()

    def post(self):
        self.set_header('Server', 'Microsoft-IIS/7.5')

        RNIC_PREFIX = self.request.headers.get('X-RNIC-PREFIX')
        if not RNIC_PREFIX:
            response = error_tmpl.generate(error_text='RNIC prefix not found')
            self.write(response)
            return
        ALWAYS_SEND_ACK = self.request.headers.get('X-ALWAYS-SEND-ACK') == '1'
        PROJECT_MARKS_FILTER = self.request.headers.get('X-PROJECT-MARKS-FILTER', '')

        filtered_list = filter(lambda x: not x[3] and x[2].startswith(RNIC_PREFIX), self.handler.device_list)

        device_map = {str(dev[2]).lstrip('0'): dev[0] for dev in filtered_list}
        device_map.update({str(dev[1]).lstrip('0'): dev[0] for dev in filtered_list})
        try:
            new_header = u'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope" xmlns:ws="http://ws">'
            # удаляем заголовок xml текста
            text = u'%s%s' % (new_header, self.request.body[self.request.body.index('<soapenv:Body'):])
            tree = ET.fromstring(text)

            body = tree.find('./{http://schemas.xmlsoap.org/soap/envelope}Body')
            marks = []
            for message in body:
                MARK_FOUND = False
                PROJECT_MARK = True
                mark_dict = {'in_d': {}, 'in_a': {}}
                for field in message:
                    parse_field(field, mark_dict)
                m = Mark.from_dict(mark_dict)
                m.datetime = mark_dict['datetime']

                if m.device_id and m.datetime and self.handler.on_mark_callback:
                    sn = m.device_id
                    try:
                        m.device_id = device_map[str(sn)]
                        MARK_FOUND = True
                    except KeyError:
                        if self.handler.on_unknown_device_callback:
                            self.handler.io_loop.add_callback(partial(self.handler.on_unknown_device_callback, str(sn)))
                        if not ALWAYS_SEND_ACK:
                            continue
                    marks.append(mark_dict)

                    # sensor handling stuff. in_d is zero (inactive) if nothing is transmitted
                    self.handler.in_a = mark_dict['in_a'].copy()
                    self.handler.in_d = mark_dict['in_d'].copy()
                    # we have MAX_SENSOR_INDEX pins, they may start with 1 or 0
                    for i in range(MAX_SENSOR_INDEX+1):
                        if i not in self.handler.in_d:
                            self.handler.in_d[i] = 0

                    if MARK_FOUND:
                        if PROJECT_MARKS_FILTER:
                            PROJECT_MARK = not mark_dict['device_id_original'].startswith(PROJECT_MARKS_FILTER)
                        self.handler.device_id = m.device_id
                        self.handler.io_loop.add_callback(
                            partial(
                                self.handler.on_mark_callback,
                                m,
                                prefix=RNIC_PREFIX,
                                project_mark=PROJECT_MARK))
                        self.handler.process_inputs(m.datetime)
                        self.handler.process_alarm_button(m.datetime)

        except Exception as e:
            response = error_tmpl.generate(error_text=str(e))
            self.write(response)
            return

        response = response_tmpl.generate(datetime_to_kml=datetime_to_kml, marks=marks)
        self.write(response)


handlers = [
    (r'/', MainHandler)
]

application = Application(handlers)

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.