1 2#!/usr/bin/env python 3# -*- encoding: utf-8 -*- 4''' 5@File : haasdevice.py 6@Description: file description 7@Date : 2021/06/03 10:03:12 8@Author : guoliang.wgl 9@version : 1.0 10''' 11 12# here put the import lib 13 14import time 15import os 16import logging 17import serial 18import logging 19from subprocess import * 20import threading 21import time 22import sys 23import argparse 24import socket 25import json 26 27logging.basicConfig(level=logging.INFO, 28 format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') 29 30 31class HaaSboard: 32 def __init__(self, device, baudrate=115200, user="micro", password="python", wait=0): 33 self.serial = serial.Serial() 34 self.serial.port = device 35 self.serial.baudrate = baudrate 36 self.serial.parity = "N" 37 self.serial.bytesize = 8 38 self.serial.stopbits = 1 39 self.serial.timeout = 0.05 40 41 try: 42 self.serial.open() 43 except Exception as e: 44 raise Exception("Failed to open serial port: %s!" % device) 45 46 47 48 def close(self): 49 self.serial.close() 50 51 52 def run_cmd(self,cmd): 53 self.serial.write(cmd) 54 55 def run_cmd_follow(self,cmd,ending,timeout = 10): 56 self.serial.write(cmd) 57 return self.read_until(1,ending,timeout) 58 59 60 61 def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): 62 # if data_consumer is used then data is not accumulated and the ending must be 1 byte long 63 assert data_consumer is None or len(ending) == 1 64 65 data = self.serial.read(min_num_bytes) 66 if data_consumer: 67 data_consumer(data) 68 timeout_count = 0 69 while True: 70 if data.endswith(ending): 71 break 72 elif self.serial.inWaiting() > 0: 73 new_data = self.serial.read(1) 74 if data_consumer: 75 data_consumer(new_data) 76 data = new_data 77 else: 78 data = data + new_data 79 timeout_count = 0 80 else: 81 timeout_count += 1 82 if timeout is not None and timeout_count >= 100 * timeout: 83 logging.warning("Waiting for %s timeout" %(ending.decode('utf-8'))) 84 break 85 time.sleep(0.01) 86 return data 87 88 89class HaaSDevice(object): 90 """ 91 all the HaaSDevice functions can be found int this class 92 """ 93 94 CMD_REBOOT = 'reboot \n' 95 FLAG_REBOOT = 'register python command succeed' 96 CMD_UNZIP = 'unzip' 97 FLAG_UNZIP = 'register python command succeed' 98 CMD_WIFI = "python /data/python-apps/wifi/main.py " 99 FLAG_WIFI = "DeviceIP:" 100 101 CMD_TFTP = "tftp get " 102 FLAG_TFTP = "tftp received" 103 104 105 106 107 def __init__(self): 108 self._baudrate = 115200 109 self._dev_node = None 110 self._board = None 111 self._ip = None 112 self._ssid = None 113 self._pwd = None 114 115 def is_str_empty(self,value): 116 if value is None or value == "" : 117 return True 118 else: 119 return False 120 121 @property 122 def baudrate(self): 123 return self._baudrate 124 125 @baudrate.setter 126 def baudrate(self,value): 127 self._baudrate = value 128 129 130 @property 131 def dev_node(self): 132 return self._dev_node 133 134 @dev_node.setter 135 def dev_node(self,value): 136 self._dev_node = value 137 138 139 @property 140 def ip(self): 141 return self._ip 142 143 @property 144 def ssid(self): 145 return self._ssid 146 147 @ssid.setter 148 def ssid(self,value): 149 self._ssid = value 150 151 152 @property 153 def pwd(self): 154 return self._pwd 155 156 @ssid.setter 157 def pwd(self,value): 158 self._pwd = value 159 160 161 def connect_board(self): 162 self._board = HaaSboard(self._dev_node,self._baudrate,wait=5) 163 164 def reboot(self): 165 if self._board is None: 166 raise OSError("Please connect the board first") 167 logging.info("Try to reboot device...") 168 169 ret = self._board.run_cmd_follow(self.CMD_REBOOT.encode('utf-8'),self.FLAG_REBOOT.encode('utf-8')) 170 if ret : 171 logging.info("reboot succeed ") 172 else : 173 raise OSError("reboot failed ") 174 175 def connect_wifi(self): 176 """ 177 todo : verify if the wifi is connected 178 """ 179 if self._board is None: 180 raise OSError("Please connect the board first") 181 182 if self.is_str_empty(self._ssid): 183 raise ValueError("Error:ssid is null") 184 185 if self.is_str_empty(self._pwd): 186 raise ValueError("Error:password is null") 187 188 189 cmd = self.CMD_WIFI+ ' ' + self._ssid + ' ' + self._pwd + ' ' + '\r\n' 190 logging.info("connect_wifi:" +cmd) 191 ret = self._board.run_cmd_follow(cmd.encode('utf-8'),self.FLAG_WIFI.encode('utf-8'),10) 192 193 if ret : 194 pass 195 else: 196 raise("Connect wifi failed") 197 198 self._ip = self._board.read_until(1,b"\n").decode('UTF-8').strip() 199 self._board.read_until(1,b"ConnectWifi finished",3) 200 return self._ip 201 202 203 def tftp_get(self,src_ip,port,src_file,dest = None): 204 if self._board is None: 205 raise OSError("Please connect the board first") 206 207 if self.is_str_empty(src_ip): 208 raise ValueError("src IP can't be None") 209 210 if self.is_str_empty(src_file): 211 raise ValueError("src file can't be None") 212 213 if not os.access(src_file,os.R_OK): 214 raise PermissionError("src file can't be read") 215 216 if self.is_str_empty(dest): 217 dest = '/data/' + src_file 218 else: 219 dest = dest + src_file 220 221 if src_file.endswith('.zip'): 222 tftp_get_cmd = self.CMD_TFTP + ' ' + src_ip + ' ' + port + ' ' + src_file + ' ' + dest + ' ' + 'binary' + '\n' 223 else: 224 tftp_get_cmd = self.CMD_TFTP + ' ' + src_ip + ' ' + port + ' ' + src_file + ' ' + dest + ' ' + 'text' + '\n' 225 226 227 self._board.run_cmd_follow(tftp_get_cmd.encode('utf-8'),b"tftp received") 228 ret = self._board.read_until(1,b"\n").decode('UTF-8').strip() 229 if 'failed' in ret: 230 logging.error('run '+ tftp_get_cmd + ' failed:tftp received ' + ret ) 231 return None 232 else: 233 return dest 234 235 def unzip(self,src,dest = None): 236 if self._board is None: 237 raise OSError("Please connect the board first") 238 239 if self.is_str_empty(dest): 240 dest = '/data/' 241 242 dest = dest + 'app' + '/' 243 244 # 创建目录 245 mk_dir = 'mkdir ' + dest + ' \n' 246 self._board.run_cmd(mk_dir.encode('utf-8')) 247 time.sleep(1) 248 unzip_cmd = 'unzip ' + src + ' ' + dest + '\n' 249 logging.info("unzip cmd:" +unzip_cmd) 250 ret = self._board.run_cmd_follow(unzip_cmd.encode('utf-8'),b"unzip succeed") 251 return dest+'main.py' 252 253 254 def run_app(self,main_entry): 255 if self._board is None: 256 raise OSError("Please connect the board first") 257 258 if self.is_str_empty(main_entry): 259 raise ValueError("app entry:" + main_entry + "cat't be null") 260 261 run_cmd = 'python ' + main_entry + '\r\n' 262 logging.info("Try to run app:" + main_entry) 263 264 self._board.run_cmd_follow(run_cmd.encode('utf-8'), '\r\n'.encode('utf-8')) 265 266 267 def close(self): 268 269 if self._board is None: 270 raise OSError("Please connect the board first") 271 else: 272 self._board.close() 273 274def start_client_and_run(dev_node,baudrate,ssid,pwd,host,port,srcfile,dest_dir): 275 """ 276 @description : 277 --------- 278 @param : 279 ------- 280 @Returns : 281 ------- 282 """ 283 device = HaaSDevice() 284 285 286 device.baudrate = baudrate 287 device.dev_node = dev_node 288 device.ssid = ssid 289 device.pwd = pwd 290 291 device.connect_board() 292 #device.reboot() 293 294 if device.ip is None: 295 try: 296 logging.info("Try to connect wifi for device first time") 297 device.connect_wifi() 298 except SystemError: 299 print('') 300 301 if device.ip is None: 302 logging.info("Try to connect wifi for device second time") 303 device.connect_wifi() 304 305 if device.ip is None: 306 raise OSError("Connect wifi failed ,no ip found") 307 308 logging.info("GetDevice IP succeed: " + device.ip) 309 #todo get pc ip 310 #ping pc and device 311 pc_ip = host 312 src_file = srcfile 313 tftp_port = port 314 if 'sdcard' in dest_dir: 315 dest_dir = '/sdcard/' 316 else: 317 dest_dir = '/data/' 318 319 ret = device.tftp_get(pc_ip,tftp_port,src_file,dest_dir) 320 if ret: 321 logging.info("Push files to device succeed: " + ret) 322 if src_file.endswith('.zip'): 323 py_main = device.unzip(ret,dest = dest_dir) 324 logging.info("unzip files to device succeed: " + py_main) 325 elif src_file.endswith('.py'): 326 py_main = dest_dir + src_file 327 else: 328 py_main = None 329 330 else: 331 raise OSError("tftp_get failed" ) 332 333 if not is_str_empty(py_main): 334 device.run_app(py_main) 335 logging.info("run " + py_main + " succeed" ) 336 337 device.close() 338 return True 339 340 341def start_tftp_server(host,post): 342 global event 343 global on_server_started 344 logging.info("Trying to start tftp server...") 345 tftp_info = Popen(["py3tftp", "--host", host,"-p",port],stdout = PIPE,stderr = STDOUT) 346 347 retry = 0 348 while tftp_info.poll() is None: 349 retry = retry + 1 350 info = tftp_info.stdout.readline().decode('utf-8') 351 if "Listening..." in info: 352 on_server_started = True 353 break 354 if (retry > 10 ): 355 logging.error("Start tftp server failed") 356 break 357 358 logging.info(info) 359 360 if on_server_started: 361 event.wait() 362 tftp_info.kill() 363 else: 364 raise SystemError("tftp server start failed,by retry 10 times") 365 366 367 368 369 370 371def stop_tftp_server(): 372 global event 373 event.set() 374 375 376def is_str_empty(value): 377 if value is None or value == "" : 378 return True 379 else: 380 return False 381 382if __name__ == '__main__': 383 on_server_started = False 384 logging.info("main function start ") 385 386 if(len(sys.argv) == 2): 387 config = sys.argv[1] 388 logging.info("tftp config is : %s " % config ) 389 if not os.path.isfile(config): 390 logging.error("tftp config must be a file") 391 sys.exit(1) 392 393 with open(config,'r') as f : 394 json_str = json.load(f) 395 logging.info(json_str) 396 397 398 # 以下是必选项 399 host = json_str['ip'] 400 port = '6069' 401 baudrate = json_str['baudrate'] 402 dev_node = json_str['serialPort'] 403 ssid = json_str['ssid'] 404 pwd = json_str['pwd'] 405 dest_dir = json_str['dstDir'] 406 if 'file' in json_str: 407 target_file = json_str['file'] 408 else: 409 target_file = 'app.zip' 410 else: 411 412 cmd_parser = argparse.ArgumentParser() 413 cmd_parser.add_argument('-d', '--device', default='', help='the serial device ') 414 cmd_parser.add_argument('-b', '--baudrate', default='', help='the baud rate of the serial device') 415 cmd_parser.add_argument('-s', '--ssid', default='', help='the wifi ssid') 416 cmd_parser.add_argument('-p', '--password', default='', help='the wifi password') 417 cmd_parser.add_argument('-ip', '--ip', default='', help='the PC ip address') 418 cmd_parser.add_argument('-f', '--file', default='', help='the file to be send') 419 cmd_parser.add_argument('--dest', default='', help='the dest dir file to be send') 420 args = cmd_parser.parse_args() 421 host = args.ip 422 port = '6069' 423 424 baudrate = args.baudrate 425 dev_node = args.device 426 ssid = args.ssid 427 pwd = args.password 428 429 if is_str_empty(args.file): 430 targetfile = 'app.zip' 431 else: 432 targetfile = args.file 433 434 435 # host = '192.168.3.241' 436 # port = '6069' 437 438 # baudrate = 1500000 439 # dev_node = "/dev/cu.SLAB_USBtoUART" 440 # ssid = "KIDS" 441 # pwd = "12345678" 442 443 444 # host = socket.gethostbyname(socket.gethostname()) 445 446 447 if is_str_empty(host) or is_str_empty(baudrate) or is_str_empty(dev_node) or is_str_empty(ssid) or is_str_empty(pwd): 448 #logging.error("Usage1:python haasdevice.py -d /dev/cu.SLAB_USBtoUART -b 1500000 -s KIDS -p 12345678 -ip 192.168.3.241") 449 logging.error("Usage2:python haasdevice.py tftpcfg.json") 450 sys.exit(1) 451 452 453 454 event = threading.Event() 455 event.clear() 456 457 server_t = threading.Thread(target=start_tftp_server,args=((host,port))) 458 server_t.start() 459 460 while ( not on_server_started): 461 time.sleep(1) 462 logging.info("Waiting for tftp server started....") 463 else: 464 logging.info("tftp server start succeed") 465 start_client_and_run(dev_node,baudrate,ssid,pwd,host,port,target_file,dest_dir) 466 stop_tftp_server() 467