From bcffdb4eb1520f2f3a2bc7b8de1562cd7df933a0 Mon Sep 17 00:00:00 2001 From: csrichter Date: Fri, 16 Dec 2016 22:34:48 +0100 Subject: [PATCH] dynamic gogole maps markers, tmc event class, full event list, removed duplicate psn and rt in database, fixed AF-freq-decode, fixed rt char decode, disabled osm map --- python/rds_parser_table_qt.py | 244 ++++++++++++++------------------- python/rds_parser_table_qt.pyc | Bin 0 -> 38980 bytes 2 files changed, 105 insertions(+), 139 deletions(-) create mode 100644 python/rds_parser_table_qt.pyc diff --git a/python/rds_parser_table_qt.py b/python/rds_parser_table_qt.py index 35bbb8c..40b11b8 100644 --- a/python/rds_parser_table_qt.py +++ b/python/rds_parser_table_qt.py @@ -47,6 +47,21 @@ def ordinal(num): # the second parameter is a default. suffix = SUFFIXES.get(num % 10, 'th') return str(num) + suffix +class tmc_event: + def __init__(self,ecn,tableobj,tags=[]): + self.text_raw="" + try: + #Code,Text CEN-English,Text (German),Text (German) kein Quantifier,Text (Quantifier = 1),Text (Quantifier >1),N,Q,T,D,U,C,R ,Comment + event_array=tableobj.ecl_dict[ecn] + self.text_raw=event_array[1] + self.is_valid=True + except KeyError: + print("event '%i' not found"%ecn) + self.is_valid=False + def __repr__(self): + return self.text_raw + def __add_tag(self,tag): + pass class tmc_location: def __ref_locs(self,lcn,name_string=""): #if not self.is_valid: #not used, since not called from outside @@ -126,7 +141,7 @@ class tmc_message: else: return self.ci def __repr__(self): - #event_name=self.ecl_dict[self.tmc_event] + #event_name=self.ecl_dict[self.tmc_event][1] #message_string="TMC-message,event:%s location:%i,reflocs:%s, station:%s"%(event_name,self.tmc_location,self.ref_locs(self.tmc_location,""),self.RDS_data[PI]["PSN"]) return "single:%i,complete:%i,event:%i location:%s"%(self.is_single,self.is_complete,self.event,self.location) @@ -159,7 +174,7 @@ class tmc_message: self.event=int(tmc_y&0x7ff) #Y10-Y0 self.tmc_event=self.event#decrepated try: - self.event_name = self.tableobj.ecl_dict[self.event] + self.event_name = self.tableobj.ecl_dict[self.event][1] except KeyError: self.event_name="##Error##" self.tmc_extent=int((tmc_y>>11)&0x7) #3 bits (Y13-Y11) @@ -203,17 +218,6 @@ class tmc_message: #print("out of sequence") pass -#workdir="/media/clemens/intdaten/uni_bulk/forschungsarbeit/data/" -##workdir="/user/wire2/richter/data/" #TODO store in repo -##read location code list: -#reader = csv.reader(open(workdir+'LCL15.1.D-160122_utf8.csv'), delimiter=';', quotechar='"') -#reader.next()#skip header -#lcl_dict=dict((int(rows[0]),rows[1:]) for rows in reader) -##read TMC-event list -#reader = csv.reader(open(workdir+'event-list_code+de-name_sort.csv'), delimiter=',', quotechar='"') -##no header -#ecl_dict=dict((int(rows[0]),rows[1]) for rows in reader) - class mgm_tag:#mgm=multi group message field_lengths=[3, 3, 5, 5, 5, 8, 8, 8, 8, 11, 16, 16, 16, 16, 0, 0] field_names={0:"Duration (value 000 is not allowed)" @@ -275,6 +279,10 @@ class mgm_tag:#mgm=multi group message return "length affected: %i km"%self.length_to_km(self.data.uint) elif(self.label==3): return "speed limit: %i km/h"%(self.data.uint*5) + elif(self.label==4): + return "5 bit quantifier: %i"%(self.data.uint) + elif(self.label==5): + return "8 bit quantifier: %i"%(self.data.uint) elif(self.label==6): return "info:%s"%self.tableobj.label6_suppl_info[self.data.uint] elif(self.label==7): @@ -282,7 +290,7 @@ class mgm_tag:#mgm=multi group message elif(self.label==8): return "stop: %s"%self.decode_time_date(self.data.uint) elif(self.label==9): - event_string="event: %s"%self.tableobj.ecl_dict[self.data.uint] + event_string="event: %s"%self.tableobj.ecl_dict[self.data.uint][1] return event_string elif(self.label==10): #location_string="divert via: %s"%",".join(self.tableobj.lcl_dict[self.data.uint][3:5])#roadname(col3) and firstname (col4) @@ -315,8 +323,9 @@ class rds_parser_table_qt(gr.sync_block): docstring for block qtguitest """ def goodbye(self): + self.clean_data_and_commit_db() print("quitting rds parser table, closing db") - self.db.commit() + #self.db.commit() self.db.close() def __init__(self,signals,nPorts,slot,freq,log,debug,workdir): #QObject.__init__() @@ -390,13 +399,52 @@ class rds_parser_table_qt(gr.sync_block): reader.next()#skip header self.rtp_classnames=dict((int(rows[0]),rows[1]) for rows in reader) #read TMC-event list - reader = csv.reader(open(self.workdir+'event-list_code+de-name_sort.csv'), delimiter=',', quotechar='"') - #no header - self.ecl_dict=dict((int(rows[0]),rows[1]) for rows in reader) + reader = csv.reader(open(self.workdir+'event-list_with_forecast_sort.csv'), delimiter=',', quotechar='"') + reader.next()#skip header + #Code,Text CEN-English,Text (German),Text (German) kein Quantifier,Text (Quantifier = 1),Text (Quantifier >1),N,Q,T,D,U,C,R ,Comment + #N:nature (blank): information, F:forecast, S:silent + #Q:quantifier type: (0..12) or blank (no quantifier) + #T:duration type: D:dynamic, L:long lasting, in brackets or if time-of-day quantifier (no 7) is used in message -> no display, only for management + #D:direction: 1:unidirectional, 2:bidirectional + #U:urgency: blank: normal, X:extremely urgent, U:urgent + #C: update class: +#1. LEVEL OF SERVICE Verkehrslage +#2. EXPECTED LEVEL OF SERVICE Erwartete Verkehrslage +#3. ACCIDENTS Unfälle +#4. INCIDENTS Vorfälle +#5. CLOSURES AND LANE RESTRICTIONS Straßen- und Fahrbahnsperrungen Straßen- und Fahrbahnsperrungen +#6. CARRIAGEWAY RESTRICTIONS Fahrbahnbeschränkungen +#7. EXIT RESTRICTIONS Beschränkungen der Ausfahrt +#8. ENTRY RESTRICTIONS Beschränkungen der Einfahrt +#9. TRAFFIC RESTRICTIONS Verkehrsbeschränkungen +#10. CARPOOL INFORMATION Informationen für Fahrgemeinschaften +#11. ROADWORKS Bauarbeiten +#12. OBSTRUCTION HAZARDS Behinderungen auf der Fahrbahn +#13. DANGEROUS SITUATIONS Gefährliche Situationen +#14. ROAD CONDITIONS Straßenzustand +#15.TEMPERATURES Temperaturen +#16. PRECIPITATION AND VISIBILITY Niederschlag und Sichtbehinderungen +#17. WIND AND AIR QUALITY Wind und Luftqualität +#18. ACTIVITIES Veranstaltungen +#19. SECURITY ALERTS Sicherheitsvorfälle +#20. DELAYS Zeitverluste +#21. CANCELLATIONS Ausfälle +#22. TRAVEL TIME INFORMATION Reiseinformationen +#23. DANGEROUS VEHICLES Gefährliche Fahrzeuge +#24. EXCEPTIONAL LOADS/VEHICLES Außergewöhnliche Ladungen und Fahrzeuge +#25. TRAFFIC EQUIPMENT STATUS Störungen an Lichtsignalanlagen und sonstigen Straßenausrüstungen +#26. SIZE AND WEIGHT LIMITS Beschränkung der Fahrzeugmaße und -gewichte +#27. PARKING RESTRICTIONS Parkregelungen +#28. PARKING Parken +#29. REFERENCE TO AUDIO BROADCASTS Information +#30. SERVICE MESSAGES Service Meldungen +#31. SPECIAL MESSAGES Spezielle Meldungen + + self.ecl_dict=dict((int(rows[0]),rows[1:]) for rows in reader) #read supplementary information code list reader = csv.reader(open(self.workdir+'label6-supplementary-information-codes.csv'), delimiter=',', quotechar='"') - #no header - self.label6_suppl_info=dict((int(rows[0]),rows[1]) for rows in reader) + reader.next()#skip header + self.label6_suppl_info=dict((int(rows[0]),rows[2]) for rows in reader)#code,english,german #read PTY list f=open(self.workdir+'pty-list.csv') reader = csv.reader(f, delimiter=',', quotechar='"') @@ -404,26 +452,11 @@ class rds_parser_table_qt(gr.sync_block): self.pty_dict=dict((int(rows[0]),rows[1]) for rows in reader) f.close() - with open(workdir+'google_maps_template.html', 'r') as myfile: - self.gmaps_html_template=myfile.read() + #with open(workdir+'google_maps_template.html', 'r') as myfile: + # self.gmaps_html_template=myfile.read() self.map_markers=[] self.save_data_timer=time.time() - self.marker_template="""var marker_{marker_id} = new google.maps.Marker({{ - position: {{lat: {lat}, lng: {lon}}}, - map: map, - title: '{text}' - }}); - var infowindow_{marker_id} = new google.maps.InfoWindow({{ - content: '{text}' - }}); - marker_{marker_id}.addListener('click', function() {{ - infowindow_{marker_id}.open(map, marker_{marker_id}); - }}); - - - """ - - + self.marker_template="addMarker({{lat: {lat}, lng: {lon}}},'{text}')" self.osm_map = folium.Map(location=[48.7,9.2],zoom_start=10)#centered on stuttgart self.osm_map.save(self.workdir+'osm.html') def clean_data_and_commit_db(self): @@ -432,19 +465,12 @@ class rds_parser_table_qt(gr.sync_block): #print(self.PI_dict) self.db.commit() self.osm_map.save(self.workdir+'osm.html') - #re read template during development #TODO - with open(self.workdir+'google_maps_template.html', 'r') as myfile: - self.gmaps_html_template=myfile.read() - - f=open(self.workdir+'google_maps.html', 'w') + f=open(self.workdir+'google_maps_markers.js', 'w') markerstring="\n".join(self.map_markers) - f.write(self.gmaps_html_template.format(markers=markerstring)) + markerstring+='\n console.log("loaded "+markers.length+" markers")' + markerstring+='\n document.getElementById("errorid").innerHTML = "loaded "+markers.length+" markers";' + f.write(markerstring) f.close() - #f=open(self.workdir+'google_maps.html', 'w') - #markerstring="\n".join(self.map_markers) - #formatted_html=self.gmaps_html_template.format(markers=markerstring) - #f.write(formatted_html) - #f.close() def set_freq_tune(self,freq): self.tuning_frequency=int(freq) message_string="decoder frequencies:" @@ -485,9 +511,9 @@ class rds_parser_table_qt(gr.sync_block): self.RDS_data[PI]["TA"]=-1 self.RDS_data[PI]["PTY"]="" self.RDS_data[PI]["DI"]=[2,2,2,2] - self.RDS_data[PI]["internals"]={"last_rt_tooltip":"","unfinished_TMC":{}} + self.RDS_data[PI]["internals"]={"last_rt_tooltip":"","unfinished_TMC":{},"last_valid_rt":"","last_valid_psn":""} def handle_msg(self, msg, port):#port from 0 to 3 - if time.time()-self.save_data_timer > 10:#every 10 seconds + if time.time()-self.save_data_timer > 2:#every 10 seconds #TODO lower frequency (high freq for testing) self.save_data_timer=time.time() self.clean_data_and_commit_db() pr.enable() @@ -536,7 +562,7 @@ class rds_parser_table_qt(gr.sync_block): #save block to sqlite (commit at end of handle_msg) #(time text,PI text,PSN text, grouptype text,content blob) content="%02X%02X%02X%02X%02X" %(array[3]&0x1f,array[4],array[5],array[6],array[7]) - #db=sqlite3.connect(self.db_name)#TODO + #db=sqlite3.connect(self.db_name) db=self.db #t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],groupType,content) #db.execute("INSERT INTO groups VALUES (?,?,?,?,?)",t) @@ -548,7 +574,8 @@ class rds_parser_table_qt(gr.sync_block): # db.execute("INSERT OR REPLACE INTO grouptypeCounts (PI,grouptype,count) VALUES (?,?,?)",t) #InterfaceError: Error binding parameter 0 - probably unsupported type. #fix?: added str() to PI, but it should already be a string - t=(str(PI),groupType,self.RDS_data[PI]["blockcounts"][groupType]) + + t=(str(PI),groupType,self.RDS_data[PI]["blockcounts"][groupType])#TODO only update DB every few seconds db.execute("INSERT OR REPLACE INTO grouptypeCounts (PI,grouptype,count) VALUES (?,?,?)",t) if (groupType == "0A"):#AF PSN @@ -619,10 +646,12 @@ class rds_parser_table_qt(gr.sync_block): valid = False if(valid): textcolor="black" - t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"PSN_valid",self.RDS_data[PI]["PSN"]) - db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t) - t=(self.RDS_data[PI]["PSN"],PI) - db.execute("UPDATE OR IGNORE stations SET PSN=? WHERE PI IS ?",t) + if not self.RDS_data[PI]["internals"]["last_valid_psn"]==self.RDS_data[PI]["PSN"]:#ignore duplicates + t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"PSN_valid",self.RDS_data[PI]["PSN"]) + db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t) + t=(self.RDS_data[PI]["PSN"],PI) + db.execute("UPDATE OR IGNORE stations SET PSN=? WHERE PI IS ?",t) + self.RDS_data[PI]["internals"]["last_valid_psn"]=self.RDS_data[PI]["PSN"] else: textcolor="gray" formatted_text=self.color_text(self.RDS_data[PI]["PSN"],adr*2,adr*2+2,textcolor,segmentcolor) @@ -712,9 +741,16 @@ class rds_parser_table_qt(gr.sync_block): if(self.RDS_data[PI]["RT_all_valid"]): textcolor="black" l=list(self.RDS_data[PI]["RT"]) - rt="".join(l[0:text_end]) - t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"RT",rt) - db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t) + rt="".join(l[0:text_end])#remove underscores(default symbol) after line end marker + if not self.RDS_data[PI]["internals"]["last_valid_rt"]==rt:#ignore duplicates + t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"RT",rt) + db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t) + self.RDS_data[PI]["internals"]["last_valid_rt"]=rt + try:#print rt+ if it exist + t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"RT+",str(self.RDS_data[PI]["RT+"])) + db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t) + except KeyError: + pass#no rt+ -> dont save else: textcolor="gray" #formatted_text="%s%s%s"% (textcolor,self.RDS_data[PI]["RT"][:adr*4],segmentcolor,self.RDS_data[PI]["RT"][adr*4:adr*4+4],textcolor,self.RDS_data[PI]["RT"][adr*4+4:]) @@ -842,67 +878,7 @@ class rds_parser_table_qt(gr.sync_block): #if not ci==0: #print("ci %i not found, discarding"%ci) pass - - #if tmc_T == 0: #message #TODO: test message uniqueness here - ##print("TMC-message") - #tmc_F=int((tmc_x>>3)&0x1) #identifies the message as a Single Group (F = 1) or Multi Group (F = 0) - #if tmc_F==1 or (tmc_F==0 and Y15==1):#single group or 1st group of multigroup - #if tmc_F==1:#single group - #tmc_D=Y15 #diversion bit(Y15) - #else:#1st group of multigroup -> no diversion bit - #tmc_D=-1 - #tmc_event=int(tmc_y&0x7ff) #Y10-Y0 - #tmc_location=tmc_z - #tmc_DP=int(tmc_x&0x7) #duration and persistence 3 bits - #tmc_extent=int((tmc_y>>11)&0x7) #3 bits (Y13-Y11) - #tmc_dir=int((tmc_y>>14)&0x1) #+-direction bit (Y14) - ##LOCATIONCODE;TYPE;SUBTYPE;ROADNUMBER;ROADNAME;FIRST_NAME;SECOND_NAME;AREA_REFERENCE;LINEAR_REFERENCE;NEGATIVE_OFFSET;POSITIVE_OFFSET;URBAN;INTERSECTIONCODE;INTERRUPTS_ROAD;IN_POSITIVE;OUT_POSITIVE;IN_NEGATIVE;OUT_NEGATIVE;PRESENT_POSITIVE;PRESENT_NEGATIVE;EXIT_NUMBER;DIVERSION_POSITIVE;DIVERSION_NEGATIVE;VERÄNDERT;TERN;NETZKNOTEN_NR;NETZKNOTEN2_NR;STATION;X_KOORD;Y_KOORD;POLDIR;ADMIN_County;ACTUALITY;ACTIVATED;TESTED;SPECIAL1;SPECIAL2;SPECIAL3;SPECIAL4;SPECIAL5;SPECIAL6;SPECIAL7;SPECIAL8;SPECIAL9;SPECIAL10 - #try: - #location=self.lcl_dict[tmc_location] - #loc_type=location[0] - #loc_subtype=location[1] - #loc_roadnumber=location[2] - #loc_roadname=location[3] - #loc_first_name=location[4] - #loc_second_name=location[5] - #loc_area_ref=int(location[6]) - #event_name=self.ecl_dict[tmc_event] - #refloc_name="" - #try: - #refloc=self.lcl_dict[loc_area_ref] - #refloc_name=refloc[4] - #except KeyError: - ##print("location '%i' not found"%tmc_location) - #pass - #if not self.TMC_data.has_key(tmc_hash):#if message new - #message_string="TMC-message,event:%s location:%i,reflocs:%s, station:%s"%(event_name,tmc_location,self.ref_locs(tmc_location,""),self.RDS_data[PI]["PSN"]) - #self.TMC_data[tmc_hash]={"PI":PI,"string":message_string} - #self.signals.DataUpdateEvent.emit({'TMC_log':message_string}) - #t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"ALERT-C",message_string.decode("utf-8")) - #db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t) - ##(hash,time,PI, F,event,location,DP,div,dir,extent,text) - #message_string="%s ,firstname:%s, reflocs:%s"%(event_name,loc_first_name,self.ref_locs(tmc_location,"")) - #t=(tmc_hash,str(datetime.now()),PI, tmc_F,tmc_event,int(tmc_location),tmc_DP,tmc_D,tmc_dir,tmc_extent,message_string.decode("utf-8")) - #db.execute("INSERT INTO TMC (hash,time,PI, F,event,location,DP,div,dir,extent,text) VALUES (?,?,?,?,?,?,?,?,?,?,?)",t) - ##print(message_string) - #except KeyError: - ##print("location '%i' not found"%tmc_location) - #pass - #else:#subsequent groups in multigroup -> Y0..Y11 and Z0..Z15 are special format - #sg=int((tmc_y>>14)&0x1)#=1 if second group Y14 - #gsi=int((tmc_y>>12)&0x3)#group sequence indicator Y12..13 ,max length:5 - #if sg==1:#if second group - #pass - #if not self.TMC_data.has_key(tmc_hash):#if message new - #data1=tmc_y&0xfff#data block 1 - #data2=tmc_z#data block 2 - #message_string="SG:%i, GSI:%i, content:%03X%04X"%(sg,gsi,data1,data2) - ##decode_TMC_MGM(array) - #self.TMC_data[tmc_hash]={"PI":PI,"string":message_string} - ##self.signals.DataUpdateEvent.emit({'TMC_log':message_string}) - #t=(tmc_hash,str(datetime.now()),PI,message_string) - #db.execute("INSERT INTO TMC (hash,time,PI,text) VALUES (?,?,?,?)",t) - ##code.interact(local=locals()) + else:#alert plus or provider info adr=tmc_x&0xf if 4 <= adr and adr <= 9: @@ -1107,18 +1083,7 @@ class rds_parser_table_qt(gr.sync_block): PI=tmc_msg.PI tmc_F=tmc_msg.is_single tmc_hash=tmc_msg.tmc_hash - #tmc_event=tmc_msg.event - #location=self.lcl_dict[tmc_location] - #loc_type=location[0] - #loc_subtype=location[1] - #loc_roadnumber=location[2] - #loc_roadname=location[3] - #loc_first_name=location[4] - #loc_second_name=location[5] - #loc_area_ref=int(location[6]) - #event_name=self.ecl_dict[tmc_msg.event] refloc_name="" - #reflocs=self.ref_locs(tmc_msg.location.lcn,"")#old method reflocs=tmc_msg.location.reflocs if not self.TMC_data.has_key(tmc_hash):#if message new try: @@ -1140,12 +1105,12 @@ class rds_parser_table_qt(gr.sync_block): #self.signals.DataUpdateEvent.emit({'TMC_log_str':multi_str}) if tmc_msg.location.has_koord:#show on map map_tag=tmc_msg.event_name+"; "+multi_str - folium.Marker([tmc_msg.location.ykoord,tmc_msg.location.xkoord], popup=map_tag.decode("utf-8")).add_to(self.osm_map) - #self.osm_map.save(self.workdir+'osm.html') - #code.interact(local=locals()) + #print to osm map (disabled because slow) + #folium.Marker([tmc_msg.location.ykoord,tmc_msg.location.xkoord], popup=map_tag.decode("utf-8")).add_to(self.osm_map) + #print to google map - marker_string=self.marker_template.format(lat=tmc_msg.location.ykoord,lon=tmc_msg.location.xkoord,text=map_tag,marker_id=len(self.map_markers))#without hmtl escape - #marker_string=self.marker_template.format(lat=tmc_msg.location.ykoord,lon=tmc_msg.location.xkoord,text=self.html_escape(map_tag)) + marker_string=self.marker_template.format(lat=tmc_msg.location.ykoord,lon=tmc_msg.location.xkoord,text=map_tag,marker_id=len(self.map_markers)) + marker_string=self.marker_template.format(lat=tmc_msg.location.ykoord,lon=tmc_msg.location.xkoord,text=map_tag)#without ID self.map_markers.append(marker_string) #code.interact(local=locals()) @@ -1165,7 +1130,8 @@ class rds_parser_table_qt(gr.sync_block): ps.print_stats() print(s.getvalue()) def decode_AF_freq(self,freq_raw): - if freq_raw in range(1,205):#1..204 + #if freq_raw in range(1,205):#1..204 BAD coding -> memory usage + if 1<=freq_raw <=204: return(87500000+freq_raw*100000)#returns int #return(87.5e6+freq_raw*0.1e6)#returns float else: @@ -1188,7 +1154,7 @@ class rds_parser_table_qt(gr.sync_block): 0b1000:u"áàéèíìóòúùÑÇŞßiIJ", 0b1001:u"âäêëîïôöûüñçş??ij", 0b1100:u"ÁÀÉÈÍÌÓÒÚÙŘČŠŽĐĿ", - 0b1101:u"áàéèíìóòúùřčšžđŀ"} + 0b1101:u"ÂÄÊËÎÏÔÖÛÜřčšžđŀ"} charlist=list(charstring) for i,char in enumerate(charstring): #code.interact(local=locals()) diff --git a/python/rds_parser_table_qt.pyc b/python/rds_parser_table_qt.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c330c4ff25ab51e969785232402723e31b6e4552 GIT binary patch literal 38980 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%0DX|>lYG+qv6GRt%=L4|%cJ3!tDTBhoXJRZ<2?`kc zFV*;Qfr>O@m{M5)N#nt8?^*87iSa8DJChkB6DpUHrd+5Au?jPwNhl^gA&eN6Sb$}U zR@<0sD)-wJszt-sEqg@+FY4n%Jmt{_IaG)b{<#LsMr~Ss6Gw?S;ibrUg6Mfs_x$WT zsP-ux@yWq_kvnrT2ArF|BC_GpLkuPhQuuK3t#YQ4i}lIt?5{~z^tTH>rn)~a=My-d zPw~l{bb|~m?BB&m5Uds0S8$$egg+1D&uN5ZBG8WQPMAeIhwor8{*x!-k2MzYj{w(9 z=ZE#wWuX_y&@zg{~iG>g}W?2&fP3~RX8sVVLO(TO~ zC57MVPn-X<9PU?I1NU2Rd;M9MpPeUzeDrEKG~a1Q+e{)9=ks5m9P2V^W3z+`2^4s? zMl%H^ZJQlh)3l*&DdA5Ra=#a#sGap`A$HMkc163?8+?PXSzOrc9pHjyp3@{>CeE(s zn(m*40oX5w*DWo+?a?DR;?pnahyIt=#?|NNN@F-r59G{DSw!f(PXBj2fF zX8cwz^TKb7S_%6Pz@1Dzm{)i20-`}?Fdi(yArJ|BSw*bXcAw4u;itU}ZAdIO(PveJ zgddoZv#$&Lk54Lu)yazz3;1{N#}sc&90;^qR;lf_skeuZXM5F#g8$KM|Y>EPp1Sb=yYKj5qr!z}AuR zY3qIPF9jsN@nkGR_Pk%!ARMD0hy1Yvw!vv|u?(2U(&QGFggq<}tDCx_ZTaP-G$X-? zwXE|;sVKxw0GZ^mSxqwffp&};!~#eo_m*_!jYHfRG%KV34%86we#ErF>?uuz{bb$w z9C`jo-4Vw1sbK%r`R7#VNjdxEmG^?B0_0;r+*aym{sO75sC-s~&__EnfOYsVDP~ zDESRJ897>1t+H%oB-87O>CY+Ai^$X0s>eJ93$uPb{R{bq1fl^NsBkJ|dH^{YGpm^Id