1# -*- coding: utf-8 -*-
2# Copyright 2019 Oticon A/S
3# SPDX-License-Identifier: Apache-2.0
4
5import string;
6import re;
7from xml.dom import minidom;
8
9class GATTData:
10    __instance = None;
11
12    @staticmethod
13    def instance():
14        if GATTData.__instance == None:
15           GATTData();
16        return GATTData.__instance;
17
18    def __init__(self):
19        if GATTData.__instance != None:
20            raise Exception("GATTData is a Singleton!");
21        else:
22            GATTData.__instance = self;
23            self.mydoc = minidom.parse('src/components/GATT Database.xml');
24
25    def __handleRange(self, service):
26        characteristics = service.getElementsByTagName('Characteristic');
27        descriptors = service.getElementsByTagName('Descriptor');
28        includes = service.getElementsByTagName('Include');
29
30        first = int(service.attributes['handle'].value);
31        last = [];
32        if len(includes) > 0:
33            last += [ max([int(include.attributes['handle'].value) for include in includes]) ];
34        if len(descriptors) > 0:
35            last += [ max([int(descriptor.attributes['handle'].value) for descriptor in descriptors]) ];
36        if len(characteristics) > 0:
37            last += [ max([int(characteristic.attributes['handle'].value) for characteristic in characteristics]) ];
38            last[-1] += 1;
39        return first, max(last) if len(last) > 0 else first;
40
41    def __allInSet(self, setNo, elementType):
42        sets = self.mydoc.getElementsByTagName('ServiceSet_%d' % setNo);
43        return sets[0].getElementsByTagName(elementType);
44
45    def __shortServices(self, setNo, serviceType=None, uuid=None):
46        services = { 'uuid': [], 'handle': [] };
47
48        alls = self.__allInSet(setNo, 'Service');
49        for service in alls:
50            if (serviceType is None) or (serviceType == service.attributes['type'].value):
51                _uuid = int(service.attributes['uuid'].value, 16);
52                if (uuid is None) or (uuid == _uuid):
53                    services['uuid'] += [ _uuid ];
54                    first, last = self.__handleRange( service );
55                    services['handle'] += [ first ];
56        return services;
57
58    def __services(self, setNo, serviceType=None, uuid=None):
59        services = { 'uuids': [], 'handles': [] };
60
61        alls = self.__allInSet(setNo, 'Service');
62        for service in alls:
63            if (serviceType is None) or (serviceType == service.attributes['type'].value):
64                _uuid = int(service.attributes['uuid'].value, 16);
65                if (uuid is None) or (uuid == _uuid):
66                    services['uuids'] += [ _uuid ];
67                    first, last = self.__handleRange( service );
68                    services['handles'] += [ [first, last] ];
69        return services;
70
71    def allServices(self, setNo):
72        return self.__services(setNo);
73
74    def primaryServices(self, setNo, uuid=None):
75        return self.__services(setNo, 'Primary Service', uuid);
76
77    def secondaryServices(self, setNo, uuid=None):
78        return self.__shortServices(setNo, 'Secondary Service', uuid);
79
80    def includedServices(self, setNo):
81        includes = { 'uuids': [], 'handles': [] };
82
83        alls = self.__allInSet(setNo, 'Include');
84        for include in alls:
85            includes['uuids'] += [ int(include.attributes['uuid'].value, 16) ];
86            includes['handles'] += [ [ int(include.attributes['first'].value), int(include.attributes['last'].value) ] ];
87        return includes;
88
89    def descriptors(self, setNo, serviceHandle=None, permissionsMask=None, invertMask=False):
90        descriptors = { 'uuid': [], 'handle': [] };
91
92        alls = self.__allInSet(setNo, 'Service');
93        for service in alls:
94            _serviceHandle = int(service.attributes['handle'].value);
95            if (serviceHandle is None) or (serviceHandle == _serviceHandle):
96                for descriptor in service.getElementsByTagName('Descriptor'):
97                    _permissions = int(descriptor.attributes['permissions'].value);
98                    if (permissionsMask is None) or ((_permissions & permissionsMask) == (0 if invertMask else permissionsMask)):
99                        descriptors['uuid'] += [ int(descriptor.attributes['uuid'].value, 16) ];
100                        descriptors['handle'] += [ int(descriptor.attributes['handle'].value) ];
101                if not serviceHandle is None:
102                    break;
103        return descriptors;
104
105    def serviceCovering(self, setNo, handle):
106        service = {};
107
108        services = self.__services(setNo);
109        for _uuid, _handles in zip(services['uuids'], services['handles']):
110            if _handles[0] <= handle and handle <= _handles[1]:
111                service['uuid'] = _uuid;
112                service['handles'] = _handles;
113                break;
114        return service;
115
116    def characteristics(self, setNo, serviceHandle=None, permissionsMask=None, invertMask=False):
117        characteristics = { 'uuid': [], 'handle': [], 'value_handle': [], 'property': [], 'permission': [] };
118
119        alls = self.__allInSet(setNo, 'Service');
120        for service in alls:
121            _serviceHandle = int(service.attributes['handle'].value);
122            if (serviceHandle is None) or (serviceHandle == _serviceHandle):
123                for characteristic in service.getElementsByTagName('Characteristic'):
124                    _permissions = int(characteristic.attributes['permissions'].value);
125                    if (permissionsMask is None) or ((_permissions & permissionsMask) == (0 if invertMask else permissionsMask)):
126                        characteristics['uuid'] += [ int(characteristic.attributes['uuid'].value, 16) ];
127                        characteristics['handle'] += [ int(characteristic.attributes['handle'].value) ];
128                        characteristics['value_handle'] += [ int(characteristic.attributes['handle'].value)+1 ];
129                        characteristics['property'] += [ int(characteristic.attributes['properties'].value) ];
130                        characteristics['permission'] += [ int(characteristic.attributes['permissions'].value) ];
131                if not serviceHandle is None:
132                    break;
133        return characteristics;
134
135    def __toArray(self, value):
136        data = [];
137        if value:
138            if re.match('[0-9A-F]{2}( [0-9A-F]{2})+', value):
139               data = [ int(hexNumber, 16) for hexNumber in value.split(' ') ];
140            elif len(value) > 2:
141               data = [ ord(char) for char in value ];
142            else:
143               data = [ int(value, 16) ];
144        return data;
145
146    def characteristicValue(self, setNo, handle):
147        value = None;
148        alls = self.__allInSet(setNo, 'Characteristic');
149        for characteristic in alls:
150            if int(characteristic.attributes['handle'].value) == handle:
151                value = characteristic.firstChild.data.strip('\r\n\t');
152                break;
153        return self.__toArray(value);
154
155    def characteristicString(self, setNo, handle):
156        value = None;
157        alls = self.__allInSet(setNo, 'Characteristic');
158        for characteristic in alls:
159            if int(characteristic.attributes['handle'].value) == handle:
160                value = characteristic.firstChild.data.strip('\r\n\t');
161                break;
162        return value;
163
164    def descriptorValue(self, setNo, handle):
165        value = None;
166        alls = self.__allInSet(setNo, 'Descriptor');
167        for descriptor in alls:
168            if int(descriptor.attributes['handle'].value) == handle:
169                value = descriptor.firstChild.data.strip('\r\n\t');
170                break;
171        return self.__toArray(value);
172
173    def descriptorString(self, setNo, handle):
174        value = None;
175        alls = self.__allInSet(setNo, 'Descriptor');
176        for descriptor in alls:
177            if int(descriptor.attributes['handle'].value) == handle:
178                value = descriptor.firstChild.data.strip('\r\n\t');
179                break;
180        return value;
181
182    def characteristicWithDescriptor(self, setNo, handle):
183        j, _characteristics = -1, self.characteristics(setNo);
184        for i, _handle in enumerate(_characteristics['handle']):
185            if _handle > handle:
186                j = i-1;
187                break;
188        return { 'uuid':         _characteristics['uuid'][j],
189                 'handle':       _characteristics['handle'][j],
190                 'value_handle': _characteristics['value_handle'][j],
191                 'property':     _characteristics['property'][j],
192                 'permission':   _characteristics['permission'][j] };
193