air640w.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. #!/usr/bin/python3
  2. # -*- coding: UTF-8 -*-
  3. import os.path
  4. import shutil
  5. import zipfile
  6. import time
  7. import subprocess
  8. import sys
  9. import json
  10. import io
  11. #------------------------------------------------------------------
  12. # 读取配置信息
  13. import configparser
  14. config = configparser.ConfigParser()
  15. config['air640w'] = {
  16. "FTC_PATH" : ".\\",
  17. "USER_PATH": ".\\demo\\10.gpio_irq",
  18. "LIB_PATH" : ".\\lib\\",
  19. "DEMO_PATH": ".\\demo\\",
  20. "TOOLS_PATH": ".\\tools\\",
  21. "MAIN_LUA_DEBUG" : "false",
  22. "LUA_DEBUG" : "false",
  23. "COM_PORT" : "COM5",
  24. "FLS_PATH" : "rtt\\Bin\\rtthread_1M.FLS"
  25. }
  26. if os.path.exists("local.ini") :
  27. config.read("local.ini")
  28. FTC_PATH = os.path.abspath(config["air640w"]["FTC_PATH"]) + os.sep # 工作目录
  29. USER_PATH = os.path.abspath(config["air640w"]["USER_PATH"]) + os.sep # 用户脚本所在的目录
  30. LIB_PATH = os.path.abspath(config["air640w"]["LIB_PATH"]) + os.sep # 库脚本所在的目录
  31. DEMO_PATH = os.path.abspath(config["air640w"]["DEMO_PATH"]) + os.sep # demo脚本所在的目录,仅用于固件发布包
  32. MAIN_LUA_DEBUG = config["air640w"]["MAIN_LUA_DEBUG"] == "true"
  33. LUA_DEBUG = config["air640w"]["LUA_DEBUG"] == "true"
  34. COM_PORT = config["air640w"]["COM_PORT"]
  35. TOOLS_PATH = os.path.abspath(config["air640w"]["TOOLS_PATH"]) + os.sep
  36. FLS_PATH = os.path.abspath(config["air640w"]["FLS_PATH"])
  37. #LUA_DEBUG = False
  38. # TODO 从环境变量获取上述参数
  39. '''
  40. 获取git库的当前版本号,为一个hash值
  41. '''
  42. def get_git_revision_short_hash():
  43. try :
  44. return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).strip()
  45. except:
  46. return ""
  47. '''
  48. 打印帮助信息
  49. '''
  50. def usage():
  51. print('''
  52. python air640w.py [action]
  53. lfs - 编译文件系统
  54. dlrom - 下载底层固件(暂不支持)
  55. dlfs - 下载lua脚本(即整个文件系统)
  56. dlfull- 下载底层和lua脚本
  57. pkg - 生成发布用的压缩包
  58. build - 构建源码(仅内部使用,暂不可用)
  59. 用例1, 生成文件系统并下载到开发板
  60. python air640w.py lfs dlfs
  61. 用例2, 生成文件系统,并下载固件和文件系统到开发板
  62. python air640w.py lfs dlfull
  63. 用例3, 仅下载底层固件
  64. python air640w.py dlrom
  65. 用例四, 编译,打包,构建文件系统,全量下载
  66. python air640w.py build lfs pkg dlfull
  67. ''')
  68. '''
  69. 执行打包程序,内部使用
  70. '''
  71. def _pkg():
  72. if os.path.exists("tmp"):
  73. shutil.rmtree("tmp")
  74. _tag = time.strftime("%Y%m%d%H%M%S", time.localtime())
  75. _tag = _tag + "-" + get_git_revision_short_hash().decode()
  76. os.mkdir("tmp")
  77. # 拷贝固件文件
  78. if os.path.exists("rtt/Bin/rtthread_1M.FLS") :
  79. shutil.copy("rtt/Bin/rtthread_1M.FLS", "tmp/LuatOS_Air640W_V0004_testing.FLS")
  80. # 拷贝库文件和demo
  81. shutil.copytree(LIB_PATH, "tmp/lib")
  82. shutil.copytree(DEMO_PATH, "tmp/demo")
  83. shutil.copytree(TOOLS_PATH, "tmp/tools")
  84. #拷贝自身
  85. shutil.copy(sys.argv[0], "tmp/air640w.py")
  86. shutil.copy("README.md", "tmp/README.md")
  87. shutil.copy("YModem.py", "tmp/YModem.py")
  88. shutil.copy("YMTask.py", "tmp/YMTask.py")
  89. if os.path.exists("userdoc") :
  90. shutil.copytree("userdoc", "tmp/userdoc")
  91. if os.path.exists("../../docs/api/lua"):
  92. shutil.copytree("../../docs/api/lua", "tmp/userdoc/api")
  93. if os.path.exists(USER_PATH):
  94. shutil.copytree(USER_PATH, "tmp/user")
  95. with open("tmp/文档在userdoc目录.txt", "w") as f:
  96. f.write("QQ群: 1061642968")
  97. # 写入默认配置文件
  98. with open("tmp/local.ini", "w") as f:
  99. f.write('''
  100. [air640w]
  101. USER_PATH = user\\
  102. LIB_PATH = lib\\
  103. DEMO_PATH = demo\\
  104. TOOLS_PATH = tools\\
  105. MAIN_LUA_DEBUG = false
  106. LUA_DEBUG = false
  107. COM_PORT = COM56
  108. FLS_PATH = luatos_air640w_v0006.fls
  109. ''')
  110. pkg_name = "air640w_V0004_"+_tag + ".zip"
  111. shutil.make_archive("air640w_V0004_"+_tag, 'zip', "tmp")
  112. print("ALL DONE===================================================")
  113. print("Package Name", pkg_name)
  114. '''
  115. 下载底层或脚本
  116. '''
  117. def _dl(tp, _path=None):
  118. import serial
  119. from YModem import YModem
  120. serial_io = serial.Serial()
  121. serial_io.port = COM_PORT
  122. serial_io.baudrate = "115200"
  123. serial_io.parity = "N"
  124. serial_io.bytesize = 8
  125. serial_io.stopbits = 1
  126. serial_io.timeout = 2
  127. #serial_io.rtscts = 1
  128. try:
  129. serial_io.open()
  130. except Exception as e:
  131. raise Exception("Failed to open serial port!")
  132. def sender_getc(size):
  133. return serial_io.read(size) or None
  134. def sender_putc(data, timeout=15):
  135. return serial_io.write(data)
  136. ## 适配CH340的RTS接到W600的RTS脚
  137. ## 如果无法下载, 先尝试手动复位模块, 还是不行的话, 把rts的值从当前的 1和0 改成 0和1
  138. serial_io.rts = 1
  139. time.sleep(0.5)
  140. serial_io.rts = 0
  141. if tp == "fs" or tp == "full":
  142. serial_io.write("reboot\r\n".encode())
  143. for k in range(10) :
  144. serial_io.write("reinit\r\n".encode())
  145. resp = serial_io.read_until()
  146. try :
  147. line = resp.decode().strip()
  148. print(line)
  149. if "DONE!" in line :
  150. print("格式化完成")
  151. break
  152. except:
  153. pass
  154. time.sleep(0.03) # 50ms
  155. for k in range(10) :
  156. resp = serial_io.read_until()
  157. time.sleep(1)
  158. print(">> 开始ymodem发送文件")
  159. os.chdir("disk")
  160. for root, dirs, files in os.walk(".", topdown=False):
  161. for name in files:
  162. serial_io.write("ry\r\n".encode())
  163. serial_io.read_until()
  164. sender = YModem(sender_getc, sender_putc)
  165. _path = os.path.join(root, name)
  166. sent = sender.send_file(_path, retry=1)
  167. #print (">>" + sent)
  168. serial_io.close()
  169. if tp == "rom" or tp == "full":
  170. if _path == None :
  171. _path = FLS_PATH
  172. serial_io.close()
  173. cmd = "tools\\wm_tool.exe -ds 2M -ws 115200 -c %s -rs rts -dl %s -eo secboot" % (COM_PORT, _path)
  174. print("CMD", cmd)
  175. subprocess.check_call(cmd, shell=True)
  176. print(">>> ok")
  177. '''
  178. 生成文件系统镜像
  179. '''
  180. def _lfs(_path=None):
  181. _disk = FTC_PATH + "disk"
  182. if os.path.exists(_disk) :
  183. shutil.rmtree(_disk)
  184. os.mkdir(_disk)
  185. if not _path:
  186. _path = USER_PATH
  187. # 收集需要处理的文件列表
  188. _paths = []
  189. # 首先,遍历lib目录
  190. if os.path.exists(LIB_PATH) :
  191. for name in os.listdir(LIB_PATH) :
  192. _paths.append(LIB_PATH + name)
  193. # 然后遍历user目录
  194. for name in os.listdir(_path) :
  195. _paths.append(_path + name)
  196. TAG_PROJECT = ""
  197. TAG_VERSION = ""
  198. for name in _paths :
  199. # 如果是lua文件, 编译之
  200. if name.endswith(".lua") :
  201. cmd = [TOOLS_PATH + "luac_536_32bits.exe"]
  202. #print ("Using Lua 32bits!!!")
  203. if name.endswith("main.lua") :
  204. if not MAIN_LUA_DEBUG :
  205. cmd += ["-s"]
  206. with io.open(name, mode="r", encoding="utf-8") as f :
  207. for line in f.readlines() :
  208. if line :
  209. line = line.strip()
  210. if line.startswith("PROJECT =") :
  211. TAG_PROJECT = line[line.index("\"") + 1:line.index("\"",line.index("\"")+1)]
  212. elif line.startswith("VERSION =") :
  213. TAG_VERSION = line[line.index("\"") + 1:line.index("\"",line.index("\"")+1)]
  214. elif not LUA_DEBUG :
  215. cmd += ["-s"]
  216. else:
  217. print("LUA_DEBUG", LUA_DEBUG, "False" == LUA_DEBUG)
  218. cmd += ["-o", FTC_PATH + "disk/" + os.path.basename(name) + "c", os.path.basename(name)]
  219. print("CALL", " ".join(cmd))
  220. subprocess.check_call(cmd, cwd=os.path.dirname(name))
  221. # 其他文件直接拷贝
  222. else:
  223. print("COPY", name, FTC_PATH + "disk/" + os.path.basename(name))
  224. shutil.copy(name, FTC_PATH + "disk/" + os.path.basename(name))
  225. if TAG_PROJECT == "" or TAG_VERSION == "" :
  226. print("!!!!!!!miss PROJECT or/and VERSION!!!!!!!!!!")
  227. for root, dirs, files in os.walk("disk", topdown=False):
  228. import struct
  229. print("write flashx.bin", root)
  230. with open("disk/flashx.bin", "wb") as f :
  231. # 写入文件头
  232. f.write(struct.pack("<HHI", 0x1234, 0x00, 0x00))
  233. for name in files:
  234. # 写入文件名
  235. f.write(struct.pack("<HHI", 0x0101, 0x00, len(name)))
  236. f.write(name.encode())
  237. # 写入文件内容
  238. _path = os.path.join(root, name)
  239. _size = os.path.getsize(_path)
  240. print(_path, _size)
  241. f.write(struct.pack("<HHI", 0x0202, 0x00, _size))
  242. with open(_path, "rb") as f2 :
  243. shutil.copyfileobj(f2, f, _size)
  244. if TAG_PROJECT != "" and TAG_VERSION != "":
  245. # otademo_1.2.7_LuatOS_V0003_w60x
  246. TAG_NAME = "%s_%s_LuatOS_V0006_w60x.bin" % (TAG_PROJECT, TAG_VERSION)
  247. print("update bin --> " + TAG_NAME)
  248. shutil.copy("disk/flashx.bin", TAG_NAME)
  249. def main():
  250. argc = 1
  251. while len(sys.argv) > argc :
  252. if sys.argv[argc] == "build" :
  253. print("Action Build ----------------------------------")
  254. elif sys.argv[argc] == "lfs" :
  255. print("Action mklfs ----------------------------------")
  256. _lfs()
  257. elif sys.argv[argc] == "pkg" :
  258. print("Action pkg ------------------------------------")
  259. _pkg()
  260. elif sys.argv[argc] == "dlrom": #下载底层
  261. print("Action download ROM ---------------------------")
  262. if len(sys.argv) > argc + 1 and sys.argv[argc+1].startsWith("-path="):
  263. _dl("rom", sys.argv[argc+1][6:])
  264. argc += 1
  265. else:
  266. _dl("rom")
  267. elif sys.argv[argc] == "dlfs":
  268. print("Action download FS ---------------------------")
  269. if len(sys.argv) > argc + 1 and sys.argv[argc+1].startsWith("-path="):
  270. _dl("fs", sys.argv[argc+1][6:])
  271. argc += 1
  272. else:
  273. _dl("fs")
  274. elif sys.argv[argc] == "dlfull":
  275. if len(sys.argv) > argc + 1 and sys.argv[argc+1].startsWith("-path="):
  276. _dl("full", sys.argv[argc+1][6:])
  277. argc += 1
  278. else:
  279. _dl("full")
  280. elif sys.argv[argc] == "clean":
  281. if os.path.exists("tmp"):
  282. shutil.rmtree("tmp")
  283. if os.path.exists(FTC_PATH + "disk"):
  284. shutil.rmtree(FTC_PATH + "disk")
  285. else:
  286. usage()
  287. return
  288. argc += 1
  289. print("============================================================================")
  290. print("every done, bye")
  291. if len(sys.argv) == 1 :
  292. usage()
  293. else :
  294. main()