From de049999ee8e331aaee5067fd008f55c9562db23 Mon Sep 17 00:00:00 2001 From: Lukas Droste Date: Mon, 16 Aug 2021 20:52:35 +0200 Subject: [PATCH] Python server gracefully shutdown and send scan data to converter --- PointCloudWeb.Scanner/server.py | 223 ++++++++++-------- PointCloudWeb.Scanner/test/serverWSonly.py | 30 ++- .../PointCloudWeb.Server/Models/PointCloud.cs | 6 +- .../Tools/PotreeConverter/test.las | Bin 0 -> 79627 bytes PointCloudWeb.Web/src/views/Scanner.vue | 59 +++-- 5 files changed, 200 insertions(+), 118 deletions(-) create mode 100644 PointCloudWeb.Server/Tools/PotreeConverter/test.las diff --git a/PointCloudWeb.Scanner/server.py b/PointCloudWeb.Scanner/server.py index 77448cc..82966fc 100644 --- a/PointCloudWeb.Scanner/server.py +++ b/PointCloudWeb.Scanner/server.py @@ -4,10 +4,12 @@ import time import PyLidar3 import asyncio import websockets -from concurrent.futures import ThreadPoolExecutor +import threading +import collections +import requests -f = open("PointCloudWeb.Scanner\datafile.txt","wt") -f.write("y, x, z\n") +# f = open("PointCloudWeb.Scanner\datafile.txt","wt") +# f.write("y, x, z\n") arduino_status = False arduino_port = "COM9" @@ -18,30 +20,34 @@ lidar_port = "COM6" lidar_chunk_size = 20000 lidar = None scan_progress = 0 -_executor = ThreadPoolExecutor(1) -newMessage = "" -lastMessage = "" +scan_running = False +stop_scan = False +ws_message_queue = collections.deque(maxlen=100) +scan_id = "32a3acd0-4d67-4281-bf9c-3eefc093eca4" -async def init(websocket): - global arduino - arduino = serial.Serial(port=arduino_port, baudrate=arduino_baud) +async def init(): + global arduino, ws_message_queue, arduino_status, lidar, lidar_status try: - await websocket.send("arduino connected " + arduino_port) - global arduino_status + arduino = serial.Serial(port=arduino_port, baudrate=arduino_baud) + ws_message_queue.appendleft("arduino connected " + arduino_port) arduino_status = True except: - await websocket.send("can not connect to arduino! " + arduino_port) + ws_message_queue.appendleft("can not connect to arduino! " + arduino_port) + arduino_status = False try: - global lidar lidar = PyLidar3.YdLidarX4(port=lidar_port,chunk_size=lidar_chunk_size) #PyLidar3.your_version_of_lidar(port,chunk_size) if(lidar.Connect()): - global lidar_status lidar_status = True - await websocket.send("lidar connected " + lidar_port) + ws_message_queue.appendleft("lidar connected " + lidar_port) else: raise ValueError except: - await websocket.send("can not connect to lidar! " + lidar_port) + ws_message_queue.appendleft("can not connect to lidar! " + lidar_port) + lidar_status = False + if( arduino_status == True and lidar_status == True): + ws_message_queue.appendleft("true") + else: + ws_message_queue.appendleft("false") def arduino_write_read(x): arduino.write(bytes(x, 'utf-8')) @@ -50,130 +56,161 @@ def arduino_write_read(x): def setY(y): tmp = arduino_write_read("<"+str(y)+">") - print(tmp) - -# def getY(): -# print(arduino_write_read("")) - -# def resetY(): -# print(arduino_write_read("")) - -# def zerotY(): -# print(arduino_write_read("")) def filterY(data): temp = data[data.find("<"):data.find(">")] return temp + data[data.find("><"):data.find(">", data.find("><")+2)+1] def senddata(data,posy): + temp ="{\"Id\": "+scan_id+",\"ScanPoints\":[" for x,y in data.items(): - f.write(str(posy) + ", " + str(x) + ", " + str(y) + "\n") + temp += ("{\"RAY\":" + str(posy) + ",\"RAX\":" + str(x) + ",\"DistanceMM\":" + str(y) + "},") + # f.write("{\"RAY\":" + str(posy) + ",\"RAX\":" + str(x) + ",\"DistanceMM\":" + str(y) + "},") + l = len(temp) + temp = temp[:l-1] + "]}" + r = requests.put(url='http://localhost:35588/scandata', data=temp, headers={'content-type': 'application/json'}) + #print(r.status_code) -def startScaner(websocket, mode): - global scan_progress - global lidar + +def startScaner(mode): + global scan_progress, lidar, stop_scan, scan_id if lidar_status == True: - print(lidar.GetDeviceInfo()) + ws_message_queue.appendleft(str(lidar.GetDeviceInfo())) gen = lidar.StartScanning() if mode == "0": print("Mode 0") - for y in range(18): + ws_message_queue.appendleft("running") + for y in range(19): + if(stop_scan == True): + break + print("send data") senddata(next(gen),y*10) time.sleep(2) setY( y*10) time.sleep(2) - scan_progress = y/18*100 - print(str(scan_progress) + " %") + scan_progress = round(y/18*100) + ws_message_queue.appendleft("" + str(scan_progress)) + requests.put(url='http://localhost:35588/scandata', data='finished/'+scan_id, headers={'content-type': 'application/json'}) setY(0) lidar.StopScanning() - lidar.Disconnect() elif mode == "1": - print("Mode 1") - for y in range(90): + ws_message_queue.appendleft("running") + for y in range(91): + if(stop_scan == True): + break senddata(next(gen),y*2) time.sleep(1) setY(y*2) time.sleep(1) - scan_progress = y/90*100 - print(str(scan_progress) + " %") + scan_progress = round(y/90*100) + ws_message_queue.appendleft("" + str(scan_progress)) + requests.put(url='http://localhost:35588/scandata', data='finished/'+scan_id, headers={'content-type': 'application/json'}) setY(0) lidar.StopScanning() - lidar.Disconnect() elif mode == "2": - print("Mode 2") - for y in range(360): + ws_message_queue.appendleft("running") + for y in range(361): + if(stop_scan == True): + break senddata(next(gen),y*0.5) time.sleep(1) setY(y*0.5) time.sleep(1) - scan_progress = y/360*100 - print(str(scan_progress) + " %") + scan_progress = round(y/360*100) + ws_message_queue.appendleft("" + str(scan_progress)) + requests.put(url='http://localhost:35588/scandata', data='finished/'+scan_id, headers={'content-type': 'application/json'}) setY(0) lidar.StopScanning() - lidar.Disconnect() - elif mode == "3": - print(scan_progress) - scan_progress += 1 - print(scan_progress) else: - print("mode error") - - f.close() - print("scan stoped") + ws_message_queue.appendleft("mode error") + #f.close() + if(stop_scan == True): + stop_scan = False + ws_message_queue.appendleft("canceld") + ws_message_queue.appendleft("scan canceld !") + else: + ws_message_queue.appendleft("finished") + ws_message_queue.appendleft("scan finished") else: - print("Error connecting to device") + ws_message_queue.appendleft("Error connecting to device") -async def wsfilter(websocket, message): +async def wsfilter(message): command = message[message.find("<")+1:message.find(">")] value = message[message.find("><")+2:message.find(">", message.find("><")+2)] - await wsaction(websocket, command,value) + await wsaction(command,value) -async def wsaction(websocket, command, value): +async def wsaction(command, value): + global ws_message_queue, stop_scan if command == "start": if value == "0": - await websocket.send("start scan resolution 0") - await loop.run_in_executor(_executor, startScaner(websocket, value)) + ws_message_queue.appendleft("start scan on low resolution") + x = threading.Thread(target=startScaner, args=(value)) + x.start() elif value =="1": - await websocket.send("start scan resolution 1") - await loop.run_in_executor(_executor, startScaner(websocket, value)) + ws_message_queue.appendleft("start scan on medium resolution") + x = threading.Thread(target=startScaner, args=(value)) + x.start() elif value =="2": - await websocket.send("start scan resolution 2") - await loop.run_in_executor(_executor, startScaner(websocket, value)) - elif value =="3": - await websocket.send("start scan test") - await loop.run_in_executor(_executor, startScaner(websocket, value)) + ws_message_queue.appendleft("start scan on high resolution") + x = threading.Thread(target=startScaner, args=(value)) + x.start() else: - await websocket.send("mode error") + ws_message_queue.appendleft("mode error") elif command == "connect" and arduino and lidar != None: - await websocket.send("try to connect to Adruino and LIDAR") - await init(websocket) + ws_message_queue.appendleft("try to connect to Adruino and LIDAR") + await init() elif command == "status": - await websocket.send("progress: " + scan_progress) + ws_message_queue.appendleft("progress: " + scan_progress) + elif command == "stop": + stop_scan = True + elif command == "init": + await init() else: - await websocket.send("command error") + ws_message_queue.appendleft("command error") -async def wscom(websocket, path): - await websocket.send("Websocket connected") - await init(websocket) +async def producer_handler(websocket, path): while True: - data2 = await websocket.recv() - await wsfilter(websocket, data2) - print({data2}) - if scan_progress != lastMessage: - await websocket.send(scan_progress) - lastMessage = scan_progress + global ws_message_queue + if len(ws_message_queue) > 0: + message = ws_message_queue.pop() + print(message) + await websocket.send(message) + await asyncio.sleep(0.01) -async def main(): - server = await websockets.serve(wscom, 'localhost', 6789) - await server.wait_closed() +async def consumer_handler(websocket, path): + async for message in websocket: + await wsfilter(message) + +async def handler(websocket, path): + print("Start Websocket Connection at ") + await init() + consumer_task = asyncio.ensure_future(consumer_handler(websocket, path)) + producer_task = asyncio.ensure_future(producer_handler(websocket, path)) + done, pending = await asyncio.wait([consumer_task, producer_task], return_when=asyncio.FIRST_COMPLETED) + for task in pending: + task.cancel() -loop = asyncio.get_event_loop() -loop.run_until_complete(main()) -#asyncio.run(main()) - -#while True: -# startScaner(input("Scan Modus(0,1,2):")) - #wsfilter(input("Befehlt Eingeben:")) - # for x in range(18): - # setY(x*10) - # time.sleep(1) \ No newline at end of file +try: + ws_server = websockets.serve(handler, 'localhost', 6789) + loop = asyncio.get_event_loop() + loop.run_until_complete(ws_server) + loop.run_forever() +finally: + print("shutting down server") + ws_message_queue.appendleft("shutting down server") + stop_scan = True + print("set stop_scan") + try: + setY(0) + print("reset stepper") + except: + print("can´t reset stepper") + ws_message_queue.appendleft("false") + print("Status an UI senden") + try: + lidar.Disconnect() + print("LIDAR Disconnecten") + except: + print("can´t disconnecten lidar") + loop.close() + print("server down") \ No newline at end of file diff --git a/PointCloudWeb.Scanner/test/serverWSonly.py b/PointCloudWeb.Scanner/test/serverWSonly.py index 048c77d..7b1cd09 100644 --- a/PointCloudWeb.Scanner/test/serverWSonly.py +++ b/PointCloudWeb.Scanner/test/serverWSonly.py @@ -4,7 +4,6 @@ import time import PyLidar3 import asyncio import websockets -from concurrent.futures import ThreadPoolExecutor import threading import collections @@ -20,7 +19,8 @@ lidar_port = "COM6" lidar_chunk_size = 20000 lidar = None scan_progress = 0 -messageQeue = ['test', 'test1', 'abc'] +scan_running = False +stop_scan = False ws_message_queue = collections.deque(maxlen=100) async def init(): @@ -40,6 +40,7 @@ async def init(): ws_message_queue.appendleft("lidar connected " + lidar_port) except: ws_message_queue.appendleft("can not connect to lidar! " + lidar_port) + ws_message_queue.appendleft("true") def arduino_write_read(x): data1 = x @@ -47,7 +48,6 @@ def arduino_write_read(x): def setY(y): tmp = arduino_write_read("<"+str(y)+">") - print(tmp) def filterY(data): temp = data[data.find("<"):data.find(">")] @@ -58,12 +58,14 @@ def senddata(data,posy): f.write(str(posy) + ", " + str(x) + ", " + str(y) + "\n") def startScaner(mode): - global scan_progress, lidar, messageQeue + global scan_progress, lidar, stop_scan if lidar_status == True: ws_message_queue.appendleft("start scan mode: " + mode) if mode == "0": ws_message_queue.appendleft("running") for y in range(19): + if(stop_scan == True): + break time.sleep(0.2) setY( y*10) scan_progress = round(y/18*100) @@ -72,14 +74,19 @@ def startScaner(mode): elif mode == "1": ws_message_queue.appendleft("running") for y in range(91): + if(stop_scan == True): + break time.sleep(0.2) setY(y*2) scan_progress = round(y/90*100) ws_message_queue.appendleft("" + str(scan_progress)) setY(0) + elif mode == "2": ws_message_queue.appendleft("running") for y in range(361): + if(stop_scan == True): + break time.sleep(0.1) setY(y*0.5) scan_progress = round(y/360*100) @@ -89,8 +96,13 @@ def startScaner(mode): ws_message_queue.appendleft("mode error") f.close() - ws_message_queue.appendleft("finished") - ws_message_queue.appendleft("scan finished") + if(stop_scan == True): + stop_scan = False + ws_message_queue.appendleft("canceld") + ws_message_queue.appendleft("scan canceld !") + else: + ws_message_queue.appendleft("finished") + ws_message_queue.appendleft("scan finished") else: ws_message_queue.appendleft("Error connecting to device") @@ -100,7 +112,7 @@ async def wsfilter(message): await wsaction(command,value) async def wsaction(command, value): - global ws_message_queue + global ws_message_queue, stop_scan if command == "start": if value == "0": ws_message_queue.appendleft("start scan on low resolution") @@ -121,6 +133,8 @@ async def wsaction(command, value): await init() elif command == "status": ws_message_queue.appendleft("progress: " + scan_progress) + elif command == "stop": + stop_scan = True else: ws_message_queue.appendleft("command error") @@ -137,7 +151,7 @@ async def consumer_handler(websocket, path): await wsfilter(message) async def handler(websocket, path): - print("Start Websocket Connection") + print("Start Websocket Connection at ") await init() consumer_task = asyncio.ensure_future(consumer_handler(websocket, path)) producer_task = asyncio.ensure_future(producer_handler(websocket, path)) diff --git a/PointCloudWeb.Server/PointCloudWeb.Server/Models/PointCloud.cs b/PointCloudWeb.Server/PointCloudWeb.Server/Models/PointCloud.cs index 7fe2930..8378cd5 100644 --- a/PointCloudWeb.Server/PointCloudWeb.Server/Models/PointCloud.cs +++ b/PointCloudWeb.Server/PointCloudWeb.Server/Models/PointCloud.cs @@ -115,9 +115,9 @@ namespace PointCloudWeb.Server.Models { // + 0.001 Otherwise points are outside of the bounding box by a floating-error, then Potree-Converter fails stringBuilder.AppendLine(string.Join(',', - (point.X + 0.001).ToString(CultureInfo.InvariantCulture), - (point.Y + 0.001).ToString(CultureInfo.InvariantCulture), - (point.Z + 0.001).ToString(CultureInfo.InvariantCulture) + (point.X + 0.1).ToString(CultureInfo.InvariantCulture), + (point.Y + 0.1).ToString(CultureInfo.InvariantCulture), + (point.Z + 0.1).ToString(CultureInfo.InvariantCulture) ) ); } diff --git a/PointCloudWeb.Server/Tools/PotreeConverter/test.las b/PointCloudWeb.Server/Tools/PotreeConverter/test.las new file mode 100644 index 0000000000000000000000000000000000000000..c48002989d17dc2dd370560d4b364c871e623716 GIT binary patch literal 79627 zcmagH1$-S<*N2--jzw#@GbFgSxOYgf;)UYUA;H~Uh8(Q8J7q|4cPLO865QQon&1wN zC+E(y`S$MGllR{H{eFDy`*dxYIp@sm_1|l)z0KsQo0t8M|0{QPXq>#w|M&-k`1hqI zuDZsmt1q?ux)ax3Z{2lPF#pD1zAo#|&ncUu*?eWem@@UBCi>664w`fR<9%Hlr~lOd z{=fb#-{#s);iWULeIfZ{)3TkP+BEsItTf|>7x=UMg=;oV{`huq$&+62ZojzDocs^| zD=Vv1|NnonaIuR<**Vu-Sja59?5MVFQ?{{Kyuzi0#-oq1Sn$wgg|2I^8^v0mecod3 zI#(1r5Bh!-Yq_MRT+W#>vX4!)7-KZ;w}-{BE7k-SQ)4`Jx5a3iZF$dP*mYk!Fp4!a zx{jQS-*|N)d*YN)tho9K7IWUXrqHtgt)p1?Vy{~)-0`|X%g6s(%=`5ELa@Nn6>{8o zpBoF|v~4XGbl+6)7eCcv(ciZg+^)MuvG|^kE#{wmTfytgX5<*KuxTxeh3B>vyqO1D z%qhFQ5dLq3eo*vSuA?_-9m8smesPYx8CsuZ-ezL=6v`; zA!=N%#mQSZ(b&K4_JpOG`BH{&gGsNFU`Arn9I zqIF;VNBxR${t4Fn;&slh@UB|VzE?Y)SK&Riiq$rpdvt|!`9~IOZ`-CkJoR?#cht4a zW@Sx1ldb1p>!W9vb$)rX^=xXqd2(6tljAMBuHCjTD=sl)vDWIP%F@sF%wG$awO{eR z^}KIBWTCRooBy_0>&Fwy8W#M^Vr|}-vX*DQuvqh2HKXi!XE|3kThE-f+KN$je6B6^ z|62WJ7XPUfOkCf(FOB>5mD2O;IO@ex)c1rnM(a9%jIwK7@z+s|F^YT6vBqt6=9JQN zX`D8z)b!=A);;a`{IgMZZSOo?YWe62%dU0RM@refuCiGDl5dV~`|@d}mRTEFpKs=j zj!|}92On4}F1Ma#$NbXwr8#K`xX>{ct6$*K z67!L5VfEfV7Nc$R_R}rayv!A)=0$d~SpDOVmYRPrTC8Kt_oc=cKCoEV<{yfGjJdUc``#$laQSSD zbsX4QYFYChi*+5-R%$=9$6_5fT~=yX?oNxfzV&gb{=O6J`Q83=sbho3EynM%{;WGK z#{63k8?;!*$s?ti3?%ta#U1!?JDT z``eZ|Uo1Fau`ezxb4x4sN!Gsi%VqKYi}qS<=;yL%`nnctSZdYs;D+6Mq+QFKyOcXi zo@BAk?Jh5O=Zv#h!%Z)gd)wDqtm)2g%bl~P+U%jl9e-BAVyvgbSF0`7^!ou$bmv|c z>l}BFlOChxurHlpxr}93^!{|b$hVkxmsb%V+A~*PTkITP;dTFOvEccjBA&3D)i&Dr zi;8fce=Ixy`r1soKmYB8Gv3A1thVmKCuP!Yy{{k2_&fe-^~d`peXrc@m&wL!|7_XC zTkoCq9{SN@(bv+p{_)Uu1WS-KFnd}A@%rkS(yF$I6; z1+3>mH2=hcbI!>Yi)WuuaO=+=#hi%A47e_o-*U40a5e|uMp(YE`PGcCp#>Gz=+~4|L-W)zQm%|y9c|* z#>t}?>q7Y%`C~39sI^Y=KGeQ0OszvwjJ2$6tNr2Z-=~)AwlLQ7lJWaMgOZHj0vc4B zp+QL&EpcQ44N5Y8=lCg-(I0EQG(&@ujQ-G|(hLnM%{mheN;1YpgGw_rD9QLuqd`d) zK60$mpxRG5-P|jF3usVjh6W`WzYjF1G(&@u%zf)*r9nv+O+8gn4XV3%mYEB`1vDth_)dnm ztd&hOG$_gV-bX*&Vlg$B?LW+>=Z6L*8S_K8kc|1cI}~$r48;-+D$SxF%kpU!7Cnop z`K@-I#ke;2hqo<;E*jmtu+m1&Z!2N`QOy=Z1C3vO)MDtJ;i|dHhAYo8_Y}=Cylqd5 zp-;w3KV~uauv-ew+T*G^7cS0gi=Oz)@fJg0LxXCWdG0*BL`#b zM>SPUzbnc#jDc;hyVM$+p3}LlxmJOV!D13l8Y2@GgF#uJM!yO{a}?U(tF`V_Em22bFg`P&JQzvqM?a zF&~y^o~<>kW1(frTK@Q7Hax*qz?*ul_g(yVQ{-7JPq({bfgi=k#^mU-2G_?FVigiBGZ80>Nmckbn%N&1RsU=xE%Kl*JG?J;d ziB4m&-EJv0-So`}`ly}@_q6k*n?~e)Mwdc=k>5YAlkcTmC-S}LKFIGqzrD;oSK4;B z)|Z7dI|k+R0KFu5VcdXxZnQmrTUoUI^F3`RE^2<$JPYHG>Uvmv>N$5Gn^T%c{VS^& z#(EGPr17A&3~TszpYmX@u|3U|8yVL9%thts8$8Q&f9+>KQ;rV8oHXlv@%wVdZ9aAv z!y3jc;6!WYds^q)+pzABHd7i(yal!k&Yp!@UA zSVw6iEiWx(n0j~L-aS{oyQ6!a%%ORpsp+=i7X3N2k%nE?H!S*VBc+!#T)4Sm@r{Af zOB!bHWSBSg_5bXW@0aPXYJ5TUx5A-@x&2!zy@Wj)V>e;@0{T#z#XH_MiiJ=9V=+Is zO|r+ld9Q5iT)eH)OC-b2Tji@!cFsP_^rh{hOAfY}>TjicEk=Lt_P>u}=q022`{Cfx z?9V$a#@ftz|MPxpZT8r-WQ{&vg0 z1|PZNt&UTq#1gNWQ>bmA{qU4Zu8bChF+4kLoZ1)^pZ3~FOe)f{XnIcq#1gN zWcVIx?@!!QZ?C+H6=;9p7f1ijVZ5^}cKHOQm!OGjyWrp|rI$3V^_#_bu1n_rS*`RE z$)Xc#m0luQ{KR^N?z_g9H;fEh{qcU3cJzl{l4j^7l6lFTTQ<43T+W$s(MzNq4824$ zf4ENRCEc@rH*r{Ttt^>Wq zvg6*zg@U}#;c+V}y+mFw`vIQ!NTKPFesjGsn)%Wdm6EB?W1Y<^t!uk`?Lu(ZXJpY+$r-!#u`f99;(&Zn#A*)8#W zKHhk>TGo|%4B}ddHR{~;ZSxKY+E&Q~J&)z37UcgtG3P%1V|Dwho0_)nx&P$RP>Qu( zhBaM0!|^WNKHGH8uV%gGop+qlM%pLtZtUEzR?9i>EK!&BHZd%G`q!*~`U-W~PnR`} zn4+-yxJuUQdd~Q?#g@QnJ8L&HM(|{1&YAf9*p_qGFl`%Nc+7FSf5{g+2aTP#Ns>NUeZ)0mhZSc>M)kqqL01x!;V^EiylT zsoLVp>U&kc^QX#AY5?dKjKQ3=UGvKCC}#Q7No91}1XUe3{uKe&M7D~=V`QeF%!g@uwC0+#$CC$)K(hLnnvc@N` zDWRcA)|JGjs92XoAJJp5W@_B_j1r%LX2jy4FG+m}tqQ$G-qVivZYp*DyK0HgMYkn} z1r3FG4av|@Bx?*VEH$j&HHs1IBJH}8*cmhw#@GG9idD~BjQ%?BzsqXd_-j~dpa1I- z^f=u%^UbZL&IMl_k#pwnD&9wYulQ{EJgx6;qL-+9nEZBhf5hv>Ph^JV?_9-~OnRbU zY9ozfuPt*980qQyeQnb=Tl3z3dP(QjhN0ssZ6v>kVQA&i!4rB~w>sFc?uWPkFP3Ed zDTcB3MF(ly`2xeb>)Xp6uc}9C4=Uy)iSuY|x_zFV|9iRLzhjTo8M;@A%l(~p>Xn*w z@wufOukxoJsWWsRx{~9cw@+{PA+MSKIzQOYaqn2MH#_+&!@A2SIC1!LU;Sn;7}oIX zIgazhi@n(selo24>$9AA&rSOzYw17NalgE-uRV#s(f6KphI`RRy-n^o^K5Uq?p`N6 z_}@O_(6n9nE?H{@um0M{`diz@9V=D%=f7atq2D{xw(RfzY6)W( zEmL0+o_a*T+`|Z#zp;XA<8#sdq0>aC{n0D;XPg7C&bZrM)XO?u+XZLen2E-J(wlwx zKEvG2zt8xcclI`I^r~UOO*>_swk>*#KmB1?RJBId`Q@$N;$w3R3)fsc>rY<4uVuoQ z^H>tM>blSMw){|I-pTQ&2j~2I{_N?zW#@V9`T6qh(tGx_ta_zk&YfYNn4R`9uNW4E zU*^%*h$AxB@#0;1?~Pk~cwZZ~(u;X_(nmen=if6dO7`BOwGQrWn*OX|;hV4K{bvv9 zZF=KF!@T*=&c{1d^tFHfjA7Bb9r@t1OkaEB2Zj+(?w@&1U-u8s8Rk9`<(&;Kx9v)s z=EETl}3-Pi@moa3#R@0WA-{{B55ythJs_qnmLgSpcWvKZ@`DA7xr%Dyvp@kxK? z!`;5@m+P>oW^P{TC0BJDJO9d_JbFokVAv5~BHLo8^pcdF(n|!x&YQgGpzP0Cq%R-e zxUI!lCkBaLk{(0pCF%YWy+roM{D?1+EWUpz?_Rg=Q0F2)m}^tl+kK#A$GGSva$M}t zOCsm)CAa-JZ`_0tU(%9dN-vQNU;Ki{hNb@- zC2i45vdjN$^{4a_(bj*}QF@8AQ-1Q6(?+EKiyeB2v||kP63MVbFOdxYyycjGM&$gk zLobnb&h{Aqbu4{mE%yv8AU-bL=!TUd&{9E6i2mH-15U`Jmw0P$Z&>i#iFx#rG(#`3*mhI%@sCSpg3s17ZKHux z^2C?K&+cHDfA(p4;!A?fb~4Nd2MJaXgbb5yBRy)jp(Y9`SGrK zVu+&eH#BzPOEdDsmw1QnX;}R3C3)gY;=lGX%>DZEJn<#LN&6V)thsI8yYZ1q@6ELh zi?cW8r-{v zv(7jD@jHmix2!8(zN2BRW5e=~)N@Do7ajXUMfAoqW27b%PMTE_f3x6NsiAnM9a|9| zG&H86eb}tcm^0tAMzObu&yEgY`SJHH_n)SaXa;&*vK6FmFzMT~uMd+k3G8z#?Lqik_{`WzKVIke zNIw}}fVMJTfc<_ohV;)9T>v}u27O<&F2J6-X87W?mHw~J`C)u*r{gUcUo+w@)VRXG zL>Ex^Px#o%cdod~nhQQQ^O4tvKds^@63sx50aI}lf;A_x0cZed71|Dl29RcG0FvSV zq5*I{x^35&&z4&EINR0=lKF9ca(>)r@?B({0{vy*YOwkvwgL@+d@nr)3=KdsG!Nn^ zBt!Ecjsnd^w^fW7T)}ADH2ln{w%J+tT6SG`K2&1wiMSfwA9E%zM&1K70(20m)1Yth zeU#c4-}hqi_hG5S@!JX~EHcz6ep}=PI!&t&$XEzsX}pc+_OvE(Sb8q_!YXz_#2>Kk zK?9JnYT5bglmR7q&MQQ)#eZ%}Srm6S{a{jcP%0)^1)$TWQ=9$gjkvn%rPs<}^ zhQ*scUlG2yWlvN03WgDLhh6)XYZ}J7$G>#Fo|Zj)!&oc0D_+;reqf7XQE__4Tlv%; zvKr$8AkrEJ9e{PVp;TD$ais$+@RNW z|DMOzosbXbZrB^FKfigO2Y39Hi;jJymz-qX)^DrJ``3Tk8$UY1FmK?sT<9Lr=bZhs z>CY`*o+IvoJZ0TBEIT&m?LVd8U-l`(;zZwwX3ptzUtGZS7ympb=Vzw&doT4HJO8sq z@X8KIA`BH`6S^flMTZk%+~wE z(pSR{e_z@$2JsPc3>f~tWcY*K2W3Yr|LrnAPd>@2E7x-+z$?#3tACU7451FCr131%8&+6|z zjQ5yi-d`8x@b`%c({}NRm*nvGz5UO!82-Lwqb>0fk})p+z8n{Zzn^CK`;zfo>wJ9~N6wEqzZu82-Lw z{uNi{*k6{+S@;^YXCIw@u4xBxt|X zjH?*_ev0AmOU88&ACbNeeEBp}{=T@5_`5rD#G?4?U1i2~|F}Cx%v8AfwH6~jLNb5o zz8wC3JoN@+7gj!$!`}}+ztOOGfk$)1M-WeG$cVoQv&UL}bFBnGNgsOv+-Oar7>JQkdjPNtnzXH(%>vrc5)g1;XwJ=xefM;=uXo%4RJ)c*15o!xJ*lzS}r@%Z~v z3+Fc(9I)`%>;;`>UFUCFTj9h5V~RIiVi^8@yzZRj2ltd&x8vs%KOp^m9akXpQ}FFo z{6SI^MBgjz+Y?KmnfC4R=QUHg@F`sYjX=c^h!_VQYartp_`CAYMLr2WJxu!hI=@2h z9qZTtxo?mEj(;xqlkwH*Px|)w>dMC!zP;83tbO+Xix-f7yp9);emwC4j4SFS_-**A>H;*Y_EJn|q43pqcWWlRaSke$ALG<4BD{)l#75MN$() z+jZpbRvJK>p#f0qL)xJMNY;GuJt__$&FX7+vh27Haxvs}aGk9k*IWDh=n?IYIic)74d}VxW8?2j*4A=csb!-dM)1kConncdwe`o|P;}$BBGo#yb|5{2nkI4JScb3mv?(d^(pi{{29~uCffQ)rP18BM9=3$v5 z(M;aN#^((X_oK(+$OvMy>!k4Xl&2UB|w6D-EFa$BPYXIO)wYl@B3u0r=g+ zWPTZY-}vIJopgWb0`W|zN9M4#f3}-q)N#>v~*9f4YE(UsJh#NsdHI$0^2+oIUa;8m8T3SnzmL1^EkdpE#O+ zcm*{jEEB31SKKT+q8|>uS_Tv$TvF65(`dg;$ zN5`r>4DXp|4GZ1dGt{jh7tq+9{!zwBawMpmVc3qjEcGa)e(yD`qnOmp-HQ*)l1t%j za*=7v8h{!V{+Ul17R>oHOZ|xG%^wW&r_`yOi{Q%n=e>WEbrg9M?7ir@IHw$)L(lR0 z4>XLtj`-^-J&dpIV8N$h4{JNg(8DCdXD7yioMLT9%p!Fr(suEqUt4y{-#>KOKG~M} zIX4~FC+7zXWe}Yq-Y>5=TH1{lSO>kSx0GlpGoWdmhpb4M5t_mN)>( zXiJ?5*;e(p;J2fg*D_`(ZAadOWbUFT=gwLC(vItmk~jc47vh)6n}{Df z-n50G3j`aSXjt^|xjA$JH#*5Ml`Ed)O{5vRfMm2q7f3U7foRK9tp11tkaqsw=jYG` zq#X=hAkEMPBx79S03@R=x`1R(*9AFr0soy-&0N$Ni7p`RVCVvAJ9Gib=nq{W&CmtX zOz8q*TzvoL z7`g!ZfM)0c=mL_V2k?$VAJBF%bOFim{Z$-5q6J7hbb+*;iUUaW0d0pyfG&V$pc%fu z+W$=S0gENN0Q!Jt_}1(Zk79`~fd8%S@YT@;&<8ZbXGRymuhtBI+rNBb7N1-*e1D$* z=mVNz=T5)bvSZG^_g+@c1%@t=X7PGW7Gr)vQtMRCPqFu3uo&Y~XF|>oMjU`-TpPLo z`h}h|zJGN1yLovX-dopYsdXA1f3LB__eU2%AJA=K=mL_V2Y8R?E1h#5Fm^C!(LF4N z9)K=@KA`(^=H8YiZ^EsA!Z6Qi&k_gVJoSuW`2OCjbE<;mKIpdo?e}HT1<(gH!}muQ zKp&7SIBfMA$?*NdB_F9l3($7{r%z?0_b1i*H@;_>(zy2dIUmpWhglDGu2d?I!@-Q) zi{be|45N(Y?09mEvd#?`EVpuklUz~aC1pG&dsN}u7gfo;82_6!D*Rc;)JgrIVc*Xj z>aG$~qvuSFq;tX&wcdy03?o+2o%WukDUgTZulj18%oFJ>t>#2s_t(nxX~&F{%E%w4 z#-kpCSl;Nae=20GE`9sQ!{ab${%? zs{TYR2J#HBll~h%9dnkk2IvqlsXg#_nA9HlySiSH3xFS}`V-m%J|Zy_QhUIEhe@3Q z-2x_c2L4^eTZnjh{v9TD2K-XSl{y3ej-A}cM$5nsT|(b0veiC`KrV)E z%V&)yBjYI0nb3mJowOa-)|PqMy3a7`z{-2i{iU7!ZSfoAen}mK-zuLEf9f$5OU95f z6EMCrX*=dC8PALO%44it3Z6gF%Qp^5Z4(XDS$U%&8C%h@?P_KIt8WcjTFbz+GWXcH zzbiV>%#XORsPF8)*2QlyjAtzI-trmSyy%ljJnHy9Sto(tMmT-p0sc*2FEw@J+Q|bl zA0qonN0~q6vwoSsK(0qHBRe4T63~I8bygXmMuzT>n!3(!mk%_YU1?auHp6A!d7BK# zng~1#@co*0UEJ6a%i+$PFevjJ&?}?2PZ*TC54uor(Sd{LL}uLNIgC#y>obsx5^Y^M z)O67b#;)bst;&P0?4Yb0g`OJDI(10aabP_5iL+C8o!2&5vt!qO1EBUSundR=;GCm?hAX@GIqhge`ccN=If6ypJVL&^XF#JJN^59GR$pVE$b}3VSk*| zAF9BS9g4V06$nW zbc5)&rUB}WX@<}251lxG|1Fuwng5wMCmyx=Ny(=(aF)6|@wKCAx}a=s3hy zpmS(D81*R9jCvH3F^1Ar#29ENN>@p=6x|@oSbQRf_d#`jAaqnrXbQRGSJ9HKKS;CaABHF6{5?v*2 zr*xH+9lA=|PU$M*`=Z83bd|K7inkE`@m*88if9W%SCQHXjI}|Uv5u1rZJ2i;ITiYz z!c@G4VB|NTt4KST%CAUrFLYa&inmB&V>BZ^M(HX^UW8=$P2@^Qrs6F`Tl!P+7O64N zRml0!ZLw4F7NRZVqN@<6Bkj;tB*U(0T>mJBu9BV~x=Na%t4KzC4CgNp&!fkMk>kZ4 zux7-)P+NdF7|G&Gm#j!L=iMn4FxALz zK1su?mzK+TVDgfQ@$Fc8KeM*&KJFmLnSZldYEbC5{MN#M8|!3jUU$_FPB3+&IvKY~ zPO#fPvCiG@MYHBAPAxdzxK-=irdGpR%KdWp+g++;94%*35MwFV-kEi0mhs+`H9a%e zpIYXBdu67$>?dZJ&st&PCa+6X!b?+g8T;Ag4+gf@bYruq}LCh_HH zi@&Ga;xogfuZuqiv*P{}y+r9gLIR3>eBd!S6co)&p*zL zkk3C=pCMt=7jOIL{xb3dq~EQ4`;Mj_ndiXyO3_A*Ju*Im_#e)PXu9Qg83cxyh{+`FSSf`xxA+onGVtm-9m9{I73hVYz!%}}=nyx-TvChKb^H@?( z9J}@nt}(1VzPdaZT(hTP?hEtSHV-H!bA72b5FWUCk9*Jp=J`ww2pH#p=yAyj;S35H zrHTnDmpQTc4=#o^I%g4m-m5OvLQN&bM)})8TYD(dctJ?Niy<3oTTP~WYqIW+c^u~ zJc@;1{4lz0&8mHJTs4SGpTe%_yREvDL@xn@6WJ3T+vztq)mXKra&ywkdw zc(1VEo4kWzmY(X@2Btrv8+o{+JVMmSv8h~sIQyM_ZPQ_Lvc@cUH?34zOVk)*G z#n1rKW2o4QlpPuX`54w1i3X5viw2NpXaKS;KD&yoNb)Rne=rqWkz%Yb$j8ujJcD@` zQhz}*-gnfdluX4|q?n4WNb2rMJEZ{#J8DUw0Z_wI+riKPs4uA*b5XGsss7LasO=~1 zRL+GMgSINRB4yVw@vc#ARnA4q4h=w#!Titw(sOS4bE{FTX_;e3F{J^du9q4%@_MO% z!MlMP9{So~XaG*@Si{&KMgyR3hPH#D0Z^+^GBg0{ElP$4Kn+XHs9ogz;$%j#QHJsbsg0GN$NLhMxG>j z`P7%xtozRaN7dI%&U2ECvuZduOEZ;kdgAj1>NiT}Pa9Jy88s%oy$`PR-`m94!QyEz zSd1Ez-bqVUQFBuF2Me!1t138V3&W@<8J_TaRlL+r7IW8|R&BB5JLw+bSIh4yxdrat zTh~x8LXQCpevB-}I>&!$+gi@u(RRey`XfhG$~6r&yS(?7s+JmPaoruuy`2uq$y`wK zYJ>VMt7IOFdfp^!qT;=Gm~||_k03X5ocw)I-zi?=rE%1I(sQBiiF@&FpK!FfnHUde2E#iJz3j^ZD-d~|6GrO_TVnE%{XFhG^1{OxZl#_ zIJ;D{?!J%8onNmXM@@9isLO?&Te#WuM?J~#mwcVvQ{tQse}%8}#WO!KbD?Hsc){y6 zS&4_|3FuPNezYfm};BY>v3T!P9gbIGVIcR zBTVk)^Y1XJTj_mPH46N0?0A3c zahZc^D>Mpxb~UcZ|6pD)={uu?sJ5aO1Ae?}o77&=V=#Z!HiD= zKWvfVj$i|0NA8HTOxaM|+mRpY>OIylG>_oa?T64kja`y&l6~`~ z;f`OY8m3|)W_&+Xzy3LfbzQt?nSa1#1H=;OwrHj71@b)7j59fkA8tRKN$Qno#@QXL z+hu*4`l0p8I?p|t3~lILIck6nh*QgmB& zhS60Y4$E^c&~>}VWQS#}A2EK#uPz$q`>WeFCiOGBr_C59hCwqm#>{_*C^o({(#tv_=aElsa2u->rQHGqGiPM z?`YmB)S>W}*|8FZCLE4h3tD7)Cuw^a$!p zN;?&kkknDsjCv{P5!6SNjAxEyjGLT{<3$kF^VZYB4vjjA;)EY z=n-i~P2*8xkn54U4r)N8?a(8r{V3;$9+74$CLv{~ViE+yXJNDLo>^@DI{<=n?o~vMqXq^i${$J%aN~r5$=inxRLe8G6L1F_M^s zG*h`AsWFruk-83*>yetX(j(Gs(IZj}Jt93WdPMqq@v}L%FxkQCz)V(L~kTXrS z9Sl7p&B*oOd`4*(-uyuY=lg1gzeP*}bp|Cp-13z$uWpylKnA1 z^a#!em3Gvd3>Uk|FuqgN*Ys|`*kZ&Aa2BX;tKwnK|JY)j2a1ltxu4n&#<{j><|TSW z(0!A!;|z$PcY(?FF%$tlz066cGC60degMzfBq9xwB|7OnF7vWBrnCYHqeeuh~m7XNy^EST?#n)tG_ zja|zQFO`MQe_TV{itev-G$Yoy}j#cheaQ$c;`r5E-oYOKY?tvT=sYftITDO2TeZIVvcf$7x-AI0~ zuxprlf_1hhtQ-GV#AR^r+izQTSmtZcPtz{DTj$ZE&9UB1#~C$UvFWhX|FCQMvbrSm zw`jxJqf)m(i=^hJjLYEMVZZIO0b=;|y(0dpWu?PMWc*e0UDM0ry4wed2heu>&O5L8 z%ktyVKkz?f4PAcA&Wq6iI=*hpS~9rG8<4qp%@5zAawMG3&Nt66p6%Wt9X;-?-<#(% zxexf9vaS#sfp_iPURmpkd=BrP_xg#))8q2o4W9h4pO`bv$iWNFS#eO-803uRaKo2+ zWlk{fP-lS+dgT5Q&q8ndc|CIP1Ut^xkUD?+W=EIDtB&b)mRZZZ&v{R~r|j7)-_tP8 zj7T%iprF>Vo-=mwvTODQ8*N}Q=e?6gvH0+3`l78jw(Q*2fj-XM(*1Ssa9p{2@jCr- zPXrd9e{{ds(rD~Be=Af zGc@(JafW1=?B#p!>}4^|OW>SIZHND+eCOm0NXdxj=Uhn1*vn5d!>%Q>rf{*)cQbJ`ByIX$k5=TEgImM+~E-p zR>kuRe}=YdFF$36|B{{`d->`4;X9}8l<%CHi;Cw@F?{EAf9&O_uLIvX&G4Pm4BuJ$ zU|bu%bM)Mm*1f`a_8YIZ7{0Uf#x)jWFW>w0dW*4_FMTz}z;~9u6-=G~^Y^V*TNN9g zVrnnH>~_meosB2_Jo=+fLVV?&mL0iX)Wp-@N$l{Q{Ri&1n9Bc8*^yf&eM#ElJC9;- zZIw%pOKwkkT>pU1ob)->J?N>(rYnC z8TSCg2bF%P>Mzl9WGn=X^JZim0L-~7Q#r~m+4E1&C7kc!O6k|qHoD-aN*S}G#`tWo zGXCrhJ1%Sox?^Gr89AIJR_Ep?3`T0giUR4#G;#$nV==7>+zITmZOIz{{ zJW+=Q=KcFfm5fDHY=zlXGFMQuXpeu*x(ODa@>rFu?+A0Bd#Q?CCpo|P!56Chmp8Uq z*PB(rK9v@uE%iWTTX(rHt2hr!vhb7Fs$~8p&GfIQ_13U{;JCE{Itc$>)>oN%kHg;{HT8wi>Wt=bb3%|`(HyrhsSyQX)NZJK>e3SB-ul?ScubSIgRR+D`5T!kkstuC~_q3G+XwsBZpnb*sPd%$2IKmt(js4b`$< z0sju8uAsDwcXg}r%_IxHUZ%R?_Vq00-??UW_lsGJp+TXq%eMY8%U3sD8CuNEY+c>3 z#32?7*4nR9`o!uUynSz;b%^YbSe2I7FSnS#qQuYn!={xW_;jgjORq#6C;^UD63UVUIh=3&ql-#k5T=WN&V z*|BRsf0A{sC5-bmWZVdhx|jI)a$Njt{!Oy>Pp-0jZ2rBw>DUppC}~H{OnDw8b}bie zIU@J>VO>cag!HFj?lEWewOo3H^^Bn2gWPAwuKv@eQu;T@=f?iMoD1vXR#EGKwhfQ% zI3mx|g=LRx7{PCqZR6RKhh%O6cK%VT4Rzjmxi!DAe&S$!-Covn(wXyjpEqj>>v``S zb#tGrkxYNV2426cCkS)gTL#k4`{>Y5`l3tAtoNI9^5=ct8S7ZgUvJ-j=b&C|-016- z`{Ud9w(Rgj-Mf~tShUCNekVBxO5Owa+K&gECwH^@^Ot^mh`N!|j`J$hYno`4OZw95 z)3EeKpWK_^dgJ*Q>z8{IicM|l_trb!Ui*AJa};CWIi7ujWk(#m+}oi)chSZHxjzG= z#-Q96f~h!ou~!7+?18kM`_Ynvaz6^YXtkY2vx;*@F?YvDMzP?Q?*`>Q1pVPVrx|rW zMveRTA)^?+bGj|QbNZg*C(FGo#>G!ge;)YB>CX*6IcG<6-xJ22gr=Yibw!cOfmi#;!xig{1EeJAmgrCrO_MfEJT_Lviv-ecxnoZe%G;U~-eRaknD8J6B-hNbtIVd*_)=BHxb z#r`jL_{q{wf#D~o=YpS{X6Zd<)n5|xF873C={;r`esbClKRM0Pd(5;=?=izvEO)Z! zsGngl{ABhaCBsjazqf|-gAo<;E}l&={ABsefu;ADVZ^-4X9?eB{AB<0)6BSvrS_O% z_{s7&2*Xd7zaumxbHN1U;{=VVL=cGRQfyjFeh8~(mwguQ6FAIhGN^I~r< z-mJ^mWzW5J#4WE^XAA1KoQvhSALN|Z*EDvlqsSkZx%I4jiYs>&hK00 zC1<_r>)9j-8Zt_rOb%_5NJZu!i-PE(L2xRn*qeZHqswREjq1tdjMbo5rlA zm{)a{vCFKpaS1``JD&OO>N z*3Q_;^AhUsJE4RRE6=0C2f&WlKz$v=0>e1}OEbQI&a7{%tvBPjRruiEz>sf zJ;>Ln-|Pj$*n4D*`XetjjJ;9%tG{s{!-z*m-(c@c_s9Fvn>bX(pPCU*Pk%CAnSC|J zm38FU#|u|GsEW@(w`FfTuJ|M)>%%E_+w~RXk!w5pBL+b3^}_t)7q%F2&E%o-*~_+^ zXTfKsnW~+T)RaG7kbA^gPjho8kcX~pkfGmJHf%9#{<>g=1cUn%`_7&ZUdtJm`*zk)n}Sto&g zSk_2#UzeB!?vso)psn&7lUM@XAKx8(TIoB}A7e^im+wKt<+H7G71;MJe)RAN^D+G; zjAyUZ1^6s^rpoJ3`6=H8qxQDb{<*rgB(EUt@6(s82cYbd^B2;-c*n$(EMJ`KbdPHs zl=V00hnjY6$r-8oIv6j0clSXVrw@yMT4l(J(@%b@?v}?7TK+UOLW4bq`7Y`HRK1LK zj~bD`Uo?xR^}}J}60{v-v|m4EM2^9}J=ZIB0Y3kh6|0BYyVh-qm1ueJ^I>9bG~+px zeYj?rngW_}y^T*^Q{s#m&1l>9_mq-stIi{xJT%N6vbN(n*--K5aKnL*8Ad$`#+CV7 zoQ2^n`O6SBUv%57GqI#6^+ipPafvdG+65}9awpsrL5B3i@ z-M^UiOM7_!NO~U|hA-~ju!EV4VyS&>Sd{qUa{pZQm*k{JVn122BnCt7S;O$f({0oH z*w_V0PP)IrZq{5>40vh`d~s>V+R>l(`H)=8v+nF}9B1!EC#>_t^@pv!*@O`TZtYno zOvQl9ecgn$FaGqfyZ>}EKQ#t9=_!VPo@OcrT<&c%ZsMP(?bJFrWru&Bo*x>u^edQ) z%1KYP#XnEm;h(4b8?}#}^fziBJ7GNQ)AujEkBu(Edqu{D!_qnFFy4i7&kL5$Nrx%_ zT;zNc-;aMT^Sfc`oOGD-&kvrLyH0=UoOGD-&r@S4|6H_HV2Z~Rp0Z;fJ8h@@ zbCEMof7CUR@v$&f@Bf5a^IhY+Ouc^@+XJI6oy;|dDgRvNq$ez$la8J8&qba(OzmR} z*7W?C;dD+qES;0yzE0h6IwxJRR8BhYBI=pRJy*sJwz)JTucQ5&BZtGWD`cX@Yt1vP z;itVvI!}#~Ba7zwF74Hu3rKBW&RPdYcVxTMLoCfg}h;M*JVAo z_D!b@t8)NEy}pJ$cN~tNdnC)AvVIoBs8uTSj$!D+vfe{eTruJv^LR#{JJPc2h9lIb zamRgb-aXmx7aFD}gq5?h?}Eee`gdB4drz*0%!}^)bpB!DtYi)cd1;(Y%Q-4~&P_Y_ z4h7R!tCVp;?QhK;@(;S8GTd@c^Igks@$ZmZ@nfZYHbv|GH59EerAqqo!OFi6sXbwl z8%}@Wy#rOUzZR}zyY4D?R>|DI&Lf70h?ABvUVJyarMs$So`3fPD+~wypH~O_y=B@K zk3DYK9jvL2l6rso+Q>T$Z(p)T*0|)I>Q5hA!@0f2?yj?j{Wq4ciMIN3-hIhlGaN6n zL5(~|qUp#7hTY{iu5lmU&)9YE`|5CX@}wHek58WeFzLrP-QTO2JcAKGA^dcun&|Fb z%$lL|0C&WB)~kspFEx+7yZ(q@wpNY5#8>9KLF_H_^X_gltm(oQ#i#?QudQXjlSjzu zM-$Twy(xNNlNx#MFz3pKUSkbsSZh1ZNAuU3T;q!RdMZ{bnIC5!Wv)J20sS@nyN{U*x&ZMEvOYC_FF6P@PZNJOSfyA+Zl=CA*1OJ@Gpf4E9ySa= z+FL59vhrb*d}qeR2Q+OHABwi**=p9D{LOvrT+2Vl*A4nks*?Mh)FkH|3C{1+ZSfO` z!DxH=G{fkd+M@M|-=yu7UpsDAo_g(uC4PsKu;zz0G>kaxaAtqLAv3>W+wwq?Nmwsvb!;h!7xXcYF z4!d*QJr25)?vFfY&Tng-wU=QkuQ%~2+Me6QFzniAS2%nRbz5=&bCxi_dP=T|mo7{6ASIfws(z9D9BLRDa2N2eLmj3N#<7WvKZjXH3d5 z@b}plml^=yzi8b{2l-QvL7Ql+JMEf){8o3pXF&c|sgn?H(J{dHP`4$&->>_7K<3B8 z;`oC>`P=7S25Y`HC~F7u+=$yQ9F)%ur4yd;>0sCNi_93Do#|Y>(2$IiAwGe1wv4Ms z6Xdr`KE0kF_Xzz}`tfKA#K=$=K-=+5jmhPAIY zX(SlgW57*nz-v49`@LnG2i!##H1DnKCJT>*A09E_KD~iq#rg>&)D7?lw>C`Ko%Zg4 zteeub&0-_oC(a=8ha!*J0!<#`e)H$|JN6Xt%}@4a}f4!^f6++c19nl5vlD#z=oUK53SE&dK#I zOvQ?e+V9@qPY)`+ImPg?Wes-hRIIqDw;mq))S!wL7j@oY_}H>$It(9MGWx^EmbKck z!^f5kJA7=()EJ46E&GGvW2c#l6;I6%A6wRMceBh=dnl5)jmuDkgOx5C zjK{2B>7M?CvGbO=V37Up@Y-h$C$;*Z^`arp&y#g^ zgWaAR3U9ijju=_p7OmL%`J*~{mTvKy+;DWvGj;x6%No1RT6Z}9;qN+UOs`pQpffm+ zzgFjNbhlwjt7@N9=TAP;FwXdnHhQ2gIC5#j@LAZOryjGOOUrhp;b7~sI-Uud;X9)- z6vKm!9d)-#9LT*#wxT&{srys8+~+@8C2N=CPrFm^t&-=V zu|G$DazB|p_wewCt5{d-{-~Mf%v_FIeTJRu*;hn_RaJd_}A1ew0x^1kKRja0b~x_ z+SqZfmh&x>^ zn|)*UvD@3WDMxoLKKz_v-AVoN;O5724WG<0Ox0bu{`VaFhPpreCu%Lq9Cv(YVh`|% zj9rq)-ErmA68XcL;m70Gx2=A)#khXoF0QU9oHsf*@+Dxre6Ba z*r5S1j&6(A()@eT^7rv8*~gWd0{(P5hMvzO`&NVXc`z>ixfL6bwEbT(1YEz=g3ul^ zo1SW&Jpdy=UVfj|cYL2Mtb4`p8=Zl@aD8oR&PncvWM~EKe~yarPh!*LGeYSC$-05D zbbnmG_uXwnR^7`)7jaI#WiYenL}Q0m&^hJMVHq#Lv&LI*(;?1|)%~$&PyVkwLy)yS zIp0zjpl%{@@NE}dJ+Ez|Svt3@J}A#=MK8m*s(<$oV@G`e>Rpo`t;Z#2q&WWi;p}>j z(Ox^38W|}*GcYXkfQZRxxc-LW?5E2bJN%oLbE-#LzJ6z3e>V(`;1{>t{gPo#Ppml- z^nN$w3@u{bTP>5e9A?eIeuth5@zAVwWUdyPzMrh8h{w@()Fn`BXc51Leor0&XVXYK zZ}p0y`0zgrbI+JF6it3}(D}7_-n)DG&xV`!UUN8{G|SA-{rK!5@BG^a{V8>39nty9 z@NiS2|ND!dYV4fKdxyd;mmPAhdfD_B9Q@IsbIkNXnNJ7fIwW&vy`K2>j}KDgN6#4+ z-P<+j-#^7-_~(+LA@LnbGymknhMYCGHEppAp1#gv@%^t1;-AY}_s$|O4dOe>vw!?6 zUmWB+BncaK?bz1D9vTCw9s|7k0E&{<&a` zq5SjI7-|n(*wGgMT%LIkOP_fUQ~tSV8z=KipLy?HcG^Ja>&sYY)Wh)4<+=65+!R+o zVH8WBd5@j)&#$=3{HBR9qQ-Y9&a#KOH@nsu^)UQ%Pn=uN`7!A;@7>)m9w6RPo~=k8 z6l-y6aq8c>^X18dQM6@+d=5EJ|1iiiQl9PUg*^lAt|JvPCn;QX?m)cw${FY8ih0+O zXwRG_=G~Js3@y>y!Sf~fo*&_rdPHZb~pNZY}~(&Va4L_ z!{OO~)bbqDbEXCtwE*N9P))Os9*Lejt)^wdm*)Oue>i2tP4pMJ4kK5=x&EOVnYW$Y zn0Ps@4bF!ExEnA zt%|iz^dV~KYsNlc@a#V|oRMYN^e;+5=jyd`U#*#U;Jqu>@~5#&_NIb?4Qr`^r5W+# z(K7WF(kCId8(q_?*_HS+*-THl^cU0~bCRDT_r;5Q&Qarb{_@v6A2zA!MT+2UgBn(M%~;-273Xgl_G zsR0dHo9(}U9$I1ExgR}=NZO1VqWo~3aJ6G{x3eal!KqB$EZ)Vg3dKJ zbkJcm>zH;}S<{!lTKU~wZ?u(ho`BRcxDM(#%j;15fJ4 z;hX7k(N&tapI(xA1k}sp`zCV+VCYL}#@r;M4qSR~8%BPM-0z0rud=_b=K^E>C+&!t zKtGVV1=JHH{#p9YXhuo{nDL6~kG)p(BB?Fm2a@9;b1v`~sW*_0alpTl=ld}iVxZ+* zh^?S5f%NzJeV{Q&O~Lr~NxeXGy~+ML-!r){uJ+lJJz=?j&ToqxcdLdyzqjN$A%Aa5 zQ%K^DI<{fBlpjOTm8E4a$0v>RhLPZyF?zL$~F3iygHy4cq_uQbWz3!+c*g zYdm?|68T@UYg9o50dp-_|mk&uFO!?R;rhM!aQ$Ds}jG^|x#hL9eeC!~_l#eYKF%N1F zT%5ZOQ+wbkruM+a`RCYi)`whUI?1|I`Pjk^ruM+)Iq6BQK74F>zL0y(asAE7ufUmh zx>?r+V~*`tKK7SqSWNlYDW>+o1*7Jm+5;Cgc|GT-ezgbw)7hp!nDVg&qb+M<7vGBM$9nZ7P{N z{hhR=oWUiv^z_(67$XnsI@#i<7z&_}H=*CTAcJXC8?8u}1H_WoO4- z_;K@$Xvv>E%$cODiS#pvoRfxWH#z>E9mWp-$Z0#R!u|3s^K5dCF7-PLzF#3@cHFN9 z`Z-ftp2ZYQ>+g40S}o(8JjTq~+p4EOIBB0uSQ=}X|NN>0QE_@k`t0q^n~V@=O5I33 zKlhUB2fUdFX8e24Gjnm*9vtB8L;PoB*Smc``=hcZba2#m{oZ^_=47sK_~llr)`;|H z{C76*4<_A|lQ~Xqv{^rKn=(JjTXmCua=GPRs-STe&{^>TV;3H?Mn8MtGz+0pxQt2E5o9yp+4fh<^DAFW!&t2HO{18j9onWm%ixzNwvYYe;F1G_w{*K-&h+g z)NL5`VB9O8s+H%gM5piA@7^@GHlBKevGeBNW-#1yohsM;+kAK3H_qyJraWxzJEPr` z2S6=$b6>{i2cr5}W2ygS*mdg;#qI*t@&8lSdB;mp-ECavvRqI=2`C2p%C&2vvBs7x zESA@T#u97H$l9W>9T609;95vxL5#+ph1Db)5z9xSXmrC`qGCymu@PNe3o2@Sb>`kJ zyytuJnd5VYzqmY~-DtFGMQx0p_|JoT=dUQ`Wws!dHE18!Xx#_(5^Y3g`KfYm9X_uFrUA1{v zE1Y@$Iz3yx;4r>zg}J*8rQUlwws+@WcM3;*v4+Ng+nN%iUmKSDcrRyHJ$7cRzRgBG z^omT!=C-}PU3m104b*?$d(pyneeSt=-Cx=G#06Vznmcx`J03Y~)iry^D(@~<)?Zn; zXU9$WhNN@AQwyC($7;MN-?93J$p-wJ+S%I2A6>bA*0)&N`|bf6t4oig;Hh_q!MW10 z^uDM*zSTOl(N8+QN>88sdhcym`SN4uhsFOJj+woN7X!ZF(bdrK^mx>C#}7kHa5@J$ zuO-t5#p8J>p@N`G(yyM$u53CrR zn0y&MKlmawl0EH)-nxcEXly(&CuoH_mN)=>iTbSad`Ex(%!^S+D+0^rcb5%^)q3le zuNr@X#(T4N;5`}_PVPN^2egIszVVD&JF}KjjJk>Rkk%L*_Tto{*4h*3QQ@G`UXM92 z-k(~Rj%(C=@yL~ELg^e}+zZ86-{d~%`;WC(dFSPh*301ASn1!aqu)kixYKNa#?dt!z zvX9@E#cYha?BVRZ*IwPy82VtV3;U`DtL{71%~3rh>VvnJ?>fg}(fCXHY#s3J&T|-c z#3EhgmahKT#eJo-uWi>KJl`D;%<@{n>{xhh2D7|YFveP5o53uv6|At! zMSYgn?tX!r7tHe73}$()VCDO!_E}zAo#MuVS>LhPaZRo7SnR;ywfPKYd97UgaNY%d z)_0u2EU%S1xVAr}d4kl}mVfKU1YR2rpYScOFc{x6#iGM+NZ_@q=hT;8pTKJsE9^Tr zVNX(BZ$Edu@{@BCc&%cLh1X`sl1q}s;I%rII)T>)TOQ!f z4-8(bb&9By2(MMU{9V6FdLF*O!=jCUnZRqy3l4P0tN-@OguSHpy(x^mBB{I=dl+La zua&XEj_WoXzt}pUCvp1xHa5AWO+O2*mqFeL{%QQesU7_e;IP4^%NxGZ{+}XiD0(yVn{Ds4!m$&Q-cimy2a*gn?b^57YjK4RX7auM?D2vlZxN|OV zGpxV<(MALHT^F2uLV}J#o_0F6{MpYFIG$qBr6(nDsBq|s&aQCYiAk`>7K5l&o{r^s zL9sMQ$5xLzE-4&S83cb$?V8m4<}D^SboP{lKPh5{K|bxTICfWa}_S|`~38H>>K%xsa9XPyVfCZjvijAoz3N*Q|#7$RQR^SFYoTse!cii$T?-d zosMM=V*YgwV*SYTr+;{ASDG+(CGqasbC>v0a_KY{k(j`6&;f%r_mDa}g*oRBQLJ*w zH=WS*L*pBg&Vj!xIOjLrnj1-dxM<%!y!zgikB6_!|L)(T^nS3{D{r)Z55CCsc;v^H zr~e?&_bkOGnZKej%SQ2Uis6^ZUv%Yn)c=OBEn0p^m-0O3s~@*pw{mA{8IwEW_An{-{x&mKW6cl`kYcPmVi1&v^6XIuQ8BI)&zxdB1I3p=>8KwTo{S%seNsBd ztbb3m{$JGv;3Ad%mv@vW!_VQw%9Ce}I?iGmXO7RFbJ3h{)|KU#CI&d2mwO3U)$7MP zfa7S)F}ekPu#_W`lZ($)V;tbl@K-&a)j`ZU)8kov!^D~E{k7}B#G@;}ghLWztv*-Q z8ajrqHFDgEdB~1s51bu~hNWXUXX-KNbIIT2Jt)RH%kBJD8$WP*{dh)+t;Oe?V#Lam zPo{C#_|3tT>)INP2BW86Iu>6)zDoLur5JeuncUoE>jyMQnmNkx>9)33V zTxz!w-@qK27sI`%zW6V%KfA3FYT~FguiSpO42RY47CKP0>B1I$9Mkio&KPxJ)UJ2$ zzjcU((!T4qPO3Sc_EXP&@o=Z`&?_ER8Tz{p^||Orna)AYMQR$U2HJbWDV@RvSGFpx z<<5?Jul!!E$wAE@^2D@$T`jjyr?AewEn;`lIoOxLb#-5YKRsOYuvTf??cM&Rw*5Nx zc)9(@IE?&rV%ckZjCNS-6jSF~b&OJn!RE8rf{Up4)0xry7igVQc+l~%vqVQ`H~5O}ru z;PCkP>BE?gEj|0aHfvJHhQIuvg}%}F>$A>|Ys~M^IxJ}L^^@nd=s}+zFS_rT7W+Nz zJ5v4rgDqyo*%3p)F>BY4c6&ndDChxD>buNg)k(*jx^f$|avS~0VNw3zW_|Wf zt)3?ybQm=oqiF}XDsMdEu<}*=H#wKm3lBRiymTKM8?QZ8!Y%*Mq~1_t*$ih_-1xvw zW$k4h`uC;3t^V38Nq*$1&FV#C%?sQk5f8hzo$wwaMm8N= zeg6K07=OmMzTdpB&Yf}!20UHQ!qgIiRkcTf6(b7o*)MX6=l2qZs$HboyICahv+LmCC(tENci~s$#4I>R;;|=w{LO|LRh07|o5?``+Ci zaK{UFzcwiscN(bgy2cap+PN!E9H9Q%YHhbB*S$D+v3tiBu38k6Tc4W~IE^Rle;_8F zqj-DHVbyc*kIRimJ&dvVx^--D=|gdJ#CC%!>kf2dqmvfI^!#e{bUUp4${*uk=Di+P z-RsV{zR?$hdhZ$R>?6D>3S)5E{wxxCk=t?Iqcap z;vji?2)zqZth&>wadpmStD`HVSb5tso78fnFGGrzZatx0jSuT0&(>ik=Np|+_b=2< zL95evdHVA5yBeoG`|Vg$(}ed;ZtdZ2j>gvu+SFs~-Rw1IhaYd!+h?o>w@c?`-$`vV z_4iP#iuL39h0NZT{Sn+G9h)1ue+O++>j?LT$9D32?9-)upISlI?xHzvEc-Pu-FMc; zUGrZ%lOOHoFntDy0fH;07`|ol3iMpy>S!D4D*`JX^v)_6ej>HQ|H^()F*pSJ6Wl4C zm+R8F>ahWOUEscG&Dw*ExJ~NWX}l;tV{)j~Cr1u0YXCl#&HAS1c@LbC ze0g|(I+pJVc8U?B%ey`6A4EffE2d-dWj4+oG(a)@;lzr<0aH7)iJk-6t0^zDn8_DV z44%t=RW&F$DHt3uJs#gX`o{5Zig6q;<+YrD<@KKq@VKP;?R<}v>vG+=E{bv8c&?P2 z!WZe&tQ?l}!l$g~!ufMd#kj9ae)b1Xr{TWB=Tv`z+gm@lNm>PBsXyx8oN)=KH@U8Uk4fmF#{UUPi;bc z`2%;o!_5m1qE{KudU`IE+L#V87~`ida9HD%T&KMH+s%RHMGiyDu(2BBp7<6seI~;8 z>rKyhV&d%cU;MLKne&RXqZTl6|GJ+nzI3IjBm82M@02?ivqs6IZwy)Cu-dzan!0UI zH`QO!d)h8-;wvgAb-BF?HQsFgweDZuNNf&*YAv?*j;ZTZO2)de=mgXZQk}+fUsLO^ z6z=KlqA};Ss^z@)W4+gr6WiUSaa)P2xPXPD1_{-L^(R?Z~ChuG~A! z)SOz}?0NZlhgH8E+$8Qs?dr!3Z1OCqpRPP{Kr`HZ(-v{y>Ab<;R`ydPNwMnURzJBN zaI@47%xZ3e(I)^cNbQ1C$2QTg^mmZUNN*_h$<-IHmr#4La^b%npDFA-CgHj$h8?jt ziebkyqZmF?@--A=Y&2nX)yX)GMF18kg1GIhUsn zO^9{DUzuJj{IJvv@0r{1u%6nf9#;LJJ0aFZ?ZArjA6W(KF*R}2E_!{S)k|t)K6G<{ zQFBwV{0W1S#+yfb72vE0o8hM!2Ws_|Ll!%?h${`FQbAzn2-KQQzX#mcA5v3iMOa75ng ziiO8qZS@kxs{i1yJa=0zu68|7Po|tsyzvRo}n^6pZ zGj^KyN!)ACC!6?~iJ68^D<{T3%viGgwS(3@Q`_(?H8!sFs_jEeak$~7!Me|tj@ z<9cvBrf1?Mm)RDVKo7h=c^dkm-Ern2k%2p5xX>Z?rYFEN7QIa4=&0vv!uVXYEWJRo1RCbFqh6 zO~LRcwF9GvsABlJi4oD5e6AgJ_&nW$8hM#|ZdRi)nw?%N)+rj6-Xk!c8^!E(HqXC~ zwfENe={?NsE8y(jwKw++POO}rJPhiID!1qTRUSE}rE3ii4yJQ(USN8DTn}64U34>k zKX&>)Ks&)s-!Evs#NVq1Kn@CV;as!yeMKz^c)V(n)czwD!qY%a4hDQkHDvTyxCYOi zJD$l+Ar^w?F2$&w2=C@vq!=~m@>e&TXkaNuT};cnjn0;0^lJ-NeAU$YAgo)sIC&eX zU1{)}mWyl6J@Plmk)Sq0YDb)4;i=_bo`dakYj_2kT56YD|8mR4wYEQbN7PK$co1}N zIGAb_J(ml4AA4p6*A()0h}A>S8HYSX%2RmRqs#hfns`+p}B6nV-5j^4mUcxxI%y@Og{+ zujKQk*9BdU_>@_f-~TOk^P@4`zA^DUw`a}gAB(xBS&TXpSqzO#vB(^c`_Si39E zC3C#7V2!O7uY$qtYwu5S*9w0qbu<;rFPa})ZZCFVmfL4A%k2e=wx1W*;P!&m|MuoXIK8smy|BI>aoP*J~{+HVe7G86+<@P1P@ZrMk6~l*XeOF=^9e0l9 z_WB(JgWD@sn0Th;_KKB_Z`S%#%!*9cDHuLUwF}0bZn?c;`2{CiZm$@AUATP~gXb#7 zSU7DKgWD?>ZE=$2_KKC~{@ikV#e(4{#N-ky7G8dQOwCor@>7qA>20i7^wm*uX~|R% zD|~Q7+_<>nVZrM^iOG}JvEiYI#=RG`JPaOMet3&6wJSU@yuWtpzMh@sl7^?KT`+jR zCbbR~3t!tLDHy)5Sa8ef3GwoZq4mM{6${I2TfVOt`4I4Z#lr9B`=|}4SbhI3`p9)r zEWD-DEUq}oJ3pJ(cIR5&@uKDH$J86}#!lZPre?fi`Lj2SsZFC;;qC9n)P7N{e8jpj z^$!(`9$G6dEbaHOaMX~P+V^T#pZ-d0^Q>i!6RSZzS+zqq%VLFP(~|Jaz_W|SZD8Wp zZu2m-!?NkmqQ?tfo|4pGf6l|IJ3p6%v;XX2(eeLC!t1{AFk;{Geftj9;}zDgCHd2D z^Dt_kb#=}f)YVJ zt=bVYn>YOf6eAX&+7XJuwTKz^FvCA8CzrhIXL|_dOI|I0;vVSZr(@AUvhP^9A$dG% z2S+2eB#Y&SzU*P}G2(R9j=$LxD~3+O{!%gY8GL0~42?oD{Hl$G2YYj%mr!?4?b!R$ zcS}CA}k?UJe5HgBp-(2SzNsVrUuUYbeGZ84X1-egiyPF=Ci(%?5do zv&SLNNbTU@clICu7Td97=M#zqnLeuP27lLXffIQ#&x3ioM&e=6yov~ zBW9vxYS}2pb*g200I*-Ru_MC}`E>L4%$Kr>`V&qNWcUC)U z4dJ&}tn$wt+SF%I4Bu}SBPWL5OKKNYUu~fQC`Rr>b?Iae3)j82h1Q^U0rdq<9I1yH zUSIv~X+BmR+p6bI@-VbDdJpJWbfx^VV?B)62;vevyW#V!mZ6yKACOt&RtGV;4{FD6 z%{~kDEswS}AJwjU{^WjoRe0Emt-j#;uWSu`$KN)6sV^MY@v!`9mA-Jw>fTteXwl9d zMjZ_DLUfM$#|Ny6nSCeyyVQ>QW@Qr(qFD9AQ_cSLJr4`k8f$8UPxEx!=(=~3tX;i- zQj)zc#2TQ@=^XGQc!^^0`1%2#u7YLtV(h{<_VLaI9*;Jn#{&xwyw$_t?Dav*Bo8esnDUbK-Us!{6O7ebE)OzH;+?Wie`>C}#cQMjKHKA2`~GV#IOto=^HUpenF#qf)xjbt&j5ykM8qm3v=tT;J!is3&;8&M4ZIo}4w@ShX+pja?viq%FG M3%{IfwGqYsA1Qyvd;kCd literal 0 HcmV?d00001 diff --git a/PointCloudWeb.Web/src/views/Scanner.vue b/PointCloudWeb.Web/src/views/Scanner.vue index 332581f..ba8f6f5 100644 --- a/PointCloudWeb.Web/src/views/Scanner.vue +++ b/PointCloudWeb.Web/src/views/Scanner.vue @@ -1,19 +1,21 @@ @@ -33,7 +35,9 @@ export default { logs: [], connection_status: false, scan_status: false, + scanner_status: false, progress: 0, + progress_color: "yellow", command: "", value: "" }; @@ -54,13 +58,20 @@ export default { if(event.data) that.msgFilter(event.data) } + + this.wsConnection.onclose = function(){ + that.connection_status = false + that.scanner_status = false + that.scan_status = false + that.logs.push("Websocket Connection closed"); + } }, msgFilter(message){ let that = this if(message.search("<") != -1){ that.command = message.substr(message.search("<")+1, message.search(">")-1) that.value = message.substr(message.search(">")+1) - console.log("command: " + that.command + " / value: " + that.value) + //console.log("command: " + that.command + " / value: " + that.value) this.action(that.command, that.value) } else{ @@ -78,13 +89,29 @@ export default { that.logs.push(value); } else if(command == "scan"){ - if(value == "running") + if(value == "running"){ that.scan_status = true + that.progress_color = "yellow" + } + else if(value == "finished"){ + that.scan_status = false + that.progress_color = "greenyellow" + } + else{ + that.scan_status = false + that.progress_color = "red" + that.progress = "canceld" + } + } + else if(command == "connection"){ + if(value == "true") + that.scanner_status = true else + that.scanner_status = false that.scan_status = false } else - that.logs.push("Unknow command: " + value); + that.logs.push("Unknow command: " + value) } }, @@ -122,7 +149,6 @@ li { } .progressbar>div { - background-color: greenyellow; /* Adjust with JavaScript */ height: 20px; border-radius: 4px; @@ -141,7 +167,12 @@ li { cursor: pointer; } -.button2 {background-color: #008CBA;} /* Blue */ -.button3 {background-color: #f44336;} /* Red */ +.button:hover{ + background-color: #e9962a; +} + +.button2 {background-color: #00ba9b;} +.button3 {background-color: #008cff} +.button4 {background-color: #f44336;} /* Red */ \ No newline at end of file