#%TITLE% eurotherm2700.mac
#%NAME% TEST macros for a the use of a EuroTherm 2700 temperature controller.
#%CATEGORY% temperature
#%DESCRIPTION%
# Implement macro motors and macro counters to control the regulation loop
# and to read any MODBUS address from the controller.
#
# For the macro counters, the channel is used to defined the MODBUS address
# to read. But as a scaler controller can only have 999 channels, to read
# a higher address, there is an optional counter parameter named "address".
# When MODBUS address to read is defined like than, there is a second
# optional parameter named "floatvalue" to get a float value from
# the controller instead of a rounded temperature value.
#
#$Revision: 1.12 $
#Package bliss version 1.2 is corrupted.
need modbus-rtu.mac
if (!(whatis("__e2700debug") & 2)) rdef __e2700debug \'#$*\'
#%IU%(unit, fwaddr, value)
#%MDESC%
#
def __e2700_put(unit, fwaddr, value) '{
if (__E2700[__E2700[unit]]["type"]) {
return __e2700_put_tango(__E2700[unit], fwaddr, value)
}
else {
return __e2700_put_serial(__E2700[unit], fwaddr, value)
}
}
'
#%IU%(unit, fwaddr, num, data)
#%MDESC%
#
def __e2700_blockput(unit, fwaddr, num, data) '{
if (__E2700[__E2700[unit]]["type"]) {
return __e2700_blockput_tango(__E2700[unit], fwaddr, num, data)
}
else {
return __e2700_blockput_serial(__E2700[unit], fwaddr, num, data)
}
}
'
#%IU%(unit, fwaddr, num, data)
#%MDESC%
#
def __e2700_get(unit, fwaddr, num, data) '{
if (__E2700[__E2700[unit]]["type"]) {
return __e2700_get_tango(__E2700[unit], fwaddr, num, data)
}
else {
return __e2700_get_serial(__E2700[unit], fwaddr, num, data)
}
}
'
# some modbus functions might need to be declared although not needed, when
# connecting through a Modbus tango server. When working with a ds, it will
# never show, but if needed it is crucial, that people see this!!!
if (!(whatis("modb_rtu_addnode") & 2)) rdef modb_rtu_addnode() '{
eprint "##########################################################"
eprint "##########################################################"
eprint "##### #####"
eprint "##### Sorry, your lacking the modbus-rtu.mac file! #####"
eprint "##### #####"
eprint "##########################################################"
eprint "##########################################################"
return -1
}
'
#%UU%
#%MDESC% toggle debug mode for the present macros.
def e2700_debug '{
if ((whatis("__e2700debug")>>16) <= 2) { # just a # sign -> off
rdef __e2700debug "eprint"
print "e2700 debug is ON"
} else {
rdef __e2700debug \'#$*\'
print "E2700 debug is OFF"
}
}'
#%IU%(node, fwaddr, value)
#%MDESC%
# Called by spec
def __e2700_put_serial(node, fwaddr, value) '{
__e2700debug "__e2700_put_serial(" node "," fwaddr "," value ")"
if (modb_rtu_write_a_word(node, fwaddr, value) < 0) {
return(-1)
}
return(0)
}'
#%IU%(node, fwaddr, value)
#%MDESC%
# Called by spec
def __e2700_blockput_serial(node, fwaddr, nwords, data) '{
__e2700debug "__e2700_blockput_serial(" node "," fwaddr "," nwords ", data)"
if (modb_rtu_write_n_words(node, fwaddr, nwords, data) < 0) {
return(-1)
}
return(0)
}'
#%IU%(fwaddr, fwaddr, num, data)
#%MDESC%
# Called by spec
def __e2700_get_serial(node, fwaddr, num, data) '{
__e2700debug "__e2700_get_serial(" node "," sprintf("0x%x", fwaddr) "," num ", data)"
if (modb_rtu_read_n_words(node, fwaddr, num, data) < 0) {
return(-1)
}
return(0)
}'
#%IU%(fwaddr, value)
#%MDESC%(server, fwaddr, value)
# Called by spec
def __e2700_put_tango(server, fwaddr, value) '{
local short array x[2];
x[0]=fwaddr; x[1]=value;
__e2700debug "__e2700_put_tango(" server ", " fwaddr ", " value ")"
tango_io(server, "PresetSingleRegister", x);
if (TANGO_ERR != "0") {
eprint "FAILED: Writing", value, "to modbus tag address", fwaddr
# eprint TANGO_ERR_STACK["0"]["desc"];
return(-1)
}
return(0)
}'
#%IU%(server, fwaddr, nwords, data)
#%MDESC%
# Called by spec
def __e2700_blockput_tango(server, fwaddr, nwords, data) '{
local short array x[2+nwords]
x[0]=fwaddr; x[1]=nwords; x[2:] = data
__e2700debug "__e2700_blockput_tango(" server ", " fwaddr ", " nwords ", data)"
__e2700debug x
whats x
tango_io(server, "PresetMultipleRegisters", x);
if (TANGO_ERR != "0") {
eprint "FAILED: Writing", value, "to modbus tag address", fwaddr
# eprint TANGO_ERR_STACK["0"]["desc"];
return(-1)
}
return(0)
}'
#%IU%(server, fwaddr, num, data)
#%MDESC%
# Called by spec.<BR>data is ushort array
#
def __e2700_get_tango(server, fwaddr, num, data) '{
local short array x[2], y[num];
local i, got
x[0]=fwaddr; x[1]=num;
__e2700debug "__e2700_get_tango(" server ", " fwaddr ", " num ", data)"
got = tango_io(server, "ReadHoldingRegisters", x, y);
if (TANGO_ERR != "0") {
# eprint TANGO_ERR_STACK["0"]["desc"];
return(-1)
}
data = y
return(0)
}'
def __e2700_rfloat(unit, val) '{
return(val / __E2700[__E2700[unit]]["scale"])
}
'
def __e2700_wfloat(unit, val) '{
return(int(val * __E2700[__E2700[unit]]["scale"]))
}
'
#%IU%(mne, type, unit, mod, chan)
#%MDESC%
# Called by spec
def E2700_config(mne, type, unit, mod, chan) '{
# chan will be used for counters to designate the tag address. Leave alone.
__e2700debug "Configuring Eurotherm", mne, type, unit, mod, chan
global __E2700[]
__E2700["savedir"] = BLISSADM "/local/spec/userconf/"
ushort array data[120]
# Using unit as the serial line number, must be already defined, and chan as
# modbus address, use E2700_ADDR to grab Modbus address.
# E2700_ADDR must contain a string with serial line index number and the
# modbus address separated by a blank. If there is only one string, it will be
# the serial line index number!
# New addition, where the connection to a Eurotherm 2700 is made through a
# Tango Modbus ds.
# Here we need to detect, whether the address field contains a serial index
# number or a device domain name. Sorry, no taco device here, it is always
# assued to have a tango ds. Simply check the lenght to be bigger than 2!
# Warning: the modbus device address is hidden as property in the tango ds.
# Try to work out how to change it from Spec.
if (mne == "..") {
local x, aux[], modbaddr
if ((x = split(E2700_ADDR, aux)) == 2) {
modbaddr = aux[1] # only used with direct serial line, tango ds has a
# property for that.
E2700_ADDR = aux[0]
} else if (x == 1) {
modbaddr = 1
}
if (length(E2700_ADDR) > 2) { # modbus tango ds !
if (tango_io(E2700_ADDR, "State") == -1) {
eprint "Eurotherm: can`t reach", E2700_ADDR "!!!"
return ".error."
}
__E2700[unit] = E2700_ADDR
# now check if is a naked Modbus or a eurotherm ds.
# use the status message here, as the tango_get-Temperature will yield an
# error message with the naked modbus server.
local statmess
statmess = tango_get(E2700_ADDR, "Status")
if (index(statmess, "Modbus node address")) {
# naked modbus server
__E2700[__E2700[unit]]["type"] = 1 # which put/get function to use
}
else if (index(statmess, "Connected to device")) {
__E2700[__E2700[unit]]["type"] = 2 # use tango_get Temperature to read PV
}
else if (index(statmess, "Unable to read temperature")) {
# sometime yields this message, although delivering temp correctly.
local tempx
tempx = tango_get(E2700_ADDR, "Temperature")
temps += 0
if (tempx == 0) {
eprint "Using", E2700_ADDR, "as naked Modbus server"
__E2700[__E2700[unit]]["type"] = 1 # DONOT use tango_get Temperature to read PV
} else {
__E2700[__E2700[unit]]["type"] = 2 # use tango_get to read PV
}
}
} else { # assume a Spec serial line.
need modbus-rtu # load the necessary modbus rtu macros
local name
name = unit ":" modbaddr
if ((__E2700[unit] = modb_rtu_addnode(E2700_ADDR, modbaddr, name)) < 0) {
tty_cntl("updated?"); tty_cntl("ce"); printf("\r")
return ".error."
}
__E2700[__E2700[unit]]["type"] = 0
}
delete __E2700[__E2700[unit]]["max_progs"]
if (__e2700_get(unit, 122, 1, data)) {
return ".error."
}
local ident
ident = data[0]
# ident now contains a number in hex format which will identify your
# controller.
# mine was 0x2480 :-)
# Controller identifier
# in format >ABCD (hex),
# A = 2 (series 2000) B = Range number C = Size D = Type
# 2: 2200 3: 1/32 din 0: PID/on-off
# 4: 2700 6: 1/16 din 2: VP
# 8: 1/8 din
# 4: ¼ din
if(((ident >> 12) != 2) && (((data[0]>>8 & 0x0f) == 4) || ((data[0]>>8 & \
0x0f) == 2))) {
print "This is not an Eurotherm 2000 series."
print "Check you connected the right controller "
return ".error."
}
__E2700[__E2700[unit]]["ident"] = sprintf("%x00", ident >> 8)
# There is the possibility to config the 2700 series with floating point
# or integer values. Tag address 525 tells you how many digits appear
# after the radix character. BUT !!!!! there was one controller with
# firmware version 0x0411, on which this cell`s contents has no influence
# on the modbus readings. Exclude it. The other versions at hand are the
# 0x0461, so between the two, we`ll act as the more recent version.
if (__e2700_get(unit, 107, 1, data)) {
return ".error."
}
__E2700[__E2700[unit]]["firmwareversion"] = data[0]
if (__e2700_get(unit, 5076, 1, data)) {
return ".error."
}
__E2700[__E2700[unit]]["scale"] = pow(10, data[0])
# catch the number of definable programs. Possible are 1, 4 and 20.
# reading 20 segments in one go is not possible, as the modbus protocol
# allows only 512 byte in one read. To avoid splitting this into two
# read cycles, we only read 16 segments. This sounds sufficient.
if (__e2700_get(unit, 89, 1, data)) { # to be verified!!!
return ".error."
}
if (data[0] == 32768) { # not controlling a heater or cooler ?!?
;
} else {
__E2700[__E2700[unit]]["max_segments"] = x > 16 ? 16 : data[0]
# set to auto mode
if (__e2700_put(unit, 273, 0)) {
return ".error."
}
}
if (__e2700_get(unit, 5786, 1, data)) {
return ".error."
}
__E2700[__E2700[unit]]["max_progs"] = data[0]
}
else {
if (type == "mot") {
if (__E2700[__E2700[unit]]["max_segments"] == 0) {
return ".error."
}
# __E2700[__E2700[unit]]["dead_band"] = motor_par(mne, "dc_dead_band")
# __E2700[__E2700[unit]]["dead_band"] = __E2700[__E2700[unit]]["dead_band"] ? \
# __E2700[__E2700[unit]]["dead_band"] : 1
# now read the SP
if (__e2700_get(unit, 2, 1, data)) {
return ".error."
}
__E2700[__E2700[unit]]["setpoint"] = __e2700_rfloat(unit, data[0])
motor_par(mne, "ismotor", 1, "add")
return(0)
}
}
}
'
#%IU%
#%MDESC%
# Called by spec
def E2700_cmd(mne, cmd, p1, p2, p3) '{
local unit, module, channel, x
ushort array data[120]
__e2700debug "Command Eurotherm", mne, cmd, p1, p2, p3
if (mne != "..") {
if (cmd == "start_one") {
# start counter, when it is one
if (p2 == 0) {
return
}
# must be a motor then
unit = motor_par(mne, "unit")
channel = motor_par(mne, "channel")
local ieeevals
if (channel > 900) {
channel -= 900
ieeevals = 1
}
# make a move
# check status, only answer if status is RESET
if (__e2700_get(unit, 5844, 1, data)) {
eprint "Communication error with Eurotherm"
return ".error."
}
if (data[0] != 1) {
eprint "This command is not allowed! Try to set the Eurotherm to RESET!"
return ".error."
}
# this is address 2, Target setpoint
if (__e2700_put(unit, 2, __e2700_wfloat(unit, p1))) {
return ".error."
}
__E2700[__E2700[unit]]["setpoint"] = p1
}
else if (cmd == "position") {
unit = motor_par(mne, "unit")
channel = motor_par(mne, "channel")
local ieeevals
if (channel > 900) {
channel -= 900
ieeevals = 1
}
if (channel = 2) {
channel = 5 # use working setpoint rather than setpoint
}
# get angle
# please consult manual to find out about Eurotherms way to offer
# floating point values. Basically they are at addresses 0x8000 + original
# address * 2
local retval
if (ieeevals) {
ushort array bla[2]
local y
if (__e2700_get(unit, 0x8000 + 2*channel, 2, data)) {
return(".error.")
}
# take 32 bit just read and turns into IEEE float
y = __euro__float(data)
retval = y
}
else {
if (__e2700_get(unit, channel, 1, data)) {
return ".error."
}
retval = __e2700_rfloat(unit, data[0])
}
# return position
#
return(retval)
}
else if (cmd == "get_status") {
# this get_status is targeted only to the SP and working SP. Any other channel
# will get the not-busy right from the start.
unit = motor_par(mne, "unit")
channel = motor_par(mne, "channel")
if (!__E2700[unit][channel]) {
return 0
}
if (channel > 900) {
channel -= 900
}
if (channel != 2) {
return(0)
}
# compare with where to go
local wsp, sp
wsp = sp = 0
if (__e2700_get(unit, 5, 1, data)) {
return ".error."
}
wsp = __e2700_rfloat(unit, data[0])
if (__e2700_get(unit, 2, 1, data)) {
return ".error."
}
sp = __e2700_rfloat(unit, data[0])
# now this might seem risky, to compare to "=", but as the modbus protocol only
# delivers shorts, which might then be treated with the [rw]float function, this
# should not be a problem.
if (wsp != sp) {
return 2
}
return(0)
}
else if (cmd == "counts") { # this is for the counter
# a bit dirty, but why not, address 1 delivers the PV (process value),
# address 2 the SP (set point) and address 3 the OP (Output power).
# and for that matter, it would allow to set any tag address as a counter.
# just set a sufficient number of channels in the E2700 counter
# controller. there seems, however, to be a limit of 999 in Spec
unit = counter_par(mne, "unit")
channel = counter_par(mne, "channel")
# for modbus addresses above 999, use an extra optional counter
# parameter named "address"
local addr
local ieeevals
addr = counter_par(mne, "mbaddress")
if(addr) {
channel = addr
ieeevals = counter_par(mne, "floatvalue")
} else {
if (channel > 900) {
channel -= 900
ieeevals = 1
}
if (channel + 900 * ieeevals == 998) {
channel = 1025
ieeevals = 1
}
}
# please consult manual to find out about Eurotherms way to offer
# floating point values. Basically they are at addresses 0x8000 +
# original address * 2, answer is two shorts
local retval
if (ieeevals) {
# Temperature reading with the Eurotherm2700 tango server
if((__E2700[__E2700[unit]]["type"] == 2) && (channel == 1)) {
return(tango_get(__E2700[unit], "Temperature"))
}
# and the rest is for Modbus servers and serial line comm.
local y
if (__e2700_get(unit, 0x8000 + 2*channel, 2, data)) {
return ".error."
}
y = __euro__float(data) # takes 32 bit just read and returns IEEE float
retval = y
}
else {
if (__e2700_get(unit, channel, 1, data)) {
return ".error."
}
retval = __e2700_rfloat(unit, data[0])
}
# return count
#
return(retval)
# end of count, this will read the value and return it!
}
}
} '
#%IU%
#%MDESC%
# Called by spec with different keys to handle motor_par(mot,"key",new_value)
# and counter_par(cnt,"key") actions.
def E2700_par(num, key, action, p1, p2) '{
local ismotor
local unit
local channel
# Guess if called for a counter or a motor
if(num < MOTORS) {
ismotor = motor_par(num, "ismotor")
} else {
ismotor = 0
}
# Called for a counter
if(!ismotor) {
if ((key == "?") && (action == "get")) {
return("setvalue")
}
if(key == "setvalue") {
unit = counter_par(num, "unit")
if(!(channel = counter_par(num, "mbaddress"))) {
channel = counter_par(num, "channel")
}
if(action == "set") {
if(__e2700_put(unit, channel, __e2700_wfloat(unit, p1))) {
return ".error."
}
}
# therefore, return 0 if write went fine
return(E2700_cmd(num, "counts"))
}
# WARNING: do not use a simple return otherwise
# the caller of the counter_par() will get value 1
return ""
}
# Called for a motor
if ((key == "?") && (action == "get")) {
return("waitformv, ramprate")
}
unit = motor_par(num, "unit")
channel = motor_par(num, "channel")
if (key == "waitformv") {
if (action == "get") {
return __E2700[unit][channel]
}
else {
__E2700[unit][channel] = p1
}
}
else if (key == "ramprate") {
local ushort array data[120]
if (action == "get") {
if (__e2700_get(unit, 35, 1, data)) {
return ".error."
}
return data[0]
}
else {
if (__e2700_put(unit, 35, p1)) {
return ".error."
}
}
}
}
'
#%IU% (<data>)
#%MDESC%
# Converts 4 byte size values into IEEE floating point value and returns it.
#%BR%
# The 2408 can be put into a floating point mode (read cell 12275) which then
# displays values with variable number of floating point numbers. To avoid
# getting in trouble with that, the protocol allows to read 4 bytes from
# adresses over 0x8000. Thus, the cell 2 reads from %BR%
# 2 x 2 + 8000h = 8004h = 32772 decimal. %BR%
def __euro__float(inarr) '{
# so s should contain 32 bits! but I have 2 shorts
local i j retval, s[]
__e2700debug "Eurotherm IEEE float conversion"
local f1, x, e
# this would be with 4 bytes, independant of the array type
s[0] = inarr[0] >> 8 & 0xff; s[1] = inarr[0] & 0xff
s[2] = inarr[1] >> 8 & 0xff; s[3] = inarr[1] & 0xff
f1 = 0x800000 | (s[1]&0x7f) << 16 | s[2] << 8 | s[3]
e = ((s[0]&0x7F)<<1) | ((s[1]&0x80)>>7)
e -= 127
x = f1 * pow(2., -23. + e)
if (s[0]&0x80)
x = -x
# and this is with 2 16 bit values, independant of the array type
# s = inarr
# f1 = 0x800000 | (s[0]&0x7f) << 16 | s[1] & 0xff00 | s[1] & 0xff
# e = ((s[0] >> 8 &0x7F)<<1) | ((s[1]>>8&0x80)>>7)
# e -= 127
# x = f1 * pow(2., -23. + e)
# if (s[0]>>8&0x80)
# x = -x
__e2700debug sprintf("converting %04x and %04x to %g\n", inarr[0]&0xffff, inarr[1]&0xffff, x)
return(x)
} '
#%UU% [unit]
#%MDESC%
# Gives information about the status of the controller. Not to confuse with
# europrogstatus, which gives information about a program execution in the
# controller. This one reads the status bytes on a controller and puts them out
# in human readable form. Saves you to rtfm.
# The bits of the fast status read (tag 75) are as follows:%BR%
#%PRE%
# 0 Alarm 1 State ( 0 = Safe, 1 = Alarm )%BR%
# 1 Alarm 2 State ( 0 = Safe, 1 = Alarm )%BR%
# 2 Alarm 3 State ( 0 = Safe, 1 = Alarm )%BR%
# 3 Alarm 4 State ( 0 = Safe, 1 = Alarm )%BR%
# 4 Manual Mode ( 0 = Auto, 1 = Manual )%BR%
# 5 Sensor Break ( 0 = Good PV, 1 = Sensor Broken )%BR%
# 6 Loop Break ( 0 = Good closed loop, 1 = Open Loop )%BR%
# 7 Heater Fail ( 0 = No Fault, 1 = Load fault detected )%BR%
# 8 Tune Active ( 0 = Auto Tune disabled, 1 = Auto Tune active)%BR%
# 9 Ramp/Program Complete ( 0 = Running/Reset, 1 = Complete )%BR%
# 10 PV out of range ( 0 = PV within table range, 1 = PV out of table range )%BR%
# 11 DC control module fault (0= Good,. 1= BAD)%BR%
# 12 Programmer Segment Synchronise (0= Waiting, 1 = Running)%BR%
# 13 Remote input sensor break (0 = Good, 1 = Bad)%BR%
#%PRE%
# After reflexion, Ill give you all that is there :-), 74 to 77
def euro2700status '{
eprint "The 2700s of Eurotherm offer some diagnostics. Here are the 4 bytes"
eprint "one can look at with their bit`s meaning."
local unit, x
ushort array data[4]
if ($#) unit = $1
if (__e2700_get(unit, 74, 4, data)) {
return(-1)
}
# tag address 74: Fast Status Byte
x = data[0]
if (x & 0x01) eprint "Alarm 1 State"
if (x & 0x02) eprint "Alarm 2 State"
if (x & 0x04) eprint "Alarm 3 State"
if (x & 0x08) eprint "Alarm 4 State"
if (x & 0x10) eprint "Manual Mode"
else eprint "Auto Mode"
if (x & 0x20) eprint "Sensor Broken"
else eprint "Good PV"
if (x & 0x40) eprint "Open Loop"
else eprint "Good closed loop"
if (x & 0x80) eprint "Heater Fail: Load fault detected"
else eprint "Heater Fail: No Fault"
# Summary Output Status Word, tag address 75
x = data[1]
# skip all information already present from byte 74
eprintf("Tune Active: Auto Tune active: ")
if (x & 0x100) eprint "active"
else eprint "disabled"
eprintf("Ramp/Program Complete: ")
if (x & 0x200) eprint "Complete"
else eprint "Running/Reset"
if (x & 0x400) eprintf("PV out of")
else eprintf("PV within")
eprint " table range"
eprintf("DC control module fault: ")
if (x & 0x800) eprint "BAD"
else eprint "Good"
eprintf("Programmer Segment Synchronise: ")
if (x & 0x1000) eprint "Running"
else eprint "Waiting"
eprintf("Remote input sensor break: ")
if (x & 0x2000) eprint "Bad"
else eprint "Good"
# tag address 76: Control Status Word
x = data[2]
if (x & 0x0001) eprint "Control algorithm Freeze"
if (x & 0x0002) eprint "PV input sensor broken"
if (x & 0x0004) eprint "PV out of sensor range"
if (x & 0x0008) eprint "Self Tune failed"
if (x & 0x0010) eprint "PID servo signal"
if (x & 0x0020) eprint "PID debump signal"
if (x & 0x0040) eprint "Fault detected in closed loop behaviour (loop break)"
if (x & 0x0080) eprint "Freezes the integral accumulator"
if (x & 0x0100) eprint "Indicates that a tune has completed successfully"
if (x & 0x0200) eprint "Direct/reverse acting control"
if (x & 0x0400) eprint "Algorithm Initialisation flag"
if (x & 0x0800) eprint "PID demand has been limited."
if (x & 0x1000) eprint "Autotune enabled"
if (x & 0x2000) eprint "Adaptive tune enabled"
if (x & 0x4000) eprint "Automatic Droop compensation enabled"
if (x & 0x8000) eprint "Manual / Auto mode switch"
# tag address 77: Instrument Status Word
x = data[3]
if (x & 0x0001) eprint "Config/Oper mode switch"
if (x & 0x0002) eprint "Disables limit checking"
if (x & 0x0004) eprint "SRL ramp running (Read Only)"
if (x & 0x0008) eprint "Remote setpoint active"
if (x & 0x0010) eprint "Alarm acknowledge switch."
}'
def __e2700_simple '{
# only for holger
# $1 address
# $2 # of bytes
# $3 unit
local numb
numb = $2 ? $2 : 1
__e2700_get($3, $1, numb, data)
numb--
print data[0:numb]
}
'
#%UU% unit
#%MDESC%
# Show the PID values for unit
#%BR%
def euro2700showpid '{
local prop, inte, deri
__e2700_get(unit, 6, 1, data)
prop = data[0]
__e2700_get(unit, 8, 2, data)
inte = data[0]
deri = data[1]
print "The PID values are : ", prop, inte, deri
}
'
#%UU%
#%MDESC%
# Please run without arguments to find out about the possible actions.
#
# Put controller into configuration mode and set one of several special cells.
# The following values influence the macros considerably. They can be set from
# the computer to make the user experience less painful.
#%BR%
# Note: no spaces are allowed around the `=` signs!
#%BR%
# Note: the decimal display will only be valid, if the Resolution is set to 0 (full)!!!
#
#%DL%
#%DT% Decimal places displayed
# (mb address 525)
#%DL%
#%DT%0: nnnn
#%DT%1: nnn.n
#%DT%2: nn.nn
#%XUL%
#
#%DT% Resolution
#(mb address 12275)
#%DL%
#%DT%0: Full
#%DT%1: Integer
#%XUL%
#%DT% Low Range Limit
#(mb address 11)
#%DT% High Range Limit
#(mb address 12)
#%DT% Instrument Units
#(mb address 516)
#%DL%
#%DT%0: oC
#%DT%1: oF
#%DT%2: oK
#%DT%3: None
#%XUL%
#%XUL%
def euro2700configuration '{
local currstat, _states, option, unit, x, i, good
_states[0] = "NORMAL"; _states[1] = "STANDBY"; _states[2] = "CONFIGURATION"
# the variable usine refers to the French word `usine a gaz`, which characterizes an excessive
# complexity, which remains incomprehsible for the uninitiated :-)
local myarr[], aux[], bla[], varnam, usine[], doit
# I just hate to have the same strings twice, so use x as first index in usine[].
x = "Resolution"; usine[x]["address"] = 12275; usine[x]["poss"] = "0(Full)|1(Integer)"
x = "DecimalPlaces"; usine[x]["address"] = 5076; usine[x]["poss"] = "0|1|2"
x = "LowRangeLimit"; usine[x]["address"] = 11; usine[x]["poss"] = "value"
x = "HighRangeLimit"; usine[x]["address"] = 12; usine[x]["poss"] = "value"
x = "InstrumentUnits"; usine[x]["address"] = 10369; usine[x]["poss"] = "0(C)|1(F)|2(K)|3(None)"
# those values don`t need a reset.
# x = "Proportional"; usine[x]["address"] = 6; usine[x]["poss"] = "value"
# x = "Integral"; usine[x]["address"] = 8; usine[x]["poss"] = "value"
# x = "Derivate"; usine[x]["address"] = 9; usine[x]["poss"] = "value"
# x = "RampHoldBack"; usine[x]["address"] = 70; usine[x]["poss"] = "0(off)|1(low)|2(high)|3(Band)"
# x = "RampHoldValue"; usine[x]["address"] = 65; usine[x]["poss"] = "value"
# x = "RampRate"; usine[x]["address"] = 35; usine[x]["poss"] = "value"
# x = "RampRateUnits"; usine[x]["address"] = 531; usine[x]["poss"] = "0(Sec)|1(Min)|2(Hour)"
# x = "TimeUnit"; usine[x]["address"] = 529; usine[x]["poss"] = "value"
doit = 0 # any action necessary ?
if(!$#){
local x, y[], z, oldz
printf("usage: %s ", "$0")
for (x in usine) {
split(x, y, "\034")
z = y[0]
if (z != oldz) {
if (usine[z]["address"]) {
print "\t" z "=" usine[z]["poss"], " \\ "
}
oldz = z
}
}
exit
}
split("$*", myarr)
for (option in myarr) {
split(myarr[option], aux, "=")
varnam = aux[0]
usine[varnam]["newval"] = aux[1]
@varnam = aux[1]
bla[varnam] = "" # as a placeholder for the next for()
}
# Now do some checking.
# Note: the decimal display will only be valid, if the Resolution is set to 0 (full)!!!
# do this any time DecimalDisplay is changed, the overhead is minimal
if (("DecimalDisplay" in bla) && !("Resolution" in bla)) {
bla["Resolution"] = "" # make sure Resolution is set with the DecimalDisplay
usine["Resolution"]["newval"] = 0
}
# the following checks if anything at all needs doing
for (x in bla) {
if (usine[x]["address"]) {
if (__e2700_get(unit, usine[x]["address"], 1, data)) {
eprint "Can`t access unit", unit, "!"
exit
}
usine[x]["value"] = data[0]
if (usine[x]["value"] != usine[x]["newval"]) {
doit = 1 # yes, we need to go into config mode.
usine[x]["doit"] = 1 # and change this value
}
}
}
if (doit) {
# put controller into config mode
if (__e2700_get(unit, 199, 1, data)) {
eprint "Can`t access unit", unit, "!"
exit
}
currstat = data[0]
print "The controller was in", _states[currstat], "mode."
if (__e2700_put(unit, 199, 2)) {
eprint "Can`t access unit", unit, "!"
exit
}
for (x in bla) {
if (x == "unit") {
continue
}
if (usine[x]["doit"]) {
if (__e2700_put(unit, usine[x]["address"], usine[x]["newval"])) {
eprint "Can`t access unit", unit, "!"
exit
}
if (__e2700_get(unit, usine[x]["address"], 1, data)) {
eprint "Can`t access unit", unit, "!"
exit
}
print x "`s old value was", usine[x]["value"] ", the new value is", data[0]
}
}
# set controller back into normal mode
if (__e2700_get(unit, 199, 1, data)) {
eprint "Can`t access unit", unit, "!"
exit
}
currstat = data[0]
if (__e2700_put(unit, 199, 0)) {
eprint "Can`t access unit", unit, "!"
exit
}
# here the controller is going to go offline for a time.
print
print "The controller will go through a reset now, this will take less than a minute!"
print "Please wait until the controller becomes responsive."
good = 0
for (i = 0; i < 60; i ++) {
sleep(5)
if (__e2700_get(unit, 199, 1, data) == 0) {
good = 1
break
} else {
tty_cntl("updated?"); tty_cntl("ce"); printf("\r")
}
}
if (! good && __E2700[__E2700[unit]]["type"]) {
print "Apparently there is a problem with the communication with the controller."
print "Please restart the device server!"
} else if (! good) {
# I found that when using the modbus-rtu macros a good bash at the serial line
# will solve the problem. Usually a ct 1 will get it back to work.
COUNT_TIME = 1
count_em
} else {
if (__e2700_get(unit, 199, 1, data)) {
eprint "Can`t access unit", unit, "!"
exit
}
currstat = data[0]
print "Now the controller is in", _states[currstat], "mode!"
}
print "After these changes, it is probable you have to reconfigure."
reconfig
}
else {
print "No action necessary!"
}
}
'
#%UU%
#%MDESC%
# Please run without arguments to find out about the possible actions.
#
# Set one of several special cells.
# The following values influence the macros considerably. They can be set from
# the computer to make the user experience less painful.
#%BR%
# Note: no spaces are allowed around the `=` signs!
#%BR%
def euro2700parameters '{
local currstat, _states, option, unit, x, i, good
# the variable usine refers to the French word `usine a gaz`, which characterizes an excessive
# complexity, which remains incomprehsible for the uninitiated :-)
local myarr[], aux[], bla[], varnam, usine[], doit
# I just hate to have the same strings twice, so use x as first index in usine[].
# x = "Proportional"; usine[x]["address"] = 6; usine[x]["poss"] = "value"
# x = "Integral"; usine[x]["address"] = 8; usine[x]["poss"] = "value"
# x = "Derivate"; usine[x]["address"] = 9; usine[x]["poss"] = "value"
x = "unit"
x = "RampHoldBack"; usine[x]["address"] = 70; usine[x]["poss"] = "0(off)|1(low)|2(high)|3(Band)"
x = "RampHoldValue"; usine[x]["address"] = 65; usine[x]["poss"] = "value"
x = "RampRate"; usine[x]["address"] = 35; usine[x]["poss"] = "value"
x = "RampRateUnits"; usine[x]["address"] = 531; usine[x]["poss"] = "0(Sec)|1(Min)|2(Hour)"
split("$*", myarr)
for (option in myarr) {
split(myarr[option], aux, "=")
varnam = aux[0]
usine[varnam]["newval"] = aux[1]
@varnam = aux[1]
bla[varnam] = "" # as a placeholder for the next for()
}
doit = 0 # any action necessary ?
if((!$#) || (($# == 1) && unit)) {
local x, y[], z, oldz, str, len
ushort array data[120]
printf("usage: %s \\\n", "$0")
for (x in usine) {
split(x, y, "\034")
z = y[0]
if (z == "unit") {
print "\tunit=[1|2|..] \\ "
continue
}
if ( ! __E2700[unit]) {
local i, xstr
eprint "No controller with unit number", unit
for (i=0; i < 50; i++) {
if (__E2700[i]) xstr = xstr " " i
}
eprint "Please use one of:" xstr
exit
}
if (z != oldz) {
if (usine[z]["address"]) {
str = sprintf("\t%s=%s \\ ", z, usine[z]["poss"])
}
len = length(str)
for(i = len; i < 50; i++) {
str = str " "
}
if (__e2700_get(unit, usine[z]["address"], 1, data)) {
eprint "Can`t access unit", unit, "!"
exit
}
str = str "(current value: " data[0] ")"
print str
oldz = z
}
}
}
split("$*", myarr)
for (option in myarr) {
split(myarr[option], aux, "=")
varnam = aux[0]
usine[varnam]["newval"] = aux[1]
@varnam = aux[1]
bla[varnam] = "" # as a placeholder for the next for()
}
# the following checks if anything at all needs doing
for (x in bla) {
if (usine[x]["address"]) {
if (__e2700_get(unit, usine[x]["address"], 1, data)) {
eprint "Can`t access unit", unit, "!"
exit
}
usine[x]["value"] = data[0]
if (usine[x]["value"] != usine[x]["newval"]) {
doit = 1 # yes, we need to go into config mode.
usine[x]["doit"] = 1 # and change this value
}
}
}
if (doit) {
for (x in bla) {
if (x == "unit") {
continue
}
if (usine[x]["doit"]) {
if (__e2700_put(unit, usine[x]["address"], usine[x]["newval"])) {
eprint "Can`t access unit", unit, "!"
exit
}
if (__e2700_get(unit, usine[x]["address"], 1, data)) {
eprint "Can`t access unit", unit, "!"
exit
}
print x "`s old value was", usine[x]["value"] ", the new value is", data[0]
}
}
}
else {
print "No action necessary!"
}
}
'
#%UU% mne
#%MDESC%
# toggle behaviour for motor mne to wait for the end of a move, if a simple mv
# command is using a ramp rate.
#%END%
# call this macro euro... instead of euro2400 as I will try to make macros for
# both types 2400s and 2700s.
def eurowaitformove '{
if ($# != 1 ) {
print "Usage: $0 motor_num"
exit
}
local num, unit, channel, x
if ((num = motor_num("$1")) == -1) {
eprint "No such motor", "$1"
exit
}
unit = motor_par(num, "unit")
channel = motor_par(num, "channel")
x = motor_par(num, "waitformv")
motor_par(num, "waitformv", x ? 0 : 1)
print "Motor", motor_mne(num), "will", motor_par(num, "waitformv") ? "NOT " : \
"" "go back to the prompt after a mv command"
}
'
#%MACROS%
#%IMACROS%
#%INTERNALS%
#%AUTHOR% H. Witsch, BLISS - ESRF, derived from the macros in eurotherm2400.mac,
# mainly programmed by H. Witsch
# Revision: preliminary, Date: 2009/06/16
#%TOC%
|