"""
#%TITLE% eurotherm2400.mac
#$Revision: 4.2 $
#%NAME% Macros for a the use of a EuroTherm 2400 temperature controller.
#%DESCRIPTION%
# The macros provide users with an interface between SPEC and a EuroTherm 2400
# temperature controller.
#%CATEGORY% temperature
#%INTERNALS%
# You may declare a motor and a counter, even for the same
# device! The macro motor/counter macros can handle that.
#%BR%The temperature setpoint (or any other writable modbus tag) is driven as a
# (macro) motor, the process value "PV" and other tags can be read as (macro)
# counter.
#%SETUP%
# When used through a serial line, this macro motor set is using a macro set
# called modbus-rtu.mac. The communication with the Eurotherm controller is done
# using the modbus binary, called RTU, protocol!
#%BR%%BR%
# Only one controller is allowed in a Spec session. If need arises, that might
# be changed.
#
#
#Now declare a %B%motor\0controller%B%:
#
#
#%PRE%
#MOTORS DEVICE ADDR <>MODE NUM <>TYPE
# YES E2400 3 999 Macro Motors
#%PRE%
# or
#%PRE%
#MOTORS\0\0\0\0DEVICE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0ADDR\0\0<>MODE\0\0NUM\0\0\0\0\0\0\0\0\0\0\0\0\0<>TYPE
#\0\0\0YES\0\0\0\0\0E2400\0\0\0id00/modbus/e2408\0\0\0\0\0\0\0\0\0\0999\0\0\0\0\0\0\0Macro Motors
#%PRE%
# %BR%
# If you use a serial line access, please put the index number of your serial line into the ADDR field (alphanum, hit "'" first). Optionally the Modbus %B%instrument%B% address can be added, seperated from the serial line number with blank.
#
# If you access your instrument through a Modbus Tango device server, please
# enter its address. The Modbus instrument address is a property of the server,
# you can't change it in Spec.
# %BR% %BR%
#Then create the macro motor:
#%BR% %BR%Unit is index num of controller, second is the Modbus %B%TAG%B%
# address.
#%PRE%
#Number:\0<>Controller\0\0\0\0\0\0\0\0\0\0\00:\0MAC_MOT
#Unit/[Module/]Channel\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00/1
#Name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Eurot SP
#Mnemonic\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0m_euro
#Steps per degree/mm\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#Sign of user * dial\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#Backlash [steps]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00
#Steady-state rate [Hz]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#Base rate [Hz]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#Acceleration time [msec]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01
#Motor accumulator\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00
#Restrictions <>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0NONE
#
#Dial = accumulator / steps
#\0\0High limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01000.0000
#\0\0Current\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#\0\0Low limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#
#User = sign * dial + offset
#\0\0Offset\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#\0\0`High' limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\01000.0000
#\0\0Current\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#\0\0`Low' limit\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00.0000
#%PRE%
# The value steps per degree should reflect, if the controller accepts floating
# point numbers or not (check Modbus tag address 525).
#%BR%
# Then hit 'm' twice. Set the following for your motor:
#%PRE%
#Hardware\0read\0mode\0<>\0\0\0\0\0\0\0\0PR\0+\0AL\0+\0NQ
#%PRE%
#%BR% %BR%Now declare a %B%scaler\0controller%B%:
#%PRE%
#SCALERS\0\0\0\0\0\0\0\0\0DEVICE\0\0\0\0\0\0\0\0\0\0ADDR\0\0<>MODE\0\0NUM\0\0\0\0\0\0\0\0\0\0\0\0\0<>TYPE
#\0\0\0YES\0\0\0\0\0\0\0\0\0\0\0\0POLL\0\0\0\0\0\0\0\0\0\0\0\0\01\0\0\0\0\0\0\0\0\0\0\0\02\0\0\0\0\0Software Timer
#\0\0\0YES\0\0\0\0\0\0\0\0\0\0\0E2400\0\0\0\0\0\0\0\0\0\0\03\01\0\0\0\0\0\0\0\0\0\0999\0\0\0\0\0\0Macro Counter
#%PRE%%BR%
#%BR% %BR%
# The %B%counter%B% is then configured as :
#%PRE%
#Number\0\0\0\0\0\0\0\0Name\0\0Mnemonic\0\0<>Device\0\0Unit\0\0Chan\0\0\0<>Use\0As\0\0Scale\0Factor
#\0\0\0\0\03\0\0\0\0\0\0T_euro\0\0\0\0t_euro\0\0\0MAC_CNT\0\0\0\0\00\0\0\0\0\01\0\0\0\0counter\0\0\0\0\0\0\0\0\0\0\0\0\01
#\0\0\0\0\04\0\0\0\0\0\0europv\0\0\0\0europv\0\0\0MAC_CNT\0\0\0\0\00\0\0\0901\0\0\0\0counter\0\0\0\0\0\0\0\0\0\0\0\0\01
#%PRE%%BR%
#%BR% %BR%
# This reads the Process Value (tag address 1) and places the value in the
# counter t_euro. One can read tag addresses as IEEE floating point numbers. To
# read a tag address as floating point number, add 900 to its tag address. To
# read the PV use 901.
#%BR%Other modbus tag addresses are:
# %UL%
# %LI%\0\01 - Read Process value
# %LI%\0\02 - Change Setpoint
# %LI%273 - Select Manual Mode
# %LI%\0\03 - Change Output Power
# %LI%\0\05 - Working Set Point
# %LI%163 - Programmer setpoint
# %LI%\036 - Segment time remaining (secs)
# %LI%160 - Target setpoint (current segment)
# %LI%161 - Ramp rate
# %LI%\0\06 - Proportional band PID1
# %LI%\0\08 - Integral time PID1
# %LI%\0\09 - Derivative time PID1
# %XUL%
# But please have a look at the documentation, if you have more specific needs:
# http://wikiserv.esrf.fr/bliss/images/5/52/Series_2000_Modbus_and_Ei-Bisynch_Digital_Communications_Handbook.pdf
#%EXAMPLE%
# As application example, one could create two separate spec session. The first
# session would start a previously defined program in the controller and then
# start a %B%timescan.%B% The timescan updates the process value continuously. A client
# spec session can now fetch the counter value, using the prop_get() builtin.
# Assume the counter number 3 reads you Eurotherm:
#%PRE%
# 16.HOLG> y = prop_get("localhost:spec", "var/S[3]")
# 17.HOLG> p y
# y["3"] = 100.3
#%PRE%
# The same can be accomplished with a simple remote counter :-), but you still
# have to count to read the instrument!%BR%
# Alternatively, one may trigger the read-out when needed by executing the
# %B%euro_read%B% macro. This macro will write the result to S[3], too.
#%PRE%
# remote_async("localhost:spec", "euro_read 3")
#%PRE% If you use a remote counter, then you can even just say
#%B%euro_read\0mne%B%.
#%BR% %BR%
# If you read your instrument through a %B%Eurotherm2400\0Tango\0device\0server%B%,
# you can declare the same instrument in several Spec sessions and just read out
# the process value in a timescan, while the first session could serve to define
# new programs in the controller.
#%BR% %BR%
# Due to the slow serial line connection, it has to be accounted for the long
# read out time. For faster handling, it is planned to write a device server,
# which does a PV polling.
#%BR% %BR%
#%B%For\0your\0information\0from\0the\0documentation:%B%%BR%%BR%
#There are four different Holdback types. The choice of type is made by setting
#a parameter when creating a program, and may be one of the following:
#%DL%
#%DT%OFF%DD% Disables Holdback - therefore no action is taken.
#%DT%Lo%DD% Deviation Low Holdback holds the program back when the process
# variable deviates below the setpoint by more than the holdback value.
#%DT%Hi%DD% Deviation High Holdback holds the program back when the process
# variable deviates above the setpoint by more than the holdback value.
#%DT%bAnd%DD% Deviation Band Holdback is a combination of the two. It holds the
# program back when the process variable deviates either above, or below, the
# setpoint by more than the holdback value.
#%XDL%
#%BR%%BR%There is a single Holdback Value which applies to the whole program.
# However, the Holdback type and whether or not it is enabled, can be applied to
# the program as a whole, or individually in each segment.
#%BR%%BR%
#%INTERNALS%
# Great care has to be taken, when reading selected addresses from the Modbus.
# Some of the mb addresses are given in 10th. This concerns the values for ramp
# rate/time and dwell time in segment programming. Also the status values for
# the remaining segment time in seconds (36) and minutes (63) are return as 10
# times of the real value.
#%END%
#%HISTORY%
#$Log: eurotherm2400.mac,v $
#Revision 4.2 2018/05/22 09:40:35 witsch
#Macros should now accept arguments unit=1 or Unit=1, as suggested by Alejandro
#to avoid confusion about camelback argument names. Now `unit` is being
#cased to lower and thus cases are no longer important.
#
#Revision 4.1 2017/07/06 14:28:26 witsch
#Refactoring of _euro_mne2unit, as it didn't work, when the controller
#index number was 0. It basically always chose the one with zero.
#
#Revision 4.0 2017/02/01 10:33:44 witsch
#Several changes asked by the beam lines:
#1 - when using europrogreset occasionally the setpoint wasn`t written
# correctly and lead to the controller to go to unwished setpoints.
#2 - guess unit number or accept unit number or mnemonic as argument:
# unit=3 or mne=euro. Mnemonic can be motor or counter. However, if
# nothing it specified, the macros will just go and find the first
# of the controllers in the config.
#3 - a futil attempt to make a kind of test with all user macros, where
# the variable STARTUP_TEST is set and all user macros have an if to
# quit from this test for STARTUP_TEST.
#
#Revision 3.8 2016/12/02 10:34:32 guilloud
#changed TANGO_ERR test.
#
#Revision 3.7 2016/08/05 14:23:25 witsch
#Small change in the europrog reset part, where a printed line was deleted
#and when resetting isn't wanted at the question, return to europrog, rather
#than the spec prompt.
#
#Revision 3.6 2016/01/05 16:06:35 guilloud
#fix strange character : ΒΌ ---> 1/4
#
#Revision 3.5 2014/10/22 08:29:15 witsch
#very small changes, to allow europrog to work again.
#
#When did this stop to work ? basically when you store "$1" in a variable, it becomes 0.
#Before it was "".
#
#Also the figure checking the controller number was increased to 100.
#
#Revision 3.4 2013/04/10 08:58:26 witsch
#error found by Roberto:
#when in the interactive part of europrog, after going to ".hold", the following run
#yielded an error about being unable to write to modbus address 22.
#
#This address is the program number (inside the e2400) to be executed.
#
#Bug has been corrected to only execute that part of the macro, when the controller is
#in RESET.
#
#Also corrected when the dection of the unit number went wrong and showed odd strings like
#Try one of the following arguments: 1max_progs, 1setpoint, 1pnum, 1max_segments, 1scale, 1firmwareversion, 1ident.
#
#Revision 3.3 2013/02/07 15:09:50 witsch
#bugfix: where one physical controller could be defined as counter and motor controller.
#if different controllers had the MAC_MOT index number, there could be a clash. Corrected!
#
#feature request from Roberto, for a motor_par(x, "ramprate") done!
#
#Revision 3.2 2012/11/12 15:55:19 witsch
#Bug fixed, where the read out of ramp rates and parameters were wrong, when controller
#was in Full/Integer Precision.
#
#Macro euro2400showpid allows to use other than unit 0.
#Macro euro2400menu did not declare bla[] local, which lead to an error, when bla was used
#globally.
#
#Revision 3.1 2012/11/09 13:36:52 witsch
#Fixed bug, where it was impossible to do a reset from europrog, when the E2400 macro motor
#controller was not in index position 0.
#Added printout of TANGO_ERR[0][desc] in case of a tango error.
#Changed the behaviour of the set point motor position. When not using the "eurowaitformove"
#
#feature, the position will be the value returned from the modbus address 2
#
#Revision 3.0 2012/06/20 08:00:58 witsch
#after a number of changes, a major release to mark the
##step, that has been made in making the macros more reliable.
#
#Revision 2.11 2012/05/14 12:30:00 witsch
#make the skipsegement work.
#
#make sure there is no ramping in the normal setpoint movement, when
#trying to run a program.
#
#Revision 2.10 2011/11/10 13:05:50 witsch
#Rewrite of the europrogreset macro. Apparently a bug had slipped into it.
#
#
#The new version will allow for a non-interactive setting of the setpoint
#The unit is set as "unit=<unit>" option.
#
#Please consult help local eurotherm2400 and search to europrogreset.
#
#Revision 2.9 2011/07/06 10:32:16 witsch
#some of the __europrog_show calls were missing the unit as 4 arg.
#
#Reprogram the europrogreset macro to allow a smooth setting of the
#temperature setpoint, to avoid the connected power supply plunging deep
#only to pump high power after a second.
#
#Revision 2.8 2011/02/16 15:06:14 witsch
#To treat the conflict between europrog from temperature.mac or
#eurotherm2400.mac all macros concerned with europrog were moved to the
#end of the file.
#Eurotherm2400.mac will overwrite the europrog macros, if they come from
#temperature.mac.
#
#Revision 2.7 2010/12/03 15:36:55 witsch
#A bug in E2400_config, where a MAC_CNT controller was declared unusable, was corrected.
#
#Added a list of Modbus addresses to the documentation.
#
#Added functions _euro_set_serial_parameters and _euro_set_serial_parameters
#to allow a search for a possible BiSync configuration of the controller. Thi
#is a often asked for feature, although the bad serial line cable is in 99%
#of the cases the problem!
#
#Revision 2.6 2010/05/07 13:38:56 perez
#Fix another bug in europrog
#
#Revision 2.5 2010/05/07 10:27:35 perez
#Fix bug on europrog/write missing unit field
#
#Revision 2.4 2009/12/11 10:22:27 witsch
#problem with setting a program number in tag address 22, when the programmer
#type only offered a single possible program.
#surrounded by an if () {}
#
#Revision 2.4 2009/12/09 11:22:51 witsch
#problem with setting a program number in tag address 22, when the programmer
#type only offered a single possible program.
#surrounded by an if () {}
#
#Revision 2.3 2009/12/01 16:39:01 witsch
#there was a problem with a variable declared global (loop) which already
#existed as a macro. Change variable name to _e2400_loop and others as well.
#
#Revision 2.2 2009/12/01 13:44:01 witsch
#perform reset during E2400_config to make sure the tango interface works.
#
#Revision 2.1 2009/11/17 13:53:46 witsch
#bug in euro2400parameters :
#local ushort array data[120] was declared only if there were no args. Moved
#it up to the beginning of the macro.
#
#Revision 2.0 2009/07/17 09:43:28 witsch
#added "abort_one" part to E2400_cmd, which allows to stop the macro motor, when a ^C is hit.
#Added setting the second set of PID values in euro2400menu.
#
#Revision 1.17 2009/07/06 08:07:47 witsch
#Several major changes:
#* changed E2400_config to check for cell 12550 for scaling. Happened on BM01
# that 12550 was 1 and the values read were all integers.
#* Added macro euro2400menu, which allows to set ramprate and timeunit, as
# well as the holdback behaviour of a simple setpoint change. This will allow
# a simple "mv" to act as a ramp, which in turn will allow simple scans, with
# ccd integration to work.
#* added macro eurowaitformove, which will switch the motor to wait for the
# end of movement or not. (former motors_run)
#* some cosmetics, like make sure that data is always local and not global.
#
#Revision 1.16 2009/06/16 09:15:38 witsch
#* improved the lack of tango_[io|get] in older versions of spec.
#* include a firmware version check, as the version 4.11 don't do a
# [rw]_float treatment of the temperature values.
#* fixed bug, where the max_segment member wasn't addressed correctly.
#* reintroduce the "get_status " function in E2400_cmd. This will allow
# for umv to show the set point move, with the changes below.
#* new function "euro2400parameters", which allows to move the setpoint at
# at ramprate with a ramprateunit. umv will show the moving setpoint with
# the previous change.
#* in function "_europrog_status()" deleted possibility to skip segment.
#
#Revision 1.15 2009/06/09 12:17:08 witsch
#some little cosmetic changes.
#
#change the usage of a motor returning .error. from the _config function, when it had no programmable segments. This is the case,
#when the secondary E2400 is used.
#
#Revision 1.14 2009/05/18 14:29:51 witsch
#many little changes and bugfixes.
#
#Most notably the problem with the ramp rate and dwell times, where the
#value had to be multiplied with 10 to be correct, has been solved.
#
#Added macro euro2400_configuration, which allowes to change some of the
#configuration in config mode. This comprises: Resolution,
#DecimalPlaces, LowRangeLimit, HighRangeLimit and InstrumentUnits.
#
#Revision 1.13 2009/05/04 15:35:34 witsch
#put the ident treatment into the E2400_config function.
#
#the config now configures the controller and then each counter/motor.
#
#_europrog_write() will now offer an error message, if the input value is
#higher than the allow value programmed in mb address 12.
#
#deleted output power macro.
#
#Revision 1.12 2009/04/28 15:14:25 witsch
#Introduction of the Eurotherm tango server into the macros.
#
#Couple the access routines to the access point (device server or serial line)
#, rather than the controller unit number. The latter might create
#multiple entries into the global associative array (__E2400) for a single
#access point.
#
#The macro motor command function with argument "count" will read the polled
#value of the device server rather than read any modbus tag addresses from
#the device.
#
#Revision 1.11 2009/04/21 08:32:03 witsch
#after testing multiple controllers can be used.
#
#Change in _config, to set max_segments for controllers with only counters.
#
#Revision 1.10 2009/04/08 15:30:53 witsch
#quite some bug fixes, thanks to Roberto and Wouter.
#
#Revision 1.9 2009/03/31 15:05:16 witsch
#large changes program editing. Added saving a program after its definition
#as a scan in the data file. (and perhaps more).
#
#Revision 1.7 2008/12/08 10:30:52 witsch
#more cosmetics
#
#Revision 1.5 2008/10/06 12:07:40 witsch
#added a euro2400status macro. plus some cosmetic work.
#
#Revision 1.4 2008/09/29 08:31:22 witsch
#the channel number of a counter can now be the modbus tag address.
#In the _config, that means that the modbus address is fished out of
#E2400_ADDR.
#
#Revision 1.3 2008/09/25 15:34:00 witsch
#lots of bug fixes, help local eurotherm2400 should yield something
#intelligible.
#
#Revision 1.2 2008/09/18 06:51:14 witsch
#rename file and macros and internal varialbes to E2400. Handbook says, the
#definition of programs is only for 2400 family.
#
#Revision 1.1 2008/09/10 11:43:10 witsch
#Initial revision
#
"""
if (!(whatis("__e2400debug") & 2)) rdef __e2400debug \'#$*\'
def __e2400_put(unit, fwaddr, value) '{
if (__E2400[__E2400[unit]]["type"]) {
return __e2400_put_tango(__E2400[unit], fwaddr, value)
}
else {
return __e2400_put_serial(__E2400[unit], fwaddr, value)
}
}
'
def __e2400_blockput(unit, fwaddr, num, data) '{
if (__E2400[__E2400[unit]]["type"]) {
return __e2400_blockput_tango(__E2400[unit], fwaddr, num, data)
}
else {
return __e2400_blockput_serial(__E2400[unit], fwaddr, num, data)
}
}
'
def __e2400_get(unit, fwaddr, num, data) '{
if (__E2400[__E2400[unit]]["type"]) {
return __e2400_get_tango(__E2400[unit], fwaddr, num, data)
}
else {
return __e2400_get_serial(__E2400[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 over serial line,
# it is crucial, that people see this!!!
if (!(whatis("modb_rtu_addnode") & 2)) {
global _hw_bla
rdef modb_rtu_addnode() \'
eprint "##########################################################"
eprint "##########################################################"
eprint "##### #####"
eprint "##### Sorry, your lacking the modbus-rtu.mac file! #####"
eprint "##### #####"
eprint "##########################################################"
eprint "##########################################################"
return -1
\'
_hw_bla=1
}
# only here, modbus_rtu_addnode is a valid macro function
if (_hw_bla) {
modb_rtu_addnode("bla")
unglobal _hw_bla
}
# older versions of Spec don`t have tango_io(), but the macros need it, even
# when not using it.
if (whatis("tango_io") == 0) {
__StR__ = "def tango_io() \'{print \"no tango_io\"}\'"
eval(__StR__)
__StR__ = "def tango_get() \'{print \"no tango_get\"}\'"
eval(__StR__)
unglobal __StR__
}
#%UU%
#%MDESC% toggle debug mode for the present macros.
def e2400_debug '{
if ((whatis("__e2400debug")>>16) <= 5) { # just a # sign -> off
rdef __e2400debug "eprint \"Euro: \""
print "e2400 debug is ON"
} else {
rdef __e2400debug \'#$*\'
print "E2400 debug is OFF"
}
}
'
#%IU%(node, fwaddr, value)
#%MDESC%
# Called by spec
def __e2400_put_serial(node, fwaddr, value) '{
__e2400debug "__e2400_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 __e2400_blockput_serial(node, fwaddr, nwords, data) '{
__e2400debug "__e2400_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 __e2400_get_serial(node, fwaddr, num, data) '{
__e2400debug "__e2400_get_serial(" node "," 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 __e2400_put_tango(server, fwaddr, value) '{
local short array x[2];
x[0]=fwaddr; x[1]=value;
__e2400debug "__e2400_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 __e2400_blockput_tango(server, fwaddr, nwords, data) '{
local short array x[2+nwords]
x[0]=fwaddr; x[1]=nwords; x[2:] = data
__e2400debug "__e2400_blockput_tango(" server ", " fwaddr ", " nwords ", data)"
__e2400debug 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 __e2400_get_tango(server, fwaddr, num, data) '{
local short array x[2], y[num];
local i, got
x[0]=fwaddr; x[1]=num;
got = tango_io(server, "ReadHoldingRegisters", x, y);
if (TANGO_ERR != "0") {
# eprint TANGO_ERR_STACK["0"]["desc"];
return(-1)
}
data = y
__e2400debug "__e2400_get_tango(" server ", " fwaddr ", " num ", data) =", y
return(0)
}
'
def __euro_rfloat(unit, val) '{
__e2400debug "__euro_rfloat(" unit ", " val ")"
return(val)
}
'
def __euro_wfloat(unit, val) '{
return(int(val))
}
'
#%IU%(mne, type, unit, mod, chan)
#%MDESC%
# Called by spec
def E2400_config(mne, type, unit, mod, chan) '{
# chan will be used for counters to designate the tag address. Leave alone.
__e2400debug "Configuring Eurotherm", mne, type, unit, mod, chan
global __E2400[]
# list_init __E2400 never do that. It deletes our array!
__E2400["savedir"] = BLISSADM "/local/userconf/" SPEC "/"
local ushort array data[120]
"""
# Using unit as the serial line number, must be already defined, and chan as
# modbus address, use E2400_ADDR to grab Modbus address.
# E2400_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 2400 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(E2400_ADDR, aux)) == 2) {
modbaddr = aux[1] # only used with direct serial line, tango ds has a
# property for that.
E2400_ADDR = aux[0]
} else if (x == 1) {
modbaddr = 1
}
if (length(E2400_ADDR) > 2) { # modbus tango ds !
if (tango_io(E2400_ADDR, "State") == -1) {
eprint "Eurotherm: can`t reach", E2400_ADDR "!!!"
return ".error."
}
# perform reset to make sure the interface will work.
if (tango_io(E2400_ADDR, "Init") == -1) {
eprint "Eurotherm: can`t reach", E2400_ADDR "!!!"
return ".error."
}
# If a counter controller and a motor controller have the same
# index number but differenct addresses, we need to create an
# escape unit, which should allow to use both. This needs, of
# course, to be exploited in the E2400_{cmd,par} macro functions!
# Usually the counter controllers should be configured after the
# motor controllers.
if ((__E2400[unit] != 0) && (__E2400[unit] != E2400_ADDR)) {
unit = "C" unit # should make a C0 or C1 or C2
}
__E2400[unit] = E2400_ADDR
# now check if is a naked Modbus or a eurotherm ds.
TANGO_ERR = -1
if (tango_get(E2400_ADDR, "Temperature") != -1) {
__E2400[__E2400[unit]]["type"] = 2 # use tango_get Temperature to read PV
} else {
# naked modbus server
__E2400[__E2400[unit]]["type"] = 1 # which put/get function to use
}
} else { # assume a Spec serial line.
need modbus-rtu # load the necessary modbus rtu macros
local name
name = unit ":" modbaddr
if ((__E2400[unit] = modb_rtu_addnode(E2400_ADDR, modbaddr, name)) < 0) {
tty_cntl("updated?"); tty_cntl("ce"); printf("\r")
global ___e2400_check_bisync
if ((time() - ___e2400_check_bisync) < 20) {
return ".error."
}
___e2400_check_bisync = time()
if (ser_par(E2400_ADDR, "data_bits") != 8) {
# give it a shot at checking BiSync prot.
tty_cntl("mr")
local aux[]
eprint "E2400_config: No answer from Eurotherm, checking if it is on BiSync:"
aux = _euro_search_bisync(E2400_ADDR)
if (aux["addr"] == -1) {
eprint "E2400_config: Still no answer from Eurotherm, check cable!"
}
else {
eprint "E2400_config: Controller answered via BiSync protocol!"
eprint "Please switch to modbus ..."
eprint "(http://wikiserv.esrf.fr/bliss/index.php/Eurotherm_BySync_Configuration)"
}
}
tty_cntl("se")
return ".error."
}
__E2400[__E2400[unit]]["type"] = 0
}
delete __E2400[__E2400[unit]]["max_progs"]
if (__e2400_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: 2400 6: 1/16 din 2: VP
# 8: 1/8 din
# 4: 1/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."
}
__E2400[__E2400[unit]]["ident"] = sprintf("%x00", ident >> 8)
"""
# There is the possibility to config the 2400 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. The sample environment controllers have the
# 0x0461 version.
"""
if (__e2400_get(unit, 107, 1, data)) {
return ".error."
}
__E2400[__E2400[unit]]["firmwareversion"] = data[0]
# full (floating) or integer resolution, decides on wheather the modbus
# values are like on the display or not.
if (__e2400_get(unit, 12550, 1, data)) {
return ".error."
}
if (data[0] == 1) { # Full resolution
rdef __euro_ramp_rate_read_helper(unit,val) \'{return val}\'
rdef __euro_ramp_rate_write_helper(unit,val) \'{return val}\'
rdef __euro_rfloat(unit, val) \'{return val}\'
rdef __euro_wfloat(unit, val) \'{return val}\'
__E2400[__E2400[unit]]["scale"] = 1
} else { # Integer resolution
rdef __euro_ramp_rate_read_helper(unit,val) \'{return val / 10}\'
rdef __euro_ramp_rate_write_helper(unit,val) \'{return val * 10}\'
if (__e2400_get(unit, 525, 1, data)) {
return ".error."
}
__E2400[__E2400[unit]]["scale"] = pow(10, data[0])
local str
str = "{return(val / " __E2400[__E2400[unit]]["scale"] ")}"
rdef __euro_rfloat(unit, val) str
str = "{return(int(val * " __E2400[__E2400[unit]]["scale"] "))}"
rdef __euro_wfloat(unit, val) str
}
"""
# 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 (__e2400_get(unit, 211, 1, data)) {
return ".error."
}
if (data[0] == 32768) { # not controlling a heater or cooler ?!?
;
} else {
__E2400[__E2400[unit]]["max_segments"] = x > 16 ? 16 : data[0]
# set to auto mode
if (__e2400_put(unit, 273, 0)) {
return ".error."
}
}
if (__e2400_get(unit, 517, 1, data)) {
return ".error."
}
__E2400[__E2400[unit]]["max_progs"] = data[0]
}
else if (type == "mot") {
return E2400_cmd(mne, "position")
}
}
'
#%IU%
#%MDESC%
# Called by spec
def E2400_cmd(mne, cmd, p1, p2, p3) '{
local unit, module, channel, x, ieeevals, retval
local ushort array data[120]
unglobal ___e2400_check_bisync
ieeevals = retval = 0
__e2400debug "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")
if (channel > 900) {
channel -= 900
ieeevals = 1
}
# make a move
# check status, only answer if status is RESET
if (__e2400_get(unit, 23, 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 (__e2400_put(unit, 2, __euro_wfloat(unit, p1))) {
return ".error."
}
__E2400[__E2400[unit]]["setpoint"] = p1
}
else if (cmd == "position") {
unit = motor_par(mne, "unit")
channel = motor_par(mne, "channel")
if (channel > 900) {
channel -= 900
ieeevals = 1
}
if ((!__E2400[unit][channel]) && (channel = 2)) {
channel = 5 # return 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
if (ieeevals) {
local ushort array bla[2]
if (__e2400_get(unit, 0x8000 + 2*channel, 2, data)) {
return(".error.")
}
# take 32 bit just read and turns into IEEE float
retval = __euro__float(data)
}
else {
if (__e2400_get(unit, channel, 1, data)) {
return ".error."
}
retval = __euro_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 (!__E2400[unit][channel]) {
return 0
}
if (channel > 900) {
channel -= 900
}
if (channel != 2) {
return(0)
}
# check status, only answer if status is RESET
if (__e2400_get(unit, 23, 1, data)) {
return ".error."
}
if (data[0] != 1) {
return ".error."
}
# compare with where to go
local wsp, sp
wsp = sp = 0
if (__e2400_get(unit, 5, 1, data)) {
return ".error."
}
wsp = __euro_rfloat(unit, data[0])
if (__e2400_get(unit, 2, 1, data)) {
return ".error."
}
sp = __euro_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 E2400 counter
# controller. there seems, however, to be a limit of 999 in Spec
unit = counter_par(mne, "unit")
# goodness, this is tedious. If there is a counter controller with
# the same index number as a motor controller, the unit will be
# preceeded with a "C". But only if the units are different. So
# check if C1 or whatever it is here exists and if not, go on with
# the unit number without the C.
local cunit
cunit = "C" unit
if (__E2400[cunit] != 0) {
unit = cunit
}
channel = counter_par(mne, "channel")
if (channel > 900) {
channel -= 900
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
if (ieeevals) {
# Temperature reading with the Eurotherm2400 tango server
if((__E2400[__E2400[unit]]["type"] == 2) && (channel = 1)) {
return(tango_get(__E2400[unit], "Temperature"))
}
# and the rest is for Modbus servers and serial line comm.
local y
if (__e2400_get(unit, 0x8000 + 2*channel, 2, data)) {
return ".error."
}
retval = __euro__float(data) # takes 32 bit just read and returns IEEE float
}
else {
if (__e2400_get(unit, channel, 1, data)) {
return ".error."
}
retval = __euro_rfloat(unit, data[0])
}
# return count
#
return(retval)
# end of count, this will read the value and return it!
}
else if (cmd == "abort_one") {
unit = motor_par(mne, "unit")
channel = motor_par(mne, "channel")
if (channel > 900) {
channel -= 900
}
if (channel != 2) {
return(0)
}
# check status, only answer if status is RESET
if (__e2400_get(unit, 23, 1, data)) {
return ".error."
}
if (data[0] != 1) {
return ".error."
}
# set the current temperature as setpoint
if (__e2400_get(unit, 1, 1, data)) {
return ".error."
}
if (__e2400_put(unit, 2, data[0])) {
return ".error."
}
return(0)
}
}
}
'
#%IU%
#%MDESC%
# Called by spec with different keys to handle motor_par(mot,"key",new_value)
# actions.
def E2400_par(num, key, action, p1, p2) '{
__e2400debug "Parameters Eurotherm", num, key, action, p1, p2
unit = motor_par(num, "unit")
channel = motor_par(num, "channel")
if ((key == "?") && (action == "get")) {
return("waitformv, ramprate")
}
else if (key == "waitformv") {
if (action == "get") {
return __E2400[unit][channel]
}
else {
__E2400[unit][channel] = p1
}
}
else if (key == "ramprate") {
local ushort array data[120]
if (action == "get") {
if (__e2400_get(unit, 35, 1, data)) {
return ".error."
}
return data[0]
}
else {
if (__e2400_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 12550) 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[]
__e2400debug "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
__e2400debug sprintf("converting %04x and %04x to %g\n", inarr[0]&0xffff, inarr[1]&0xffff, x)
return(x)
}
'
#%UU%mne
#%MDESC%
# read PV (process value) from the counter with mnemonic mne. Allows to trigger
# a read-out without counting. Value is returned, i.e. x = euro_read euro01
def euro_read ' E2400_cmd("$1", "counts")'
#%UU% <manual_setpoint> [unit=<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 euro2400status '{
local ushort array data[4]
local unit, x
_euro_mne2unit $*
# attempt for a kind of startup test
if (STARTUP_TEST) {
print "startup test: unit", unit, numargs, _myarr, prgnum, prgsavefile
unglobal STARTUP_TEST
} else {
if (__e2400_get(unit, 74, 4, data)) {
return(-1)
}
eprint "The 2400s of Eurotherm offer some diagnostics. Here are the 4 bytes"
eprint "one can look at with their bit`s meaning."
# 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 1G"
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."
}
}
'
#%UU% program_number [file_name] <unit=[0|1|2|..] or mne=some-counter-or-motor>
#%MDESC%
# Save the program with number program_number. Optional argument is the
# filename. Files are saved in ~blissadm/local/spec/userconf.
def euro2400saveprogram '{
local unit, prgnum, prgsavefile
prgnum = 1
_euro_mne2unit $*
if (!__E2400[__E2400[unit]]["max_segments"]) {
eprint "$0: you have no controller defined yet!"
eprint " Please correct config."
exit
}
if (! numargs ) {
eprint "$0: use with arguments: program_number [file_name]"
} else {
if (numargs == 1) {
prgnum = _myarr[$# - numargs]
while (prgsavefile == "") {
prgsavefile = getval("Save program under which name","")
}
}
else if (numargs == 2) {
prgsavefile = _myarr[$# - numargs + 1]
}
if (index(prgsavefile, "/") == 1) { # that`s an absolute pathname
prgsavefile = prgsavefile
} else {
prgsavefile = __E2400["savedir"] prgsavefile
}
local str
str = "_" USER
if (!index(prgsavefile, str)) { # already contains user name
prgsavefile = prgsavefile str
}
# attempt for a kind of startup test
if (STARTUP_TEST) {
print "startup test: unit", unit, numargs, _myarr, prgnum, prgsavefile
unglobal STARTUP_TEST
} else {
# array_dump appends data, so we have make sure, the file doesn`t exit
unix(sprintf("/bin/rm -rf %s", prgsavefile))
# read the program to be sure we save the real thing
global ushort array __E2400SAVE[(__E2400[__E2400[unit]]["max_segments"] + 1) * 8]
__E2400SAVE = _europrog_read(unit, prgnum)
__e2400debug "euro2400saveprogram", __E2400SAVE
# now array containes data
create_directory_tree(__E2400["savedir"])
array_dump(prgsavefile, __E2400SAVE)
unglobal __E2400SAVE
# note that if this program calls another one, it would have to be saved
# separately, but this seems rare, so for now, we keep it simple.
close(prgsavefile)
print "Program", prgnum, "saved to", prgsavefile
}
}
}
'
need editfilename
need spec_utils
#%UU% [file_name] [program number] <unit=[0|1|2|..] or mne=some-counter-or-motor>
#%MDESC%
# Restore a program previously saved. Only and optional argument is the
# filename. Files are restored from ~blissadm/local/spec/userconf.
#%BR%
def euro2400restoreprogram '{
local unit, prgnum, prgsavefile
prgnum = -1
_euro_mne2unit $*
if (!__E2400[__E2400[unit]]["max_segments"]) {
eprint "$0: you have no controller defined yet!"
eprint " Please correct config."
exit
}
if (numargs > 0) {
prgnum = _myarr[$# - numargs]
}
if (numargs > 1) {
prgsavefile = _myarr[$# - numargs +1]
}
if (!prgsavefile) {
while (1) {
if (whatis("editfilename") & 2) {
print "Restore program from which file (use TAB to show files):"
prgsavefile = __E2400["savedir"]
prgsavefile = editfilename(prgsavefile)
print
} else {
prgsavefile = getval("Restore program from which name","")
prgsavefile = __E2400["savedir"]
}
if (!file_info(prgsavefile, "-f")) {
eprint "No such file!"
} else {
break
}
}
}
if (prgnum == -1) {
while(1) {
prgnum = getval("Enter the program number you want to restore \
your saved program to:", __E2400[__E2400[unit]]["pnum"])
if ((prgnum >= 0) && (prgnum < __E2400[__E2400[unit]]["max_progs"])) {
break
}
}
}
if ((prgnum < 0) || (prgnum > __E2400[__E2400[unit]]["max_progs"])) {
eprint "$0: Program number is out of bounds, can be 1 to", __E2400[__E2400[unit]]["max_progs"]
exit
}
if (!file_info(prgsavefile, "-f")) {
eprint "No such file!"
exit
}
# attempt for a kind of startup test
if (STARTUP_TEST) {
print "startup test: unit", unit, numargs, _myarr, prgnum, prgsavefile
unglobal STARTUP_TEST
} else {
# read the program to be sure we save the real thing
global ushort array __E2400PRESTORED[(__E2400[__E2400[unit]]["max_segments"] + 1) * 8]
array_read(prgsavefile, __E2400PRESTORED)
# note that if this program calls another one, it would have to be save
# separately, but this seems rare, so for now, we keep it simple.
local astr, __i, __aux[]
__i = split(prgsavefile, __aux, "/"); __i --
astr = "Stored program with name \"" __aux[__i] "\" (without sub-programs!)"
unglobal _europrog_show_call
_europrog_show(astr , 20, __E2400PRESTORED, nosubprog, 1)
_europrog_write(unit, __E2400PRESTORED, prgnum)
}
}
'
#%IU%(starttemp, memarr, nosubprg)
#%MDESC% Save eurotherm 2400 program from array `memarr` into the SPEC
# datafile.
def __europrog_save_as_scan(unit, starttemp, memarr, nosubprg) '{
if (DATAFILE != "") {
ond ; offt
local _mysegtype, _segtstr[], title
local __units[]
__units[0] = "sec"; __units[1] = "min"; __units[2] = "hour";
__units[3] = 1; __units[4] = 60; __units[5] = 3600 ;
_segtstr[0] = "End"
_segtstr[1] = "Ramp(rate)"
_segtstr[2] = "Ramp(time)"
_segtstr[3] = "Dwell"
_segtstr[4] = "Step"
_segtstr[5] = "CallProgram"
if (_europrog_save_call == 0) {
global _europrog_save_call __e2400_loop[]
global __e2400_pli, __e2400_iiisave, _time_units
_time_units = memarr[2]
global float array pleuro[50000][2]
local iii, ii
__e2400_pli = 0
pleuro[0][0] = __e2400_pli
pleuro[0][1] = starttemp
__e2400_pli ++
title = "Eurotherm program definition " memarr[4] " cycle(s)"
# HEADER
printf("\n#S %d %s\n#D %s\n", ++SCAN_N, title, date())
printf("#T %g (%s)\n", __units[_time_units + 3], "Seconds")
_head_par G 0
_head_par U 1
_head_par M 2
printf("#Q %s\n", _hkl_val)
print "#P0"
Fheader
print "#N 2"
print "#L Time Eurotherm program"
_time_units = __units[memarr[2]+3]
}
_europrog_save_call ++
tstart = starttemp
# segments
for (ii = 1 ; ii < __E2400[__E2400[unit]]["max_segments"] ; ii++ ) {
_mysegtype = memarr[ii * 8]
__e2400debug "__europrog_save_as_scan: segment type", _segtstr[_mysegtype], "start at", tstart
if (_mysegtype == 0 ) {
plisave = __e2400_pli
break
}
else if (_mysegtype == 1 ) {
local tstart, tstep, tend, tloop
tend = __euro_rfloat(unit, memarr[(ii * 8) + 1]) # target temp
tstep = __euro_rfloat(unit, memarr[(ii * 8) + 2]) # ramp rate
__e2400debug " __europrog_save_as_scan: ramp/rate!", tend, tstep
tstart = pleuro[__e2400_pli-1][1] # starting temp
# tstep reading is always positiv, even if ramp goes down!
tstep = (tend - tstart) > 0 ? tstep : -tstep
__e2400debug "from", tstart, "to", tend, "stepping", tstep, "degrees"
__e2400debug "starting pli", __e2400_pli
tloop = tstart
for (tloop = tstart; fabs(tend - tloop) > fabs(tstep);) {
pleuro[__e2400_pli][0] = __e2400_pli * _time_units
tloop += tstep
pleuro[__e2400_pli][1] = tloop
__e2400_pli ++
}
if (pleuro[__e2400_pli-1][1] != tend) {
pleuro[__e2400_pli][0] = __e2400_pli * _time_units
pleuro[__e2400_pli][1] = tend
__e2400_pli++
}
__e2400debug " end pli", __e2400_pli
}
else if (_mysegtype == 2 ) {
local tstart, tstep, tend, tloop
tend = __euro_rfloat(unit, memarr[(ii * 8) + 1]) # ramp target
tloop = __euro_rfloat(unit, memarr[(ii * 8) + 2]) # ramp time
__e2400debug "__europrog_save_as_scan: ramp/time!", tend, tloop
tstart = pleuro[__e2400_pli-1][1] # starting temp
tstep = (tend - tstart) / tloop
__e2400debug "from", tstart, "to", tend, "stepping", tstep, tloop, "times"
__e2400debug "starting pli", __e2400_pli
for (_iii = 0; _iii < tloop; _iii++) {
pleuro[__e2400_pli][0] = __e2400_pli * _time_units
pleuro[__e2400_pli][1] = pleuro[__e2400_pli-1][1] + tstep
__e2400_pli ++
}
if (pleuro[__e2400_pli-1][1] != tend) {
pleuro[__e2400_pli][0] = __e2400_pli * _time_units
pleuro[__e2400_pli][1] = tend
__e2400_pli++
}
__e2400debug " end pli", __e2400_pli
}
else if (_mysegtype == 3 ) {
local x2, bla, _iii
x2 = __euro_rfloat(unit, memarr[(ii * 8) + 2]) # dwell time
__e2400debug "__europrog_save_as_scan: dwell!", x2
__e2400debug "starting pli", __e2400_pli
for (_iii = 0; _iii < x2; _iii++) {
pleuro[__e2400_pli][0] = __e2400_pli * _time_units
pleuro[__e2400_pli][1] = pleuro[__e2400_pli-1][1]
__e2400_pli ++
}
__e2400debug " end pli", __e2400_pli
}
else if (_mysegtype == 4 ) { # segment type: step
local x1
x1 = __euro_rfloat(unit, memarr[(ii * 8) + 1])
__e2400debug "__europrog_save_as_scan: step!", x1
pleuro[__e2400_pli][0] = __e2400_pli * _time_units
pleuro[__e2400_pli][1] = x1
__e2400debug "pli", __e2400_pli
__e2400_pli++
}
else if (_mysegtype == 5 ) {
local cycle subprog
cycle = memarr[(ii * 8) + 4]
subprog = memarr[(ii * 8) + 3]
if(nosubprg) {
print "#C The displayed program demands the subprogram", subprog, "for",\
cycle, "cycles"
print "#C They are not included here!!!"
} else {
__e2400debug "subprogram", cycle, "times"
print "#C The displayed program demands the subprogram", subprog, "for",\
cycle, "cycles"
__e2400_loop[_europrog_save_call] = ii
for (; iii < cycle; iii ++ ) {
local bstr
bstr = "__E2400PDATA" subprog
cyclesave = cycle
__e2400_iiisave = iii
__europrog_save_as_scan(unit, pleuro[__e2400_pli-1][1], @bstr)
# after the recursive function call, the on and offs are gone
ond ; offt
# as are the local variables.
__e2400_pli = plisave
iii = __e2400_iiisave
cycle = cyclesave
__units[0] = "sec"; __units[1] = "min"; __units[2] = "hour";
__units[3] = 1; __units[4] = 60; __units[5] = 3600 ;
_segtstr[0] = "End"
_segtstr[1] = "Ramp(rate)"
_segtstr[2] = "Ramp(time)"
_segtstr[3] = "Dwell"
_segtstr[4] = "Step"
_segtstr[5] = "CallProgram"
}
ii = __e2400_loop[_europrog_save_call]
}
}
}
_europrog_save_call --
if (_europrog_save_call == 0) {
__e2400debug "Dumping pleuro[0:" __e2400_pli-1 "]"
array_dump(pleuro[0:__e2400_pli-1])
unglobal _europrog_save_call __e2400_loop[] __e2400_pli pleuro plisave __e2400_iiisave
unglobal cyclesave _time_units
}
offd ; ont
}
}
'
def __e2400_simple '{
# only for holger
# $1 address
# $2 # of bytes
# $3 unit
local _myarr, option, unit, aux[], numargs
_euro_mne2unit $*
local mbaddr
if (!numargs) {
eprint "Give me at least a Modbus address to read!"
exit
} else {
mbaddr = _myarr[$# - numargs + 1]
}
local ushort array data[120]
__e2400_get(unit, mbaddr, 2, data)
numb--
print data[0:numb]
}
'
#%UU% <unit=[0|1|2|..] or mne=some-counter-or-motor>
#%MDESC%
# Show the PID values for unit
#%BR%
def euro2400showpid '{
local _myarr, option, unit, aux[], bla
_euro_mne2unit $*
# attempt for a kind of startup test
if (STARTUP_TEST) {
print "startup test: unit", unit, numargs, _myarr
unglobal STARTUP_TEST
} else {
local ushort array data[120]
local prop, inte, deri
__e2400_get(unit, 6, 1, data)
prop = __euro_rfloat(unit, data[0])
__e2400_get(unit, 8, 2, data)
inte = data[0]
deri = data[1]
print "The PID values are : ", prop, inte, deri
}
}
'
#%UU% <unit=[0|1|2|..] or mne=some-counter-or-motor>
#%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 12550)
#%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 euro2400configuration '{
local currstat, _states, option, unit, x, i, good, usage, bla
_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
local bla[], usine[], doit
# I just hate to have the same strings twice, so use x as first index in usine[].
x = "Resolution"; usine[x]["address"] = 12550; usine[x]["poss"] = "0(Full)|1(Integer)"
x = "DecimalPlaces"; usine[x]["address"] = 525; 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"] = 516; usine[x]["poss"] = "0(C)|1(F)|2(K)|3(None)"
x = "RampRateUnits"; usine[x]["address"] = 531; usine[x]["poss"] = "0(Sec)|1(Min)|2(Hour)"
doit = 0 # any action necessary ?
# unit recognition is done by the _euro_mne2unit macro
_euro_mne2unit $*
local aux[], bla[]
for (option in _myarr) {
split(_myarr[option], aux, "=")
varnam = aux[0]
if (string_tolower(varnam) == "unit")
continue
usine[varnam]["newval"] = aux[1]
@varnam = aux[1]
bla[varnam] = aux[1]
}
# attempt for a kind of startup test
if (STARTUP_TEST) {
print "startup test: unit", unit, numargs, _myarr
unglobal STARTUP_TEST
} else {
local ushort array data[120]
if(!numargs) {
local x, y[], z, oldz
printf("usage: %s \\\n", "$0")
for (x in usine) {
split(x, y, "\034")
z = y[0]
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 (__e2400_get(unit, usine[z]["address"], 1, data)) {
eprint "Couldn`t read value, try a different unit"
exit
}
str = str "(current value: " data[0] ")"
print str
oldz = z
}
}
# 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 (__e2400_get(unit, usine[x]["address"], 1, data)) {
eprint "Can`t read address", usine[x]["address"], "from 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 (__e2400_get(unit, 199, 1, data)) {
eprint "Can`t read address 199 from unit", unit, "!"
exit
}
currstat = data[0]
print "The controller was in", _states[currstat], "mode."
if (__e2400_put(unit, 199, 2)) {
eprint "Can`t write 2 to address 199 on unit", unit, "!"
exit
}
if (__e2400_put(unit, 138, 2)) {
eprint "Can`t write Configuration Level Password to unit", unit, "!"
exit
}
for (x in bla) {
if (string_tolower(x) == "unit") {
continue
}
if (usine[x]["doit"]) {
if (__e2400_put(unit, usine[x]["address"], usine[x]["newval"])) {
eprint "Can`t write", usine[x]["newval"], "to address", usine[x]["address"],\
"on unit", unit, "!"
}
else if (__e2400_get(unit, usine[x]["address"], 1, data)) {
eprint "Can`t read address", usine[x]["address"], "from unit", unit, "!"
}
print x "`s old value was", usine[x]["value"] ", the new value is", data[0]
}
}
# set controller back into normal mode
if (__e2400_get(unit, 199, 1, data)) {
eprint "Can`t read address 199 from unit", unit, "!"
exit
}
currstat = data[0]
if (__e2400_put(unit, 199, 0)) {
eprint "Can`t write 0 to address 199 on 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 (__e2400_get(unit, 199, 1, data) == 0) {
good = 1
break
} else {
tty_cntl("updated?"); tty_cntl("ce"); printf("\r")
}
}
if (! good && __E2400[__E2400[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 (__e2400_get(unit, 199, 1, data)) {
eprint "Can`t read address 199 from 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% <unit=[0|1|2|..] or mne=some-counter-or-motor>
#%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 euro2400parameters '{
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 bla[], usine[], doit
local ushort array data[120]
# 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 = "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"
# this parameter has been found to be part of the system configuration.
# one needs to go to the configuration mode.
# x = "RampRateUnits"; usine[x]["address"] = 531; usine[x]["poss"] = "0(Sec)|1(Min)|2(Hour)"
_euro_mne2unit $*
# ~ split("$*", myarr)
local aux[], bla[]
for (option in _myarr) {
split(_myarr[option], aux, "=")
varnam = aux[0]
if (string_tolower(varnam) == "unit")
continue
usine[varnam]["newval"] = aux[1]
@varnam = aux[1]
bla[varnam] = "" # as a placeholder for the next for()
}
# attempt for a kind of startup test
if (STARTUP_TEST) {
print "startup test: unit", unit, numargs, _myarr
unglobal STARTUP_TEST
} else {
doit = 0 # any action necessary ?
if(!numargs) {
local x, y[], z, oldz, str, len
printf("usage: %s \\\n", "$0")
for (x in usine) {
split(x, y, "\034")
z = y[0]
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 (__E2400[__E2400[unit]]["max_segments"]) {
if (__e2400_get(unit, usine[z]["address"], 1, data)) {
eprint "Can`t access unit", unit, "!"
exit
}
str = str "(current value: " data[0] ")"
}
print str
oldz = z
}
}
exit
}
# the following checks if anything at all needs doing
for (x in bla) {
if (usine[x]["address"]) {
if (__e2400_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"]) {
if (usine[x]["value"] != @x) {
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 (string_tolower(x) == "unit") {
continue
}
if (usine[x]["doit"]) {
if (__e2400_put(unit, usine[x]["address"], usine[x]["newval"])) {
eprint "Can`t access unit", unit, "!"
exit
}
if (__e2400_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!"
}
}
}
'
#%IU%(memarr, unit)
#%MDESC% this is a special macro for europrog
# shows the values in a eurotherm program, just the values without conversion.
def _europrog_show_values(memarr) '{
printf("%-20s %3s %3s", "Holdback type :", memarr[0], memarr[1])
print
printf("%-20s %3s", "Time units :", memarr[2])
print
printf("%-20s %3s", "Run (cycles) :", memarr[4])
print
local _mysegtype
# segments
for (ii = 1 ; ii < __E2400[__E2400[unit]]["max_segments"] ; ii++ ) {
printf("%-10s", "Segment # "); _europrog_print_so(ii); printf(" : ")
_mysegtype = memarr[(ii * 8)]
if (_mysegtype > 5 || _mysegtype < 0 ) {
_europrog_print_error("Type has to be between 0 and 5")
ii --
break
} else {
_mysegtype = memarr[ii * 8]
if (_mysegtype == 0 ) {
local __type[], str
__type[0] = "Reset"; __type[1] = "Indefinite_Dwell"
__type[2] = "SetOutput"
printf("%-11s %3s ", "End", memarr[(ii * 8) + 3])
print
break
}
else if (_mysegtype == 1 ) { # ramp (rate)
local x1, x2
x1 = memarr[(ii * 8) + 1]
x2 = memarr[(ii * 8) + 2]
printf("%-11s ", "Ramp (rate)")
printf( "%3s ", x1)
printf( "%3s ", x2)
print
}
else if (_mysegtype == 2 ) { # ramp (time)
local x1, x2
x1 = memarr[(ii * 8) + 1]
x2 = memarr[(ii * 8) + 2]
printf("%-11s ", "Ramp (time)")
printf( "%3s ", x1)
printf( "%3s ", x2)
print
}
else if (_mysegtype == 3 ) { # dwell
x2 = __euro_rfloat(unit, memarr[(ii * 8) + 2])
printf("%-11s ", "Dwell")
printf( "%3s ", " ")
printf( "%3s ", x2)
print
}
else if (_mysegtype == 4 ) { # Step
local x1
x1 = __euro_rfloat(unit, memarr[(ii * 8) + 1])
printf("%-11s ", "Step")
printf( "%3s ", x1)
print
}
else if (_mysegtype == 5 ) { # Call
local cycle subprog
cycle = memarr[(ii * 8) + 4]
subprog = memarr[(ii * 8) + 3]
printf( "%s", "Call subprog")
printf( "%3s ", subprog)
printf( "%3s ", cycle)
print
}
}
}
}
'
#%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 '{
local mne, unit
_euro_mne2unit $*
local num
num = motor_num(mne)
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"
}
'
#%IU%<unit=[0|1|2|..] or mne=some-counter-or-motor>
#%MDESC% Set various parameters in the controller. Only possible for unit number 0 in
# this spec session.
def euro2400menu '{
# the function assumes that the _show function was used and the
# display started from line 0.
local __units[], _time_units, holdbacktype[], answer[], x, usine[], bla[]
local unit, mne
__units[0] = "seconds"; __units[1] = "minutes"; __units[2] = "hours"
holdbacktype[0] = "OFF"; holdbacktype[1] = "Low"
holdbacktype[2] = "High"; holdbacktype[3] = "Band"
_euro_mne2unit $*
local ii, line, lines
if (COLS < 80) {
eprint "Your window is too small!"
eprint "Please increase size!"
return(-1)
}
# attempt for a kind of startup test
if (STARTUP_TEST) {
print "startup test: unit", unit, numargs, _myarr
unglobal STARTUP_TEST
} else {
for(ii = 0; ii < ROWS; ii ++) {
print
} # don`t do a clear screen as often visual information is lost.
_time_units = memarr[2]
x = "Proportional2"; bla[x] = ""; usine[x]["address"] = 48; usine[x]["dp"] = 1; usine[x]["poss"] = "value"
x = "Integral2"; bla[x] = ""; usine[x]["address"] = 49; usine[x]["dp"] = 0; usine[x]["poss"] = "value"
x = "Derivate2"; bla[x] = ""; usine[x]["address"] = 51; usine[x]["dp"] = 0; usine[x]["poss"] = "value"
x = "Proportional1"; bla[x] = ""; usine[x]["address"] = 6; usine[x]["dp"] = 1; usine[x]["poss"] = "value"
x = "Integral1"; bla[x] = ""; usine[x]["address"] = 8; usine[x]["dp"] = 0; usine[x]["poss"] = "value"
x = "Derivate1"; bla[x] = ""; usine[x]["address"] = 9; usine[x]["dp"] = 0; usine[x]["poss"] = "value"
x = "Current PID set";bla[x] = "";usine[x]["address"] = 72; usine[x]["dp"] = 0; usine[x]["poss"] = "0(Set 1)|1(Set 2)"
x = "RampHoldBack"; bla[x] = ""; usine[x]["address"] = 70; usine[x]["dp"] = 0; usine[x]["poss"] = "0(off)|1(low)|2(high)|3(Band)"
x = "RampHoldValue"; bla[x] = ""; usine[x]["address"] = 65; usine[x]["dp"] = 1; usine[x]["poss"] = "value"
x = "RampRate"; bla[x] = ""; usine[x]["address"] = 35; usine[x]["dp"] = 1; usine[x]["poss"] = "value"
x = "RampRateUnits"; bla[x] = ""; usine[x]["address"] = 531; usine[x]["dp"] = 0; usine[x]["poss"] = "0(Sec)|1(Min)|2(Hour)"
local ushort array data[120]
for (x in bla) {
if (usine[x]["address"]) {
if (__e2400_get(unit, usine[x]["address"], 1, data)) {
eprint "Can`t access unit", unit, "!"
exit
}
usine[x]["value"] = usine[x]["dp"] ? __euro_rfloat(unit, data[0]) : data[0]
}
}
#build the screen
local twid, fwid, format
twid = 17; fwid = 8; format = "%-" twid "s%-2s"
twid += 2 # for the :
tty_cntl("ho") # go home
print " Eurotherm 2400 setpoint parameters" # line 0
print # line 1
x = "RampHoldBack"
printf(format, "Holdback type", ":")
_europrog_print_so(holdbacktype[usine[x]["value"]], fwid)
if (usine[x]["value"]) {
local y
y = "RampHoldValue"
printf(" with ")
_europrog_print_so(usine[y]["value"], 4)
print " degree(s) width"
} else {
print
}
# that was line 2
printf(format, "Time units", ": ")
_europrog_print_so(__units[usine["RampRateUnits"]["value"]], fwid)
print # line 3
printf(format, "Ramp rate", ": ");
_europrog_print_so(usine["RampRate"]["value"], fwid)
print # line 4
print # 5
x = "Current PID set"
printf(format, x, ": ")
_europrog_print_so(usine[x]["value"], fwid)
print # 6
printf(format, "P1", ": ")
_europrog_print_so(usine["Proportional1"]["value"], fwid)
print # 7
printf(format, "I1", ": ")
_europrog_print_so(usine["Integral1"]["value"], fwid)
print # 8
printf(format, "D1", ": ")
_europrog_print_so(usine["Derivate1"]["value"], fwid)
print # 9
printf(format, "P2", ": ")
_europrog_print_so(usine["Proportional2"]["value"], fwid)
print # 10
printf(format, "I2", ": ")
_europrog_print_so(usine["Integral2"]["value"], fwid)
print # 11
printf(format, "D2", ": ")
_europrog_print_so(usine["Derivate2"]["value"], fwid)
print # 12
line = 2
answer[1] = "HWnext field"
while(1) {
if (line == 2) {
x = "RampHoldBack"
usine[x]["newvalue"] = usine[x]["value"]
if (answer[1] == "HWnext field") { # coming from next field
answer = europrog_multiplechoice(usine[x]["value"], holdbacktype, fwid, twid, line)
usine[x]["newvalue"] = answer[0]
if (answer[1] == "HWprevious field") { # goto previous edit
line --
} # else stay on the same line
}
if (line == 2 && usine["RampHoldBack"]["newvalue"]) {
x = "RampHoldValue"
_europrog_print_so(holdbacktype[usine["RampHoldBack"]["newvalue"]], fwid)
printf(" with ")
_europrog_print_so(usine[x]["value"], 4)
print " degree(s) width"
answer = europrog_editfield(usine[x]["value"], fwid, 2*twid, line)
usine[x]["newvalue"] = answer[0] * 1
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else { # previous is the first one on same line
answer[1] = "HWnext field"
}
} else { # no second value to edit
line ++
}
}
if (line == 3) {
x = "RampRateUnits"
answer = europrog_multiplechoice(usine[x]["value"], __units, fwid, twid, line)
usine[x]["newvalue"] = answer[0] * 1
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else {
line --
}
}
if (line == 4) {
x = "RampRate"
answer = europrog_editfield(usine[x]["value"], fwid, twid, line)
usine[x]["newvalue"] = answer[0] * 1
if (answer[1] == "HWnext field") { # goto next edit
line = 6
} else {
line --
}
}
if (line == 6) {
x = "Current PID set"
answer = europrog_editfield(usine[x]["value"], fwid, twid, line)
usine[x]["newvalue"] = answer[0] * 1
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else {
line = 4
}
}
if (line == 7) {
x = "Proportional1"
answer = europrog_editfield(usine[x]["value"], fwid, twid, line)
usine[x]["newvalue"] = answer[0] * 1
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else {
line --
}
}
if (line == 8) {
x = "Integral1"
answer = europrog_editfield(usine[x]["value"], fwid, twid, line)
usine[x]["newvalue"] = answer[0] * 1
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else {
line --
}
}
if (line == 9) {
x = "Derivate1"
answer = europrog_editfield(usine[x]["value"], fwid, twid, line)
usine[x]["newvalue"] = answer[0] * 1
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else {
line --
}
}
if (line == 10) {
x = "Proportional2"
answer = europrog_editfield(usine[x]["value"], fwid, twid, line)
usine[x]["newvalue"] = answer[0] * 1
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else {
line --
}
}
if (line == 11) {
x = "Integral2"
answer = europrog_editfield(usine[x]["value"], fwid, twid, line)
usine[x]["newvalue"] = answer[0] * 1
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else {
line --
}
}
if (line == 12) {
x = "Derivate2"
answer = europrog_editfield(usine[x]["value"], fwid, twid, line)
usine[x]["newvalue"] = answer[0] * 1
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else {
line = 4
}
}
if (line == 13) {
print
break
}
}
for (x in bla) {
if (usine[x]["address"]) {
if (usine[x]["address"] == 531 && \
usine[x]["newvalue"] != usine[x]["value"]) {
local str
str = "euro2400configuration RampRateUnits=" usine[x]["newvalue"]
eval(str)
}
else if (usine[x]["newvalue"] != usine[x]["value"]) {
if ((usine[x]["address"] == 65) && usine[x]["newvalue"] == 0 ) {
; # add 65 doesn`t like 0
}
else {
if (__e2400_put(unit, usine[x]["address"], usine[x]["dp"] ? __euro_wfloat(unit, usine[x]["newvalue"])\
: usine[x]["newvalue"])) {
eprint "Can`t write", usine[x]["newvalue"], "to address", usine[x]["address"],\
"on unit", unit, "!"
}
}
}
}
}
_europrog_print_so("Use \"eurowaitformove mne\" to set a motor to wait for\
the end of a move!")
}
}
'
#-----------------------------------------------------------
#%IU%(mne)
#%MDESC% Setting serial line parameters
def _euro_set_serial_parameters(serialline, parity, speed) '{
global ESRF_ERR
local params[], device, lspeed, lparity, lbytes
device = ser_par(serialline, "device_id")
if (index(device, "/dev/")) {
eprint "_euro_set_serial_parameters(): can`t set parameters on serial line", \
device
return(-1)
}
if (!speed) { lspeed = 9600 }
else { lspeed = speed }
if (parity) { lparity = 3; lbytes = 1 }
else { lparity = 0; lbytes = 0 }
# setting the parameters as wished by the Tango DS
params[ 0] = 3 # SL_TIMEOUT /* timeout parameter */
params[ 1] = 500 # timeout value
params[ 2] = 4 # SL_PARITY /* number of parity bits parameter */
params[ 3] =lparity# even parity
params[ 4] = 5 # SL_CHARLENGTH /* number of data bits parameter */
params[ 5] =lbytes # 7 data bits
params[ 6] = 6 # SL_STOPBITS /* number of stop bits parameter */
params[ 7] = 0 # 1 stop bit
params[ 8] = 7 # SL_BAUDRATE /* baud rate parameter */
params[ 9] =lspeed # baud
ESRF_ERR = -1
esrf_io(device, "DevSerSetParameter", params)
if (ESRF_ERR){
__e2400debug "_euro_set_serial_parameters: esrf_io(\"" device "\", \"DevSerSetParameter\", params) failed!"
return(0)
} else {
return(1)
}
}
'
# this is all a little tedious, I have no way to interrogate the server, what config it
# is running. But I can set it. However, I can ask the spec serial line how many data
# bits it is using. Usually 8 databits mean no parity, 7 databits mean even parity.
# Let`s hope, this assumption is right.
def _euro_search_bisync(serialline, enda) '{
local sl, i, answer, retval[], oldslconf
retval["addr"] = -1
retval["answer"] = ".error."
sl = serialline
if (! enda) end = 4
else end = enda
oldslconf = ser_par(serialline, "data_bits") # issues 7 or 8, 7 is evenp (likely!)
if (oldslconf == 8) { # 8 databits, switsch to raw evenp
_euro_set_serial_parameters(serialline, 1)
}
for (i = 0; i < end; i++) {
ser_put(sl, sprintf("\00400%d%dPV\005", i, i))
sleep(1)
answer = ser_get(sl)
if (answer != "") {
retval["addr"] = i
local l
l = length(answer)
retval["answer"] = substr(answer, 2, l - 3)
break
}
}
if (oldslconf == 8) { # was 8 databits, switsch back to raw evenp
_euro_set_serial_parameters(serialline, 0)
}
return retval
}
'
if (whatis("europrog") & 2) { #macro present
# some cosmetics
tty_cntlred = "[47m[1;31m"
tty_cntlnotred = "[0m"
### introduce some code for the unfortunate choice to have a second europrog
### in eurotherm2400.mac on top of temperature.mac. Mea culpa, Holger
global grepeuroprog
local fname, str
pname = "/tmp/" SPEC rand()
str = "/bin/rm -f " pname
unix(str)
on(pname)
offt
prdef europrog
close(pname)
ont
str = "head -2 " pname
unix(str, grepeuroprog)
if (index(grepeuroprog, "temperature.mac")) {
eprint tty_cntlred \
" europrog is now loaded from eurotherm2400.mac !!!" \
tty_cntlnotred
}
unglobal grepeuroprog tty_cntlred tty_cntlnotred
}
#%IU% (unit, state)
#%MDESC% this is a special macro for europrog
# Sends a program control command to a Eurotherm 2400 series instrument.
def _europrog_cmd(unit, state) '{
local _ret _states
local ushort array data[120]
# 1: Off
# 2: Run
# 4: Hold
# 16 End
# 32: Dwell
# 64 Ramp
_states[1] = "RESET" ; _states[2] = "RUN" ; _states[4] = "HOLD"
_states[8] = "HOLDBACK"; _states[16] = "COMPLETED" ; _states[32] = "Dwell"
_states[64] = "Ramp"
if (__e2400_get(unit, 23, 1, data)) {
return -1
}
_ret = data[0]
printf("\rProgram state is ")
# _ret is not power of 2
if ((_ret-1) & _ret) {
print "UNKNOWN=" _ret
} else {
print _states[_ret]
}
if (!state) return(_ret)
# returns the state
if ((state-1) & state) {
# state is not power of 2
_europrog_status(unit)
} else {
# write the new state
if (__e2400_put(unit, 23, state)) {
eprint
eprint "This state might not be allowed to be set from the state"
eprint "you are in right now!"
}
}
# and read back
if (__e2400_get(unit, 23, 1, data)) {
return -1
}
_ret = data[0]
print "New program state is", _states[_ret]
return _ret
}
'
#%IU% (unit)
#%MDESC% this is a special macro for europrog
# Reads the status of a program on a Eurotherm 2400 series instrument.
# This macro has been adapted from the former one in temperature.mac. There
# seems to be more testing necessary!
def _europrog_status(unit) '{
_states[1] = "RESET" ; _states[2] ="RUN" ; _states[4] = "HOLD";
_states[8] = "HOLDBACK"; _states[16] = "COMPLETED"
local ushort array data[120]
if (__e2400_get(unit, 23, 1, data)) {
return -1
}
_ret = data[0]
printf("\rProgram state is ")
# _ret is not power of 2
if ((_ret-1) & _ret) {
print "UNKNOWN=" _ret
} else {
print _states[_ret]
}
local _tags _ii _val _mytype
_tags[0][1] = \
" Programmer # active : "; _tags[1][1] = "22"
_tags[0][2] = \
" Programmer setpoint : "; _tags[1][2] = "163"; _tags[2][2] = 1
_tags[0][3] = \
" Program cycles remaining : "; _tags[1][3] = "59"
_tags[0][4] = \
" Current segment number : "; _tags[1][4] = "56"
_tags[0][5] = \
" Current segment type : "; _tags[1][5] = "29"
_tags[0][6] = \
" Segment time remaining (secs) : "; _tags[1][6] = "36"; _tags[2][6] = 2
_tags[0][7] = \
" Segment time remaining (mins) : "; _tags[1][7] = "63"; _tags[2][7] = 2
_tags[0][8] = \
" Target setpoint (current segment): "; _tags[1][8] = "160"; _tags[2][8] = 1
for (_ii = 1;_ii < 9; _ii++) {
__e2400debug "_europrog_status" i
if (__e2400_get(unit, _tags[1][_ii], 1, data)) {
return -1
}
if (_tags[2][_ii]) {
_tags[2][_ii] = __euro_rfloat(unit, data[0])
} else {
_tags[2][_ii] = data[0]
}
print _tags[0][_ii] _tags[2][_ii]
sleep(.1)
}
}
'
#%IU%(label, starttemp, memarr, nosubprg, unit)
#%MDESC% this is a special macro for europrog
# Reads a program from a Eurotherm 2400 series instrument.
def _europrog_show(label, starttemp, memarr, nosubprg, unit) '{
local _mysegtype
local __units[], _time_units, holdbacktype[]
__units[0] = "sec"; __units[1] = "min"; __units[2] = "hour"
__units[3] = "seconds"; __units[4] = "minutes"; __units[5] = "hours"
holdbacktype[0] = "OFF"; holdbacktype[1] = "Low";
holdbacktype[2] = "High"; holdbacktype[3] = "Band";
if (_europrog_show_call == 0) {
#print " Reading parameters for" , label
global _europrog_show_call, __e2400_loop[], __e2400_pli
global float array pleuro[512][2]
local x0, x1, y0, y1, iii, ii
__e2400_pli = x0 = y0 = iii = 0
pleuro[0][0] = 0 # first x value is 0
pleuro[0][1] = starttemp
# due to a problem with the data handling with this function, which calls
# itself recursively, the data in __E2400PDATA are lost in a call to self.
if (COLS < 80) {
eprint "Your window is too small!"
eprint "Please increase size!"
return(-1)
}
for(ii = 0; ii < ROWS; ii ++) {
print
} # don`t do a clear screen as often visual information is lost.
_time_units = memarr[2]
tty_cntl("ho") # go home
print " Eurotherm 2400 program definition"
printf("%s", " "); _europrog_print_so(label); print
printf("%-20s", "Holdback type :")
_europrog_print_so(holdbacktype[memarr[0]])
if (memarr[0]) {
printf(" with ");
_europrog_print_so(__euro_rfloat(unit, memarr[1]))
print " degree(s) width"
} else {
print
}
printf("%-20s", "Time units :"); _europrog_print_so(__units[memarr[2]+3])
print
printf("%-20s", "Run (cycles) :"); _europrog_print_so(memarr[4], 2)
printf(" time(s)");
}
_europrog_show_call ++
# segments
for (ii = 1 ; ii < __E2400[__E2400[unit]]["max_segments"] ; ii++ ) {
printf("\n%10s ", label)
printf("%-10s", "Segment # "); _europrog_print_so(ii); printf(" : ")
_mysegtype = memarr[(ii * 8)]
if (_mysegtype > 5 || _mysegtype < 0 ) {
_europrog_print_error("Type has to be between 0 and 5")
ii --
break
} else {
_mysegtype = memarr[ii * 8]
if (_mysegtype == 0 ) {
local __type[], str
__type[0] = "Reset"; __type[1] = "Indefinite_Dwell"
__type[2] = "SetOutput"
printf("%s %-9s %s ", "End", label, "with:")
_europrog_print_so(__type[memarr[(ii * 8) + 3]], 5)
print
break
}
else if (_mysegtype == 1 ) { # ramp (rate)
local x1, x2
x1 = __euro_rfloat(unit, memarr[(ii * 8) + 1])
x2 = __euro_ramp_rate_read_helper(unit, memarr[(ii * 8) + 2])
printf("%-10s", "Ramp (rate)")
printf( "%9s", "target: ")
_europrog_print_so(x1, 5)
printf( "%-9s", " degrees")
printf( "%4s", "at: ")
_europrog_print_so(x2, 5)
printf(" degrees/%s", __units[memarr[2]+3])
__e2400_pli++
pleuro[__e2400_pli][1] = x1
if (pleuro[__e2400_pli][1] > y1) y1 = pleuro[__e2400_pli][1]
local bla
# make sure the ramp rate is not 0! otherwise division by zero error
bla = x2
x2 = bla ? bla : 1
bla = fabs(pleuro[__e2400_pli][1] - pleuro[__e2400_pli-1][1]) # new value - old value
bla /= x2 # devide by ramp rate
pleuro[__e2400_pli][0] = pleuro[__e2400_pli-1][0] + bla # and add to former time
}
else if (_mysegtype == 2 ) { # ramp (time)
local x1, x2
x1 = __euro_rfloat(unit, memarr[(ii * 8) + 1])
x2 = __euro_ramp_rate_read_helper(unit, memarr[(ii * 8) + 2])
printf("%-10s", "Ramp (time)")
printf( "%9s", "target: ")
_europrog_print_so(x1, 5)
printf( "%-9s", " degrees")
printf( "%4s", "in: ")
_europrog_print_so(x2 , 5)
printf(" %s", __units[memarr[2]+3])
__e2400_pli++
pleuro[__e2400_pli][1] = x1
if (pleuro[__e2400_pli][1] > y1) y1 = pleuro[__e2400_pli][1]
local bla
bla = x2 # ramp time
pleuro[__e2400_pli][0] = pleuro[__e2400_pli-1][0] + bla # and add to former time
}
else if (_mysegtype == 3 ) { # dwell
local x2
x2 = __euro_ramp_rate_read_helper(unit, memarr[(ii * 8) + 2])
printf("%-33s", "Dwell")
printf("%5s", "for: ")
_europrog_print_so(x2, 5)
printf(" %s", __units[memarr[2]+3])
__e2400_pli++
pleuro[__e2400_pli][1] = pleuro[__e2400_pli-1][1] # new value is old value
if (pleuro[__e2400_pli][1] > y1) y1 = pleuro[__e2400_pli][1] * 1.01
local bla
bla = x2 # dwell time
pleuro[__e2400_pli][0] = pleuro[__e2400_pli-1][0] + bla # and add to former time
}
else if (_mysegtype == 4 ) { # Step
local x1
x1 = __euro_rfloat(unit, memarr[(ii * 8) + 1])
printf("%-11s", "Step")
printf( "%9s", "to: ")
_europrog_print_so(x1, 5)
printf( "%-9s", " degrees")
__e2400_pli++
pleuro[__e2400_pli][1] = x1
if (pleuro[__e2400_pli][1] > y1) y1 = pleuro[__e2400_pli][1] * 1.01
pleuro[__e2400_pli][0] = pleuro[__e2400_pli-1][0] + .001
# add a little bit to see ups and downs
}
else if (_mysegtype == 5 ) { # Call
local cycle subprog
cycle = memarr[(ii * 8) + 4]
subprog = memarr[(ii * 8) + 3]
printf( "%s", "Call subprog")
printf("%8s", "# ")
_europrog_print_so(subprog, 5)
printf( "%8s", "")
printf( "%5s", "")
_europrog_print_so(cycle, 5)
printf( " %s", "times")
if(!nosubprg) {
__e2400_loop[_europrog_show_call] = ii
# do not read here, as the plotting, showing and saving are confused
#_europrog_read(unit, subprog)
local ushort array subprgdat[(__E2400[__E2400[unit]]["max_segments"] + 1) * 8]
subprgdat = _europrog_read(unit, subprog)
for (; iii < cycle; iii ++ ) {
local astr, bstr
astr = "Subprog " subprog
_europrog_show(astr, 0, subprgdat, nosubprog, unit)
}
ii = __e2400_loop[_europrog_show_call]
}
}
}
}
# plotting part
if (pleuro[1][1]) { # second point is non null, so plot
x0 = -.2
x1 = pleuro[__e2400_pli][0] + .2
y1 *= 1.01
local cntlstr
cntlstr = "filter2,open,geometry=+50-50,title=Eurotherm 2400 " label
plot_cntl(cntlstr)
plot_cntl("erase")
plot_cntl("lines")
plot_cntl("-ebars")
plot_cntl("colors=49:16:9:3:2:3:11:6")
plot_move(0,2,"Degrees")
plot_cntl("colors=::2")
cntlstr = "Eurotherm 2400 Definition: " label
plot_move(20, 1, cntlstr)
#plot_cntl("colors=::3")
#plot_move(20, 2, "setpoint")
#plot_cntl("colors=49:16:9:3:2:3:11:6")
# this is necessary, as Spec forget the local variables after the first return
__units[0] = "sec"; __units[1] = "min"; __units[2] = "hour";
__units[3] = 1; __units[4] = 60; __units[5] = 3600 ;
local tstr
tstr = "Time (" __units[memarr[2]] ")"
plot_move(0,-1, tstr)
plot_range("auto","auto","auto","auto")
#plot
#plot_cntl("erase")
#plot_range(0, x1, 0, y1)
array_plot(pleuro[0:__e2400_pli])
}
_europrog_show_call --
if (_europrog_show_call == 0) {
printf("\n End type... this is the last segment !!!\n")
if(!nosubprg) {
print "The time for each cycle should be close to", \
int(pleuro[__e2400_pli][0]), __units[memarr[2]]
}
unglobal _europrog_show_call, iii, __e2400_pli
}
plot_cntl("filter1")
}
'
#%IU% (memarr, label, unit)
#%MDESC% this is a special macro for europrog
# Edit a program
#
# Adress calculation is base address 8192 plus number of program (skip 0) times
# 17*8
def _europrog_edit(memarr, label, unit) '{
# the function assumes that the _show function was used and the
# display started from line 0.
local __units[], _time_units, holdbacktype[], answer[], _segt[]
__units[0] = "seconds"; __units[1] = "minutes"; __units[2] = "hours"
holdbacktype[0] = "OFF"; holdbacktype[1] = "Low"
holdbacktype[2] = "High"; holdbacktype[3] = "Band"
_segt[0] = "End" ; _segt[1] = "Ramp (rate)"; _segt[2] = "Ramp (time)"
_segt[3] = "Dwell "; _segt[4] = "Step"; _segt[5] = "Call program"
local ii _mysegtype, line, lines, numseg
line = 2
answer[1] = "HWnext field"
while(1) {
if (line == 2) {
if (answer[1] == "HWnext field") { # coming from next field
answer = europrog_multiplechoice(memarr[0], holdbacktype, 4, 20, line)
memarr[0] = answer[0]
if (answer[1] == "HWprevious field") { # goto previous edit
line --
} # else stay on the same line
}
if (line == 2 && memarr[0]) {
answer = europrog_editfield(__euro_rfloat(unit, memarr[1]), 2, 33, line)
memarr[1] = __euro_wfloat(unit, answer[0])
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else { # previous is the first one on same line
answer[1] = "HWnext field"
}
} else { # no second value to edit
line ++
}
}
if (line == 3) {
answer = europrog_multiplechoice(memarr[2], __units, 8, 20, line)
memarr[2] = answer[0]
memarr[3] = memarr[2]
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else {
line --
}
}
if (line == 4) {
answer = europrog_editfield(memarr[4], 2, 20, line)
memarr[4] = answer[0]
if (answer[1] == "HWnext field") { # goto next edit
line ++
} else {
line --
}
}
# segments
if (line > 4) {
ii = line - 4
_mysegtype = memarr[(ii * 8)]
# it would be nice to offer to insert a segment here. One could perhaps
# shift the data of the current and following segments up.
line = 4 + ii
printf("\n%10s ", label)
printf("%-10s", "Segment # "); _europrog_print_so(ii); printf(" : ")
# this will be common for all segments
answer = europrog_multiplechoice(_mysegtype, _segt, 14, 25, line)
memarr[ii * 8] = _mysegtype = answer[0]
if (answer[1] == "HWprevious field") { # goto previous edit
line --
continue
#} else {
# line ++
# continue
}
if (_mysegtype == 0 ) {
local _endt[]
_endt[0] = "Reset"; _endt[1] = "Indefinite_Dwell"
_endt[2] = "SetOutput"
x1 = memarr[(ii * 8) + 3] > 2 ? 0 : memarr[(ii * 8) + 3]
printf("%s %-9s %s ", "End", label, "with:")
_europrog_print_so(_endt[x1], 5)
answer = europrog_multiplechoice(x1, _endt, 9, 45, line)
memarr[(ii * 8) + 3] = answer[0]
_europrog_print_so(_endt[answer[0]], 5)
if (answer[1] == "HWprevious field") { # goto previous edit
line --
} else {
tty_cntl("cd"); print; print
break
}
}
else if (_mysegtype == 1 ) { # ramp (rate)
local x1, x2
x1 = __euro_rfloat(unit, memarr[(ii * 8) + 1])
x2 = __euro_ramp_rate_read_helper(unit, memarr[(ii * 8) + 2])
tty_cntl("ce")
printf("%-10s", "Ramp (rate)")
printf( "%9s", "target: ")
_europrog_print_so(x1, 5)
printf( "%-9s", " degrees")
printf( "%4s", "at: ")
_europrog_print_so(x2, 5)
printf(" degrees/%s", __units[memarr[2]])
answer = europrog_editfield(x1, 5, 45, line)
memarr[(ii * 8) + 1] = __euro_wfloat(unit, answer[0])
answer = europrog_editfield(x2, 5, 63, line)
memarr[(ii * 8) + 2] = __euro_ramp_rate_write_helper(unit, answer[0])
}
else if (_mysegtype == 2 ) { # ramp (time)
local x1, x2
x1 = __euro_rfloat(unit, memarr[(ii * 8) + 1])
x2 = __euro_ramp_rate_read_helper(unit, memarr[(ii * 8) + 2])
tty_cntl("ce")
printf("%-10s", "Ramp (time)")
printf( "%9s", "target: ")
_europrog_print_so(x1, 5)
printf( "%-9s", " degrees")
printf( "%4s", "in: ")
_europrog_print_so(x2 , 5)
printf(" %s", __units[memarr[2]])
answer = europrog_editfield(x1, 5, 45, line)
memarr[(ii * 8) + 1] = __euro_wfloat(unit, answer[0])
answer = europrog_editfield(x2, 5, 63, line)
memarr[(ii * 8) + 2] = __euro_ramp_rate_write_helper(unit, answer[0])
}
else if (_mysegtype == 3 ) { # dwell
local x2
x2 = __euro_ramp_rate_read_helper(unit, memarr[(ii * 8) + 2])
tty_cntl("ce")
printf("%-33s", "Dwell")
printf("%5s", "for: ")
_europrog_print_so(x2, 5)
printf(" %s", __units[memarr[2]])
answer = europrog_editfield(x2, 5, 63, line)
memarr[(ii * 8) + 2] = __euro_ramp_rate_write_helper(unit, answer[0])
}
else if (_mysegtype == 4 ) { # Step
local x1
x1 = __euro_rfloat(unit, memarr[(ii * 8) + 1])
tty_cntl("ce")
printf("%-11s", "Step")
printf( "%9s", "to: ")
_europrog_print_so(x1, 5)
printf( "%-9s", " degrees")
answer = europrog_editfield(x1, 5, 45, line)
memarr[(ii * 8) + 1] = __euro_wfloat(unit, answer[0])
}
else if (_mysegtype == 5 ) { # Call
local cycle subprog
subprog = memarr[(ii * 8) + 3]
cycle = memarr[(ii * 8) + 4]
tty_cntl("ce")
printf( "%s", "Call subprog")
printf("%8s", "# ")
_europrog_print_so(subprog, 5)
printf( "%8s", "")
printf( "%5s", "")
_europrog_print_so(cycle, 5)
printf( " %s", "times")
answer = europrog_editfield(subprog, 5, 45, line)
memarr[(ii * 8) + 3] = answer[0]
answer = europrog_editfield(cycle, 5, 63, line)
memarr[(ii * 8) + 4] = answer[0]
}
if (answer[1] == "HWprevious field") { # goto previous edit
line --
} else {
line ++
}
}
# if (line < 2) line = lines
# if (line > lines) line = 2
# if (line == lines) {
# local __ok[], __x
# __ok[0] = "Done"; __ok[1] = "Continue"
# tty_move (3, line, "Program Editing")
# answer = europrog_multiplechoice(__x, __ok, 8, 20, line)
# if (!answer[0]) {
# print
# break
# } else {
# line = 2
# }
# }
}
sleep(.1)
return(memarr)
}
'
#%IU% (unit, euro_program_number)
#%MDESC% this is a special macro for europrog
# Reads a program from a Eurotherm 2400 series instrument.
def _europrog_read(unit, program_number) '{
local pdata # name of data array
pdata = "__E2400PDATA" program_number
global ushort array @pdata[(__E2400[__E2400[unit]]["max_segments"] + 1) * 8]
# above, array name and array itself must be global to be retained through
# recursive calls.
local _mysegtype, __pb
local ushort array data[41]
__E2400["pb"] = 8192 # programming base
__pb = __E2400["pb"] + (program_number * 17 * 8)
if (program_number > __E2400[__E2400[unit]]["max_progs"]) {
eprint "Program number too high!"
return -1
}
# print " Reading parameters from program number" , program_number
# One program has 16 segments plus general data. 17 * 8 = 136. As 125 words
# is max read (3 times 5 plus 2) times 8.
if (__e2400_get(unit, __pb, 5 * 8, @pdata)) {
eprint "\n\n_europrog_read: Problem reading data from unit", unit
eprint "Exited ! Please try again!"
exit
}
if (__e2400_get(unit, __pb + 5 * 8, 5 * 8, data)) {
eprint "\n\n_europrog_read: Problem reading data from unit", unit
eprint "Exited ! Please try again!"
exit
}
@pdata[5 * 8:] = data[0:5*8]
if (__e2400_get(unit, __pb + 10 * 8, 5 * 8, data)) {
eprint "\n\n_europrog_read: Problem reading data from unit", unit
eprint "Exited ! Please try again!"
exit
}
@pdata[10 * 8:] = data[0:5*8]
if (__e2400_get(unit, __pb + 15 * 8, 2 * 8, data)) {
eprint "\n\n_europrog_read: Problem reading data from unit", unit
eprint "Exited ! Please try again!"
exit
}
@pdata[15 * 8:] = data[0:5*8]
# declare local array to be returned, if declared earlier, it will be lost
local ushort array returnarr[(__E2400[__E2400[unit]]["max_segments"] + 1) * 8]
# copy the data array to the return array
returnarr = @pdata
# return the data of the demanded segment. Of course there might be a call
# to a sub program, but that has to be defined separately and won`t be needed
# by the _europrog()
return(returnarr)
}
'
#%IU% (unit, memarr, program_number)
#%MDESC% this is a special macro for europrog
# Writes a program on a Eurotherm 2400 series instrument.
#
# Adress calculation is base address 8192 plus number of program (skip 0) times
# 17*8
#%BR%%BR%
# writing a program has to be done in a selective manner. One can`t just write a
# block of modbus adresses. Each segment type has arguments, which can be
# written other
def _europrog_write(unit, memarr, program_number) '{
if (!program_number) {
program_number = 1
}
__e2400debug "_europrog_write"
local ii _mysegtype, __pb
__E2400["pb"] = 8192 # programming base
# Attention, the values read from the controller are already treated to
# reflect its floating point representation!!!
__pb = __E2400["pb"] + (program_number * 17 * 8)
# Holdback type (0:OFF, 1:Low, 2:High, 3:Band)
if (__e2400_put(unit, __pb, memarr[0])) {
return -1
}
if (memarr[0] ) {
# Holdback value
if (__e2400_put(unit, __pb + 1, memarr[1])) {
return -1
}
}
# Time units (0:secs, 1:mins, 2:hours)
if (__e2400_put(unit, __pb + 2, memarr[2])) {
return -1
}
memarr[3] = memarr[2]
# just get on with your lifes! So what, you can`t have both!
if (__e2400_put(unit, __pb + 3, memarr[3])) {
return -1
}
# Cycles
if (__e2400_put(unit, __pb + 4, memarr[4])) {
return -1
}
# segments
__e2400debug "maxsegments", unit, __E2400[__E2400[unit]]["max_segments"]
for (ii = 1 ; ii <= __E2400[__E2400[unit]]["max_segments"] ; ii++ ) {
# Type 0:End, 1:Ramp(rate) 2:Ramp(time)
# 3:Dwell 4:Step 5:Call program"
_mysegtype = memarr[(ii * 8)]
__e2400debug "segment type", _mysegtype
if (__e2400_put(unit, __pb + (ii * 8), \
memarr[(ii * 8)]) == -1) {
return -1
}
if (_mysegtype == 0 ) { # End
# End action (0:Reset, 1:Indefinite_Dwell, 2:SetOutput)
if (__e2400_put(unit, __pb + (ii * 8) + 3, \
memarr[(ii * 8) + 3]) == -1) {
astr = "Segment " ii ", type " _mysegtype
_europrog_value_too_high(unit, astr, memarr[(ii * 8) + 3])
}
break
}
else if (_mysegtype == 1 ) { # ramp (rate)
__e2400debug "type ramp/rate values", memarr[(ii * 8) + 1], memarr[(ii * 8) + 2]
if (__e2400_put(unit, __pb + (ii * 8) + 1, memarr[(ii * 8) + 1])) {
astr = "Segment " ii ", type " _mysegtype
_europrog_value_too_high(unit, astr, memarr[(ii * 8) + 1])
}
if (__e2400_put(unit, __pb + (ii * 8) + 2, memarr[(ii * 8) + 2])) {
astr = "Segment " ii ", type " _mysegtype
_europrog_value_too_high(unit, astr, memarr[(ii * 8) + 2])
}
}
else if (_mysegtype == 2 ) { # ramp (time)
__e2400debug "type ramp/time values", memarr[(ii * 8) + 1], memarr[(ii * 8) + 2]
if (__e2400_put(unit, __pb + (ii * 8) + 1, memarr[(ii * 8) + 1])) {
astr = "Segment " ii ", type " _mysegtype
_europrog_value_too_high(unit, astr, memarr[(ii * 8) + 1])
}
if (__e2400_put(unit, __pb + (ii * 8) + 2, memarr[(ii * 8) + 2])) {
astr = "Segment " ii ", type " _mysegtype
_europrog_value_too_high(unit, astr, memarr[(ii * 8) + 2])
}
}
else if (_mysegtype == 3 ) { # dwell
__e2400debug "type dwell values", memarr[(ii * 8) + 2]
if (__e2400_put(unit, __pb + (ii * 8) + 2, memarr[(ii * 8) + 2])) {
astr = "Segment " ii ", type " _mysegtype
_europrog_value_too_high(unit, astr, memarr[(ii * 8) + 2])
}
}
else if (_mysegtype == 4 ) { # Step
__e2400debug "type step values", memarr[(ii * 8) + 1]
if (__e2400_put(unit, __pb + (ii * 8) + 1, memarr[(ii * 8) + 1])) {
astr = "Segment " ii ", type " _mysegtype
_europrog_value_too_high(unit, astr, memarr[(ii * 8) + 1])
}
}
else if (_mysegtype == 5 ) { # Call
__e2400debug "type call values", memarr[(ii * 8) + 3], memarr[(ii * 8) + 4]
if (__e2400_put(unit, __pb + (ii * 8) + 3, \
memarr[(ii * 8) + 3])) {
return -1
}
if (__e2400_put(unit, __pb + (ii * 8) + 4, \
memarr[(ii * 8) + 4])) {
return -1
}
}
}
}
'
def _europrog_value_too_high(unit, astr, value) '{
local ushort array data[10]
local eurolimit, estr
if (__e2400_get(unit, 12, 1, data)) {
return -1
}
eurolimit = data[0]
estr = astr ": Writing " value " is too big. Maximum is " eurolimit "!"
_europrog_print_error(estr)
}
'
def _europrog_print_error(str) '{
tty_move(0, -2)
tty_cntl("mb")
print str
tty_cntl("se")
}
'
def _europrog_print_so(str, width) '{
tty_cntl("so")
fmt = "%s"
if (width) fmt = "%" width "s"
printf(fmt, str)
tty_cntl("se")
}
'
#%IU% (str, width, xpos, ypos)
#%MDESC% this is a special macro for europrog
# Edit a field
def europrog_editfield(str, width, xpos, line) '{
local key, oldkey, akey, escape
local retarr[], domv, curpos, strlen, tmpstr
key = ""
outstr = str
strlen = length(outstr)
if (!width) width = strlen
fmt = "%s"
if (width) fmt = "%" width "s"
tty_cntl("so")
tty_move(xpos, line)
printf(fmt, str)
tty_move(xpos + width, line)
curpos = strlen
# wait until user hits a key
while (1) {
key = input(-1)
akey = asc(key)
#if (key) printf("%x", akey)
sleep(.05)
if (akey == 0x08 || akey == 0x7f || akey == 0x7e) {
outstr = substr(outstr, 1, --strlen); curpos --
} else if (akey == 0x9 || akey == 0x0a) {
domv = "HWnext field"
break
} else if (akey == 0x01) { #beginning of string
curpos = 0
} else if (akey == 0x05) { #end of string
curpos = strlen
} else if (akey == 0x15) { # ctrl-u, delete line
outstr = ""; curpos = 0
} else if (akey == 0x1b) {
oldkey = akey
} else if ((oldkey == 0x1b) && (akey == 0x5b)) {
escape = 1
} else if (akey) {
if (escape) {
if (akey == 0x41 || akey == 0x5a) {
domv = "HWprevious field"
break
} else if (akey == 0x42) {
domv = "HWnext field"
break
} else if (akey == 0x44) { #right
curpos = curpos > 0 ? curpos - 1 : 0
} else if (akey == 0x43) { #left
curpos = curpos < strlen ? curpos + 1 : strlen
}
escape = 0
tty_move(xpos + width - strlen + curpos, line)
continue
}
tty_move(xpos + width - strlen + curpos, line)
if (curpos == strlen) {
outstr = outstr key
strlen ++; curpos ++
} else {
tmpstr = substr(outstr, 1, curpos) key substr(outstr, ++curpos)
strlen++
outstr = tmpstr
}
}
if (curpos < 0) curpos = 0
tty_move(xpos, line)
printf(fmt, outstr)
tty_move(xpos + width - strlen + curpos, line)
}
input(1)
tty_cntl("se")
retarr[0] = outstr
retarr[1] = domv
return(retarr)
}
'
#%IU% (choice, charr, width, xpos, ypos)
#%MDESC% this is a special macro for europrog
# Edit a field
def europrog_multiplechoice(choice, charr, width, xpos, ypos) '{
# choice is the current choice, charr is the choice array
local key, oldkey, akey, escape, line, thischoice
local retarr[], domv, strlen
key = ""
curpos = strlen = length(charr[choice])
line = xpos
fmt = "%s"
if (width) fmt = "%" width "s"
tty_cntl("so")
tty_move(line, ypos)
printf(fmt, charr[choice])
thischoice = choice
# wait until user hits a key
while (1) {
key = input(-1)
akey = asc(key)
#if (key) printf("%x", akey)
sleep(.01)
if (akey == 0x9 || akey == 0x0a) {
domv = "HWnext field"
break
} else if (akey == 0x1b) {
oldkey = akey
} else if ((oldkey == 0x1b) && (akey == 0x5b)) {
escape = 1
} else if (akey) {
if (escape) {
if (akey == 0x41) { # up
if(charr[thischoice + 1] != "") {
thischoice ++
strlen = length(charr[thischoice])
}
} else if (akey == 0x42) { # down
if(thischoice > 0) {
thischoice--
strlen = length(charr[thischoice])
}
} else if (akey == 0x44 || akey == 0x5a) { #right
domv = "HWprevious field"
break
} else if (akey == 0x43) { #left
domv = "HWnext field"
break
}
escape = 0
tty_move(line, ypos)
continue
}
tty_move(line, ypos)
}
tty_move(line, ypos)
printf(fmt, charr[thischoice])
tty_move(line, ypos)
}
input(1)
retarr[0] = thischoice
retarr[1] = domv
tty_cntl("se")
return(retarr)
}
'
#%UU% <manual_setpoint> <unit=[0|1|2|..] or mne=some-counter-or-motor> [noninteractive]
#%MDESC% Forces a reset of running program (if any) and sends a new setpoint
# to the eurotherm.%BR%
# Without argument, the macro will set the last used setpoint.%BR%
# With "unit=1" unit declared as 1 will be reset. No blanks please.
# With option "noninteractive", no questions asked.
#%END%
# Asked by ID01 to have a macro that one can call with
# no need of user interaction (calling "europrog status" first, and then reset)
# Useful to concatenate many programs from a spec macro.
def europrogreset '{
local unit, setpoint, num, doit, ask
num = split("$*", aux)#; print "num", num
unit = 0
doit = 0
ask = 1
setpoint= "n.a."
_euro_mne2unit $*
if (numargs > 0) {
setpoint = _myarr[$# - numargs ]
}
if (numargs > 1 && _myarr[1] == "noninteractive") {
ask = 0; doit = 1
}
if (setpoint == "n.a.") {
setpoint = __E2400["MANSETPOINT"] # no setpoint given, use from array
} else {
__E2400["MANSETPOINT"] = setpoint # was given, store it in array.
}
# attempt for a kind of startup test
if (STARTUP_TEST) {
print "startup test: unit", unit, numargs, _myarr, setpoint, __E2400["MANSETPOINT"]
unglobal STARTUP_TEST
} else {
if (ask) {
printf("Doing this you will GO TO THE MANUAL SETPOINT : %.1f\n", \
__E2400["MANSETPOINT"])
doit = yesno("Reset anyway", 0)
}
if (doit) {
# send the reset
__eurotherm_wrapper reset unit
sleep(.5)
# write the setpoint
if (__e2400_put(unit, 2, __euro_wfloat(unit, __E2400["MANSETPOINT"]))) {
return -1
}
}
}
}
'
# On request of someone on ID11 :-) I added all those macros, although all
# actions can be achieved in the interactive version. They need to control
# the EurothermProgrammer from remote.
def __eurotherm_wrapper '{
_euro_mne2unit $*
if (!__E2400[__E2400[unit]]["max_segments"]) {
eprint "There is no such controller registered. Either use eurosetup"
eprint "or use the controller as motor and/or counter."
eprint "Try: argument 1 or 2"
exit
}
# attempt for a kind of startup test
if (STARTUP_TEST) {
print "startup test: unit", unit, numargs, _myarr, cmd, prgnum, resettemp
unglobal STARTUP_TEST
} else {
local ushort array data[120]
local rr
# see if set point ramp rate is set
if (__e2400_get(unit, 35, 1, data)) {
return -1
}
rr = data[0]
if (rr) {
eprint "The controller is set to ramp, when the set point is changed!"
eprint "Use euro2400parameters RampRate=0, before you can run a program."
exit
}
if ("$1" == "status") _europrog_status(unit)
else if ("$1" == "reset") _europrog_cmd(unit, 1)
else if ("$1" == "hold") { # check for state. No point in holding
# if not running, i.e. reset
local state
if (__e2400_get(unit, 23, 1, data)) {
return -1
}
state = data[0]
if (state != 2) {
eprint "Controller is not running any program."
eprint "Can`t hold it !!!"
} else {
_europrog_cmd(unit, 4)
}
}
else if ("$1" == "holdback") { # check for state. No point in holding
# if not running, i.e. reset
local state
if (__e2400_get(unit, 23, 1, data)) {
return -1
}
state = data[0]
if (state != 2) {
eprint "Controller is not running any program."
eprint "Can`t hold it !!!"
} else {
_europrog_cmd(unit, 8)
}
}
else if ("$1" == "run") {
_europrog_cmd(unit, 2)
}
else if ("$1" == "skipsegment") { # check for state. No point in holding
# if not running, i.e. reset
local state
if (__e2400_get(unit, 23, 1, data)) {
return -1
}
state = data[0]
if (state != 2) {
eprint "Controller is not running any program."
eprint "Can`t hold it !!!"
} else {
_europrog_cmd(unit, 4) # put into hold state
__e2400_put(unit, 36, 0)# set remaining seg time secs to zero
# but that only works from hold state!
_europrog_cmd(unit, 2) # and
}
}
else if ("$1" == "complete") {
eprint "Completing a program is not possible."
}
# else if ("$1" == "complete") _europrog_cmd(unit, 16)
}
}
'
#%UU% <unit=[0|1|2|..] or mne=some-counter-or-motor>
#%MDESC% Gives a status of the program execution in the Eurotherm
def europrogstatus '__eurotherm_wrapper status'
#%UU% <unit=[0|1|2|..] or mne=some-counter-or-motor>
#%MDESC% Forces the running program into hold mode. Please consult docmentation
# for the exact meaning of that state.
def europroghold '__eurotherm_wrapper hold'
#%UU% <unit=[0|1|2|..] or mne=some-counter-or-motor>
#%MDESC% Forces the running program into holdback mode.
#%BR%%BR%
#%B%Holdback%B%\0(from the documentation)%BR%%BR%
#As the setpoint ramps up, or down (or dwells), the measured value may lag
#behind, or deviate from, the setpoint by an undesirable amount. %B%Holdback%B%
#is available to freeze the program at its current state, should this occur.
#The action of Holdback is the same as a deviation alarm. It can be enabled, or
#disabled. Holdback has two parameters - a value and a type. If the error from
#the setpoint exceeds the set "holdback" value, then the Holdback feature, if
#enabled, will automatically freeze the program at its current point and flash
#the HOLD light. When the error comes within the holdback value, the program
#will resume normal running.
def europrogholdback '__eurotherm_wrapper holdback'
#%UU% <unit=[0|1|2|..] or mne=some-counter-or-motor>
#%MDESC% Forces the running program to complete.
def europrogcomplete '__eurotherm_wrapper complete'
#%UU% <unit=[0|1|2|..] or mne=some-counter-or-motor>
#%MDESC% Executes the defined program.
def europrogrun '__eurotherm_wrapper run'
#%UU% <unit=[0|1|2|..] or mne=some-counter-or-motor>
#%MDESC% Ends the current segment.
def europrogskipsegment '__eurotherm_wrapper skipsegment'
#%UU% <unit=[0|1|2|..] or mne=some-counter-or-motor> <run|reset|hold|holdback|complete|skip|status> <argument>]
#%MDESC%
# Macro that runs an interactive loop. It allows to define programs inside
# the controller, read them, read the status, reset the controller.
# The command run can have an additional argument containing the program number.
# %BR%
def europrog '{
# the following two ifs will allow the user to enter the controller number and
# command in any order.
local x, unit, cmd, prgnum, _myarr, option, aux[], numargs
local resettemp
numargs = $#
_euro_mne2unit $*
if (numargs) {
if (length(_myarr[$# - numargs]) > 2) { # controller # shorter than 2 :-)
cmd = _myarr[$# - numargs]
} else {
# could there be a unit number to start with
unit = _myarr[$# - numargs]
numargs --
cmd = _myarr[$# - numargs]
}
if (cmd == "run") {
if (numargs == 2)
prgnum = _myarr[$# - numargs +1]
else
prgnum = getval("Program number to run?", 0)
}
else if (cmd == "reset") {
if (numargs == 2)
resettemp = _myarr[$# - numargs +1]
}
}
if (!__E2400[__E2400[unit]]["max_segments"]) {
local x, str, aux
eprint "There is no such controller registered. Either use eurosetup"
eprint "or use the controller as motor and/or counter."
for (x in __E2400) {
if (x < 10) {
# continue if not a single index
if (split(x, aux, "\034") >1) continue
# concatenate eligible numbers
str = x ", " str
}
}
str = substr(str, 1, length(str) - 2)
eprintf("Try one of the following arguments: ");
tty_cntl("md"); print str; tty_cntl("me")
exit
}
# attempt for a kind of startup test
if (STARTUP_TEST) {
print "startup test: unit", unit, numargs, _myarr, cmd, prgnum, resettemp
unglobal STARTUP_TEST
} else {
if (cmd == 0) {
euro_main(unit, cmd, prgnum)
}
else if (cmd == "run" ) {
europrogrun unit
}
else if (cmd == "reset" ) {
eval(sprintf("europrogreset unit=%d %f", unit, resettemp))
# well, prgnum is temp, but the variable should be ok.
}
else if (cmd == "hold" ) {
europroghold unit
}
else if (cmd == "holdback" ) {
europrogholdback unit
}
else if (cmd == "complete" ) {
europrogcomplete unit
}
else if (cmd == "skip" ) {
europrogskipsegment unit
}
else if (cmd == "status" ) {
europrogstatus unit
}
else if (cmd == "explain" ) {
print "you may use europrog without argument, then you get to a kind of"
print "shell. Otherwise, you may add a controller number (controller as in"
print "defined in the config). On top you can then use commands from the"
print "list of: run, reset, hold, holdback, complete, skip and status"
}
else {
eprint "europrog [controller <run|reset|hold|holdback|complete|status> <program_number>]"
}
}
}
'
#%IU%()
#%MDESC%
# This macro function runs a loop to provide interactive communication to an
# eurotherm device. %BR%
# It reads keyboard input and executes the wished command.
#
def euro_main(unit, cmd, prgnum) '{
if (!(whatis("__E2400") & 0x01000000)) {
eprint "$0: You must first configure a macro motor or counter!"
exit
}
local _cmd euro___pr c helpstr aux[]
local message, starttemp
local ushort array data[120]
# if not yet defined, set to 1
__E2400[__E2400[unit]]["pnum"] = \
__E2400[__E2400[unit]]["pnum"] ? \
__E2400[__E2400[unit]]["pnum"] : 1
__E2400[__E2400[unit]]["pnum"] = \
prgnum ? prgnum : __E2400[__E2400[unit]]["pnum"]
euro___pr = "EURO"__E2400[__E2400[unit]]["ident"] " unit " unit " : "
helpstr = "europrog: programming Eurotherm " euro___pr "\n"
helpstr = helpstr " Device commands:\n"
helpstr = helpstr " ---------------\n"
helpstr = helpstr " .h, .help, . Display available commands\n"
helpstr = helpstr " .r, .read Read program\n"
helpstr = helpstr " .w, .write Edit/write program\n"
helpstr = helpstr " .s, .status Instrument status\n"
helpstr = helpstr " .run Run program <number>\n"
helpstr = helpstr " .hold Set program status to hold\n"
helpstr = helpstr " .holdback Set program status to holdback\n"
helpstr = helpstr " .complete Set program status to complete\n"
helpstr = helpstr " .ss, .skip Skip current segment\n"
helpstr = helpstr " .t, .reset Reset instrument status\n"
helpstr = helpstr " .q, .quit Leave europrog\n"
print helpstr
while(1) {
tty_cntl("md")
line = input(euro___pr)
# if (substr(line,1,2) == "\033\133") { print substr(line,3,1);continue}
if (line == "\033\133A") { print ; continue }
tty_cntl("me")
c = split(line, aux)
if (aux[0] == ".h" || aux[0] == "." || aux[0] == ".help") {
print helpstr
}
else if (aux[0] == ".q" || aux[0] == ".quit") {
exit
}
else if (aux[0] == ".r" || aux[0] == ".read") {
if (aux[1]) {
__E2400[__E2400[unit]]["pnum"] = aux[1]
}
if (!__E2400[__E2400[unit]]["pnum"]) {
__E2400[__E2400[unit]]["pnum"] = 1
}
if (__E2400[__E2400[unit]]["pnum"] > \
__E2400[__E2400[unit]]["max_progs"]) {
_europrog_print_error("Program number too big!")
continue
}
local ushort array prgdat[(__E2400[__E2400[unit]]["max_segments"] + 1) * 8]
prgdat = _europrog_read(unit, __E2400[__E2400[unit]]["pnum"])
local astr
__e2400_get(unit, 1, 1, data)
starttemp = __euro_rfloat(unit, data[0])
astr = "Program " __E2400[__E2400[unit]]["pnum"]
unglobal _europrog_show_call
_europrog_show(astr , starttemp, prgdat, nosubprog, unit)
}
else if (aux[0] == ".w" || aux[0] == ".write") {
if (aux[1]) {
__E2400[__E2400[unit]]["pnum"] = aux[1]
}
if (_europrog_cmd(unit, 0) != 1) { #if not RESET
eprint "In this state the programmer cannot be programmed!!!"
continue
}
local ushort array prgdat[(__E2400[__E2400[unit]]["max_segments"] + 1) * 8]
prgdat = _europrog_read(unit, __E2400[__E2400[unit]]["pnum"])
astr = "Program " __E2400[__E2400[unit]]["pnum"]
local astr
astr = "Program " __E2400[__E2400[unit]]["pnum"]
__e2400_get(unit, 1, 1, data)
starttemp = __euro_rfloat(unit, data[0])
unglobal _europrog_show_call
_europrog_show(astr , starttemp, prgdat, 1, unit)
prgdat = _europrog_edit(prgdat, astr, unit)
unglobal _europrog_show_call
_europrog_show(astr , starttemp, prgdat, 1, unit)
_europrog_write(unit, prgdat, __E2400[__E2400[unit]]["pnum"])
__europrog_save_as_scan(unit, starttemp, prgdat)
}
else if (aux[0] == ".t" || aux[0] == ".reset") {
print \
"To modify the currently running program type use: \"europrog status \" "
if (yesno("Reset", 0) == 0)
continue
__E2400["MANSETPOINT"] = getval("Manual setpoint after reset", \
__E2400["MANSETPOINT"])
__eurotherm_wrapper reset unit
if (__e2400_put(unit, 2, __euro_wfloat(unit, \
__E2400["MANSETPOINT"]))) {
return -1
}
}
else if (aux[0] == ".hold") {
__eurotherm_wrapper hold unit
}
else if (aux[0] == ".holdback" ) {
__eurotherm_wrapper holdback unit
}
else if (aux[0] == ".complete" ) {
__eurotherm_wrapper complete unit
}
else if (aux[0] == ".s" || aux[0] == ".status" ) {
__eurotherm_wrapper status unit}
else if (aux[0] == ".ss" || aux[0] == ".skip" ) {
__eurotherm_wrapper skipsegment unit}
else if (aux[0] == ".run") {
if (aux[1]) {
__E2400[__E2400[unit]]["pnum"] = aux[1]
"""
this only works if the controller has more than single program and
if no program is running
"""
# check status, only answer if status is RESET
if (__e2400_get(unit, 23, 1, data)) {
eprint "Communication error with Eurotherm"
return ".error."
}
if ((__E2400[__E2400[unit]]["max_progs"] > 1) && (data[0] == 1)) {
if (__e2400_put(unit, 22, __E2400[__E2400[unit]]["pnum"])) {
eprint "In all likelyhood, the last program left the controller in the"
eprint "state \"COMPLETED\" or \"HOLDBACK\"."
eprint " Use the command \".reset\"."
return -1
}
print "Program number is", __E2400[__E2400[unit]]["pnum"]
}
}
__eurotherm_wrapper run unit
print "\nTo observ the controller temperature, consider using timescan"
print "or uct. Remember to set the counter with plotselect!\n"
}
else {
print "I don`t understand the command \"" aux[0] "\""
}
}
}
'
def _euro_mne2unit '
""" new version of this macro does not allow just putting the unit
number as an argument. It`s just too difficult to guess.
"""
local mymne, myunit, __euro_num
local x, cmd, prgnum, _myarr, option
local aux[], numargs, varnam, None
numargs = $#
split("$*", _myarr)
__e2400debug "@@@", _myarr
None = "None"
mne = None
unit = None
for (option in _myarr) {
if (split(_myarr[option], aux, "=") == 2) {
varnam = aux[0]
__e2400debug "@@@varnam", varnam
@varnam = aux[1]
__e2400debug varnam, "value", @varnam
if (string_tolower(varnam) == "unit" || varnam == "mne") numargs --
}
}
__e2400debug "@@@ mne & unit", mne, unit
if (unit != None) { # unit is set, use it
__e2400debug "@@@ --------------> use unit", unit
}
else if (mne != None) { # no mne neither, search for first motor
# with device_id E2400
__e2400debug "@@@ --------------> use mne", mne
# mne can be motor|counter mnemonic or number, spec
# might expand string to number
if ((mne == motor_mne(mne) || (mne == motor_num(mne)) || \
(mne == motor_mne(motor_num(mne)))) && (motor_mne(mne) != "?")) {
__euro_num = motor_num(mne)
if (motor_par(mne, "device_id") == "E2400") {
unit = motor_par(__euro_num, "unit")
}
__e2400debug "@@@1 mne & unit", mne, unit
}
else
if ((mne == cnt_mne(mne) || (mne == cnt_mne(mne)) || \
(mne == cnt_mne(cnt_num(mne)))) && \
(cnt_mne(mne) != "?")) {
__euro_num = cnt_num(mne)
if (counter_par(mne, "device_id") == "E2400") {
unit = counter_par(__euro_num, "unit")
}
__e2400debug "@@@2 mne & unit", mne, unit
}
if (unit == None) {
eprint "No eurotherm 2400 with mnemonic", mne "!"
exit
}
}
# if none is set, go find something motors ?
if (( mne == None ) && (unit == None)) {
local i
for (i = 0; i < MOTORS; i ++) {
__e2400debug "motor:", motor_name(i), motor_par(i, "device_id")
if (motor_par(i, "device_id") == "E2400") {
mne = motor_mne(i)
unit = motor_par(i, "unit")
eprint "Using motor \"" motor_mne(i) "\""
break
}
}
}
# or counters
if (( mne == None ) && (unit == None)) {
local i
for (i = 0; i < COUNTERS; i ++) {
__e2400debug "counter:", cnt_name(i), counter_par(i, "device_id")
if (counter_par(i, "device_id") == "E2400") {
mne = cnt_mne(i)
unit = counter_par(i, "unit")
eprint "Using counter \"" cnt_mne(i) "\""
break
}
}
}
__e2400debug "mne = ", mne, "unit", unit
if (!__E2400[unit]) {
eprint "Please use argument eg. \"mne=eurosp\" to specify your controller"
exit
} else {
__e2400debug "_euro_mne2unit: Using unit", unit
}
'
def _euro_mne_test '{
local x, unit, cmd, prgnum, _myarr, option, aux[], numargs, RampRate
numargs = $#
_euro_mne2unit $*
print "MNE:", mne
print "UNIT:", unit
print "RR:", RampRate
}
'
def _euro_2400_test_function(cmd) '{
global STARTUP_TEST
STARTUP_TEST = 1
print "Command:", cmd
eval(cmd)
# ~ print "argument test: unit", unit, numargs, _myarr, prgnum, prgsavefile
unglobal STARTUP_TEST
}
'
def testattempt_eurotherm2400 '{
# now start a series of tests
# my test system happened to have a motor named euro,
# which was defined on a controller in 4th position.
cdef("cleanup_once", "unglobal STARTUP_TEST;\n", "testattempt_eurotherm2400")
_euro_2400_test_function("euro2400configuration")
_euro_2400_test_function("euro2400configuration mne=euro")
_euro_2400_test_function("euro2400configuration unit=3")
_euro_2400_test_function("euro2400menu mne=euro")
_euro_2400_test_function("euro2400menu unit=3")
_euro_2400_test_function("euro2400parameters")
_euro_2400_test_function("euro2400parameters mne=euro")
_euro_2400_test_function("euro2400parameters unit=3")
_euro_2400_test_function("euro2400parameters 3")
_euro_2400_test_function("euro2400parameters mne=euro RampRate=60")
_euro_2400_test_function("euro2400parameters unit=3 RampRate=60")
_euro_2400_test_function("euro2400parameters RampRate=60")
# save and restore program won`t test, because the filename being
# absolute is being interpreted as a division
# ~ _euro_2400_test_function("euro2400saveprogram unit=3 /tmp/test 0")
# ~ _euro_2400_test_function("euro2400restoreprogram /tmp/test 0")
# ~ _euro_2400_test_function("euro2400restoreprogram mne=euro")
# ~ _euro_2400_test_function("euro2400restoreprogram unit=3 /tmp/test 0")
_euro_2400_test_function("euro2400showpid")
_euro_2400_test_function("euro2400showpid mne=euro")
_euro_2400_test_function("euro2400showpid unit=3")
_euro_2400_test_function("euro2400status")
_euro_2400_test_function("euro2400status mne=euro")
_euro_2400_test_function("euro2400status unit=3")
_euro_2400_test_function("europrogreset")
_euro_2400_test_function("europrogreset 22")
_euro_2400_test_function("europrogreset unit=3")
_euro_2400_test_function("europrogreset 25 unit=3")
_euro_2400_test_function("europroghold")
_euro_2400_test_function("europroghold mne=euro")
_euro_2400_test_function("europroghold unit=3")
_euro_2400_test_function("europrogholdback")
_euro_2400_test_function("europrogholdback mne=euro")
_euro_2400_test_function("europrogholdback unit=3")
_euro_2400_test_function("europrogcomplete")
_euro_2400_test_function("europrogcomplete mne=euro")
_euro_2400_test_function("europrogcomplete unit=3")
_euro_2400_test_function("europrogrun")
_euro_2400_test_function("europrogrun mne=euro")
_euro_2400_test_function("europrogrun unit=3")
}
'
#%MACROS%
#%IMACROS%
#%INTERNALS%
#%AUTHOR% H. Witsch, BLISS - ESRF, derived from the macros in temperature.mac,
#$Revision: 4.2 $, $Date: 2018/05/22 09:40:35 $
#%TOC%
|