-- benthos.lua -- -- This is a collection of functions meant to interface with a Benthos -- acoustic modem. function Acked(s) local result = false if s ~= nil then local slen = string.len(s) if (slen >= 3) and (string.sub(s, slen-2) == "\r\n>") then result = true elseif (slen >= 1) and (string.sub(s, slen) == ">") then result = true end end SMC:Log("debug", "Acked("..tostring(s).."): "..tostring(result)) return result end function AppInit0() if port == nil then port = SMC:NewSerialPort("config1") end end function AppInit() gasynch = SMCContext gasynch:SetAsynch() AppInit0() end function DoOnline() SMC:Log("debug", "DoOnline") Send("ATO\r") local rxd = AwaitResponse(30000) local result = false if (rxd ~= nil) and string.find(rxd, "CONNECT [0-9]*") then result = true end SMC:Log("debug", "DoOnline: "..tostring(result)) return result end function DoPower(newState) if newState == 0 then part1="Dis" else part1="En" end SMC:Log("debug", part1.."abling the power"); local executeResult = os.execute("sudo SetPowerSwitch 2 "..tostring(newState)) if executeResult ~= 0 then SMC:Log("error", "--> exit code is "..tostring(executeResult)) end return executeResult == 0; end function DoPowerOff() local result = DoPower(0) if result then Wait(20000) end return result end function DoPowerOn() local result = DoPower(1) if result then local rxd = "Not nil" local found = false result = false while rxd ~= nil and not found do rxd = AwaitData(60000) if rxd ~= nil then if Acked(rxd) then found = true result = true elseif string.find(rxd, "CONNECT") ~= nil then rxd = AwaitData(3000) found = true result = DoTestComms() end else result = DoTestComms() end end end -- Send("ATI\r") -- AwaitData(5000) -- Send("ATS?\r") -- AwaitData(5000) SMC:Log("debug", "DoPowerOn: "..tostring(result)) return result end function DoTestComms() local result = false local rxd, rxd2 Send("++++") rxd = AwaitData(1000) Send("\r") rxd2 = AwaitData(1000) if Acked(rxd) or Acked(rxd2) then result = true end SMC:Log("debug", "DoTestComms: "..tostring(result)) return result end function Escape(s) local result if s == nil then result = "nil" else result = "'"..string.gsub(string.gsub(string.gsub(s,"\\","\\\\"),"\r","\\r"),"\n","\\n").."'" end return result end function PowerOff() AppInit() appRtn = coroutine.create(function() DoPowerOff() gasynch:ReportComplete() gasynch = nil end) coroutine.resume(appRtn) end function PowerOn() AppInit() appRtn = coroutine.create(function() DoPowerOn() gasynch:ReportComplete() gasynch = nil end) coroutine.resume(appRtn) end function lines(str) local t = {} local function helper(line) table.insert(t, line) return "" end helper((str:gsub("(.-)\r?\n", helper))) return t end function DoListen1Inner() SMC:Log("debug", "DoListen1Inner") local good = true local msg = "" local result = "" if not DoTestComms() then good = DoPowerOn() if not good then msg = "DoPowerOn failed" end else msg = "DoTestComm failed" end if not good then result = msg elseif not DoOnline() then result = "DoOnline failed" else --SMC:Log("debug", "DoListen1 starting wait") local accdata = "" local isdone = false --SMC:Log("debug", "here") local startTime = SMC:SystemUptime() --SMC:Log("debug", "here2") local timeout = 600 --SMC:Log("debug", "startTime="..tostring(startTime)) while not isdone do local elapsed = SMC:SystemUptime()-startTime local remaining = math.max(0, timeout-elapsed) SMC:Log("debug", "#accdata="..tostring(#accdata).." elapsed="..tostring(elapsed).." remaining="..tostring(remaining)) if remaining > 0 then local rxd = AwaitData(1000*remaining) if #rxd == 0 then isdone = true result = "Nothing received or timeout after "..tostring(#accdata).." bytes" else local t = lines(rxd) SMC:Log("debug", tostring(#t).." lines") for index,value in ipairs(t) do local rxtime = string.find(value, "Rx[ ]Time") local crc = string.find(value, "CRC") if rxtime or crc then if #accdata ~= 0 then accdata = accdata..","..value else accdata = value end if crc then isdone = true result = Escape(accdata) end end end end else isdone = true result = "Timeout after "..tostring(#accdata).." bytes" end end end SMC:Log("debug", "DoListen1Inner: "..result) return result end function DoListen1() SMC:Log("debug", "DoListen1") gasynch:AppendOutput(DoListen1Inner()) gasynch:ReportComplete() gasynch = nil SMC:Log("debug", "DoListen1 done") end function Listen1() AppInit() appRtn = coroutine.create(function() DoListen1() end) coroutine.resume(appRtn) SMC:Log("debug", "exiting from Listen1 outer") end function ReportStringAsTelemetry(str) local evt = { } evt.header = { name = "LWTelem1", typename = "LWTelem1", domainname="LqrSMAC" } evt.properties = { } evt.properties["Telem-BoardID"] = { 32, "UINT8" } evt.properties["Telem-TaskID"] = { 64, "UINT8" } evt.properties["StringMessage"] = str SMC:PublishEvent(evt) end function ListenLoop() appRtn = coroutine.create(function() AppInit0() while true do ReportStringAsTelemetry(DoListen1Inner()) Wait(5000) end end) coroutine.resume(appRtn) end function AwaitData(timeout) SMC:Log("debug", "AwaitData timeout="..timeout) local rxd awaitTimer = SMC:NewTimer("await", timeout) rxd = coroutine.yield() awaitTimer = nil collectgarbage("collect") SMC:Log("debug", "AwaitData:"..Escape(rxd)) return rxd end function AwaitResponse(timeout) SMC:Log("debug", "AwaitResponse timeout="..timeout) local rxdTotal, rxd, busy awaitTimer = SMC:NewTimer("await", timeout) busy = true rxdTotal = "" while (awaitTimer ~= nil) and busy do rxd = coroutine.yield() if rxd ~= nil then rxdTotal = rxdTotal..rxd end if Acked(rxdTotal) then SMC:Log("debug", "AwaitResponse: not busy because total was acked") busy = false end end awaitTimer = nil collectgarbage("collect") SMC:Log("debug", "AwaitResponse:"..Escape(rxdTotal)) return rxdTotal end function Send(msg) SMC:Log("debug", "Sending "..Escape(msg)) port:Transmit(msg) end function DoSendCmd(cmdname) SMC:Log("debug", "DoSendCmd "..Escape(cmdname)) if not DoTestComms() then DoPowerOn() end Send(cmdname.."\r") local response = AwaitResponse(60000) gasynch:AppendOutput(Escape(response)) gasynch:ReportComplete() gasynch = nil end function SendCmd(cmdname) AppInit() appRtn = coroutine.create(function() DoSendCmd(cmdname) end) coroutine.resume(appRtn) end function Wait(timeout) waitTimer = SMC:NewTimer("wait", timeout) while waitTimer ~= nil do coroutine.yield() end end function SMCReportSerialData(portName,data) if (data ~= nil) and (string.len(data) ~= 0) then SMC:Log("debug", "Received "..tostring(#data).." bytes:"..Escape(data).." on "..portName) if awaitTimer ~= nil then --awaitTimer = nil --collectgarbage("collect") coroutine.resume(appRtn, data) end end end function SMCReportTimeout(timerName) SMC:Log("debug", "SMCReportTimeout timerName="..timerName) if timerName == "wait" then waitTimer = nil collectgarbage("collect") coroutine.resume(appRtn, nil) elseif timerName == "await" then awaitTimer = nil collectgarbage("collect") coroutine.resume(appRtn, nil) end SMC:Log("debug", "exiting SMCReportTimeout") end gasynch = nil appRtn = nil