CDT規(guī)約轉(zhuǎn)Modbus協(xié)議
深圳市一天廣聯(lián)科技有限公司
參考價(jià): | 面議 |
- 產(chǎn)品型號(hào)
- 品牌
- 其他 廠商性質(zhì)
- 所在地
訪問(wèn)次數(shù):1078更新時(shí)間:2021-07-27 15:22:11
此方案符合污染物在線監(jiān)控(監(jiān)測(cè))系統(tǒng)數(shù)據(jù)傳輸標(biāo)準(zhǔn)HJ212-2017規(guī)范。HJ212環(huán)保協(xié)議用于用于向數(shù)據(jù)中心上傳鍋爐、水質(zhì)等各類環(huán)保數(shù)據(jù),采用TCP/IP Client傳輸字符串的方式進(jìn)行通訊。
此方案下,AiMaster網(wǎng)關(guān)作為Server,接受HJ212的客戶端連接,并接收其上傳的數(shù)據(jù)。然后利用自身強(qiáng)大字符串處理功能,解析出HJ212數(shù)據(jù),并放入Modbus寄存器提供給上位機(jī)讀取。上位機(jī)可以是各類組態(tài)軟件、PLC、觸摸屏等等。
也可以將解析出的HJ212數(shù)據(jù)轉(zhuǎn)換為任何其他協(xié)議,例如:通過(guò)mqtt協(xié)議傳輸環(huán)保數(shù)據(jù)至云服務(wù)器等。
具體代碼示例如下,此代碼經(jīng)過(guò)實(shí)際項(xiàng)目驗(yàn)證,可以直接下載到AiMaster網(wǎng)關(guān)運(yùn)行。
--print("此例子演示啟動(dòng)可支持10個(gè)客戶端的TCP/IP Server,并通過(guò)消息模式讀取客戶端string數(shù)據(jù)")
--啟動(dòng)TCP/IP Server,端口6003,消息模式,支持10個(gè)客戶端。
--hj212_3020命令對(duì)應(yīng)的Modbus寄存器地址
mb3020addr=0000
--hj212_2051命令對(duì)應(yīng)的Modbus寄存器地址
mb2051addr=1000
--hj212_2061命令對(duì)應(yīng)的Modbus寄存器地址
mb2061addr=2000
--啟動(dòng)智能Server,端口6003,超時(shí)20秒,支持10個(gè)客戶端,消息方式處理數(shù)據(jù)。
res=lns.startserver(0,6003,20,10,1,1);
local itemlst={}
local maindata={}
local hj212value={}
local keyvallst={}
local itemdata=""
local headdata=""
local rawstr=""
local alldata=""
local rechj212state=0
local sendback=0
local arraydata={0,0}
local mbdatacnt=0
recconn=0
while(true) do
itemlst={}
maindata={}
hj212value={}
keyvallst={}
itemdata=""
headdata=""
rawstr=""
alldata=""
rechj212state=0
sendback=0
recconn=0
arraydata={0,0}
mbdatacnt=0
while(true) do
--網(wǎng)絡(luò)處理循環(huán)
sendback=0
recconn=lns.srvwaitrec(0);
-- print(string.format("
收到數(shù)據(jù)的HJ212客戶端連接索引:%d",recconn));
rawstr=lns.srvreadrecstr(0,recconn)
-- print(string.format("
收到HJ212數(shù)據(jù):%s",rawstr));
while(true) do
--HJ212協(xié)議處理循環(huán)
--收到字符串為nil,超時(shí)錯(cuò)誤。
if (rawstr==nil) then
rechj212state=0
break
end
--分割主數(shù)據(jù),報(bào)文頭之后的&&...&&之間為主數(shù)據(jù)
itemdata,headdata,crcdata,alldata=splitmaindata(rawstr)
---數(shù)據(jù)錯(cuò)誤退出循環(huán),重新接收。
if (itemdata==nil) or (headdata==nil) then
rechj212state=0
break
end
--判斷報(bào)文頭212標(biāo)識(shí),取命令碼。
cncode=getheadinfo(headdata)
if cncode==nil then
rechj212state=0
break;
end;
print(cncode)
rechj212state=1
break;
end
if (rechj212state==0) then
--如果接收的數(shù)據(jù)錯(cuò)誤,繼續(xù)網(wǎng)絡(luò)循環(huán),
lns.srvresp(0)
else
--協(xié)議接收正常進(jìn)入處理階段
break
end
end
while(true) do
itemdata=itemdata..";"
--分割項(xiàng)目數(shù)據(jù)列表,數(shù)據(jù)存儲(chǔ)在itemlist中
itemlst=splititemdata(itemdata)
if (itemlst==nil) then break end
--分割出HJ212的數(shù)據(jù),格式為key=value,例如01-Avg=2.065
keyvallst=split212value(itemlst)
if (keyvallst==nil) then break end
if (cncode==2061) then
--處理2061命令
-- print("conv 2061")
convres,hj212datatime=convtovmb_2061(keyvallst)
-- print(hj212datatime)
print(convres)
sendback=1
end
if (cncode==2051) then
--處理2051命令
-- print("conv 2051")
convres,hj212datatime=convtovmb_2051(keyvallst)
-- print(hj212datatime)
print(convres)
sendback=1
end
if (cncode==3020) then
--處理3020命令
-- print("conv 3020")
convres,hj212datatime=convtovmb_3020(keyvallst)
print(convres)
sendback=1
end
break;
end
if (sendback==1) then
--返回HJ212應(yīng)答報(bào)文
responstr=buildsenddata(headdata,hj212datatime)
-- print(responstr)
if (responstr~=nil) then
--向客戶端返回?cái)?shù)據(jù),客戶端索引reconn也是可以寫數(shù)據(jù)的。
res=lns.srvwritestr(0,recconn,responstr)
end
end
--此客戶端處理完畢
lns.srvresp(0);
end
函數(shù)定義:
local function getdelistrdata(rawstr,deli)
odata={}
zzbds="(.-)"..deli
if (rawstr==nil) then return nil end
--string.gmatch是支持正則表達(dá)式的字符串分割函數(shù),更詳細(xì)的使用方法請(qǐng)參考互聯(lián)網(wǎng)上的相關(guān)教程。
--正則表達(dá)式"(.-),"中的.表示匹配任何字符,-表示短匹配,,為匹配分割符。
for w in string.gmatch(rawstr,zzbds) do
--w輸出由,分割的字符序列。
table.insert(odata,w);
end
return odata
end
local function splitmaindata(rawstr)
--分割主數(shù)據(jù),報(bào)文頭之后的&&...&&之間為主數(shù)據(jù)
if ((string.byte(string.sub(rawstr,string.len(rawstr)-1)))~=13) or
((string.byte(string.sub(rawstr,string.len(rawstr))))~=10) then
return nil
end
s,e=string.find(rawstr, "&&")
if (s==nil) or (e==nil) then return nil end
--項(xiàng)目數(shù)據(jù)
itemdata=string.sub(rawstr,e+1,string.len(rawstr)-8)
--報(bào)文頭數(shù)據(jù)
headdata=string.sub(rawstr,1,s-1)
--crc字節(jié)
crcdata=string.sub(rawstr,string.len(rawstr)-5,string.len(rawstr)-2)
--所有有效數(shù)據(jù)
alldata=string.sub(rawstr,7,string.len(rawstr)-6)
return itemdata,headdata,crcdata,alldata
end
local function getheadinfo(rawheaddata)
--判斷報(bào)文頭212標(biāo)識(shí)
if string.sub(rawheaddata,1,2)~="##" then return nil end
headvalue=getdelistrdata(rawheaddata,";")
--取命令碼CN
key,value = string.match(headvalue[2],"(.-)=(.+)")
if (value==nil) or (key~="CN") then
return nil
end
--口令
key,pwd = string.match(headvalue[3],"(.-)=(.+)")
if (value==nil) or (key~="PW") then
return nil
end
--設(shè)備號(hào)號(hào)碼
key,mn = string.match(headvalue[4],"(.-)=(.+)")
if (value==nil) or (key~="MN") then
return nil
end
--返回命令碼CN
return tonumber(value)
end
local function splititemdata(rawstr)
--分割項(xiàng)目數(shù)據(jù)列表
return getdelistrdata(rawstr,";")
end
local function split212value(itemlst)
--分割項(xiàng)目數(shù)據(jù)
--輸入數(shù)據(jù)
--DataTime=20190704190046
--B02-Cou=81219.430
--01-Min=1.063, 01-Max=16.938, 01-Avg=2.065, 01-Cou=0.168, 01-ZsMin=0.771, 01-ZsMax=13.231, 01-ZsAvg=1.564, 01-ZsCou=0.127
local odata={}
for i=1,#itemlst do
--分割以,分割的key=value數(shù)據(jù)
itemlst[i]=itemlst[i]..","
keyvalelst=getdelistrdata(itemlst[i],",")
for i2=1,#keyvalelst do
-- print(keyvalelst[i2])
table.insert(odata,keyvalelst[i2]);
end
end
-- print(#odata)
return odata
end
local function convhj212tovmb(keyvaluelst,convmbregaddr)
--轉(zhuǎn)換HJ212數(shù)據(jù)至Modbus
local fdata
local keyandvalue
local lhj212value={}
local i2=0
local lhj212datatime=""
--DataTime=20190704190046
--B02-Cou=81219.430
--01-Min=1.063, 01-Max=16.938, 01-Avg=2.065, 01-Cou=0.168, 01-ZsMin=0.771, 01-ZsMax=13.231, 01-ZsAvg=1.564, 01-ZsCou=0.127
--轉(zhuǎn)換從第二個(gè)元素開始,個(gè)元素是時(shí)間和日期
key,lhj212datatime = string.match(keyvaluelst[1],"(.-)=(.+)")
for i=2,#keyvaluelst do
-- print(keyvaluelst[i])
--用正則表達(dá)式解析出key=value數(shù)據(jù)。
key,value = string.match(keyvaluelst[i],"(.-)=(.+)")
if (value==nil) then
return nil
end
--將字符串轉(zhuǎn)換為float數(shù)據(jù)
fdata=tonumber(value)
if (fdata==nil) then
return nil
end
--輸出至數(shù)組
table.insert(lhj212value,fdata);
end
for i=1,#lhj212value do
-- print(lhj212value[i])
-- print(convmbregaddr+((i-1)*2))
--將數(shù)組的值,放入Modbus寄存器,以供上位機(jī)讀取。
lib_vmb.setvaluefc3(convmbregaddr+((i-1)*2),1,4,lhj212value[i]);
end
return #lhj212value,lhj212datatime
end
local function convtovmb_2051(keyvaluelst)
return convhj212tovmb(keyvaluelst,mb2051addr)
end
local function convtovmb_2061(keyvaluelst)
return convhj212tovmb(keyvaluelst,mb2061addr)
end
local function convtovmb_3020(keyvaluelst)
return convhj212tovmb(keyvaluelst,mb3020addr)
end
local function strtoarray (idata,odata)
for i=1,#idata do
table.insert(odata,string.byte(string.sub(idata,i,i)));
end
return #odata
end
--****************************************************************************************
--函 數(shù): CRC16_Checkout
--描 述: CRC16 循環(huán)冗余校驗(yàn)算法。
--參 數(shù) 一: *puchMsg:需要校驗(yàn)的字符串指針
--參 數(shù) 二: usDataLen:要校驗(yàn)的字符串長(zhǎng)度
--返 回 值: 返回CRC16 校驗(yàn)碼
--****************************************************************************************/
local function calhj212crc(idata)
crc_reg = 0xFFFF
larraydata={0,0}
for i=1,#idata do
-- crc_reg = (crc_reg>>8) ^ puchMsg[i];
--右移8位
crc_reg =lib_bit.shr(crc_reg,8,1)
crc_reg =lib_bit.lxor(crc_reg,idata[i],1)
-- syslib.osresetwdog()
for j=0,7 do
-- check = crc_reg & 0x0001;
check=lib_bit.land(crc_reg ,0x0001,1)
-- crc_reg >>= 1;
crc_reg=lib_bit.shr(crc_reg,1,1)
--crc_reg=lib_bit.lxor(crc_reg,0xA001,1)
if(check==0x0001) then
-- crc_reg ^= 0xA001;
crc_reg=lib_bit.lxor(crc_reg,0xA001,1)
end
end
end
calc.wordtoarray(larraydata,crc_reg,1,0);
crcstr=string.format("%2x%2x",larraydata[1],larraydata[2])
return crcstr,crc_reg;
end
local function checkhj212crc(idata,icrcdata)
--檢查212協(xié)議的crc校驗(yàn)是否正確
--輸入的是212
--轉(zhuǎn)換字符串為ANSI數(shù)組
checkarray={}
--idata="QN=20160801085000001;ST=91;CN=9014;PW=123456;MN=010000A8900016F000169DC0;Flag=4;CP=&&&&"
--idata="ST=31;CN=3020;PW=123456;MN=WNZK03;Flag=1;CP=&&DataTime=20190704195800;T10-Info=894.427;T11-Info=966.605;T12-Info=900.502;T20-Info=1011.820;T21-Info=1008.292;T22-Info=999.574;T30-Info=1031.684;T31-Info=1052.480;T32-Info=1035.924&&"
strtoarray(idata,checkarray);
crcstr,crcword=calhj212crc(checkarray)
--print(crcstr)
--if (crcstr==icrcdata) then return 1 end
return 1
end
local function buildsenddata(headdata,idatetime)
--生成返回的應(yīng)答數(shù)據(jù)
sendtobackdatafmt="QN=%s;ST=91;CN=9014;PW=%s;MN=%s;Flag=4;CP=&&&&"
headlst={}
headlst=getdelistrdata(headdata,";")
--口令
key,pwd = string.match(headlst[3],"(.-)=(.+)")
--設(shè)備號(hào)號(hào)碼
key,mn = string.match(headlst[4],"(.-)=(.+)")
if (idatetime==nil) or (pwd==nil) or (mn==nil) then return nil end
respdata=string.format(sendtobackdatafmt,idatetime,pwd,mn);
resplen=string.len(respdata)
senddata=string.format("##%04d%s
",resplen,respdata)
return senddata
end