634 lines
18 KiB
Lua
634 lines
18 KiB
Lua
---------------------------- SECS I / SECS II ----------------------------------
|
|
|
|
SECS_IO = require('secs_i')
|
|
struct = require('struct')
|
|
|
|
local SF_LIST = 0x00
|
|
local SF_NITEMS = 0x1F
|
|
local SF_BINARY = 0x20
|
|
local SF_BOOLEAN = 0x24
|
|
local SF_ASCII = 0x40
|
|
local SF_JIS_8 = 0x44
|
|
local SF_INT8 = 0x60
|
|
local SF_INT1 = 0x64
|
|
local SF_INT2 = 0x68
|
|
local SF_INT4 = 0x70
|
|
local SF_FLOAT8 = 0x80
|
|
local SF_FLOAT4 = 0x90
|
|
local SF_UINT8 = 0xA0
|
|
local SF_UINT1 = 0xA4
|
|
local SF_UINT2 = 0xA8
|
|
local SF_UINT4 = 0xB0
|
|
|
|
local MDLN = "DS-80B"
|
|
local SOFTREV = "MI370E"
|
|
|
|
local secsId = 0x1234
|
|
local host = false
|
|
local connected = false
|
|
|
|
local transaction_id = 1
|
|
|
|
local eq_online = 3 -- 1: online remote, 2: online local, 3: offline
|
|
local eq_status = 2 -- 1: not ready, 2: ready, 3: busy, 4: process cmpl, 5: pause, 6: complete
|
|
|
|
local onSendEventMsg = nil
|
|
local onPrimaryMsg = nil
|
|
local onSecondaryMsg = nil
|
|
local onProcessMsg = nil
|
|
|
|
local msgQueue = {}
|
|
|
|
--------------------------------------------------------------------------------------------
|
|
|
|
local function hexDumpString(buf)
|
|
for i=1,math.ceil(#buf/16) * 16 do
|
|
if (i-1) % 16 == 0 then io.stderr:write(string.format('%06X ', i-1)) end
|
|
io.stderr:write( i > #buf and ' ' or string.format('%02X ', buf:byte(i)) )
|
|
if i % 8 == 0 then io.stderr:write(' ') end
|
|
if i % 16 == 0 then io.stderr:write( buf:sub(i-16+1, i):gsub('[^%g]','.'), '\n' ) end
|
|
end
|
|
io.stderr:write("\n");
|
|
end
|
|
|
|
local function dumpData(items)
|
|
if items.list then
|
|
io.stderr:write(string.format("list: %d\n",#items))
|
|
end
|
|
for i=1, #items do
|
|
if not items.list then
|
|
if i == 1 then
|
|
io.stderr:write(string.format("type: "..items[1].."\n"));
|
|
io.stderr:write(string.format("value: "..items[2].."\n"));
|
|
end
|
|
else
|
|
dumpData(items[i])
|
|
end
|
|
end
|
|
end
|
|
|
|
local function dumpMsg(msg)
|
|
io.stderr:write("dumpMsg\n-------\n")
|
|
io.stderr:write(string.format("id: %02X\n", msg.header.secsId))
|
|
io.stderr:write(string.format("reverse:%s\n", msg.header.secsReverse))
|
|
io.stderr:write(string.format("stream: %02X\n", msg.header.secsStream))
|
|
io.stderr:write(string.format("func: %02X\n", msg.header.secsFunction))
|
|
io.stderr:write(string.format("blkn: %02X\n", msg.header.secsBlock))
|
|
io.stderr:write(string.format("sys1: %04X\n", msg.header.secsSys1))
|
|
io.stderr:write(string.format("sys2: %04X\n", msg.header.secsSys2))
|
|
if #msg > 0 then
|
|
dumpData(msg[1])
|
|
end
|
|
io.stderr:write("\n")
|
|
end
|
|
|
|
local function appendData(values,types,items)
|
|
|
|
for i=1, #items do
|
|
if items.list then
|
|
appendData(values,types,items[i])
|
|
else
|
|
if i == 1 then
|
|
table.insert(types,items[i])
|
|
else
|
|
table.insert(values,items[i])
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
local function msgData(msg)
|
|
local values = {}
|
|
local types = {}
|
|
|
|
if #msg > 0 then
|
|
appendData(values,types,msg[1])
|
|
end
|
|
|
|
return values,types;
|
|
end
|
|
|
|
local function dataSignature(items)
|
|
local sig = ""
|
|
|
|
if items.list then
|
|
sig = string.format("L%d",#items)
|
|
end
|
|
|
|
for i=1, #items do
|
|
if items.list then
|
|
sig = sig..dataSignature(items[i])
|
|
else
|
|
if i == 1 then
|
|
sig = sig..string.format(items[i]);
|
|
end
|
|
end
|
|
end
|
|
|
|
return sig
|
|
end
|
|
|
|
local function msgSignature(msg)
|
|
local sig = ""
|
|
|
|
if #msg > 0 then
|
|
sig = dataSignature(msg[1])
|
|
end
|
|
|
|
return sig
|
|
end
|
|
--[[
|
|
local function parseData(items,buf,idx,cnt)
|
|
local len ;
|
|
local code;
|
|
|
|
-- io.stderr:write(string.format("IN: %d\n",idx))
|
|
while (idx < #buf - 2) and (cnt>0) do
|
|
len = bit32.band(buf:byte(idx),0x03);
|
|
code = bit32.band(buf:byte(idx),0xFC);
|
|
-- io.stderr:write(string.format("CODE: %02X LEN: %02X\n",code,len))
|
|
len = buf:byte(idx+1);
|
|
-- io.stderr:write(string.format("DATA LEN: %02X\n",len))
|
|
cnt = cnt - 1;
|
|
if code == SF_LIST then
|
|
local list = {}
|
|
list.list = true
|
|
idx = parseData(list,buf,idx+2,len)
|
|
table.insert(items,list)
|
|
elseif code == SF_ASCII then
|
|
table.insert(items,{"A",buf:sub(idx+2,idx+1+len)})
|
|
idx = idx + len + 2;
|
|
elseif code == SF_INT1 then
|
|
table.insert(items,{"I1",struct.unpack(">b",buf:sub(idx+2))})
|
|
idx = idx + len + 1;
|
|
elseif code == SF_INT2 then
|
|
table.insert(items,{"I2",struct.unpack(">h",buf:sub(idx+2))})
|
|
idx = idx + len + 2;
|
|
elseif code == SF_INT4 then
|
|
table.insert(items,{"I4",struct.unpack(">i",buf:sub(idx+2))})
|
|
idx = idx + len + 4;
|
|
elseif code == SF_INT8 then
|
|
table.insert(items,{"I8",struct.unpack(">l",buf:sub(idx+2))})
|
|
idx = idx + len + 8;
|
|
elseif code == SF_UINT1 then
|
|
table.insert(items,{"U1",struct.unpack(">B",buf:sub(idx+2))})
|
|
idx = idx + len + 1;
|
|
elseif code == SF_UINT2 then
|
|
table.insert(items,{"U2",struct.unpack(">H",buf:sub(idx+2))})
|
|
idx = idx + len + 2;
|
|
elseif code == SF_INT4 then
|
|
table.insert(items,{"U4",struct.unpack(">I",buf:sub(idx+2))})
|
|
idx = idx + len + 4;
|
|
elseif code == SF_UINT8 then
|
|
table.insert(items,{"U8",struct.unpack(">L",buf:sub(idx+2))})
|
|
idx = idx + len + 8;
|
|
elseif code == SF_BOOLEAN then
|
|
table.insert(items,{"BOOL",struct.unpack(">B",buf:sub(idx+2))})
|
|
idx = idx + len + 1;
|
|
elseif code == SF_BINARY then
|
|
table.insert(items,{"B",struct.unpack(">B",buf:sub(idx+2))})
|
|
idx = idx + len + 1;
|
|
-- elseif TODO more formats
|
|
end
|
|
end
|
|
-- io.stderr:write(string.format("OUT: %d\n",idx))
|
|
return idx;
|
|
end
|
|
--]]
|
|
|
|
local function parseData(items,buf,idx,cnt)
|
|
local len ;
|
|
local code;
|
|
|
|
io.stderr:write(string.format("IN: %d\n",idx))
|
|
while (idx < #buf - 2) and (cnt>0) do
|
|
io.stderr:write(string.format("IDX: %d\n",idx))
|
|
num = bit32.band(buf:byte(idx),0x03);
|
|
code = bit32.band(buf:byte(idx),0xFC);
|
|
io.stderr:write(string.format("CODE: %02X LEN: %02X\n",code,num))
|
|
len = buf:byte(idx+1);
|
|
io.stderr:write(string.format("DATA LEN: %02X %03d\n",len,len))
|
|
cnt = cnt - 1;
|
|
if code == SF_LIST then
|
|
local list = {}
|
|
list.list = true
|
|
idx = parseData(list,buf,idx+2,len)
|
|
table.insert(items,list)
|
|
elseif code == SF_ASCII then
|
|
table.insert(items,{"A",buf:sub(idx+2,idx+1+len)})
|
|
idx = idx + len + num + 1;
|
|
elseif code == SF_INT1 then
|
|
table.insert(items,{"I1",struct.unpack(">b",buf:sub(idx+2))})
|
|
idx = idx + len + num + 1;
|
|
elseif code == SF_INT2 then
|
|
table.insert(items,{"I2",struct.unpack(">h",buf:sub(idx+2))})
|
|
idx = idx + len + num + 1;
|
|
elseif code == SF_INT4 then
|
|
table.insert(items,{"I4",struct.unpack(">i",buf:sub(idx+2))})
|
|
idx = idx + len + num + 1;
|
|
elseif code == SF_INT8 then
|
|
table.insert(items,{"I8",struct.unpack(">l",buf:sub(idx+2))})
|
|
idx = idx + len + num + 1;
|
|
elseif code == SF_UINT1 then
|
|
table.insert(items,{"U1",struct.unpack(">B",buf:sub(idx+2))})
|
|
idx = idx + len + num + 1;
|
|
elseif code == SF_UINT2 then
|
|
table.insert(items,{"U2",struct.unpack(">H",buf:sub(idx+2))})
|
|
idx = idx + len + num + 1;
|
|
elseif code == SF_INT4 then
|
|
table.insert(items,{"U4",struct.unpack(">I",buf:sub(idx+2))})
|
|
idx = idx + len + num + 1;
|
|
elseif code == SF_UINT8 then
|
|
table.insert(items,{"U8",struct.unpack(">L",buf:sub(idx+2))})
|
|
idx = idx + len + num + 1;
|
|
elseif code == SF_BOOLEAN then
|
|
table.insert(items,{"BOOL",struct.unpack(">B",buf:sub(idx+2))})
|
|
idx = idx + len + num + 1;
|
|
elseif code == SF_BINARY then
|
|
table.insert(items,{"B",struct.unpack(">B",buf:sub(idx+2))})
|
|
idx = idx + len + num + 1;
|
|
-- elseif TODO more formats
|
|
end
|
|
end
|
|
io.stderr:write(string.format("OUT: %d\n",idx))
|
|
return idx;
|
|
end
|
|
|
|
local function unpackHeader(b)
|
|
local h = {}
|
|
h.secsId, h.secsStream, h.secsFunction, h.secsBlock, h.secsSys1, h.secsSys2 = struct.unpack('>HBBHHH',b)
|
|
h.secsReverse = h.secsId > 0x8000;
|
|
h.secsId = bit32.band(h.secsId,0x7FFF);
|
|
h.secsWait = h.secsStream > 0x80;
|
|
h.secsStream = bit32.band(h.secsStream,0x7F);
|
|
h.secsEnd = h.secsBlock > 0x8000;
|
|
h.secsBlock = bit32.band(h.secsBlock,0x7FFF);
|
|
|
|
return h;
|
|
end
|
|
|
|
local function parseMsg(buf)
|
|
local msg = {};
|
|
local len = 0;
|
|
local code = 0;
|
|
|
|
msg.header = unpackHeader(buf);
|
|
msg.list = false;
|
|
parseData(msg,buf,11,#buf);
|
|
return msg;
|
|
end
|
|
|
|
local function packHeader(h)
|
|
return struct.pack('>HBBHHH',
|
|
( h.secsReverse and bit32.bor(h.secsId, 0x8000) or h.secsId ),
|
|
( h.secsWait and bit32.bor(h.secsStream, 0x80) or h.secsStream ),
|
|
h.secsFunction,
|
|
( h.secsEnd and bit32.bor(h.secsBlock, 0x8000) or h.secsBlock),
|
|
h.secsSys1,
|
|
h.secsSys2
|
|
)
|
|
end
|
|
|
|
local function packString(s)
|
|
return struct.pack('BB',SF_ASCII+1,#s)..s
|
|
end
|
|
|
|
local function packBinary(b)
|
|
return struct.pack('BBB',SF_BINARY+1,1,b)
|
|
end
|
|
|
|
local function packList(l)
|
|
return struct.pack('BB',SF_LIST+1,#l)..table.concat(l)
|
|
end
|
|
|
|
local function secondaryHeader(h,s,f)
|
|
h.secsId = secsId; --
|
|
h.secsReverse = not host -- false: host --> equipment
|
|
|
|
h.secsStream = s;
|
|
h.secsFunction = f; -- secondary Message SsFf
|
|
h.secsWait = false; -- no reply
|
|
|
|
h.secsBlock = 1;
|
|
h.secsEnd = true; -- last block
|
|
return packHeader(h);
|
|
end
|
|
|
|
local function primaryHeader(s,f)
|
|
-- SECS_IO.startTransaction()
|
|
|
|
local h = {}
|
|
|
|
h.secsId = secsId; --
|
|
h.secsReverse = not host -- false: host --> equipment
|
|
|
|
h.secsStream = s;
|
|
h.secsFunction = f; -- primary Message SsFf
|
|
h.secsWait = true; -- reply
|
|
|
|
h.secsBlock = 1;
|
|
h.secsEnd = true; -- last block
|
|
h.secsSys1 = 0
|
|
h.secsSys2 = transaction_id;
|
|
|
|
transaction_id = transaction_id + 1
|
|
|
|
return packHeader(h);
|
|
end
|
|
|
|
function string.pop(s)
|
|
return string.sub(s,1,1),string.sub(s,2)
|
|
end
|
|
|
|
function table.pop(t)
|
|
local i = t[1]
|
|
table.remove(t,1)
|
|
return i,t
|
|
end
|
|
|
|
local function packData(sig,items)
|
|
local buf = ""
|
|
|
|
-- print("packData sig: ",sig)
|
|
while #sig > 0 do
|
|
-- print("packData len1: ",#sig)
|
|
typ, sig = string.pop(sig)
|
|
-- print("packData len2: ",#sig,typ,sig)
|
|
if typ == "L" then
|
|
len , sig = string.pop(sig)
|
|
-- print("packData len3: ",#sig,len,sig)
|
|
buf = buf..struct.pack('BB',SF_LIST+1,len)..packData(sig,items)
|
|
-- hexDumpString(buf)
|
|
return buf
|
|
elseif typ == "A" then
|
|
item, items = table.pop(items)
|
|
buf = buf..packString(item)
|
|
elseif typ == "B" then
|
|
item, items = table.pop(items)
|
|
buf = buf..packBinary(item)
|
|
end
|
|
end
|
|
-- hexDumpString(buf)
|
|
return buf
|
|
end
|
|
|
|
local function primaryMsg(s,f,sig,items)
|
|
return primaryHeader(s,f)..packData(sig,items)
|
|
end
|
|
|
|
local function secondaryMsg(h,s,f,sig,items)
|
|
return secondaryHeader(h,s,f)..packData(sig,items)
|
|
end
|
|
|
|
-------------------------------------------------------
|
|
|
|
local function SxF0(h)
|
|
io.stderr:write("\nSECS II: SxF0\n----\n")
|
|
return secondaryHeader(h,h.secsStream,0)
|
|
end
|
|
|
|
local function S1F1()
|
|
io.stderr:write("\nSECS II: S1F1\n----\n")
|
|
return primaryHeader(1,1)
|
|
end
|
|
|
|
local function S1F2(h)
|
|
io.stderr:write("\nSECS II: S1F2\n----\n")
|
|
|
|
if host then
|
|
return secondaryMsg(h,1,2,"L0",{})
|
|
else
|
|
return secondaryMsg(h,1,2,"L2AA",{MDLN,SOFTREV})
|
|
end
|
|
end
|
|
|
|
local function S1F13()
|
|
io.stderr:write("\nSECS II: S1F13\n----\n")
|
|
|
|
if host then
|
|
return primaryMsg(1,13,"L2BL0",{0})
|
|
else
|
|
return primaryMsg(1,13,"L2BL2AA",{0,MDLN,SOFTREV})
|
|
end
|
|
end
|
|
|
|
local function S1F14(h)
|
|
io.stderr:write("\nSECS II: S1F14\n----\n")
|
|
|
|
if host then
|
|
return secondaryMsg(h,1,14,"L2BL0",{0})
|
|
else
|
|
return secondaryMsg(h,1,14,"L2BL2AA",{0,MDLN,SOFTREV})
|
|
end
|
|
end
|
|
|
|
local function S2F41(c)
|
|
|
|
io.stderr:write("\nSECS II: S2F41("..c..")\n----\n")
|
|
if host then
|
|
return onCreateMsg(2,41,c)
|
|
else
|
|
return "" -- host only command
|
|
end
|
|
|
|
end
|
|
|
|
local function S2F42(h)
|
|
io.stderr:write("\nSECS II: S2F42\n----\n")
|
|
|
|
return secondaryMsg(h,2,42,"L2BL0",{4})
|
|
end
|
|
|
|
local function S5F2(h)
|
|
io.stderr:write("\nSECS II: S5F2\n----\n")
|
|
|
|
if host then
|
|
return secondaryMsg(h,5,2,"B",{0})
|
|
end
|
|
|
|
end
|
|
|
|
local function S6F12(h)
|
|
io.stderr:write("\nSECS II: S6F12\n----\n")
|
|
|
|
if host then
|
|
return secondaryMsg(h,6,12,"B",{0})
|
|
else
|
|
return secondaryHeader(h,h.secsStream,0)
|
|
end
|
|
|
|
end
|
|
|
|
local function S6F11(e)
|
|
io.stderr:write("\nSECS II: S6F11("..e..")\n----\n")
|
|
|
|
if not host then
|
|
return onCreateMsg(6,11,e)
|
|
else
|
|
return "" -- equipment only command
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------
|
|
|
|
local function isSF(msg,s,f)
|
|
return msg.header.secsStream == s and msg.header.secsFunction == f
|
|
end
|
|
|
|
local function isSxF0(msg)
|
|
return msg.header.secsFunction == 0
|
|
end
|
|
|
|
local function isS5Fx(msg)
|
|
dumpMsg(msg)
|
|
return msg.header.secsStream == 5
|
|
end
|
|
|
|
local function isS9Fx(msg)
|
|
dumpMsg(msg)
|
|
return msg.header.secsStream == 9
|
|
end
|
|
|
|
SECS_IO.onProcessRequest (
|
|
function(buf)
|
|
local request = parseMsg(buf)
|
|
io.stderr:write("SECS II: ID: ",request.header.secsId,"\n")
|
|
local s = request.header.secsStream
|
|
local f = request.header.secsFunction
|
|
io.stderr:write("SECS II: onProcessMsg ".."S"..s.."F"..f.."\n");
|
|
|
|
--------------------------------------------------------------------
|
|
|
|
if not request.header.wait then
|
|
SECS_IO.stopTransaction()
|
|
end
|
|
|
|
local sig = msgSignature(request)
|
|
|
|
if isSF(request,1,1) then
|
|
onPrimaryMsg(1,1,"are you there received")
|
|
SECS_IO.sendMsg(S1F2(request.header))
|
|
elseif isSF(request,1,2) then
|
|
onSecondaryMsg(1,2,"are you there received akn")
|
|
elseif isSF(request,1,13) then
|
|
connected = true;
|
|
onPrimaryMsg(1,13,"establish communication request received")
|
|
--io.stderr:write(string.format("CONNECTED %d\n",request[1][1][2]));
|
|
SECS_IO.sendMsg(S1F14(request.header))
|
|
elseif isSF(request,1,14) then
|
|
connected = true;
|
|
onSecondaryMsg(1,14,"establish communication request received akn")
|
|
--io.stderr:write(string.format("CONNECTED %d\n",request[1][1][2]));
|
|
elseif isSF(request,2,41) then
|
|
--print("SECS II: Message ",#request)
|
|
onProcessMsg(request)
|
|
onPrimaryMsg(2,41,"host command received")
|
|
SECS_IO.sendMsg(S2F42(request.header))
|
|
elseif isSF(request,2,42) then
|
|
onProcessMsg(request)
|
|
onSecondaryMsg(2,42,"host command received akn")
|
|
elseif isSF(request,6,11) then
|
|
onProcessMsg(request)
|
|
onPrimaryMsg(6,11,"event report received")
|
|
SECS_IO.sendMsg(S6F12(request.header))
|
|
elseif isSF(request,6,12) then
|
|
onSecondaryMsg(6,12,"event report received akn")
|
|
elseif isS5Fx(request) then
|
|
onProcessMsg(request)
|
|
--hexDumpString(buf)
|
|
--io.stderr:write("SIG: "..sig.."\n")
|
|
onPrimaryMsg(5,request.header.secsFunction,"alarm received")
|
|
SECS_IO.sendMsg(S5F2(request.header))
|
|
elseif isS9Fx(request) then
|
|
-- no secondary message
|
|
onProcessMsg(request)
|
|
onPrimaryMsg(9,request.header.secsFunction,"error received")
|
|
else
|
|
if not isSxF0(request) then
|
|
onPrimaryMsg(0,0,"abort transaction send")
|
|
SECS_IO.sendMsg(SxF0(request.header))
|
|
end
|
|
end
|
|
end
|
|
)
|
|
|
|
local function dequeMsg()
|
|
local m = msgQueue[1]
|
|
table.remove(msgQueue,1)
|
|
return m
|
|
end
|
|
|
|
local function queueMsg(msg)
|
|
io.stderr:write("\n====\nSECS II: queueMsg\n====\n");
|
|
table.insert(msgQueue,msg)
|
|
end
|
|
|
|
local function poll()
|
|
if not SECS_IO.isPending() and #msgQueue > 0 then
|
|
-- if not pending and #msgQueue > 0 then
|
|
io.stderr:write("\n====\nSECS II: dequeueMsg\n====\n");
|
|
SECS_IO.startTransaction()
|
|
SECS_IO.sendMsg(dequeMsg())
|
|
end
|
|
SECS_IO.poll()
|
|
end;
|
|
|
|
local function setHost(mode)
|
|
host = mode
|
|
end
|
|
|
|
local function setId(id)
|
|
secsId = id
|
|
end
|
|
|
|
local function begin(device, baud)
|
|
SECS_IO.begin(device,baud)
|
|
io.stderr:write("begin\n")
|
|
SECS_IO.stopTransaction()
|
|
end
|
|
|
|
return {
|
|
begin = begin;
|
|
sendMsg = sendMsg;
|
|
dumpMsg = dumpMsg;
|
|
msgSignature = msgSignature;
|
|
msgData = msgData;
|
|
|
|
--------- DEBUG only ----------------
|
|
-- dataSignature = dataSignature;
|
|
-- parseMsg = parseMsg;
|
|
-- dumpData = dumpData;
|
|
-- parseData = parseData;
|
|
|
|
-- packString = packString;
|
|
-- packBinary = packBinary;
|
|
-- packList = packList;
|
|
-- packData = packData;
|
|
--------------------------------------
|
|
|
|
primaryMsg = primaryMsg;
|
|
secondaryMsg = secondaryMsg;
|
|
|
|
setId = setId;
|
|
setHost = setHost;
|
|
isHost = function() return host end;
|
|
|
|
isConnected = function() return connected end;
|
|
isReading = function() return SECS_IO.isReading() end;
|
|
isWriting = function() return SECS_IO.isWriting() end;
|
|
isPending = function() return SECS_IO.isPending() end;
|
|
|
|
poll = poll;
|
|
|
|
sendS1F13 = function() queueMsg(S1F13()) end;
|
|
sendS1F1 = function() queueMsg(S1F1()) end;
|
|
sendS2F41 = function(c) queueMsg(S2F41(c)) end;
|
|
sendS6F11 = function(e) queueMsg(S6F11(e)) end;
|
|
|
|
onCreateMsg = function(cb) onCreateMsg = cb end;
|
|
onPrimaryMsg = function(cb) onPrimaryMsg = cb end;
|
|
onSecondaryMsg = function(cb) onSecondaryMsg = cb end;
|
|
onProcessMsg = function(cb) onProcessMsg = cb end;
|
|
}
|