--[[**E180_ZG120B.lua****************Copyright(c)***************************
**                Guangzhou DACAIGAUNGDIAN Technology Co. Ltd
**
**                             http://www.gz-dc.com
**
**------File Info-----------------------------------------------------------
** File Name:               E180_ZG120B.lua
** Latest modified Date:    2022/06/21
** Latest Version:          
** Description:             zigbee模块设置。只需在初始化时调用函数设置参数。设置好参数之后zigbee就当做串口使用。使用on_uart_recv_data2(packet)接收处理数据。
**
**--------------------------------------------------------------------------
** Created By:              lsj
** Created date:            2022/06/21
** Version:                 V0.1
** Descriptions:            
**
***************************************************************************--]]
e180 = {}

e180.sys_mode = 1               --模式，1=传输模式transfer，2=配置模式config

--e180.timerid_cto command timeout
e180.timerid_cto = 21           --发送AT的超时定时器

--存储接受数据的 string 类型
e180.resp_string = ''           --当前接受的数据
e180.resp_string_last = ''      --上一次接受的数据

e180.uart_recv_data2 = {}       --接收的数据

--回调函数
e180.send_callback = nil        --发送回调函数
e180.resp_callback = nil        --所有e180返回的数据的回调函数
e180.log_callback = nil         --调试信息回调函数

e180.en_debug = 0    --0-不显示调试信息;1-虚拟屏; 2-实体屏，串口1输出
--[[***************************************************************************
** Function name:  e180.my_uart_log
** Descriptions:   打印虚拟屏调试信息，设置 e180.en_debug = 1
                   注意：实体屏运行时， e180.en_debug 不能等于 1
                   打印实体屏调试信息，设置 e180.en_debug = 2
                   注意：虚拟屏运行时，e180.en_debug 不能等于 2
***************************************************************************--]]
function e180.my_uart_log(debug_msg)
    if e180.en_debug == 1
    then
        print(debug_msg)
    elseif e180.en_debug == 2
    then
        uart_send_string(debug_msg)
    end
end

--[[***************************************************************************
** Function name:  e180.log
** Descriptions :  调试信息回调函数
** @msg         :  调试信息
***************************************************************************--]]
function e180.log(msg)
    if e180.log_callback~= nil
    then
        e180.log_callback(msg)
    end
end

--[[***************************************************************************
** Function name:  e180.set_callback
** Descriptions :  设置回调函数
** @send_cb     :  向e180发送数据的回调函数
** @resp_cb     :  所有e180返回的数据的回调函数
** @log_cb      :  调试信息回调函数
***************************************************************************--]]
function e180.set_callback(send_cb,resp_cb,log_cb)
    e180.send_callback = send_cb
    e180.resp_callback = resp_cb
    e180.log_callback = log_cb
end

-----------------对e180模块的命令发送队列 BEGIN--------------------------
--对e180模块的命令发送队列
ZgbList = {}

--[[***************************************************************************
** Function name:  ZgbList.new
** Descriptions :  初始化命令队列
***************************************************************************--]]
function ZgbList.new()
    return {first = 0, last = -1}
end

--[[***************************************************************************
** Function name:  ZgbList.pushBack
** Descriptions :  填充队列
** @ZgbList        :  被填充的队列
** @value       :  填入的数据
***************************************************************************--]]
function ZgbList.pushBack(ZgbList, value)
    local last = ZgbList.last + 1
    ZgbList.last = last
    ZgbList[last] = value
end

--[[***************************************************************************
** Function name:  ZgbList.popFront
** Descriptions :  提取队列中的第一个信息
** @ZgbList        :  被提取的队列
** @return      :  被提取的数据
***************************************************************************--]]
function ZgbList.popFront(ZgbList)
    local first = ZgbList.first
    if first > ZgbList.last then
        return nil
    end
    local value = ZgbList[first]
    ZgbList[first] = nil
    ZgbList.first = first + 1
    return value
end
----------------对e180模块的命令发送队列 END--------------------------------

e180.cmd_list = ZgbList.new()         --命令队列
e180.cmd_currunt = nil             --当前发送的命令

--[[***************************************************************************
** Function name:  e180.cmd_add
** Descriptions :  命令添加到队列，当前没有已发送的命令时，发送添加的第一条命令
** @sendstr     :  发送的命令
** @ackstr      :  e180模块对命令的返回的数据
** @timeout     :  返回数据的超时时间
** @retry       :  命令超时的重发次数
** @callback    :  数据返回时的回调函数
***************************************************************************--]]
function e180.cmd_add(sendstr,ackstr,timeout,retry,callback)
    e180.log('e180.cmd_add()')
    local cmd = {}
    
    if timeout==nil
    then timeout = 0 end
    
    if retry==nil
    then retry = 0 end
    
    cmd.send = sendstr
    cmd.ack = ackstr
    cmd.timeout = timeout
    cmd.retry = retry
    cmd.callback = callback
    
    ZgbList.pushBack(e180.cmd_list,cmd)
    
    --当前没有指令在执行，立即执行一次
    if e180.cmd_currunt==nil then
        e180.cmd_list_run_next()
    end
    
end

--[[***************************************************************************
** Function name:  e180.cmd_list_timeout
** Descriptions :  命令超时的处理函数
***************************************************************************--]]
function e180.cmd_list_timeout()
    e180.log('e180.cmd_list_timeout()')
    --当前没有命令时退出
    if e180.cmd_currunt==nil 
    then 
        return 
    end
    
    
    --重发次数大于0时，重发命令
    if e180.cmd_currunt.retry>0 then
        --重发次数递减
        e180.cmd_currunt.retry = e180.cmd_currunt.retry-1
        e180.cmd_list_run()                                          --重发
    else
        --重发次数小于0时
        if e180.cmd_currunt.callback~=nil
        then
            e180.cmd_currunt.callback(e180.cmd_currunt.send,'timeout')    --向命令回调函数返回数据
        end
        e180.cmd_list_run_next()                                     --不需要重发，直接发下一条
    end
end

--[[***************************************************************************
** Function name:  e180.cmd_list_run
** Descriptions :  发送命令
***************************************************************************--]]
function e180.cmd_list_run()
    e180.log('e180.cmd_list_run()')
    stop_timer(e180.timerid_cto)
    --当前没有命令时退出
    if e180.cmd_currunt==nil
    then 
        return 
    end
    
    --如果命令不为空，则发送命令
    --if string.len(e180.cmd_currunt.send)>1
    if #(e180.cmd_currunt.send)>1
    then
        --e180.log('OUT: '..e180.cmd_currunt.send)
        e180.log('OUT')
        e180.send_callback(e180.cmd_currunt.send)
    end
    
    --启动超时计时器
    start_timer(e180.timerid_cto,e180.cmd_currunt.timeout,0,1) --单次
    
end

--[[***************************************************************************
** Function name:  e180.cmd_list_run
** Descriptions :  发送队列中的下一条命令
***************************************************************************--]]
function e180.cmd_list_run_next()
    e180.cmd_currunt = ZgbList.popFront(e180.cmd_list)
    e180.cmd_list_run()
end

--[[***************************************************************************
** Function name:  e180.cmd_list_check_ack
** Descriptions :  e180返回数据处理
** @data        ： e180返回的数据
***************************************************************************--]]
function e180.cmd_list_check_ack(data)  
    if e180.cmd_currunt==nil
    then
        e180.resp_callback(nil,data)                                     --所有数据回调函数
        e180.cmd_list_run_next()                                         --没有命令需要应答，那就发下一条
        return
    else
        e180.resp_callback(e180.cmd_currunt.send,data)              --所有数据回调函数
    end
    
    --如果又设置对于命令的回调函数，则进入
    if e180.cmd_currunt.callback ~= nil
    then
        e180.cmd_currunt.callback(e180.cmd_currunt.send,data)
        
    end

    if e180.cmd_currunt.ack == '-sasty'     --返回ACK的type与发送的一致
    then
        if e180.datatype == e180.cmd_currunt.send[3]
        then
            e180.my_uart_log('acktrue e180.cmd_list_run_next()')
            e180.cmd_list_run_next()
        end
    elseif e180.cmd_currunt.ack == nil      --大部分MCU发送的指令不需要等待返回ACK
    then
        
    else
        
        local value_long = #data
        local ack_long = #e180.cmd_currunt.ack
        local data_i = 0
        for i=0,value_long
        do
            -- e180.log('data[]='..data[i])
            -- e180.log('e180.ack[]='..e180.cmd_currunt.ack[data_i])
            if e180.cmd_currunt.ack[data_i] == data[i]
            then
                data_i = data_i + 1
                -- e180.log('ack data_i='..data_i)
            else
                data_i = 0
                -- e180.log('ack data_i=0')
            end
            if data_i-1 == ack_long
            then
                e180.log('ack ok')
                e180.cmd_list_run_next()
                break
            end
        end
    end
end

--表数据移除，后面的数据前移
function e180.recv_remove(recv_data,rem_num)
    local data_len = #recv_data
    local transfer_num = data_len-rem_num
    if data_len>=rem_num
        and rem_num > 0
        and recv_data[0] ~= nil
    then
        --record_add(0,17,'uart i+4')
        for x=0,data_len
        do  
            --table.remove (recv_data , 1)
            if x < transfer_num
            then
                --e180.log('*** trf')
                recv_data[x] = recv_data[rem_num+x+1]
            else
                --e180.log('*** nil')
                recv_data[x] = nil
            end
        end
    end
end

--[[***************************************************************************
** Function name:  e180.cmd_list_clear
** Descriptions :  清除命令队列 
***************************************************************************--]]
function e180.cmd_list_clear()
    e180.cmd_list = nil
    e180.cmd_list = {}
    e180.cmd_list = ZgbList.new()
    
end

--[[***************************************************************************
** Function name:  e180.on_timer
** Descriptions :  e180模块定时处理接口，在 maiin.lua 的 on_timer 中处理
***************************************************************************--]]
function e180.on_timer(timer_id)
    if timer_id==e180.timerid_cto then
        e180.resp_string_last = e180.resp_string
        e180.resp_string = ''
        e180.cmd_list_timeout()                      -- 超时计时器
    end
end

--[[***************************************************************************
** Function name:  e180.table2str
** Descriptions :  把表转换成字符串
***************************************************************************--]]
function e180.table2str(tbl)    
    if tbl==nil 
    then return '' end
    
    local str = ''
    local count = #(tbl)
    for i=0,count do    
      ch = string.char(tbl[i])
      str = str .. ch
    end
    return str
end

--[[***************************************************************************
** Function name:  wbr3.table2str16
** Descriptions :  把表转换成十六进制形式的字符串
***************************************************************************--]]
function e180.table2str16(tbl)    
    if tbl==nil 
    then return '' end
    
    local str = ''
    local count = #(tbl)
    for i=0,count do    
        ch = string.format('%02X',tbl[i])
        str = str..' '..ch
    end
    return str
end

--查表，在table2中找table1
--table2：待查询的表
--table1：要找的表
function e180.contrast_table(table2,table1)
    local value_long = #table2
    local ack_long = #table1
    local data_i = 0
    for i=0,value_long
    do
        if table1[data_i] == table2[i]
        then
            data_i = data_i + 1
        else
            data_i = 0
        end
        if data_i-1 == ack_long
        then
            return true
        end
    end
    return false
end

--e180的设置模式和传输模式切换
function e180.enter_confg_mode(mode_set)
    if mode_set == 'transfer'
    then
        e180.sys_mode = 1
        e180.log( 'e180.sys_mode = transfer' )
    elseif mode_set == 'config'
    then
        e180.sys_mode = 2
        e180.log( 'e180.sys_mode = config' )
    end
    e180.uart_recv_data2={}
end

--e180串口数据的总接收函数
function e180.response( packet )
    if e180.sys_mode == 2           --配置模式
    then
        local rem_num = 0
        local cur_num = 0
        
        if #e180.uart_recv_data2 == 0
        then
            if e180.uart_recv_data2[0] == nil
            then
                cur_num = 0
            else
                cur_num = 1
            end
        elseif #e180.uart_recv_data2 ~= 0
        then
            cur_num = #e180.uart_recv_data2 + 1
        end
        
        for i=0,#(packet)
        do
            e180.uart_recv_data2[cur_num] = packet[i]        --从 e180.uart_recv_data2[1] 开始存，因为table.remove只能从位置0开始操作
            cur_num = cur_num + 1
        end
        
        e180.log( 'RD2:'..e180.table2str16(e180.uart_recv_data2) )
        e180.cmd_list_check_ack(e180.uart_recv_data2)
        
        --接收数组大于1024字节时，数组清空
        if #(e180.uart_recv_data2)>50
        then
            e180.uart_recv_data2={} 
        end
    elseif e180.sys_mode == 1       --透传模式
    then
        e180.log( 'e180 transfer uart' )
        eud.usrt_recv_response( packet )
    end
end


