uartTaskDahua.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. --- 模块功能:串口1功能测试
  2. -- @author openLuat
  3. -- @module uart.uartTask
  4. -- @license MIT
  5. -- @copyright openLuat
  6. -- @release 2018.03.27
  7. module(...,package.seeall)
  8. require "utils"
  9. require "pm"
  10. require "sys"
  11. require "log"
  12. require "funlib"
  13. require "nvm"
  14. --[[
  15. 功能定义:
  16. uart按照帧结构接收外围设备的输入,收到正确的指令后,回复ASCII字符串
  17. 帧结构如下:
  18. 帧头:1字节,0x01表示扫描指令,0x02表示控制GPIO命令,0x03表示控制端口命令
  19. 帧体:字节不固定,跟帧头有关
  20. 帧尾:1字节,固定为0xC0
  21. 收到的指令帧头为0x01时,回复"CMD_SCANNER\r\n"给外围设备;例如接收到0x01 0xC0两个字节,就回复"CMD_SCANNER\r\n"
  22. 收到的指令帧头为0x02时,回复"CMD_GPIO\r\n"给外围设备;例如接收到0x02 0xC0两个字节,就回复"CMD_GPIO\r\n"
  23. 收到的指令帧头为0x03时,回复"CMD_PORT\r\n"给外围设备;例如接收到0x03 0xC0两个字节,就回复"CMD_PORT\r\n"
  24. 收到的指令帧头为其余数据时,回复"CMD_ERROR\r\n"给外围设备;例如接收到0x04 0xC0两个字节,就回复"CMD_ERROR\r\n"
  25. ]]
  26. --串口ID,1对应uart1
  27. --如果要修改为uart2,把UART_ID赋值为2即可
  28. -- air720UH 可用串口1和2,rfid串口用1
  29. -- air724UH 可用串口1、2、3,rfid串口用3
  30. local UART_ID = nvm.get("rfidUartId")
  31. local UART_ID_BAUD_RATE = nvm.get("rfidUartBaudRate")
  32. local rfidOfflineTimeout = nvm.get("rfidOfflineTimeout")
  33. local rfidMinDiffTime = nvm.get("rfidMinDiffTime") or 3
  34. --local UART_ID = 3
  35. --帧头
  36. local FRM_HEAD = 0xaa2801
  37. --rfid 去重缓存的超时时间
  38. local rfid_buffer_timeout = 20
  39. local rfid_buffer_max_count = 100
  40. --串口读到的数据缓冲区
  41. local rdbuf = ""
  42. --rfid串口初始化定参数时器
  43. local rfid_uart_config_init_timer_id = nil
  44. local rfidType = nvm.get("rfidType")
  45. local timerSyncResult = false
  46. --是否厂测读rfid标签模式
  47. local isFactoryTestReadRfidMode = false
  48. --厂测模式读rifd号
  49. local factoryTestReadRfid = ""
  50. --是否已开始心跳
  51. local isStartHeartbeat = false
  52. local isCmdReadRfidMode = false
  53. local cmdReadRfid = ""
  54. --重置时间差值
  55. local resetTime = 0
  56. require "misc"
  57. local function getTime()
  58. local tm = misc.getClock()
  59. return string.format("%04d/%02d/%02d,%02d:%02d:%02d", tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec)
  60. end
  61. --初始化rfid缓冲池
  62. local start_time = os.time()
  63. local rfids_buf = {["start_time"]=start_time,["rfid_count"]=0,["buf"]={}}
  64. --rfid缓冲池最大存储标签个数
  65. local rfidsBufMaxCount = 3000
  66. --[[
  67. 函数名:parse_dahua
  68. 功能 :按照帧结构解析处理一条完整的帧数据
  69. 参数 :
  70. data:所有未处理的数据
  71. 返回值:第一个返回值是一条完整帧报文的处理结果,第二个返回值是未处理的数据
  72. ]]
  73. local function parse_dahua(data)
  74. local rfidRssiFilterVal = nvm.get("rfidRssiFilterVal")
  75. if not data then return end
  76. if data:len() < 14 then
  77. --log.info("uartTaskDahua.parse_dahua","data length < 42", data:toHex())
  78. return false, data
  79. end
  80. local res,frm_head_pos = string.find( data, string.char(0xaa)..string.char(0x55)..string.char(0xc) )
  81. if not res then
  82. log.error("uartTaskDahua.parse_dahua","FRM_HEAD not found,drop it! data:", data:toHex())
  83. return false
  84. end
  85. -- 截取包含侦头0xaa55开始的数据包
  86. local sub_data = data:sub(frm_head_pos-2,-1)
  87. local frm_len = string.byte(sub_data,3)
  88. --检测到不够一个完整包,返回不完整的部分
  89. if res and (#sub_data < frm_len+3) then
  90. log.warn("uartTaskDahua.parse_dahua","only find sub frm_data,return and wait... data:",data:toHex(), "sub_data:",sub_data:toHex(),"frm_len:",frm_len)
  91. return false, sub_data
  92. end
  93. --检测是否有结束符0x0d0a,没有结束符则丢弃
  94. local res1,tail_pos = string.find( sub_data, string.char(0x0d)..string.char(0x0a) )
  95. if not res1 then
  96. log.error("uartTaskDahua.parse_dahua","FRM_TAIL not found,drop it! data:", data:toHex(), "sub_data:",sub_data:toHex())
  97. return false
  98. end
  99. --检测包含侦头和侦尾的数据包长度是否合法,不合法则丢弃
  100. if (tail_pos > frm_len+3) or (tail_pos < frm_len+3) then
  101. log.warn("uartTaskDahua.parse_dahua","only find sub frm_data,return and wait... data:",data:toHex(), "sub_data:",sub_data:toHex(),"frm_len:",frm_len,"tail_pos:",tail_pos)
  102. return false, sub_data:sub(tail_pos+1,-1)
  103. end
  104. local frm_data = sub_data:sub(1,tail_pos)
  105. --解析rfid
  106. local rfid_info = {}
  107. rfid_info.crc = string.byte(frm_data,4)
  108. --rfid_info.sn = string.byte(frm_data,5)
  109. rfid_info.id = frm_data:sub(6,9):toHex()
  110. rfid_info.type = string.byte(frm_data,10)
  111. rfid_info.status = string.byte(frm_data,11)
  112. rfid_info.version = string.byte(frm_data,12)
  113. rfid_info.rssi = string.byte(frm_data,13)
  114. --log.info("uartTaskDahua.parse_dahua", "frm_data:", frm_data:toHex())
  115. if rfidRssiFilterVal > 0 and rfid_info.rssi > rfidRssiFilterVal then
  116. log.info("uartTaskDahua.parse_dahua", "rfid_info.rssi > ".. rfidRssiFilterVal..", drop it! rfid_info.rssi =", rfid_info.rssi)
  117. --大于一个完整包,返回剩余部分
  118. if #sub_data > tail_pos then
  119. return false, sub_data:sub(tail_pos+1,-1)
  120. end
  121. return false
  122. end
  123. local cur_time = os.time()
  124. if resetTime >= 1 or resetTime <= -1 then
  125. cur_time = cur_time + resetTime
  126. --log.info("uartTaskDahua.parse_dahua","resetTime:",resetTime,",reset to time:",cur_time)
  127. end
  128. --进入厂测读rfid标签信号模式
  129. if isFactoryTestReadRfidMode then
  130. --读到rfid标签信号后,退出厂测模式
  131. if factoryTestReadRfid == rfid_info.id then
  132. sys.publish("factory_test_read_rfid_success",{rssi=rfid_info.rssi,rfid=rfid_info.id})
  133. isFactoryTestMode = false
  134. factoryTestReadRfid = ""
  135. end
  136. --大于一个完整包,返回剩余部分
  137. if #sub_data > tail_pos then
  138. return true, sub_data:sub(tail_pos+1,-1)
  139. end
  140. return true
  141. end
  142. --进入指令读rfid标签信号模式
  143. if isCmdReadRfidMode then
  144. --读到rfid标签信号后,退出读标签模式
  145. if cmdReadRfid == rfid_info.id then
  146. log.info("uartTaskDahua.parse_dahua", "rfid_info:", json.encode(rfid_info))
  147. sys.publish("cmd_read_rfid_success",{rssi=rfid_info.rssi,rfid=rfid_info.id})
  148. isCmdReadRfidMode = false
  149. cmdReadRfid = ""
  150. end
  151. --大于一个完整包,返回剩余部分
  152. if #sub_data > tail_pos then
  153. return true, sub_data:sub(tail_pos+1,-1)
  154. end
  155. return true
  156. end
  157. --新增数据
  158. if not rfids_buf["buf"][rfid_info.id] then
  159. --log.info(">>>>>>> push rfid to buf,rfid:", rfid_info.id)
  160. if rfids_buf.rfid_count > rfidsBufMaxCount then
  161. log.warn("uartTaskDahua.parse_dahua", "rfids_buf.rfid_count > ", rfidsBufMaxCount, ", drop it", rfid_info.id)
  162. --大于一个完整包,返回剩余部分
  163. if #sub_data > tail_pos then
  164. return true, sub_data:sub(tail_pos+1,-1)
  165. end
  166. return true
  167. end
  168. rfids_buf.rfid_count = rfids_buf.rfid_count + 1
  169. --location:0x40 rfid进入
  170. rfids_buf["buf"][rfid_info.id] = {["id"]=rfid_info.id,["first_time"]=cur_time,["time"]=cur_time,["recv_count"]=1,["rssi"]=rfid_info.rssi,["type"]=rfid_info.type,["status"]=rfid_info.status,["sn"]=0,["location"]=0x40}
  171. --发布rfid首个信号
  172. sys.publish("pub_packet_data_dahua_rfid_info", rfids_buf["buf"][rfid_info.id])
  173. --大于一个完整包,返回剩余部分
  174. if #sub_data > tail_pos then
  175. return true, sub_data:sub(tail_pos+1,-1)
  176. end
  177. return true
  178. end
  179. local first_time = rfids_buf["buf"][rfid_info.id]["first_time"]
  180. --更新数据
  181. local recv_count = rfids_buf["buf"][rfid_info.id]["recv_count"] + 1
  182. local rfid_sn = rfids_buf["buf"][rfid_info.id]["sn"]
  183. rfid_sn = (rfid_sn+1 < 0xff) and (rfid_sn+1) or 0
  184. rfids_buf["buf"][rfid_info.id] = {["id"]=rfid_info.id,["first_time"]=first_time,["time"]=cur_time,["recv_count"]=recv_count,["rssi"]=rfid_info.rssi,["type"]=rfid_info.type,["status"]=rfid_info.status,["sn"]=rfid_sn,["location"]=0x40}
  185. --log.info(">>>>>>> update rfid_buf,rfid:", rfid_info.id, ", recv_count:", recv_count)
  186. --大于一个完整包,返回剩余部分
  187. if #sub_data > tail_pos then
  188. return true, sub_data:sub(tail_pos+1,-1)
  189. end
  190. return true
  191. end
  192. --[[
  193. 函数名:proc
  194. 功能 :处理从串口读到的数据
  195. 参数 :
  196. data:当前一次从串口读到的数据
  197. 返回值:无
  198. ]]
  199. local function proc(data)
  200. if not data or string.len(data) == 0 then return end
  201. if not rfidType then
  202. log.info("uartTaskDahua.proc","rfidType undefined!!")
  203. rfidType = nvm.get("rfidType")
  204. return false
  205. end
  206. --追加到缓冲区
  207. rdbuf = rdbuf..data
  208. local result,unproc
  209. unproc = rdbuf
  210. --根据帧结构循环解析未处理过的数据
  211. while true do
  212. if rfidType == 'dahua' then
  213. result,unproc = parse_dahua(unproc)
  214. else
  215. log.info("uartTaskDahua.proc","rfidType invalid!!")
  216. return false
  217. end
  218. if not unproc or unproc == "" or not result then
  219. break
  220. end
  221. end
  222. rdbuf = unproc or ""
  223. if #rdbuf > 1000 then
  224. log.warn("uartTaskDahua.proc","rdbuf length > 1000!! rdbuf:", #rdbuf)
  225. end
  226. end
  227. --[[
  228. 函数名:read
  229. 功能 :读取串口接收到的数据
  230. 参数 :无
  231. 返回值:无
  232. ]]
  233. local function read()
  234. local data = ""
  235. --底层core中,串口收到数据时:
  236. --如果接收缓冲区为空,则会以中断方式通知Lua脚本收到了新数据;
  237. --如果接收缓冲器不为空,则不会通知Lua脚本
  238. --所以Lua脚本中收到中断读串口数据时,每次都要把接收缓冲区中的数据全部读出,这样才能保证底层core中的新数据中断上来,此read函数中的while语句中就保证了这一点
  239. while true do
  240. data = uart.read(UART_ID,"*l")
  241. if not data or string.len(data) == 0 then break end
  242. --打开下面的打印会耗时
  243. --log.info("uartTask.read bin",data)
  244. --log.info("uartTask.read hex",data:toHex())
  245. --处理串口收到的rfid数据
  246. proc(data)
  247. end
  248. end
  249. --[[
  250. 函数名:write
  251. 功能 :通过串口发送数据
  252. 参数 :
  253. s:要发送的数据
  254. 返回值:无
  255. ]]
  256. function write(s)
  257. log.info("uartTask.write",s)
  258. uart.write(UART_ID,s.."\r\n")
  259. end
  260. local function writeOk()
  261. log.info("uartTask.writeOk")
  262. end
  263. --等待时间同步成功消息通知
  264. sys.subscribe("TIMER_SYNC_SUCCESS", function ( ... )
  265. log.info("uartTaskDahua.subscribe","TIMER_SYNC_SUCCESS")
  266. timerSyncResult = true
  267. sys.publish("MESSAGE_TYPE_START_HEARTBEAT")
  268. end)
  269. --启动厂测读rfid标签模式,读取指定rfid标签信号
  270. sys.subscribe("factory_test_start_read_rfid", function ( data )
  271. if data.rfid then
  272. log.info("uartTaskDahua.subscribe","factory test mode,start read rifd:",data.rfid)
  273. isFactoryTestReadRfidMode = true
  274. factoryTestReadRfid = data.rfid
  275. if not isStartHeartbeat then
  276. sys.publish("MESSAGE_TYPE_START_HEARTBEAT")
  277. end
  278. else
  279. log.error("uartTaskDahua.subscribe.factory_test_start_read_rfid","data.rfid not existed!!")
  280. end
  281. end)
  282. --退出厂测读rfid标签模式
  283. sys.subscribe("factory_test_stop_read_rfid", function ( )
  284. log.info("uartTaskDahua.subscribe.factory_test_stop_read_rfid","factory test stop read rifd")
  285. isFactoryTestReadRfidMode = false
  286. factoryTestReadRfid = ""
  287. end)
  288. --启动读rfid标签模式,读取指定rfid标签信号
  289. sys.subscribe("cmd_start_read_rfid", function ( data )
  290. if not isStartHeartbeat then
  291. sys.publish("MESSAGE_TYPE_START_HEARTBEAT")
  292. end
  293. if data.rfid then
  294. log.info("uartTaskDahua.subscribe.cmd_start_read_rfid","cmd start read rifd:",data.rfid)
  295. isCmdReadRfidMode = true
  296. cmdReadRfid = data.rfid
  297. else
  298. log.error("uartTaskDahua.subscribe.cmd_start_read_rfid","data.rfid not existed!!")
  299. end
  300. end)
  301. --退出厂测读rfid标签模式
  302. sys.subscribe("cmd_stop_read_rfid", function ( )
  303. log.info("uartTaskDahua.subscribe.cmd_stop_read_rfid","cmd stop read rifd")
  304. isCmdReadRfidMode = false
  305. cmdReadRfid = ""
  306. end)
  307. --保持系统处于唤醒状态,此处只是为了测试需要,所以此模块没有地方调用pm.sleep("uartTask")休眠,不会进入低功耗休眠状态
  308. --在开发“要求功耗低”的项目时,一定要想办法保证pm.wake("uartTask")后,在不需要串口时调用pm.sleep("uartTask")
  309. pm.wake("uartTask")
  310. --sys.timerStart(function ( ... )
  311. --初始化rfid串口配置参数
  312. rfid_uart_config_init_timer_id = sys.timerLoopStart(function ()
  313. if not UART_ID then
  314. log.info("UART_ID empty, attempt from nvm.get")
  315. UART_ID = nvm.get("rfidUartId")
  316. return false
  317. end
  318. if not UART_ID_BAUD_RATE then
  319. log.info("UART_ID_BAUD_RATE empty, attempt from nvm.get")
  320. UART_ID_BAUD_RATE = nvm.get("rfidUartBaudRate")
  321. return false
  322. end
  323. if not rfid_uart_config_init_timer_id then
  324. log.info("rfid_uart_config_init_timer_id empty")
  325. return false
  326. end
  327. log.info("init rfid uart","UART_ID:",UART_ID,"UART_ID_BAUD_RATE:",UART_ID_BAUD_RATE)
  328. --注册串口的数据接收函数,串口收到数据后,会以中断方式,调用read接口读取数据
  329. --uart.on(UART_ID,"receive",read)
  330. uart.on(UART_ID,"receive",function() sys.publish("UART_RECEIVE") end)
  331. --注册串口的数据发送通知函数
  332. uart.on(UART_ID,"sent",writeOk)
  333. --配置并且打开串口
  334. --波特率,可选1200,2400,4800,9600,10400,14400,19200,28800,38400,57600,115200,230400,460800,921600
  335. --uart.setup(UART_ID,115200,8,uart.PAR_NONE,uart.STOP_1)
  336. uart.setup(UART_ID,UART_ID_BAUD_RATE,8,uart.PAR_NONE,uart.STOP_1)
  337. --如果需要打开“串口发送数据完成后,通过异步消息通知”的功能,则使用下面的这行setup,注释掉上面的一行setup
  338. --uart.setup(UART_ID,115200,8,uart.PAR_NONE,uart.STOP_1,nil,1)
  339. log.info("uart timer loop stop","rfid_uart_config_init_timer_id:",rfid_uart_config_init_timer_id)
  340. sys.timerStop(rfid_uart_config_init_timer_id)
  341. end, 1000)
  342. --end, 10000)
  343. local function taskRead()
  344. --等心跳开启时,再开始接收串口rfid数据
  345. while true do
  346. log.info("uartTaskDahua.taskRead","wait start_rfid_upload_task message ...")
  347. local res,data = sys.waitUntil("start_rfid_upload_task", 3600*1000)
  348. if res then
  349. log.info("uartTaskDahua.taskRead","receive start_rfid_upload_task messages, start UART_RECEIVE task...")
  350. isStartHeartbeat = true
  351. break
  352. else
  353. log.info("uartTaskDahua.taskRead","wait start_rfid_upload_task timeout")
  354. end
  355. end
  356. local cacheData = ""
  357. local count = 0
  358. while true do
  359. cacheData = uart.read(UART_ID,"*l")
  360. if cacheData == "" then
  361. if not sys.waitUntil("UART_RECEIVE",100) then
  362. --log.info("uartTask.taskRead","100ms no data")
  363. end
  364. else
  365. --log.info("uartTask.read hex",cacheData:toHex())
  366. proc(cacheData)
  367. cacheData = ""
  368. count = count + 1
  369. if count > 1200 then
  370. sys.wait(50)
  371. count = 0
  372. end
  373. end
  374. end
  375. end
  376. --启动串口数据接收任务
  377. sys.taskInit(taskRead)
  378. sys.timerLoopStart(function()
  379. log.info("uartTaskDahua.timerLoopStart","打印占用的内存:", _G.collectgarbage("count"))-- 打印占用的RAM
  380. log.info("uartTaskDahua.timerLoopStart","打印可用的空间", rtos.get_fs_free_size())-- 打印剩余FALSH,单位Byte
  381. log.info("uartTaskDahua.timerLoopStart", "rfidMinDiffTime:",rfidMinDiffTime)
  382. end, 10000)
  383. --rfid离线检测任务
  384. sys.taskInit(function()
  385. while true do
  386. while true do
  387. if rfids_buf["rfid_count"] > 0 then
  388. break
  389. else
  390. log.info("uartTaskDahua.rfidOfflineCheckTask","rfids_buf empty,wait ...")
  391. sys.wait(1000)
  392. end
  393. end
  394. local now = os.time()
  395. if resetTime >= 1 or resetTime <= -1 then
  396. now = now + resetTime
  397. --log.info("uartTaskDahua.rfidOfflineCheckTask","resetTime:",resetTime,",reset to time:",now)
  398. end
  399. --log.info("uartTaskDahua.rfidOfflineCheckTask","start time:",now)
  400. local tmp = {["rfid_count"]=0,["buf"]={}}
  401. local checkCount = 0
  402. local onlineCount = 0
  403. local offlineCount = 0
  404. local totalCount = 0
  405. for rfid, rfid_info in pairs(rfids_buf["buf"]) do
  406. totalCount = totalCount + 1
  407. if rfid_info.time and rfid_info.time > 0 then
  408. checkCount = checkCount + 1
  409. if now - rfid_info.time <= rfidOfflineTimeout then
  410. if rfid == "953651BE" then
  411. --log.info("uartTaskDahua.rfidOfflineCheckTask","rfid is online,skip! rfid:", rfid, "rfid_info.time:", rfid_info.time)
  412. end
  413. tmp["buf"][rfid] = rfid_info
  414. tmp["rfid_count"] = tmp["rfid_count"] + 1
  415. onlineCount = onlineCount + 1
  416. else
  417. --首末次信号时间差超过最小时间差值,才认为是有效的末次信号
  418. if rfid_info.time - rfid_info.first_time > rfidMinDiffTime then
  419. offlineCount = offlineCount + 1
  420. if rfid == "953651BE" then
  421. log.info("uartTaskDahua.rfidOfflineCheckTask","rfid offline! rfid:", rfid, "rfid_info.time:", rfid_info.time, "now:", now, "rfidOfflineTimeout:", rfidOfflineTimeout)
  422. end
  423. --上报离线后,末次信号
  424. if rfids_buf["buf"][rfid] then
  425. --location:0x80 rfid离开
  426. rfid_info.location = 0x80
  427. sys.publish("pub_packet_data_dahua_rfid_info", rfid_info)
  428. if offlineCount >= 100 and offlineCount%100 == 0 then
  429. --log.info("uartTaskDahua.rfidOfflineCheckTask","wait 3s")
  430. sys.wait(50)
  431. end
  432. end
  433. else
  434. log.info("uartTaskDahua.rfidOfflineCheckTask","drop it, because: rfid_info.time - rfid_info.first_time <=",rfidMinDiffTime)
  435. end
  436. end
  437. end
  438. end
  439. rfids_buf = tmp
  440. log.info("uartTaskDahua.rfidOfflineCheckTask", "totalCount:",totalCount, "checkCount:",checkCount, "onlineCount:",onlineCount, "offlineCount:",offlineCount)
  441. sys.wait(rfidOfflineTimeout*1000 + 2000)
  442. end
  443. end)
  444. --重置系统时间消息(可以校正成自定义时间)
  445. sys.subscribe("RESET_CLOCK_TIME", function ( diffTime )
  446. if type(diffTime) == "number" then
  447. resetTime = diffTime
  448. log.info("uartTaskDahua.subscribe.RESET_CLOCK_TIME","diffTime:",diffTime)
  449. end
  450. end)