#%TITLE% MODBUS.MAC
#%NAME%
# Macro functions to access slave MODBUS or MODBUS/TCP devices.
#
#%CATEGORY% Generic I/O
#
#%DESCRIPTION%
# This macro set provides functions to access remote devices using MODBUS
# protocol over serial lines or MODBUS/TCP over TCP/IP.%BR%
# %BR%
# The following MODBUS functions are implemented:
# %UL%%UL%
# %LI% FC1: Read Coils
# %LI% FC2: Read Input Discretes
# %LI% FC3: Read Multiple Registers
# %LI% FC4: Read Input Registers
# %LI% FC5: Write Coil
# %LI% FC6: Write Single Register
# %LI% FC7: Read Exception Status
# %LI% FC11: Get Comm Event Counters
# %LI% FC15: Force Multiple Coils
# %LI% FC16: Write Multiple Registers
# %XUL%%XUL%
#
# The remote devices must behave as MODBUS slave nodes and have to be
# register with the %B%modb_addnode()%B% macro function before being
# accessed. This function returns a node number that must be used as the
# first parameter of the all the macro functions used to exchange data with
# the remote devices.%BR%
# Many of the input/output functions use an array to transfer data. This
# array can be a data array or an associative array. In the case of using
# a data array, the user is responsible of providing an array of the correct
# size to hold the data.%BR%
# %BR%
# When an error happens during the execution of a MODBUS function or an
# exception is received from a slave node, the corresponding error or
# exception code is stored in the global variable MODB_ERR if it exists and
# the macro function returns the negative value -MODB_ERR.
# An error message is printed on the output terminal unless MODB_ERR is
# previously set to -1.
# If the function completes succesfully, MODB_ERR is set to 0.%BR%
# %BR%
# The error codes are the following:
# %UL%%UL%
# %LI% 1001 - Bad device or interface
# %LI% 1002 - Bad address
# %LI% 1003 - Bad data parameters
# %LI% 1004 - Not a registered slave node
# %LI% 1005 - No answer from slave node
# %LI% 1006 - Bad answer from slave node
# %LI% 1007 - Function mismatch
# %XUL%%XUL%
# The most frequent MODBUS exception codes are:
# %UL%%UL%
# %LI% 1 - Illegal Function
# %LI% 2 - Illegal Data Address
# %LI% 3 - Illegal Data Value
# %XUL%%XUL%
#
#%EXAMPLE%
#
# %B%myslave = modb_addnode(3, 47)%B% %BR%
# %B%modb_write_reg(myslave, 0x100, 0xFFFF)%B% %BR%
# This writes the value 0xFFFF in the register 0x100 of the slave with
# MODBUS address 47 that is connected to the %B%spec%B% serial
# interface 3.%BR%
# %B%modb_addnode("id33wagoeh2")%B% %BR%
# This macro checks the connection with the MODBUS/TCP slave id33wagoeh2 and
# adds it to the internal node list.
#
#%UU% (<device>, <maddr> [, <name> [, <flags>]])
#%MDESC%
# Tries to connect to a MODBUS slave node. If the node is found, the macro
# function appends it to the internal list and returns a unique node number
# that must be used to access the slave with this macro set.%BR%
#
# In the case of slave nodes interfaced by serial line connection, the
# parameter <device> must be either the number of a serial line interface
# or an ESRF device name. The parameter <maddr> is required and must
# correspond to the MODBUS address of the node.%BR%
# In the case of a MODBUS/TCP connection, <device> must be the network name
# or IP address of the slave node and the address <maddr> may be meaningless,
# as it is usually ignored by the slave.%BR%
#
# If there is an error, the macro returns -MODB_ERR, and the node is not
# added to the internal list.%BR%
#
def modb_addnode(device, maddr, name, flags) '{
global MODBUS[]
ubyte array MODBFRAME[512]
local comm answ data[] node
if (maddr < 0 || maddr > 255) {
if (MODB_ERR != -1)
printf(" MODBUS: Bad address (%d).\n", address)
return(-(MODB_ERR = 1002))
}
if (device + 0 == device) {
comm = "serial"
answ = ser_par(device, "timeout")
} else if (index(device, "/") != 0) {
comm = "esrf"
answ = esrf_io(device, "DevState")
} else {
if (!flags && modb_ipcheck(device) < 0) {
printf(" MODBUS: Bad IP network (%s).\n", device)
return(-(MODB_ERR = 1002))
}
comm = "tcp"
MODBUS[0]["tcpport"] = sprintf("%s:502", device)
answ = sock_par(MODBUS[0]["tcpport"], "connect")? 0 : -1
}
if (answ < 0) {
if (MODB_ERR != -1)
printf(" MODBUS: Bad device or interface (%s).\n", device)
return(-(MODB_ERR = 1001))
}
MODBUS[0]["comm"] = comm
MODBUS[0]["dev"] = device
MODBUS[0]["addr"] = ++maddr
modb_get_eventcnt(0, data)
MODBUS[0]["addr"] = 0
if (MODB_ERR) {
return(-MODB_ERR)
}
for (node = 1; node <= MODBUS[0]["n_nodes"]; node++) {
if ((name != "" && MODBUS[node] == name) || \
(MODBUS[node]["dev"] == device && \
(comm == "tcp" || MODBUS[node]["addr"] == maddr))) {
if (MODBUS[node] == "")
printf("Replacing unamed MODBUS node by %s:%d\n", device, maddr-1)
else if (MODBUS[node] != name)
printf("Replacing MODBUS node \'%s\' by \'%s\' at %s:%d\n", \
MODBUS[node], name, device, maddr-1)
break;
}
}
if (node > MODBUS[0]["n_nodes"])
MODBUS[0]["n_nodes"] = node
MODBUS[node] = name
MODBUS[node]["comm"] = comm
MODBUS[node]["dev"] = device
MODBUS[node]["addr"] = maddr
if (comm = "tcp")
MODBUS[node]["tcpport"] = MODBUS[0]["tcpport"]
MODB_ERR = 0
return(node)
}'
def modb_ipcheck(device) '{
local subnetwork
if ((subnet = get_subnet("`hostname`")) > 0 && \
subnet == get_subnet(device))
return(1)
else {
return(-1)
}
}'
def get_subnet(computer) '{
local comm aux aux0 aux1 aux2
comm = "nslookup " computer " | grep Address: | tail -1"
unix(comm, aux)
if (sscanf(aux, "%s 160.103.%d.%d", aux0, aux1, aux2) == 3)
return(aux1)
else
return(-1)
}'
def modb_is_tcp(device) '{
local nnodes i
nnodes = MODBUS[0]["n_nodes"]
for (i = 1; i <= nnodes; i++) {
if (MODBUS[i]["dev"] == device && MODBUS[i]["comm"] == "tcp")
return(i)
}
return(-1)
}'
#%UU%
#%MDESC%
# Displays the list of the current defined MODBUS nodes.%BR%
#
def modbshow '{
local nnodes i
nnodes = MODBUS[0]["n_nodes"]
if (!nnodes) {
print "No MODBUS nodes currently defined."
exit
}
print "Node Name Addr Interface\n"
print "---------------------------------------------\n"
for (i = 1; i <= nnodes; i++) {
printf("%3d - %-15s %3d ", i, MODBUS[i], MODBUS[i]["addr"] - 1)
if (MODBUS[i]["comm"] == "tcp") {
print "TCP: ", MODBUS[i]["dev"]
} else {
print "SERIAL: ", MODBUS[i]["dev"]
}
}
}'
#%UU% (<node>, <daddr>, <ncoils>, <data>)
#%MDESC%
# This macro executes the function FC1:Read Coils in a slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_addnode()%B%.
# The parameters <daddr> and <ncoils> contain the address of the first bit
# (coil) and the total number of bits (coils) to read.%BR%
# If no error happens, the macro function returns the number of bits actually
# read from the slave and stored in the first positions of the array <data>.
# Each valid position of <data> contains one bit with a value of 0 or 1.
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_read_coils(node, daddr, ncoils, data, opts) '{
modb__debug_start
return(modb__read(node, daddr, ncoils, data, opts, 1))
}'
#%UU% (<node>, <daddr>, <ninp>, <data>)
#%MDESC%
# This macro executes the function FC2:Read Input Discretes in a
# slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_addnode()%B%.
# The parameters <daddr> and <ninp> contain the address of first input bit
# and the total number of bits to read.%BR%
# If no error happens, the macro function returns the number of bits actually
# read from the slave and stored in the first positions of the array <data>.
# Each valid position of <data> contains one bit with a value of 0 or 1.
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_read_inputs(node, daddr, ninp, data, opts) '{
modb__debug_start
return(modb__read(node, daddr, ninp, data, opts, 2))
}'
#%UU% (<node>, <daddr>, <nreg>, <data>)
#%MDESC%
# This macro executes the function FC3:Read Multiple Registers in a
# slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_addnode()%B%.
# The parameters <daddr> and <nreg> contain the address of first input
# register (word) and the total number of registers (words) to read.%BR%
# If no error happens, the macro function returns the number of words actually
# read from the slave and stored in the first positions of the array <data>.
# Each valid position of <data> contains a 16-bit word.
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_read_multregs(node, daddr, nreg, data, opts) '{
modb__debug_start
return(modb__read(node, daddr, nreg, data, opts, 3))
}'
#%UU% (<node>, <daddr>, <nreg>, <data>)
#%MDESC%
# This macro executes the function FC4:Read Input Registers in a
# slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_addnode()%B%.
# The parameters <daddr> and <nreg> contain the address of first input
# register (word) and the total number of registers (words) to read.%BR%
# If no error happens, the macro function returns the number of words actually
# read from the slave and stored in the first positions of the array <data>.
# Each valid position of <data> contains a 16-bit word.
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_read_inpregs(node, daddr, nreg, data, opts) '{
modb__debug_start
return(modb__read(node, daddr, nreg, data, opts, 4))
}'
#%IU% (<node>, <daddr>, <nitems>, <data>, <function>)
#%MDESC%
# Executes any read data function in a slave MODBUS node.%BR%
#
def modb__read(node, daddr, nitems, data, opts, funct) '{
local err
if (err = modb__checkpar(nitems, data))
return(err)
if (!(opts & 0x02)) {
if (err = modb__frame(node, funct, daddr, nitems))
return(err)
if (err = modb__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb__get(node, funct))
return(err)
modb__data(data, nitems)
MODB_ERR = 0
modb__debug
return(nitems)
}
MODB_ERR = 0
modb__debug
return(0)
}'
#%IU% (<data>, <nitems>)
#%MDESC%
# Extracts <nitems> data from the binary frame in MODBFRAME and stores them
# in the array <data>.%BR%
#
def modb__data(data, nitems) '{
local funct i j aux
modb__debug "modb__data"
funct = MODBFRAME[7]
j = 9
if (funct == 1 || funct == 2) {
for (i = 0; i < nitems; i++) {
if ((i % 8) == 0)
aux = MODBFRAME[j++]
data[i] = aux & 0x01
aux >>= 1
}
} else if (funct == 3 || funct == 4) {
for (i = 0; i < nitems; i++) {
aux = MODBFRAME[j++] << 8
aux |= MODBFRAME[j++]
data[i] = aux
}
}
}'
#%UU% (<node>, <daddr>, <value>)
#%MDESC%
# This macro executes the function FC5:Write Coil in a slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_addnode()%B%.
# The parameter <daddr> contains the address of the bit (coil) to access and
# <value> contains the logical value (0 or 1) to write.%BR%
# If an error happens, the macro returns -MODB_ERR (see error codes),
# otherwise 0.%BR%
#
def modb_write_coil(node, daddr, value, opts) '{
local err
modb__debug_start
if (!(opts & 0x02)) {
if (err = modb__frame(node, 5, daddr, value? 0xFF00 : 0x0000))
return(err)
if (err = modb__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb__get(node, 5))
return(err)
}
MODB_ERR = 0
modb__debug
return(0)
}'
#%UU% (<node>, <daddr>, <value>)
#%MDESC%
# This macro executes the function FC6:Write Single Register in a
# slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_addnode()%B%.
# The parameter <daddr> contains the address of the word (register) to access
# and <value> contains the 16-bit value.%BR%
# If an error happens, the macro returns -MODB_ERR (see error codes),
# otherwise 0.%BR%
#
def modb_write_reg(node, daddr, value, opts) '{
local err
modb__debug_start
if (!(opts & 0x02)) {
if (err = modb__frame(node, 6, daddr, value))
return(err)
if (err = modb__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb__get(node, 6))
return(err)
}
MODB_ERR = 0
modb__debug
return(0)
}'
#%UU% (<node>, <data>)
#%MDESC%
# This macro executes the function FC7:Read Exception Status in a
# slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_addnode()%B%.
# The 8-bit status is returned in the first element of the array <data>.%BR%
# If an error happens, the macro returns -MODB_ERR (see error codes),
# otherwise 1.%BR%
#
def modb_read_status(node, data, opts) '{
local err
modb__debug_start
if (!(opts & 0x02)) {
if (err = modb__frame(node, 7))
return(err)
if (err = modb__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb__get(node, 7))
return(err)
data[0] = MODBFRAME[8]
MODB_ERR = 0
modb__debug
return(1)
}
MODB_ERR = 0
modb__debug
return(0)
}'
#%UU% (<node>, <data>)
#%MDESC%
# This macro executes the function FC11:Get Comm Event Counter in a
# slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_addnode()%B%.
# If no error happens, the node status word and the event count are stored
# in the first two positions of the array <data> and the macro function
# returns 2.
# If an error happens, the macro returns -MODB_ERR (see error codes).%BR%
#
def modb_get_eventcnt(node, data, opts) '{
local err
modb__debug_start
if (err = modb__checkpar(2, data))
return(err)
if (!(opts & 0x02)) {
if (err = modb__frame(node, 11))
return(err)
if (err = modb__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb__get(node, 11))
return(err)
data[0] = (MODBFRAME[8] << 8) | MODBFRAME[9]
data[1] = (MODBFRAME[10] << 8) | MODBFRAME[11]
MODB_ERR = 0
return(2)
}
MODB_ERR = 0
modb__debug
return(0)
}'
#%UU% (<node>, <daddr>, <ncoils>, <data>)
#%MDESC%
# This macro executes the function FC15:Force Multiple Coils in a
# slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_addnode()%B%.
# The parameters <daddr> and <ncoils> contain the address of first bit (coil)
# and the total number of bits (coils) to write.
# The first positions of the array <data> cointain the logical values to
# write. Each valid position of <data> must contain only one bit with a value
# of 0 or 1.%BR%
# If an error happens, the macro returns -MODB_ERR (see error codes),
# otherwise 0.%BR%
#
def modb_force_coils(node, daddr, ncoils, data, opts) '{
local err
modb__debug_start
if (!(opts & 0x02)) {
if (err = modb__checkpar(ncoils, data))
return(err)
if (err = modb__frame(node, 15, daddr, ncoils, data))
return(err)
if (err = modb__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb__get(node, 15))
return(err)
}
MODB_ERR = 0
modb__debug
return(0)
}'
#%UU% (<node>, <daddr>, <nregs>, <data>)
#%MDESC%
# This macro executes the function FC16:Write Multiple Registers in a
# slave MODBUS node.
# The slave node is selected by the parameter <node> that must be a valid
# value previously returned by the macro function %B%modb_addnode()%B%.
# The parameters <daddr> and <nregs> contain the address of first word
# (register) and the total number of words (registers) to write.
# The first positions of the array <data> cointain the register values to
# write. Each valid position of <data> must contain a 16-bit word.%BR%
# If an error happens, the macro returns -MODB_ERR (see error codes),
# otherwise 0.%BR%
#
def modb_write_regs(node, daddr, nregs, data, opts) '{
local err
modb__debug_start
if (!(opts & 0x02)) {
if (err = modb__checkpar(nregs, data))
return(err)
if (err = modb__frame(node, 16, daddr, nregs, data))
return(err)
if (err = modb__put(node))
return(err)
}
if (!(opts & 0x01)) {
if (err = modb__get(node, 16))
return(err)
}
MODB_ERR = 0
modb__debug
return(0)
}'
#%IU% (<nitems>, <data>)
#%MDESC%
# Checks that nitems is in the (1, 0xFFFF) range and that data is an
# array of the right capacity.%BR%
#
def modb__checkpar(nitems, data) '{
modb__debug "modb__checkpar"
if (nitems <= 0 || nitems > 0xFFFF || nitems != int(nitems)) {
if (MODB_ERR != -1)
print " MODBUS: Bad data parameters (number of items)"
return(-(MODB_ERR = 1003))
}
if (!(whatis("data") & 0x01010000)) {
if (MODB_ERR != -1)
print " MODBUS: Bad data parameters (not an array)"
return(-(MODB_ERR = 1003))
}
return(0)
}'
#%IU% (<node>, <funct>, <word1>, <word2>, <data>)
#%MDESC%
# Builds a modbus frame from the input parameters <funct>, <word1>, <word2>
# and <data>. The frame is stored in the global byte array MODBFRAME[]
# starting from the position 1.%BR%
#
def modb__frame(node, funct, word1, word2, data) '{
local addr i j byt shft
modb__debug "modb__frame"
addr = MODBUS[node]["addr"] - 1
if (addr < 0) {
if (MODB_ERR != -1)
print " MODBUS: Not a valid slave node:", node
return(-(MODB_ERR = 1004))
}
MODBFRAME[0:4] = 0
MODBFRAME[6] = addr
MODBFRAME[7] = funct
if (funct == 7 || funct == 11) {
MODBFRAME[5] = 2
return(0)
}
MODBFRAME[8] = word1 >> 8
MODBFRAME[9] = word1 & 0xFF
MODBFRAME[10] = word2 >> 8
MODBFRAME[11] = word2 & 0xFF
if (funct >= 1 && funct <= 6){
MODBFRAME[5] = 6
return(0)
}
if (funct == 15) {
MODBFRAME[12] = int((word2 - 1) / 8) + 1
j = 12
byt = 0
for (i = 0; i < word2; i++) {
shft = i % 8
byt |= (data[i] & 1) << shft
if (shft == 7) {
MODBFRAME[++j] = byt
byt = 0
}
}
if (shft != 7)
MODBFRAME[++j] = byt
MODBFRAME[5] = j - 5
return(0)
}
if (funct == 16) {
MODBFRAME[12] = 2 * word2
j = 12
for (i = 0; i < word2; i++) {
MODBFRAME[++j] = data[i] >> 8
MODBFRAME[++j] = data[i] & 0xFF
}
MODBFRAME[5] = j - 5
return(0)
}
return(-(MODB_ERR = 99))
}'
#%IU% (<node>)
#%MDESC%
# Sends a message to a slave node and gets the answer back.
# The message is constructed from a valid modbus frame previously stored in
# the byte array MODBFRAME[]. The answer from the slave is stored also in
# MODBFRAME[], overwritting the initial content. The return value is the
# length of the answer frame or -MODB_ERR in case of error.%BR%
#
def modb__put(node) '{
local comm device str len
modb__debug "modb__put"
comm = MODBUS[node]["comm"]
device = MODBUS[node]["dev"]
if (comm == "tcp") {
if (MODBUS[node]["funct"]) {
sock_par(MODBUS[node]["tcpport"], "close")
MODBUS[node]["funct"] = 0
}
len = MODBFRAME[5] + 5
if (sock_put(MODBUS[node]["tcpport"], MODBFRAME[0:len]) == -1){
if (MODB_ERR != -1)
print " MODBUS: Transmission error (socket_io)"
return(-(MODB_ERR = 1008))
}
} else {
str = modb__buildstr()
if (comm == "serial") {
if (MODBUS[node]["funct"]) {
ser_get(device)
MODBUS[node]["funct"] = 0
}
if (ser_put(device, str) == -1) {
if (MODB_ERR != -1)
print " MODBUS: Transmission error (ser_put)"
return(-(MODB_ERR = 1008))
}
} else {
if (MODBUS[node]["funct"]) {
esrf_io(device, "DevSerReadString", 2)
MODBUS[node]["funct"] = 0
}
if (esrf_io(device, "DevSerWriteString", str) == -1) {
if (MODB_ERR != -1)
print " MODBUS: Transmission error (esrf_io)"
return(-(MODB_ERR = 1008))
}
}
}
MODBUS[node]["funct"] = MODBFRAME[7]
return(0)
}'
def modb__get(node, funct) '{
local retval
retval = modb__getdata(node, funct)
if (retval == -1007) {
MODBUS[node]["funct"] =
print "Retry: ", modb__getdata(node, -1)
}
}'
def modb__get(node, funct) '{
local retval
modb__debug "modb__get"
#if (myexit) {print "EXIT", myexit = 0; exit}
if (funct != MODBUS[node]["funct"]) {
if (MODB_ERR != -1)
print " MODBUS: Function mismatch"
return(-(MODB_ERR = 1007))
}
retval = modb__getlow(node)
if (!retval)
retval = modb__chkanswer(funct)
if (retval == -1007) {
print "Retry: ", modb__getlow(node)
}
return(retval)
}'
def modb__getlow(node) '{
local comm device str len
comm = MODBUS[node]["comm"]
device = MODBUS[node]["dev"]
if (comm == "tcp") {
sock_get(MODBUS[node]["tcpport"], MODBFRAME[0:5])
len = MODBFRAME[5]
sock_get(MODBUS[node]["tcpport"], MODBFRAME[6:(5 + len)])
MODBUS[node]["funct"] = 0
return(0)
} else {
if (comm == "serial") {
str = ser_get(device)
if (HDW_ERR || str == "") {
if (MODB_ERR != -1)
print " MODBUS: No answer from slave node"
return(-(MODB_ERR = 1005))
}
} else {
ESRF_ERR = -1
str = esrf_io(device, "DevSerReadString", 2)
if (ESRF_ERR){
if (MODB_ERR != -1)
print " MODBUS: No answer from slave node"
return(-(MODB_ERR = 1005))
}
}
MODBUS[node]["funct"] = 0
if (err = modb__chkstr(str))
return(err)
return(0)
}
}'
def __modb__getdata(node, funct) '{
local comm device str len
modb__debug "modb__get"
comm = MODBUS[node]["comm"]
device = MODBUS[node]["dev"]
if (funct != MODBUS[node]["funct"]) {
if (MODB_ERR != -1)
print " MODBUS: Function mismatch"
return(-(MODB_ERR = 1007))
}
if (comm == "tcp") {
sock_get(MODBUS[node]["tcpport"], MODBFRAME[0:5])
len = MODBFRAME[5]
sock_get(MODBUS[node]["tcpport"], MODBFRAME[6:(5 + len)])
MODBUS[node]["funct"] = 0
return(modb__chkanswer(funct))
} else {
if (comm == "serial") {
str = ser_get(device)
if (HDW_ERR || str == "") {
if (MODB_ERR != -1)
print " MODBUS: No answer from slave node"
return(-(MODB_ERR = 1005))
}
} else {
ESRF_ERR = -1
str = esrf_io(device, "DevSerReadString", 2)
if (ESRF_ERR){
if (MODB_ERR != -1)
print " MODBUS: No answer from slave node"
return(-(MODB_ERR = 1005))
}
}
MODBUS[node]["funct"] = 0
if (err = modb__chkstr(str))
return(err)
return(modb__chkanswer(funct))
}
}'
#%IU% ()
#%MDESC%
# Returns an ASCII MODBUS message string build from a modbus frame previously
# stored in the byte array MODBFRAME[]. The string includes the LRC bytes and
# all the required control characters.%BR%
#
def modb__buildstr() '{
local i len lrc str
modb__debug "modb__buildstr"
len = MODBFRAME[5] + 5
str = ""
lrc = 0
for (i = 6; i <= len; i++) {
str = sprintf("%s%02X", str, MODBFRAME[i])
lrc += MODBFRAME[i]
}
lrc = (-(lrc & 0xFF)) & 0xFF
str = sprintf(":%s%02X\r\n", str, lrc)
return(str)
}'
#%IU% (<str>)
#%MDESC%
# Converts an ASCII MODBUS answer in <str> into a byte binary sequence that
# is stored in MODBFRAME[].%BR%
# If the macro detects a MODBUS exception or a LRC error returns -MODB_ERR,
# otherwise returns the length of the answer frame.%BR%
#
def modb__chkstr(str) '{
local i len lrc hd ld
modb__debug "modb__chkstr"
if (substr(str, 1, 1) != ":") {
print " MODBUS: Bad answer (no prefix)."
return(-(MODB_ERR = 1006))
}
lrc = 0
len = int((length(str) - 1) / 2) - 1
local ubyte array provi[2*len]
provi = substr(str, 2) "x"
# provi -= 48 + 7 * (provi >= 65)
MODBFRAME[5] = len
for (i = 0; i < len; i++) {
hd = provi[2*i]
hd -= 48 + 7 * (hd >= 65)
ld = provi[2*i + 1]
ld -= 48 + 7 * (ld >= 65)
MODBFRAME[i + 6] = (hd << 4) + ld
}
if (array_op("sum", MODBFRAME[6:(5+len)]) & 0xFF) {
print " MODBUS: Bad answer (LCR error)."
return(-(MODB_ERR = 1006))
}
return(0)
}'
#%IU% ()
#%MDESC%
# Checks the received frame for a MODBUS exception and returns the error code.
# If no exception, it returns the length of the answer frame.%BR%
#
def modb__chkanswer(funct) '{
modb__debug "modb__chkanswer"
if ((MODBFRAME[7] & 0x7F) != funct) {
if (MODB_ERR != -1)
print " MODBUS: Function mismatch"
return(-(MODB_ERR = 1007))
}
if (MODBFRAME[7] & 0x80) {
MODB_ERR = MODBFRAME[8]
if (MODB_ERR != -1) {
if (MODB_ERR == 1)
printf(" MODBUS: Illegal Function.\n")
else if (MODB_ERR == 2)
printf(" MODBUS: Illegal Data Address.\n")
else if (MODB_ERR == 3)
printf(" MODBUS: Illegal Data Value.\n")
else {
printf(" MODBUS: Unknown Exception (%d).\n", MODB_ERR)
}
}
return(-MODB_ERR)
}
MODB_ERR = 0
return(0)
}'
#%UU%
#%MDESC%
def modbdebug '{
if (MODBUS[0]["debug"]) {
MODBUS[0]["debug"] = 0
unglobal MODBDEBUGT
rdef modb__debug_start ""
rdef modb__debug "0==\$6"
print "MODBUS debug mode is OFF"
} else {
MODBUS[0]["debug"] = 1
global MODBDEBUGT
rdef modb__debug_start \'MODBDEBUGT = time()\'
rdef modb__debug \'print 1000*(time() - MODBDEBUGT), "$*"\'
print "MODBUS debug mode is ON"
}
}'
def modb__debug_start ''
def modb__debug '0==$6'
#%MACROS%
#%INTERNALS%
# ???.
#
#%AUTHOR% P. Fajardo, (Original 5/01).
# $Revision: 1.2 $ / $Date: 2008/08/12 13:59:19 $
#%TOC%
|