xref: /btstack/tool/compile_gatt.py (revision b3fcedb9c9ccbcc9680da165cd86f394be5d84a8)
1*b3fcedb9SMatthias Ringwald#!/usr/bin/env python
2*b3fcedb9SMatthias Ringwald#
3*b3fcedb9SMatthias Ringwald# BLE GATT configuration generator for use with BTstack, v0.1
4*b3fcedb9SMatthias Ringwald# Copyright 2011 Matthias Ringwald
5*b3fcedb9SMatthias Ringwald#
6*b3fcedb9SMatthias Ringwald# Format of input file:
7*b3fcedb9SMatthias Ringwald# PRIMARY_SERVICE, SERVICE_UUID
8*b3fcedb9SMatthias Ringwald# CHARACTERISTIC, ATTRIBUTE_TYPE_UUID, [READ | WRITE | DYNAMIC], VALUE
9*b3fcedb9SMatthias Ringwald
10*b3fcedb9SMatthias Ringwaldimport re
11*b3fcedb9SMatthias Ringwaldimport io
12*b3fcedb9SMatthias Ringwaldimport csv
13*b3fcedb9SMatthias Ringwaldimport string
14*b3fcedb9SMatthias Ringwaldimport codecs
15*b3fcedb9SMatthias Ringwald
16*b3fcedb9SMatthias Ringwaldheader = '''
17*b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack
18*b3fcedb9SMatthias Ringwald
19*b3fcedb9SMatthias Ringwald// binary representation
20*b3fcedb9SMatthias Ringwald// attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
21*b3fcedb9SMatthias Ringwald
22*b3fcedb9SMatthias Ringwald#include <stdint.h>
23*b3fcedb9SMatthias Ringwald
24*b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] =
25*b3fcedb9SMatthias Ringwald'''
26*b3fcedb9SMatthias Ringwald
27*b3fcedb9SMatthias Ringwaldusage = '''
28*b3fcedb9SMatthias RingwaldUsage: ./compile_gatt.py profile.gatt profile.h
29*b3fcedb9SMatthias Ringwald'''
30*b3fcedb9SMatthias Ringwald
31*b3fcedb9SMatthias Ringwaldimport re
32*b3fcedb9SMatthias Ringwaldimport sys
33*b3fcedb9SMatthias Ringwald
34*b3fcedb9SMatthias Ringwaldprint('''
35*b3fcedb9SMatthias RingwaldBLE configuration generator for use with BTstack, v0.1
36*b3fcedb9SMatthias RingwaldCopyright 2011 Matthias Ringwald
37*b3fcedb9SMatthias Ringwald''')
38*b3fcedb9SMatthias Ringwald
39*b3fcedb9SMatthias Ringwaldassigned_uuids = {
40*b3fcedb9SMatthias Ringwald    'GAP_SERVICE'          : 0x1800,
41*b3fcedb9SMatthias Ringwald    'GATT_SERVICE'         : 0x1801,
42*b3fcedb9SMatthias Ringwald    'GAP_DEVICE_NAME'      : 0x2a00,
43*b3fcedb9SMatthias Ringwald    'GAP_APPEARANCE'       : 0x2a01,
44*b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02,
45*b3fcedb9SMatthias Ringwald    'GAP_RECONNECTION_ADDRESS'    : 0x2A03,
46*b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04,
47*b3fcedb9SMatthias Ringwald    'GATT_SERVICE_CHANGED' : 0x2a05,
48*b3fcedb9SMatthias Ringwald}
49*b3fcedb9SMatthias Ringwald
50*b3fcedb9SMatthias Ringwaldproperty_flags = {
51*b3fcedb9SMatthias Ringwald    'BROADCAST' :                   0x01,
52*b3fcedb9SMatthias Ringwald    'READ' :                        0x02,
53*b3fcedb9SMatthias Ringwald    'WRITE_WITHOUT_RESPONSE' :      0x04,
54*b3fcedb9SMatthias Ringwald    'WRITE' :                       0x08,
55*b3fcedb9SMatthias Ringwald    'NOTIFY':                       0x10,
56*b3fcedb9SMatthias Ringwald    'INDICATE' :                    0x20,
57*b3fcedb9SMatthias Ringwald    'AUTHENTICATED_SIGNED_WRITE' :  0x40,
58*b3fcedb9SMatthias Ringwald    'EXTENDED_PROPERTIES' :         0x80,
59*b3fcedb9SMatthias Ringwald    # custom BTstack extension
60*b3fcedb9SMatthias Ringwald    'DYNAMIC':                     0x100,
61*b3fcedb9SMatthias Ringwald    'LONG_UUID':                   0x200,
62*b3fcedb9SMatthias Ringwald    'AUTHENTICATION_REQUIRED':     0x400,
63*b3fcedb9SMatthias Ringwald    'AUTHORIZATION_REQUIRED':      0x800,
64*b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_7':      0x6000,
65*b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_8':      0x7000,
66*b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_9':      0x8000,
67*b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_10':     0x9000,
68*b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_11':     0xa000,
69*b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_12':     0xb000,
70*b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_13':     0xc000,
71*b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_14':     0xd000,
72*b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_15':     0xe000,
73*b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_16':     0xf000,
74*b3fcedb9SMatthias Ringwald    # only used by gatt compiler >= 0xffff
75*b3fcedb9SMatthias Ringwald    # Extended Properties
76*b3fcedb9SMatthias Ringwald    'RELIABLE_WRITE':             0x10000,
77*b3fcedb9SMatthias Ringwald}
78*b3fcedb9SMatthias Ringwald
79*b3fcedb9SMatthias Ringwaldservices = dict()
80*b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict()
81*b3fcedb9SMatthias Ringwaldpresentation_formats = dict()
82*b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = ""
83*b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0
84*b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = ""
85*b3fcedb9SMatthias Ringwalddefines = []
86*b3fcedb9SMatthias Ringwald
87*b3fcedb9SMatthias Ringwaldhandle = 1
88*b3fcedb9SMatthias Ringwaldtotal_size = 0
89*b3fcedb9SMatthias Ringwald
90*b3fcedb9SMatthias Ringwalddef keyForUUID(uuid):
91*b3fcedb9SMatthias Ringwald    keyUUID = ""
92*b3fcedb9SMatthias Ringwald    for i in uuid:
93*b3fcedb9SMatthias Ringwald        keyUUID += "%02x" % i
94*b3fcedb9SMatthias Ringwald    return keyUUID
95*b3fcedb9SMatthias Ringwald
96*b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid):
97*b3fcedb9SMatthias Ringwald    return uuid.replace('-', '_')
98*b3fcedb9SMatthias Ringwald
99*b3fcedb9SMatthias Ringwalddef twoByteLEFor(value):
100*b3fcedb9SMatthias Ringwald    return [ (value & 0xff), (value >> 8)]
101*b3fcedb9SMatthias Ringwald
102*b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text):
103*b3fcedb9SMatthias Ringwald    if re.match("[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}", text):
104*b3fcedb9SMatthias Ringwald        return True
105*b3fcedb9SMatthias Ringwald    return False
106*b3fcedb9SMatthias Ringwald
107*b3fcedb9SMatthias Ringwalddef parseUUID128(uuid):
108*b3fcedb9SMatthias Ringwald    parts = re.match("([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})", uuid)
109*b3fcedb9SMatthias Ringwald    uuid_bytes = []
110*b3fcedb9SMatthias Ringwald    for i in range(8, 0, -1):
111*b3fcedb9SMatthias Ringwald        uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16))
112*b3fcedb9SMatthias Ringwald    return uuid_bytes
113*b3fcedb9SMatthias Ringwald
114*b3fcedb9SMatthias Ringwalddef parseUUID(uuid):
115*b3fcedb9SMatthias Ringwald    if uuid in assigned_uuids:
116*b3fcedb9SMatthias Ringwald        return twoByteLEFor(assigned_uuids[uuid])
117*b3fcedb9SMatthias Ringwald    if is_128bit_uuid(uuid):
118*b3fcedb9SMatthias Ringwald        return parseUUID128(uuid)
119*b3fcedb9SMatthias Ringwald    uuidInt = int(uuid, 16)
120*b3fcedb9SMatthias Ringwald    return twoByteLEFor(uuidInt)
121*b3fcedb9SMatthias Ringwald
122*b3fcedb9SMatthias Ringwalddef parseProperties(properties):
123*b3fcedb9SMatthias Ringwald    value = 0
124*b3fcedb9SMatthias Ringwald    parts = properties.split("|")
125*b3fcedb9SMatthias Ringwald    for property in parts:
126*b3fcedb9SMatthias Ringwald        property = property.strip()
127*b3fcedb9SMatthias Ringwald        if property in property_flags:
128*b3fcedb9SMatthias Ringwald            value |= property_flags[property]
129*b3fcedb9SMatthias Ringwald        else:
130*b3fcedb9SMatthias Ringwald            print("WARNING: property %s undefined" % (property))
131*b3fcedb9SMatthias Ringwald    return value
132*b3fcedb9SMatthias Ringwald
133*b3fcedb9SMatthias Ringwalddef write_8(fout, value):
134*b3fcedb9SMatthias Ringwald    fout.write( "0x%02x, " % (value & 0xff))
135*b3fcedb9SMatthias Ringwald
136*b3fcedb9SMatthias Ringwalddef write_16(fout, value):
137*b3fcedb9SMatthias Ringwald    fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
138*b3fcedb9SMatthias Ringwald
139*b3fcedb9SMatthias Ringwalddef write_uuid(uuid):
140*b3fcedb9SMatthias Ringwald    for byte in uuid:
141*b3fcedb9SMatthias Ringwald        fout.write( "0x%02x, " % byte)
142*b3fcedb9SMatthias Ringwald
143*b3fcedb9SMatthias Ringwalddef write_string(fout, text):
144*b3fcedb9SMatthias Ringwald    for l in text.lstrip('"').rstrip('"'):
145*b3fcedb9SMatthias Ringwald        write_8(fout, ord(l))
146*b3fcedb9SMatthias Ringwald
147*b3fcedb9SMatthias Ringwalddef write_sequence(fout, text):
148*b3fcedb9SMatthias Ringwald    parts = text.split()
149*b3fcedb9SMatthias Ringwald    for part in parts:
150*b3fcedb9SMatthias Ringwald        fout.write("0x%s, " % (part.strip()))
151*b3fcedb9SMatthias Ringwald
152*b3fcedb9SMatthias Ringwalddef write_indent(fout):
153*b3fcedb9SMatthias Ringwald    fout.write("    ")
154*b3fcedb9SMatthias Ringwald
155*b3fcedb9SMatthias Ringwalddef is_string(text):
156*b3fcedb9SMatthias Ringwald    for item in text.split(" "):
157*b3fcedb9SMatthias Ringwald        if not all(c in string.hexdigits for c in item):
158*b3fcedb9SMatthias Ringwald            return True
159*b3fcedb9SMatthias Ringwald    return False
160*b3fcedb9SMatthias Ringwald
161*b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties):
162*b3fcedb9SMatthias Ringwald    return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
163*b3fcedb9SMatthias Ringwald
164*b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type):
165*b3fcedb9SMatthias Ringwald    global handle
166*b3fcedb9SMatthias Ringwald    global total_size
167*b3fcedb9SMatthias Ringwald    global current_service_uuid_string
168*b3fcedb9SMatthias Ringwald    global current_service_start_handle
169*b3fcedb9SMatthias Ringwald    global services
170*b3fcedb9SMatthias Ringwald
171*b3fcedb9SMatthias Ringwald    if current_service_uuid_string:
172*b3fcedb9SMatthias Ringwald        fout.write("\n")
173*b3fcedb9SMatthias Ringwald        # print("append service %s = [%d, %d]\n" % (current_characteristic_uuid_string, current_service_start_handle, handle-1))
174*b3fcedb9SMatthias Ringwald        services[current_service_uuid_string] = [current_service_start_handle, handle-1]
175*b3fcedb9SMatthias Ringwald
176*b3fcedb9SMatthias Ringwald    property = property_flags['READ'];
177*b3fcedb9SMatthias Ringwald
178*b3fcedb9SMatthias Ringwald    write_indent(fout)
179*b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
180*b3fcedb9SMatthias Ringwald
181*b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
182*b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
183*b3fcedb9SMatthias Ringwald
184*b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size + 2
185*b3fcedb9SMatthias Ringwald
186*b3fcedb9SMatthias Ringwald    if service_type == 0x2802:
187*b3fcedb9SMatthias Ringwald        size += 4
188*b3fcedb9SMatthias Ringwald
189*b3fcedb9SMatthias Ringwald    write_indent(fout)
190*b3fcedb9SMatthias Ringwald    write_16(fout, size)
191*b3fcedb9SMatthias Ringwald    write_16(fout, property)
192*b3fcedb9SMatthias Ringwald    write_16(fout, handle)
193*b3fcedb9SMatthias Ringwald    write_16(fout, service_type)
194*b3fcedb9SMatthias Ringwald    write_uuid(uuid)
195*b3fcedb9SMatthias Ringwald    fout.write("\n")
196*b3fcedb9SMatthias Ringwald
197*b3fcedb9SMatthias Ringwald    current_service_uuid_string = keyForUUID(uuid)
198*b3fcedb9SMatthias Ringwald    current_service_start_handle = handle
199*b3fcedb9SMatthias Ringwald    handle = handle + 1
200*b3fcedb9SMatthias Ringwald    total_size = total_size + size
201*b3fcedb9SMatthias Ringwald
202*b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts):
203*b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2800)
204*b3fcedb9SMatthias Ringwald
205*b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts):
206*b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2801)
207*b3fcedb9SMatthias Ringwald
208*b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts):
209*b3fcedb9SMatthias Ringwald    global handle
210*b3fcedb9SMatthias Ringwald    global total_size
211*b3fcedb9SMatthias Ringwald
212*b3fcedb9SMatthias Ringwald    property = property_flags['READ'];
213*b3fcedb9SMatthias Ringwald
214*b3fcedb9SMatthias Ringwald    write_indent(fout)
215*b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
216*b3fcedb9SMatthias Ringwald
217*b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
218*b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
219*b3fcedb9SMatthias Ringwald    if uuid_size > 2:
220*b3fcedb9SMatthias Ringwald        uuid_size = 0
221*b3fcedb9SMatthias Ringwald    # print("Include Service ", keyForUUID(uuid))
222*b3fcedb9SMatthias Ringwald
223*b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 4 + uuid_size
224*b3fcedb9SMatthias Ringwald
225*b3fcedb9SMatthias Ringwald    keyUUID = keyForUUID(uuid)
226*b3fcedb9SMatthias Ringwald
227*b3fcedb9SMatthias Ringwald    write_indent(fout)
228*b3fcedb9SMatthias Ringwald    write_16(fout, size)
229*b3fcedb9SMatthias Ringwald    write_16(fout, property)
230*b3fcedb9SMatthias Ringwald    write_16(fout, handle)
231*b3fcedb9SMatthias Ringwald    write_16(fout, 0x2802)
232*b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][0])
233*b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][1])
234*b3fcedb9SMatthias Ringwald    if uuid_size > 0:
235*b3fcedb9SMatthias Ringwald        write_uuid(uuid)
236*b3fcedb9SMatthias Ringwald    fout.write("\n")
237*b3fcedb9SMatthias Ringwald
238*b3fcedb9SMatthias Ringwald    handle = handle + 1
239*b3fcedb9SMatthias Ringwald    total_size = total_size + size
240*b3fcedb9SMatthias Ringwald
241*b3fcedb9SMatthias Ringwald
242*b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts):
243*b3fcedb9SMatthias Ringwald    global handle
244*b3fcedb9SMatthias Ringwald    global total_size
245*b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
246*b3fcedb9SMatthias Ringwald    global characteristic_indices
247*b3fcedb9SMatthias Ringwald
248*b3fcedb9SMatthias Ringwald    property_read = property_flags['READ'];
249*b3fcedb9SMatthias Ringwald
250*b3fcedb9SMatthias Ringwald    # enumerate characteristics with same UUID, using optional name tag if available
251*b3fcedb9SMatthias Ringwald    current_characteristic_uuid_string = c_string_for_uuid(parts[1]);
252*b3fcedb9SMatthias Ringwald    index = 1
253*b3fcedb9SMatthias Ringwald    if current_characteristic_uuid_string in characteristic_indices:
254*b3fcedb9SMatthias Ringwald        index = characteristic_indices[current_characteristic_uuid_string] + 1
255*b3fcedb9SMatthias Ringwald    characteristic_indices[current_characteristic_uuid_string] = index
256*b3fcedb9SMatthias Ringwald    if len(parts) > 4:
257*b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_')
258*b3fcedb9SMatthias Ringwald    else:
259*b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += ('_%02x' % index)
260*b3fcedb9SMatthias Ringwald
261*b3fcedb9SMatthias Ringwald    uuid       = parseUUID(parts[1])
262*b3fcedb9SMatthias Ringwald    uuid_size  = len(uuid)
263*b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[2])
264*b3fcedb9SMatthias Ringwald    value = ', '.join([str(x) for x in parts[3:]])
265*b3fcedb9SMatthias Ringwald
266*b3fcedb9SMatthias Ringwald    # reliable writes is defined in an extended properties
267*b3fcedb9SMatthias Ringwald    if (properties & property_flags['RELIABLE_WRITE']):
268*b3fcedb9SMatthias Ringwald        properties = properties | property_flags['EXTENDED_PROPERTIES']
269*b3fcedb9SMatthias Ringwald
270*b3fcedb9SMatthias Ringwald    write_indent(fout)
271*b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3])))
272*b3fcedb9SMatthias Ringwald
273*b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (1+2+uuid_size)
274*b3fcedb9SMatthias Ringwald    write_indent(fout)
275*b3fcedb9SMatthias Ringwald    write_16(fout, size)
276*b3fcedb9SMatthias Ringwald    write_16(fout, property_read)
277*b3fcedb9SMatthias Ringwald    write_16(fout, handle)
278*b3fcedb9SMatthias Ringwald    write_16(fout, 0x2803)
279*b3fcedb9SMatthias Ringwald    write_8(fout, properties)
280*b3fcedb9SMatthias Ringwald    write_16(fout, handle+1)
281*b3fcedb9SMatthias Ringwald    write_uuid(uuid)
282*b3fcedb9SMatthias Ringwald    fout.write("\n")
283*b3fcedb9SMatthias Ringwald    handle = handle + 1
284*b3fcedb9SMatthias Ringwald    total_size = total_size + size
285*b3fcedb9SMatthias Ringwald
286*b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size
287*b3fcedb9SMatthias Ringwald    if is_string(value):
288*b3fcedb9SMatthias Ringwald        size = size + len(value)
289*b3fcedb9SMatthias Ringwald    else:
290*b3fcedb9SMatthias Ringwald        size = size + len(value.split())
291*b3fcedb9SMatthias Ringwald
292*b3fcedb9SMatthias Ringwald    if uuid_size == 16:
293*b3fcedb9SMatthias Ringwald        properties = properties | property_flags['LONG_UUID'];
294*b3fcedb9SMatthias Ringwald
295*b3fcedb9SMatthias Ringwald    write_indent(fout)
296*b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value))
297*b3fcedb9SMatthias Ringwald    write_indent(fout)
298*b3fcedb9SMatthias Ringwald    write_16(fout, size)
299*b3fcedb9SMatthias Ringwald    write_16(fout, properties)
300*b3fcedb9SMatthias Ringwald    write_16(fout, handle)
301*b3fcedb9SMatthias Ringwald    write_uuid(uuid)
302*b3fcedb9SMatthias Ringwald    if is_string(value):
303*b3fcedb9SMatthias Ringwald        write_string(fout, value)
304*b3fcedb9SMatthias Ringwald    else:
305*b3fcedb9SMatthias Ringwald        write_sequence(fout,value)
306*b3fcedb9SMatthias Ringwald
307*b3fcedb9SMatthias Ringwald    fout.write("\n")
308*b3fcedb9SMatthias Ringwald    defines.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
309*b3fcedb9SMatthias Ringwald    handle = handle + 1
310*b3fcedb9SMatthias Ringwald
311*b3fcedb9SMatthias Ringwald    if add_client_characteristic_configuration(properties):
312*b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
313*b3fcedb9SMatthias Ringwald        write_indent(fout)
314*b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
315*b3fcedb9SMatthias Ringwald        write_indent(fout)
316*b3fcedb9SMatthias Ringwald        write_16(fout, size)
317*b3fcedb9SMatthias Ringwald        write_16(fout, property_flags['READ'] | property_flags['WRITE'] | property_flags['DYNAMIC'])
318*b3fcedb9SMatthias Ringwald        write_16(fout, handle)
319*b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
320*b3fcedb9SMatthias Ringwald        write_16(fout, 0)
321*b3fcedb9SMatthias Ringwald        fout.write("\n")
322*b3fcedb9SMatthias Ringwald        defines.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
323*b3fcedb9SMatthias Ringwald        handle = handle + 1
324*b3fcedb9SMatthias Ringwald
325*b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
326*b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
327*b3fcedb9SMatthias Ringwald        write_indent(fout)
328*b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
329*b3fcedb9SMatthias Ringwald        write_indent(fout)
330*b3fcedb9SMatthias Ringwald        write_16(fout, size)
331*b3fcedb9SMatthias Ringwald        write_16(fout, property_flags['READ'])
332*b3fcedb9SMatthias Ringwald        write_16(fout, handle)
333*b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
334*b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
335*b3fcedb9SMatthias Ringwald        fout.write("\n")
336*b3fcedb9SMatthias Ringwald        handle = handle + 1
337*b3fcedb9SMatthias Ringwald
338*b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts):
339*b3fcedb9SMatthias Ringwald    global handle
340*b3fcedb9SMatthias Ringwald    global total_size
341*b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
342*b3fcedb9SMatthias Ringwald
343*b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
344*b3fcedb9SMatthias Ringwald    value      = parts[2]
345*b3fcedb9SMatthias Ringwald
346*b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
347*b3fcedb9SMatthias Ringwald    if is_string(value):
348*b3fcedb9SMatthias Ringwald        size = size + len(value) - 2
349*b3fcedb9SMatthias Ringwald    else:
350*b3fcedb9SMatthias Ringwald        size = size + len(value.split())
351*b3fcedb9SMatthias Ringwald
352*b3fcedb9SMatthias Ringwald    write_indent(fout)
353*b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:])))
354*b3fcedb9SMatthias Ringwald    write_indent(fout)
355*b3fcedb9SMatthias Ringwald    write_16(fout, size)
356*b3fcedb9SMatthias Ringwald    write_16(fout, properties)
357*b3fcedb9SMatthias Ringwald    write_16(fout, handle)
358*b3fcedb9SMatthias Ringwald    write_16(fout, 0x2901)
359*b3fcedb9SMatthias Ringwald    if is_string(value):
360*b3fcedb9SMatthias Ringwald        write_string(fout, value)
361*b3fcedb9SMatthias Ringwald    else:
362*b3fcedb9SMatthias Ringwald        write_sequence(fout,value)
363*b3fcedb9SMatthias Ringwald    fout.write("\n")
364*b3fcedb9SMatthias Ringwald    defines.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
365*b3fcedb9SMatthias Ringwald    handle = handle + 1
366*b3fcedb9SMatthias Ringwald
367*b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
368*b3fcedb9SMatthias Ringwald    global handle
369*b3fcedb9SMatthias Ringwald    global total_size
370*b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
371*b3fcedb9SMatthias Ringwald
372*b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
373*b3fcedb9SMatthias Ringwald    properties = properties | property_flags['DYNAMIC']
374*b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
375*b3fcedb9SMatthias Ringwald
376*b3fcedb9SMatthias Ringwald    write_indent(fout)
377*b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:])))
378*b3fcedb9SMatthias Ringwald    write_indent(fout)
379*b3fcedb9SMatthias Ringwald    write_16(fout, size)
380*b3fcedb9SMatthias Ringwald    write_16(fout, properties)
381*b3fcedb9SMatthias Ringwald    write_16(fout, handle)
382*b3fcedb9SMatthias Ringwald    write_16(fout, 0x2903)
383*b3fcedb9SMatthias Ringwald    fout.write("\n")
384*b3fcedb9SMatthias Ringwald    defines.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
385*b3fcedb9SMatthias Ringwald    handle = handle + 1
386*b3fcedb9SMatthias Ringwald
387*b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
388*b3fcedb9SMatthias Ringwald    global handle
389*b3fcedb9SMatthias Ringwald    global total_size
390*b3fcedb9SMatthias Ringwald
391*b3fcedb9SMatthias Ringwald    property_read = property_flags['READ'];
392*b3fcedb9SMatthias Ringwald
393*b3fcedb9SMatthias Ringwald    identifier = parts[1]
394*b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
395*b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
396*b3fcedb9SMatthias Ringwald
397*b3fcedb9SMatthias Ringwald    format     = parts[2]
398*b3fcedb9SMatthias Ringwald    exponent   = parts[3]
399*b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
400*b3fcedb9SMatthias Ringwald    name_space = parts[5]
401*b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
402*b3fcedb9SMatthias Ringwald
403*b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
404*b3fcedb9SMatthias Ringwald
405*b3fcedb9SMatthias Ringwald    write_indent(fout)
406*b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
407*b3fcedb9SMatthias Ringwald    write_indent(fout)
408*b3fcedb9SMatthias Ringwald    write_16(fout, size)
409*b3fcedb9SMatthias Ringwald    write_16(fout, property_read)
410*b3fcedb9SMatthias Ringwald    write_16(fout, handle)
411*b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
412*b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
413*b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
414*b3fcedb9SMatthias Ringwald    write_uuid(unit)
415*b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
416*b3fcedb9SMatthias Ringwald    write_uuid(description)
417*b3fcedb9SMatthias Ringwald    fout.write("\n")
418*b3fcedb9SMatthias Ringwald    handle = handle + 1
419*b3fcedb9SMatthias Ringwald
420*b3fcedb9SMatthias Ringwald
421*b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
422*b3fcedb9SMatthias Ringwald    global handle
423*b3fcedb9SMatthias Ringwald    global total_size
424*b3fcedb9SMatthias Ringwald
425*b3fcedb9SMatthias Ringwald    property_read = property_flags['READ'];
426*b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
427*b3fcedb9SMatthias Ringwald
428*b3fcedb9SMatthias Ringwald    write_indent(fout)
429*b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
430*b3fcedb9SMatthias Ringwald    write_indent(fout)
431*b3fcedb9SMatthias Ringwald    write_16(fout, size)
432*b3fcedb9SMatthias Ringwald    write_16(fout, property_read)
433*b3fcedb9SMatthias Ringwald    write_16(fout, handle)
434*b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
435*b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
436*b3fcedb9SMatthias Ringwald        format_handle = presentation_formats[identifier]
437*b3fcedb9SMatthias Ringwald        if format == 0:
438*b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
439*b3fcedb9SMatthias Ringwald            sys.exit(1)
440*b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
441*b3fcedb9SMatthias Ringwald    fout.write("\n")
442*b3fcedb9SMatthias Ringwald    handle = handle + 1
443*b3fcedb9SMatthias Ringwald
444*b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
445*b3fcedb9SMatthias Ringwald    global handle
446*b3fcedb9SMatthias Ringwald    global total_size
447*b3fcedb9SMatthias Ringwald
448*b3fcedb9SMatthias Ringwald    property_read = property_flags['READ'];
449*b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
450*b3fcedb9SMatthias Ringwald
451*b3fcedb9SMatthias Ringwald    report_id = parts[1]
452*b3fcedb9SMatthias Ringwald    report_type = parts[2]
453*b3fcedb9SMatthias Ringwald
454*b3fcedb9SMatthias Ringwald    write_indent(fout)
455*b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
456*b3fcedb9SMatthias Ringwald    write_indent(fout)
457*b3fcedb9SMatthias Ringwald    write_16(fout, size)
458*b3fcedb9SMatthias Ringwald    write_16(fout, property_read)
459*b3fcedb9SMatthias Ringwald    write_16(fout, handle)
460*b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
461*b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
462*b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
463*b3fcedb9SMatthias Ringwald    fout.write("\n")
464*b3fcedb9SMatthias Ringwald    handle = handle + 1
465*b3fcedb9SMatthias Ringwald
466*b3fcedb9SMatthias Ringwald
467*b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
468*b3fcedb9SMatthias Ringwald    global handle
469*b3fcedb9SMatthias Ringwald    global total_size
470*b3fcedb9SMatthias Ringwald
471*b3fcedb9SMatthias Ringwald    property_read = property_flags['READ'];
472*b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
473*b3fcedb9SMatthias Ringwald
474*b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
475*b3fcedb9SMatthias Ringwald
476*b3fcedb9SMatthias Ringwald    write_indent(fout)
477*b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
478*b3fcedb9SMatthias Ringwald    write_indent(fout)
479*b3fcedb9SMatthias Ringwald    write_16(fout, size)
480*b3fcedb9SMatthias Ringwald    write_16(fout, property_read)
481*b3fcedb9SMatthias Ringwald    write_16(fout, handle)
482*b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
483*b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
484*b3fcedb9SMatthias Ringwald    fout.write("\n")
485*b3fcedb9SMatthias Ringwald    handle = handle + 1
486*b3fcedb9SMatthias Ringwald
487*b3fcedb9SMatthias Ringwald
488*b3fcedb9SMatthias Ringwalddef parse(fname_in, fin, fname_out, fout):
489*b3fcedb9SMatthias Ringwald    global handle
490*b3fcedb9SMatthias Ringwald    global total_size
491*b3fcedb9SMatthias Ringwald
492*b3fcedb9SMatthias Ringwald    fout.write(header.format(fname_out, fname_in))
493*b3fcedb9SMatthias Ringwald    fout.write('{\n')
494*b3fcedb9SMatthias Ringwald
495*b3fcedb9SMatthias Ringwald    for line in fin:
496*b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
497*b3fcedb9SMatthias Ringwald
498*b3fcedb9SMatthias Ringwald        if line.startswith("#") or line.startswith("//"):
499*b3fcedb9SMatthias Ringwald            fout.write("//" + line.lstrip('/#') + '\n')
500*b3fcedb9SMatthias Ringwald            continue
501*b3fcedb9SMatthias Ringwald
502*b3fcedb9SMatthias Ringwald        if len(line) == 0:
503*b3fcedb9SMatthias Ringwald            continue
504*b3fcedb9SMatthias Ringwald
505*b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
506*b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
507*b3fcedb9SMatthias Ringwald
508*b3fcedb9SMatthias Ringwald        for parts in parts_list:
509*b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
510*b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
511*b3fcedb9SMatthias Ringwald
512*b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
513*b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
514*b3fcedb9SMatthias Ringwald                continue
515*b3fcedb9SMatthias Ringwald
516*b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
517*b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
518*b3fcedb9SMatthias Ringwald                continue
519*b3fcedb9SMatthias Ringwald
520*b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
521*b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
522*b3fcedb9SMatthias Ringwald                continue
523*b3fcedb9SMatthias Ringwald
524*b3fcedb9SMatthias Ringwald            # 2803
525*b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
526*b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
527*b3fcedb9SMatthias Ringwald                continue
528*b3fcedb9SMatthias Ringwald
529*b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
530*b3fcedb9SMatthias Ringwald
531*b3fcedb9SMatthias Ringwald            # 2901
532*b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
533*b3fcedb9SMatthias Ringwald                parseCharacteristicUserDescription(fout, parts)
534*b3fcedb9SMatthias Ringwald                continue
535*b3fcedb9SMatthias Ringwald
536*b3fcedb9SMatthias Ringwald            # 2902 Client Characteristic Configuration - included in Characteristic if
537*b3fcedb9SMatthias Ringwald            # notification / indication is supported
538*b3fcedb9SMatthias Ringwald
539*b3fcedb9SMatthias Ringwald            # 2903
540*b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
541*b3fcedb9SMatthias Ringwald                parseServerCharacteristicConfiguration(fout, parts)
542*b3fcedb9SMatthias Ringwald                continue
543*b3fcedb9SMatthias Ringwald
544*b3fcedb9SMatthias Ringwald            # 2904
545*b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
546*b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
547*b3fcedb9SMatthias Ringwald                continue
548*b3fcedb9SMatthias Ringwald
549*b3fcedb9SMatthias Ringwald            # 2905
550*b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
551*b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
552*b3fcedb9SMatthias Ringwald                continue
553*b3fcedb9SMatthias Ringwald
554*b3fcedb9SMatthias Ringwald            # 2906
555*b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
556*b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
557*b3fcedb9SMatthias Ringwald                continue
558*b3fcedb9SMatthias Ringwald
559*b3fcedb9SMatthias Ringwald            # 2907
560*b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
561*b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
562*b3fcedb9SMatthias Ringwald                continue
563*b3fcedb9SMatthias Ringwald
564*b3fcedb9SMatthias Ringwald            # 2908
565*b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
566*b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
567*b3fcedb9SMatthias Ringwald                continue
568*b3fcedb9SMatthias Ringwald
569*b3fcedb9SMatthias Ringwald            # 2909
570*b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
571*b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
572*b3fcedb9SMatthias Ringwald                continue
573*b3fcedb9SMatthias Ringwald
574*b3fcedb9SMatthias Ringwald            # 290A
575*b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
576*b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
577*b3fcedb9SMatthias Ringwald                continue
578*b3fcedb9SMatthias Ringwald
579*b3fcedb9SMatthias Ringwald            # 290B
580*b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
581*b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
582*b3fcedb9SMatthias Ringwald                continue
583*b3fcedb9SMatthias Ringwald
584*b3fcedb9SMatthias Ringwald            # 290C
585*b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
586*b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
587*b3fcedb9SMatthias Ringwald                continue
588*b3fcedb9SMatthias Ringwald
589*b3fcedb9SMatthias Ringwald            # 290D
590*b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
591*b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
592*b3fcedb9SMatthias Ringwald                continue
593*b3fcedb9SMatthias Ringwald
594*b3fcedb9SMatthias Ringwald            # 2906
595*b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
596*b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
597*b3fcedb9SMatthias Ringwald                continue
598*b3fcedb9SMatthias Ringwald
599*b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
600*b3fcedb9SMatthias Ringwald
601*b3fcedb9SMatthias Ringwald    write_indent(fout)
602*b3fcedb9SMatthias Ringwald    fout.write("// END\n");
603*b3fcedb9SMatthias Ringwald    write_indent(fout)
604*b3fcedb9SMatthias Ringwald    write_16(fout,0)
605*b3fcedb9SMatthias Ringwald    fout.write("\n")
606*b3fcedb9SMatthias Ringwald    total_size = total_size + 2
607*b3fcedb9SMatthias Ringwald
608*b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
609*b3fcedb9SMatthias Ringwald
610*b3fcedb9SMatthias Ringwalddef listHandles(fout):
611*b3fcedb9SMatthias Ringwald    fout.write('\n\n')
612*b3fcedb9SMatthias Ringwald    fout.write('//\n')
613*b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
614*b3fcedb9SMatthias Ringwald    fout.write('//\n')
615*b3fcedb9SMatthias Ringwald    for define in defines:
616*b3fcedb9SMatthias Ringwald        fout.write(define)
617*b3fcedb9SMatthias Ringwald        fout.write('\n')
618*b3fcedb9SMatthias Ringwald
619*b3fcedb9SMatthias Ringwaldif (len(sys.argv) < 3):
620*b3fcedb9SMatthias Ringwald    print(usage)
621*b3fcedb9SMatthias Ringwald    sys.exit(1)
622*b3fcedb9SMatthias Ringwaldtry:
623*b3fcedb9SMatthias Ringwald    filename = sys.argv[2]
624*b3fcedb9SMatthias Ringwald    fin  = codecs.open (sys.argv[1], encoding='utf-8')
625*b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
626*b3fcedb9SMatthias Ringwald    parse(sys.argv[1], fin, filename, fout)
627*b3fcedb9SMatthias Ringwald    listHandles(fout)
628*b3fcedb9SMatthias Ringwald    fout.close()
629*b3fcedb9SMatthias Ringwald    print('Created', filename)
630*b3fcedb9SMatthias Ringwald
631*b3fcedb9SMatthias Ringwaldexcept IOError as e:
632*b3fcedb9SMatthias Ringwald    print(usage)
633*b3fcedb9SMatthias Ringwald    sys.exit(1)
634*b3fcedb9SMatthias Ringwald
635*b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
636