目次
前
次
modbusのLRC計算(Python)
PLC(Programmable Logic Circuit)の通信で利用される
modbusに関する誤り検出に使う符号を求めるスクリプト
を作成してみました。
modbusのフレームにASCIIに関する規定は、以下。
具体的には。次のように1フレームが構成されます。
':' '01' '06' 'F9' '\r' '\n'
よく見ると、先頭とCR、LFを除いた2バイトが
必要なデータとわかります。
modbusでフレームをASCIIで表現すると、以下。
「:」で始まり、CR、LFで1レコードを終えます。
「:」とCRで挟んだ16進数で、送信するデータを
表現。CRのすぐ前の16進数2文字が、LRCです。
フレームを生成するPythonの関数を定義してみました。
def makeSData(x):
# convert list
xx = x
if isinstance(x,int) :
xx = [x]
# initialize
result = []
# header
result.append( ':' )
# make ASCII and calculate sum
sum = 0
for e in xx :
sum += e
result.append( getAscii2(e) )
# generate LRC
lrc = calc2compliment(sum)
# add LRC
result.append( getAscii2( lrc ) )
# add CR/LF
result.append( '\r' )
result.append( '\n' )
#
return result
この関数を利用すると、パラメータを与えることで
1フレームを生成します。
関数に、数値か数値を要素とするリストを渡すと
1フレーム分のリストを生成しています。
リストか数値だけかは、組込み関数isinstanceを
使って判定。
数値だけがパラメータで与えられたなら、リストにし
後の処理が単純になるようにしました。
LRCは、次のアルゴリズムで求めます。
Address、Function、Dataをそれぞれ8ビットの16進数
数値とみなして、加算。加算結果の2の補数を求めて
下位8ビットをLRCとする。
関数makeSDataのパラメータは、数値かリストで
与えられ、内部でリストになっているのでリスト
の要素の数値を2文字の数字に変換します。
この変換は、次の関数で実現。
def getAsc(x):
mycode = '0123456789ABCDEF'
if x > 15 :
result = '0'
else :
result = mycode[x]
#
return result
def getAscii2(x):
# separate
q = (x >> 4) & 15
r = x & 15
# concatenate
result = getAsc(q)
result += getAsc(r)
#
return result
関数getAscをプリミティブにし、関数getAscii2を
ラッパーにして対応しました。
LRCを計算するので、3要素(Address Function Data)を
すべて加算しています。
加算結果から、LRCを算出するために2の補数を
計算する関数を定義。
def calc2compliment(x):
# 1's complement
result = x ^ 0xffff
# 2's complement
result += 1
# get 8 bits
result &= 0xff
#
return result
LRCが求められたなら、Dataの後において、CR、LFを
追加します。
与えられたフレームの中にある3要素(Address Function Data)が
正しく伝送されてきたのかを判定するためにFRCを使います。
その判定のための関数を用意します。
フレームの中の3要素(Address Function Data)から
仮のLRCを算出し、フレームに埋め込まれているLRCと
比較して、一致するか異なるかを判定すればよいはず。
判定に使う関数は、次のように定義。
フレームの中の3要素(Address Function Data)から
仮のLRCを算出し、フレームに埋め込まれているLRCと
比較して、一致するか異なるかを判定すればよいはず。
3要素から仮のLRCを計算することとフレームに
埋められているLRCを取り出すのは、簡単。
一致すればTrueを返し、異なればFalseを返せば
よいと考えました。
def is_lrc_ok(x) :
# get LRC
xlen = len(x)
lrc = getHex2( x[xlen-3] )
# sum
sum = 0
for e in x[1:xlen-3] :
sum += getHex2( e )
# generate lrc
mylrc = calc2compliment(sum)
# default
result = False
if lrc == mylrc :
result = True
#
return result
CRの前に、LRCが要素として配置されているので
フレーム内のLRCを取り出しておきます。
スライスを使い、リストの中から3要素を取得して
加算して合計を求め、2の補数に変換。
2つのLRCが一致したなら、Trueとしています。
関数is_lrc_okの中で、数値の加算が必要になるため
数字から数値へ変換する関数を用意。
def getHex(x) :
# default
result = 0
xx = ord(x)
if ord('0') <= xx and xx <= ord('9') :
result = ord(x) - ord('0')
if ord('A') <= xx and xx <= ord('F') :
result = ord(x) - ord('A') + 10
if ord('a') <= xx and xx <= ord('f') :
result = ord(x) - ord('a') + 10
#
return result
def getHex2(x) :
# calculate
result = getHex( x[0] )
result *= 16
result += getHex( x[1] )
#
return result
数字1文字(0から9、AからF)を数値に変換する
関数getHexを定義し、そのラッパーに関数getHex2を
用意して、わかりやすくしておきます。
次のスクリプトで、動作をテストしました。
xx = [1,2]
yy = makeSData( xx )
print( xx , makeSData( xx ) )
print( is_lrc_ok( yy ) )
xx = [15,14]
yy = makeSData( xx )
print( xx , makeSData( xx ) )
print( is_lrc_ok( yy ) )
yy = makeSData( [1,6] )
print( yy , is_lrc_ok( yy ) )
結果は、次のようになります。
これで、定義した関数が問題ないことがわかり
アルゴリズムとしてのシーケンスが妥当だと
理解できました。
目次
前
次