ABACUS 4 BLOCK LANGUAGE

ABACUS 4 - Linux

Based on more than 25 years of development and use ABACUS 4 is the latest in a line of process control software systems with the ABACUS name. ABACUS 4 runs on standard intel PC hardware under the Linux operating system.

Intended Audience

This document is intended for system engineers and others engaged in the development, maintenance and support of ABACUS 4 computer systems. Top of document

Table of Contents

Introduction

ABACUS 4 blocks are the programming units with which a control scheme is written. Each block within a system has a unique block number, each block is of a particular type and blocks of the same type are numbered sequentially. The first and last block numbers and therefore the total number of each different type of block is configurable. 

Block Parameters

Blocks have parameters. The number and types of these parameters varies with block type. Furthermore parameters are of different types, namely

analogue value

Analogue values represent numerical results of measurements or calculations, or constants for use in calculations.

logical value a/m, s/n,v/n

Logical values represent logical results of measurements or calculations, or status flags.

block reference

Block references permit the interconnection of blocks. Inputs, outputs, switches and cascades may all be either a direct connection where the block addressed is used, or indirect where the block number used is negated. In this case when processing occurs it the the result of the block whose analogue result is used to direct connection.

text string

Access to the blocks and their parameters is achieved using ABACUS 4 engineers utility (see separate documentation).

Common parameters

All blocks in the system have the following parameters.

Process I/O

In this section the following block types are described.

Analogue input block (PI-Bus)

ABACUS_PIO.gif

One analogue input block is connected to each PI-Bus analogue input point. The calibrated output is presented on the block ra parameter. The block has the following parameters in addition to the common parameters tg sn ra am vn and ss.


 
rn 
gain  range 
0
1 0 - 10 volt
1
2 0 - 5 volt 
2
4 0 - 2.5 volt 
3
8 0 - 1.25 volt 
4
16 0 - 625 m volt
5
32 0 - 312.5 m volt
6
64 0 - 156.25 m volt 
7
128 0 - 78.125 m volt
8
256 0 - 39.0625 m volt
9
512 0 - 19.53125 m volt 
10
1024 0 - 9.765625 m volt 

 
ra = sp * rw + ze

Analogue output block (PI-Bus)

ABACUS_PIO1.gif

One analogue output block is connected to each PI-Bus analogue output point. The block has the following parameters in addition to the common parameters tg sn ra am vn and ss.

sp set point floating_point_value

hl high limit floating_point_value

ll low limit floating_point_value

sp set point

The full output range of the analogue output is represented by a setpoint of 0-100. On the first scan after the am is set the sp set point is made equal to the current output.

hl high limit

ll low limit

The sp is constrained between the values of hl and ll , before being further processed.

Digital Input block

ABACUS_PIO2.gif

The Digital input block reflects the status of a discrete signal from the process. The vn flag will be v when the discrete signal is active, and n otherwise. Although the block has all the common parameters including sn and am it is scanned independently of the normal block scanning process.

Digital Output block

ABACUS_PIO3.gif

The Digital output block asserts the status of its am flag in the form of a discrete digital output. Depending on the process I/O used the vn flag may indicate the actual status of the digital output. Although the block has all the common parameters including sn, it is scanned independently of the normal block scanning process.

Pulse Duration Output block

ABACUS_PIO4.gif

The Pulse duration Output block is used for plant control via two discrete digital output, producing a timed raise or lower signal, to effect the required control action. In addition to the common parameters the following exist

sp set point floating_point_value

hl high limit positive floating_point_value

ll low limit positive floating_point_value

sp set point

The required number, and duration of pulses required are cascaded into the sp of this block. Cascading +1.00 produces a raise pulse of one unit duration, -1.00 a lower pulse of one duration.

On the first scan after being set auto the sp is set to zero.

hl high limit

The maximum duration of pulse permitted in either direction is determined by the hl high limit value. If the absolute value of sp is greater than hl then hl pulses will be sent

ll low limit

The minimum duration of pulse permitted in either direction is determined by the ll low limit value. If the sp is less than ll then no pulse will be sent.

ra result a

The value of ra represents the actual pulse length to be sent. If an sp value greater than a predetermined maximum is sent then only that maximum is sent and the value of sp is reduced by that amount. Thus pulses are carried forward from one scan to the next.

The actual execution of the pulse duration depends on hardware. If a SIOX N45 module is to be used, the PDO block must be referenced in the N45 module block, see N45 section.

Indata I/O block

These blocks, normally in the range 18001 to 18511, are used to communicate with the Indata series of PLCs. Indata PLCs may be connected to a multidrop RS485 network. For ABACUS to communicate on such a network a serial port is used via an RS232 to RS485 converter. If a single Indata PLC is connected it may be via RS232.

The Indata communications protocol is based on markers. Each of the units on an Indata network has an address in the range 1-31. Markers have a number in the range 1-511 (coresponding to blocks 18001 to 18511). Only one unit on the network should write a marker, but any or all of the units may read that marker. Markers may be either analog or digital.

In the Indata programming language a marker is set using a NOUT entity, and is read using a NIN entity.

If ABACUS reads a marker (set by another unit) it will detect the type (analog or digital), the source address and the value. The value will be put into the ra of the block for analog markers, or the vn for digital markers.Text is inserted in the nm parameter describing the address and type or marker. For example if a digital marker (1) from unit with addres 1 is received

nin d adr 1 mark 1

will be written.

If ABACUS is to output a marker then the nm must have text of the following format.

nout d

or

nout a

The digital value of the am flag or analog value of the ra will be transmitted onto the network.

System blocks used by the Indata driver

Blocks 21 to 25 are used to define certain characteristics of the Indata link software. Since the ABACUS is a unit on the Indata network, it must have an address, which must not be the same as any other unit on the network. This is defined by the ra of block 21.

To avoid scanning the entire address range when only some addresses are present the first and last uPLD addresses are defined in the ra parameters of block 23 and 23 respectively.

The cycle time is defined by setting a value in the ra of block 24. This value is the polling frequency of the driver in milliseconds.

To reduce trafic, the Indata protocol allows polling of changes of signals in addition to taking all values. The C/I ratio is the ratio of the number of Change messages to Init (i.e. all signals) messages.

Multiple Indata Networks

To accommodate multiple Indata networks the indata program can be called with the optional extra parameters spec_block and ind_block. In the file /home/abacus/abacus the following lines start three networks using the ports cua1,2 & 3, all with baud rate 9600, and different system and indata block ranges. No check is made for overlapping number ranges, so care is required when configuring.
 

sudo $ABACUS_HOME/indata /dev/cua1 9600 21 18001 &
sudo $ABACUS_HOME/indata /dev/cua2 9600 31 19001 &
sudo $ABACUS_HOME/indata /dev/cua3 9600 41 20001 &

Hardware considerations

The Indata network hardware is based on RS485, multidrop two wire serial communications. The standard serial I/O ports on a PC are RS232; and a conversion must be done. Most RS232 - RS485 converters use the signal RTS (or similar) to control direction of data flow. The ABACUS/Indata link is purely half duplex. This means that sometimes a special connection must be made.

One method is to use a RS232 - RS422 (4-wire) converter with the RS422 TX and RX pairs connected together.

    TX+ ----+-----------------+--------------+-----------+
            |                 |              |           |
    TX- ----|--+--------------|--+-----------|--+--------|--+
            |  |              |  |           |  |        |  |
    RX+ ----+  |              |  |           |  |        |  |
               |
    RX- -------+

K48 Analogue input/output block

ABACUS_PIO5.gif

One K48 analogue input/output block is connected to each analogue input point and corresponding analogue output point on a SIOX K48 module. The calibrated output is presented on the block ra parameter. The block has the following parameters in addition to the common parameters tg sn ra am vn and ss.

ao analogue output block number

ds data source integer constant

mx max value floating_point_constant

ze zero floating_point_constant

The block result is scaled to the range ze to mx corresponding to 0 to 100 % input. This may be 0 - 20mA, 4 - 20mA, or 0 - 10 V depending on the hardware configuration of the K48 module.

The K48 module should be configured to have 4 addresses, (4A/I and 4A/O). The value of ds defines both the SIOX port used and the point address. ds should be 10000 + 100*port number + SIOX point address. i.e. for port number 2, SIOX point address 5 ds should be 10205.

The ao parameter should be connected to an analogue output block, whose result will be sent to the corresponding point address.

N45 Module control block

This block is used to connect one SIOX N45 I/O module to various other ABACUS I/O blocks.

The block has the following parameters in addition to the common parameters tg sn ra am vn and ss.
 
ds data source integer constant
oa output a terminal 1 block_number
ob output b terminal 2 block_number
oc output c terminal 3 block_number
od output d terminal 4 block_number
oe output e terminal 5 block_number
ia input a terminal 10 block_number
ib input b terminal 11 block_number
ic input c terminal 12 block_number
id input d terminal 13 block_number
ie input e terminal 14 block_number
if input f terminal 15 block_number
ig input g terminal 16 block_number
ih input h terminal 17 block_number
ii input i terminal 18 block_number
ij input j terminal 19 block_number
ik input k terminal 20 block_number
il input l terminal 21 block_number
im input m terminal 22 block_number
in input n terminal 23 block_number

The value of ds defines both the SIOX port used and the point address. ds should be 10000 + 100*port number + SIOX point address. i.e. for port number 2, SIOX point address 5 ds should be 10205.

The SIOX N45 I/O module is a general purpose digital I/O module with 7 sourcing output and 14 inputs, all 10V - 35V DC. The outputs can supply 0.5A each and have short circuit protection. Through the built-in CPU and a simple PLC programming language, curtain options are available.

If the PLC programme has been correctly loaded and configured, a number of the outputs may serve as pulse duration outputs. The corresponding output parameter (oa - og ) should be connected to a pdo block. Enter the pdo block as a positive number to connect the raise side, and as a negative number for the lower side. Thus it is possible to connect raise and lower in any combination. The pdo function, if programmed into to N45 module excludes ordinary digital output operation for those specified output points. It is possible to configure any combination of digital outputs and pdo outputs.

Logical Blocks

This section describes the operation of the following block types



 
This block type functions as an AND or OR gate for up to five logical inputs, any or all of which may be inverted.
The switch block derives a single logical result (vn), and can switch auto or manual two blocks, depending on the status of a single logical input. Two list processing algorithms allow ranges of blocks to be switched.
The pattern block is used to switch up to five blocks auto or manual. 


Checklist block

ABACUS_LG.gif

The checklist block derives a single logical result (vn), and can switch auto or manual one block, depending on the status of five logical inputs. The block has the following parameters in addition to the common parameters tg sn ra am vn and ss.
 
ia
logical input
block_number or indirect_block_number
aa
algorithm a
0 - 7
ib
logical input
block_number or indirect_block_number
ab
algorithm b
0 - 1
ic
logical input
block_number or indirect_block_number
ac
algorithm c
0 - 1
id
logical input
block_number or indirect_block_number
ad
algorithm d
0 - 1
ie
logical input
block_number or indirect_block_number
ae
algorithm e
0 - 1
af
algorithm f
0 - 1
sv
switch if violated
block_number s/r/+/- or indirect_block_number s/r/+/-

The logical result of the five blocks specified by each of the logical inputs in turn (ia,ib ,ic,id,ie) is processed by its coresponding algorithm ( aa,ab,ac, ad,ae) to generate five intermediate logical results in accordance with table ck1. (Note that only aa can have a value greater than one.)

Unconnected inputs are ignored.

af = 0 AND operation

The logical result of the block will be v (true) only if all of the five intermediate logical results are true (1).

af = 1 OR operation

The logical result of the block will be v (true) if any of the five intermediate logical results is true (1).

Table ck1
 
algoritm
description
logical previous
logical current
intermediate result
0
off (inverting)
-
0
1
(aa,ab,ac, ad,ae)
-
1
0
1
on (noninverting)
-
1
1
(aa,ab,ac, ad,ae)
-
0
0
2
on-going
0
1
1
aa only
1
1
0
1
0
0
0
0
0
3
off-going
1
0
1
aa only
0
0
0
0
1
0
1
1
0
4
any-change
0
1
1
aa only
1
0
1
0
0
0
1
1
0
5
no-change
0
0
1
aa only
1
1
1
0
1
0
1
0
0

The block addressed by the sv parameter will be affected in accordance with table ck2.

Table ck2
 
intermediate result switch type s switch type r switch type + switch type -
target block becomes
1 a m a m
0 - - m a

Switch block

ABACUS_LG1.gif

The switch block derives a single logical result (vn), and can switch auto or manual two blocks, depending on the status of a single logical input. The block has the following parameters in addition to the common parameters tg sn ra am vn and ss.
 
ia logical input block_number or indirect_block_number
aa algorithm a 0 - 7
sa switch a block_number s/r/+/- or indirect_block_number s/r/+/-

The status of the logical input specified by the input a ia parameter is processed by algorithm a aa and the target block's am auto/manual flag is changed in accordance with table sw1 .

The logical result of the block is set to the intermediate logical result derived by aa algorithm a.
 
ab algorithm b 0 - 7
sb switch b block_number s/r/+/- or indirect_block_number s/r/+/-

The status of the logical input specified by the input a ia parameter is processed by algorithm b ab and the target block's am auto/manual flag is changed in accordance with table sw1 .

List processing

These algorithms permit the execution of the block's algorithm over a contiguous list of blocks instead of individual blocks. The target list is specified by sa and sb . The first output block is specified by sa, the last by sb . The input list is of exactly the same length as the output list, thus it is only necessary to specify the first input block with ia. Indirection is allowed on ia, sa and sb. The direction of processing is always from the first to the last, and cannot be reversed; thus the block specified as the first output block must have a lower or equal block number as that specified as the last. If ia is zero then an imaginary input list of blocks all having a 0 (nonviolated,reset, false) logical result is used.

The only restriction to the size of the lists is that they must both be accommodated within the constraint of the system.

aa = ab = 6 List processing (inverting)

The switch type of both sa and sb must be the same. For each block an intermediate result is calculated and the corresponding target block is modified as if the algorithm were 0 (inverting).

aa = ab = 7 List processing (noninverting)

This algorithm functions in the same list processing way as above, except that for each block an intermediate result is calculated and the corresponding target block is modified as if the algorithm were 1 (noninverting).

Table sw1
 
algoritm
description
logical previous
logical current
intermediate result
switch type s
switch type r
switch type +
switch type -
0
off (inverting)
-
0
1
a
m
a
m
-
1
0
-
-
m
a
1
on (noninverting)
-
1
1
a
m
a
m
-
0
0
-
-
m
a
2
on-going
0
1
1
a
m
a
m
1
1
0
-
-
m
a
1
0
0
-
-
m
a
0
0
0
-
-
m
a
3
off-going
1
0
1
a
m
a
m
0
0
0
-
-
m
a
0
1
0
-
-
m
a
1
1
0
-
-
m
a
4
any-change
0
1
1
a
m
a
m
1
0
1
a
m
a
m
0
0
0
-
-
m
a
1
1
0
-
-
m
a
5
no-change
0
0
1
a
m
a
m
1
1
1
a
m
a
m
0
1
0
-
-
m
a
1
0
0
-
-
m
a
6
list processing (inverting) see text
7
list processing (noninverting) see text

Pattern Block

ABACUS_LG2.gif

The pattern block is used to switch up to five blocks auto or manual. The block has the following parameters in addition to the common parameters tg sn ra am vn and ss.
 
sa
switch a
block_number s/r or indirect_block_number s/r
sb
switch b
block_number s/r or indirect_block_number s/r
sc
switch c
block_number s/r or indirect_block_number s/r
sd
switch d
block_number s/r or indirect_block_number s/r
se
switch e
block_number s/r or indirect_block_number s/r

Each time the block is executed the blocks specified by switches sa to se are switched auto or manual depending on the specified switch type s or r respectively.

Note unless continual and repeated operation is desired it is usual to the set the single shot flag ss to s.

Process Control Blocks

This section of the ABACUS block language manual describes those blocks specifically designed for process control use. These are

The p.i.d. block is designed to perform P, PI, or PID control functions, with standard control constants, executing a variety of alternative control algorithms, and calculating control performance data at the same time.

The motor block is designed to allow comprehensive control of the starting/stopping of electric motors, pumps, and opening/closing of valves. The connections are made through digital inputs and outputs. Facilities are included to initiate alarm signals if the motor/pump fails to respond to control signals in allowed time periods. The accumulation of run time is also included.


PID Controller block

ABACUS_PC.gif

ABACUS_PC1.gif

The p.i.d. block is designed to perform P, PI, or PID control functions, with standard control constants, executing a variety of alternative control algorithms, and calculating control performance data at the same time. In addition to the common parameters the p.i.d. block has the following
 
sh
setpoint hl
floating_point_value
sl
setpoint ll
floating_point_value
mh
measured variable hl
floating_point_value
ml
measured variable ll
floating_point_value
ir
input run
block_number
ip
input proc
block_number
sr
future use
floating_point_value
ar
future use
integer
db
dead band
floating_point_value
gf
goodness factor
floating_point_value
ag
goodness factor algorithm
integer
kp
proportional constant
floating_point_value
ti
integral time
floating_point_value
td
derivative time
floating_point_value
ap
proportional algorithm
integer
ai
integral algorithm
integer
ad
derivative algorithm
integer
an
answer
floating_point_value
op
output block
block_number

The am flag of a pid block may be considered as an îauto-requestî flag. When the am flag is auto the loop may be regarded as requested auto. The vn flag may be regarded as that flag which indicates that the loop is in fact operating in automatic.

When conditions are right for the controller to start operating in automatic the vn falg is set, and previous stored values of er are set to zero, and previous values of mv are set to the current mv value. If the se flag is set ( e equalise) then the setpoint value is made equal to the measured variable. This is done to ensure bumpless transfer.

ir input run

If the vn violation status of any block connected here is v then the normal processing of the block may proceed. The vn of this pid block is made the same as that of the pointed to block if this pid block is auto. If this pid block is inactivated because of this connection it will be initialised when reactivated. This allows interlocking of control loops.

ip input process

If the vn violation status of any block connected here is v then the normal processing of the block may proceed. No re-initialisation is performed nor is the vn status changed as a consequence of this connection. This parameter is intended for use with the ai=1 dead time controller option.

ia input a

The measured variable (mv) in the following is derived from the analogue result ra of the block specified here.

mh input hl, ml input ll

These limits are used to constrain the value of the measured variable (mv).

is input setpoint, sp setpoint

If the is value is that of a valid block number, then the sp value is obtained from the analogue result ra of that block. The sp value is the same as the ra of this block.

sh setpoint hl, sl setpoint ll

These limits are used to constrain the value of sp. Upon execution of this block the ra of the block pointed to by is will also be constrained, That is to say the constrained sp is fed back to the block pointed to by is on each scan.

op output block, an answer

an answer is the result of the control equation below which is then added to the op output block. If the op output block is an analogue output, pulse duration output or a control block the value of an is added to the sp of that block, other wise it is added to its ra.

Note that because of the fact that an incremental value is added to the op output block it is possible to alter manually, or with other blocks, that value.

Control Equation Normal operation (ai=0)

On each execution of the block the following are evaluated

er = sp - mv

er_1 = er from previous scan

er_2 = er from two scans previous

mv_1 = mv from previous scan

mv_2 = mv from two scans previous.

an = kp * ( prop_value + int_value + deriv_value )

where prop_value, int_value and deriv_value are internal temporary variables depending on the algorithms and constants as follows

td derivative time, ad derivative algorithm, deriv_value

if ad = 0 then

deriv_value =(mv - 2*mv_1 + mv_2) * td / scan_time_in_minutes

if ad = 1 then

deriv_value =(er - 2*er_1 + er_2) * td / scan_time_in_minutes

if ad = 3 then

er = error squared, (note if error is -ve, er is also made -ve)

deriv_value =(er - 2*er_1 + er_2) * td / scan_time_in_minutes

Derivative time (in minutes) can be set to zero to disable derivative action. The derivative action may be configured to operate on the measured variable (ad=0) or the error ( ad=1). Even error squared control is available with (ad=3).

kp proportional constant, ap proportional algorithm, prop_value

if ap = 0 then prop_value = er - er_1

if ap = 1 then prop_value = mv - mv_1

The proportional action may be configured to operate on the error ( ap=0) or the measured variable ( ap=1)

ti integral time, ai integral algorithm, int_value

if ti is not equal to 0 then int_value = er * scan_time_in_minutes / ti

To disable integral action simply set ti to 0, otherwise ti represents integral time in minutes. ( ai integral algorithm is reserved for future use.)

Control Equation dead time controller ai=1

In this mode of operation the following equation is calculated each time that the block is processed and the block pointed to by ip is violated. The block pointed to by ip is normally a timer of some type which may have a process related time constant. Where A measure of the quality of control performance is available using these parameters. Each time the value of sp changes the value of db and gf goodness factor are set to zero. On each subsequent scan the following calculation is performed

if ag = 0 gf = gf + abs( er ) absolute error

if ag = 1 gf = gf + er * er error squared

if ag = 2 gf = gf + abs( er ) * db absolute error * time

if ag = 3 gf = gf + er * er * db error squared * time

After a step change in demand, one can look at the rising value of gf and compare with other tuning constants.


Motor Block

ABACUS_PC2.gif

The motor block is designed to allow comprehensive control of the starting/stopping of electric motors, pumps, and opening/closing of valves. The connections are made through digital inputs and outputs. Facilities are included to initiate alarm signals if the motor/pump fails to respond to control signals in allowed time periods. The accumulation of run time is also included.

Each of the logical inputs and outputs has associated with it an algorithm for inverting the significance of that input or output. In each case this algorithm follows the normal ABACUS convention where 1=noninverting, and 0=inverting.

In addition to the common parameters the motor block has the following
 
ir
input ready
block_number
ar
algorithm
(0=inverting, 1=noninverting)
iu
input run
block_number
au
algorithm
(0=inverting, 1=noninverting)
it
input start
block_number
at
algorithm
(0=inverting, 1=noninverting)
ip
input stop
block_number
ap
algorithm
(0=inverting, 1=noninverting)
ia
input auto
block_number
aa
algorithm
(0=inverting, 1=noninverting)
c1
control input 1
block_number
a1
algorithm
(0=inverting, 1=noninverting)
c2
control input 2
block_number
a2
algorithm
(0=inverting, 1=noninverting)
af
algorithm
(0=AND, 1=OR)
sr
start output
block_number
nr
algorithm
(0=inverting,1=noninverting)
sp
stop output
block_number
np
algorithm
(0=inverting, 1=noninverting)
sa
alarm output
block_number
na
algorithm
(0=inverting, 1=noninverting)
t1
time limit
floating_value
d1
scratchpad for above
block_number
t2
time counter
floating_value
d2
scratchpad for
above block_number
t3
run time min.sec
floating_value
d3
scratchpad for above
block_number
t4
run time
hours floating_value
d4
scratchpad for above
block_number


Motor block flow diagram

START

Check if sn = current scan & am = a No - exit

Yes

Read in all logical inputs to internal flags

IRVAL = logical result of ir if ar=1 else not logical result of ir

IUVAL = logical result of iu if au=1 else not logical result of iu

ITVAL = logical result of it if at=1 else not logical result of it

IPVAL = logical result of ip if ap=1 else not logical result of ip

ICVAL = logical result of ic if ac=1 else not logical result of ic

C1VAL = logical result of c1 if a1=1 else not logical result of c1

C2VAL = logical result of c2 if ar=1 else not logical result of c2

SAVAL = logical result of sa if na=1 else not logical result of sa

SRVAL = logical result of sr if nr=1 else not logical result of sr

SPVAL = logical result of sp if np=1 else not logical result of sp

Read in time values from scratchpads if specified

t1 = if (d1 is a block) d1's ra

t2 = if (d2 is a block) d2's ra

t3 = if (d3 is a block) d3's ra

t4 = if (d4 is a block) d4's ra

if t1 > t2 then set saval (alarm) (time-out)

if IPVAL (stop input) then reset SAVAL (alarm)

if ITVAL (start i/p) is set then set SRVAL (start o/p)

if SRVAL (start o/p) is set then

if IRVAL (run i/p) ir reset

or t2 > t1 or IPVAL (stop i/p) or ICVAL (auto) is set

then reset SRVAL (start switch)

if ICVAL (auto) is set then

if IRVAL (ready i/p) is reset

or t2 > t1 or IPVAL (stop i/p) or ITVAL (start) is set

then reset ICVAL (auto flag)

if ICVAL is set (auto)

then

if af = 0 do îANDî control action i.e.

if C1VAL AND C2VAL then set SRVAL (start)

else reset SRVAL (start)

else

if af = 1 do îORî control action i.e.

if C1VAL OR C2VAL then set SRVAL (start)

else reset SRVAL (start)

Calculate timer values

if SRVAL is set (start requested) and IUVAL (running i/p) is reset

then increment t2

if IUVAL is set (runnning) then increment t3

if t3 > 3600

then increment t4, and subtract 3600 from t3

(t4 becomes hours if t3 is seconds)

set SPVAL to the complement of SRVAL

(i.e. stop o/p opposite of start o/p)

reset is and ip blocks (start and stop scratchpads)

is = if (as = 1) then 0 else 1

ip = if (ap = 1) then 0 else 1

set / reset logical block in accordance with internal flags

ic block = if ( (ICVAL = 1) & (ac = 1) )

or ( (ICVAL = 0) & ( ac = 0) ) then 1 else 0

sr block = if ( (SRVAL = 1) & (nr = 1) )

or ( (SRVAL = 0) & ( nr = 0) ) then 1 else 0

sp block = if ( (SPVAL = 1) & (np = 1) )

or ( (SPVAL = 0) & ( np = 0) ) then 1 else 0

sa block = if ( (SAVAL = 1) & (na = 1) )

or ( (SAVAL = 0) & ( na = 0) ) then 1 else 0

FINISH

Calculation Timing and type conversion

This section describes the operation of the following block types



 
Index Block This block type is used for counting, timing and integer arithmetic.
Input code conversion block  The input conde conversion block is used to reference a continuous range of logical block results and produce an analogue result depending upon their status.
Output code conversion block  The output conde conversion block is used to convert an analogue result into a bit pattern and switch a continuous range of blocks.
Control Block The control block is a multifunction block capable of many different types of calculation, control functions, accumulation functions, and list processing.
Sequence Block This block causes the execution of curtain other blocks independently of their scan parameters in a sequence determined by an input list to the sequence block. Alternatively a serial programme can be executed.
Scratchpad Block All blocks which have no other block type are classified as scratchpad blocks. Scratchpad blocks are nonexecutable, and only have the common parameters. They are used for analogue and data storage only.


Index Block

ABACUS_CTT28.gif
 
 
 
 
 

The index block is used for counting, timing and integer arithmetic. The block has the following parameters in addition to the common parameters tg sn ra am vn and ss.



 
ia logical input block_number or indirect_block_number
aa algorithm a 0 - 7
ib analogue input block_number or indirect_block_number 
ab algorithm b 0 - 7
hl high limit floating_point_constant
ll low limit floating_point_constant
k1 constant floating_point_constant

ia input a, aa algorithm a

The status of the logical input specified by the ia parameter is processed by aa in accordance with table IX1. If the intermediate logical result is 1 then further processing is carried out as described below, otherwise processing is discontinued, except for the initialisation described for ab of 1.

ib input b

The analogue result of the block specified by this parameter is used as the measured variable in further processing of this block.

hl high limit, ll low limit, k1 constant

The value of these parameters are used in the various calculations performed in accordance with ab.

Table IX1

algoritm logical logical intermediate
description previous current result
0 off (inverting) - 0 1
- 1 0
1 on (noninverting) - 1 1
- 0 0
2 on-going 0 1 1
1 1 0
1 0 0
0 0 0
3 off-going 1 0 1
0 0 0
0 1 0
1 1 0
4 any-change 0 1 1
1 0 1
0 0 0
1 1 0
5 no-change 0 0 1
1 1 1
0 1 0
1 0 0

ab = 0 Integer addition

ra = measured variable + k1

ab = 1 cyclic progression

The cyclic progression is initialised the first time the block is scanned after being set auto, the violation status being reset, and the block result initialised according to the following conditions
 
If ib is nonzero ra = measured variable

If ib is zero and k1 = 0 ra = ll

If ib is zero and k1 < 0 ra = hl
 

Further processing of this algorithm only occurs if the logical input ia and its algorithm aa produce an intermediate result of 1. Each time this algorithm is processed, the value of ra and vn are updated according to the following conditions.If k1 = 0 then
 
if ra+k1 hl then

ra = ll, and vn = v

else

ra = ra + k1 , and vn = n

If k1 < 0 then

if ra+k1 < ll then

ra = hl, and vn = v

else

ra = ra + k1 , and vn = n


In this way the value of ra steps from one limit to the other with steps of k1. When arriving at the limit the violation flag is set for one scan.

ab = 2 Integer subtraction

ra = k1 - measured variable

If ( ra < ll ) or ( ra hl ) vn = v

else vn = n

ab = 3 Integer multiplication

ra = k1 * measured variable

If ( ra < ll ) or ( ra hl ) vn = v

else vn = n

ab = 4 Integer division

ra = measured variable / k1

If ( ra < ll ) or ( ra hl ) vn = v

else vn = n

ab = 5 Integer division

ra = k1 / measured variable

If ( ra < ll ) or ( ra hl ) vn = v

else vn = n

ab = 6 Delay Timer

This algorithm performs as a normal delay timer where the vn logical result of the block is a delayed version of the intermediate logical result (logical result from block ia and aa). The block counts k1 each scan from ll to hl while the intermediate logical result is 1, then sets the vn flag. (If the k1 < 0 then the block counts down from hl to ll.).

ABACUS_CTT29.gif


Input Code Conversion Block

ABACUS_CTT30.gif







The input code conversion block is used to reference a continuous range of logical block results and produce an analogue result depending upon their status.

In addition to the common parameters the input conde conversion block has the following

  • ia The first of a continuous range of blocks
  • ds number of blocks to be accessed
  • aa algorithm number
  • aa = 0 binary transfer

    Transfers the binary pattern of the logical inputs to the block result. The logical input specified by ia is regarded as the least significant bit in the word and is represented by 1 in the ra if it is set. The next logical input is represented by 2 if set, the next by 4, 8 etc.

    aa = 2 displacement

    Causes the block to search for the first logical input in the range that is set. If the first input is set the ra will be 1, if the second is set but not the first ra will be 2 etc. If none of the inputs are set then ra will be 0.

    Output Code Conversion Block

    ABACUS_CTT31.gif







    The output conde conversion block is used to convert an analogue result into a bit pattern and switch a continuous range of blocks.

    In addition to the common parameters the output conde conversion block has the following

  • ia The block whose ra will be used
  • aa algorithm number
  • sw The first block to be affected
  • ds number of blocks to be affected
  • aa = 0 binary transfer

    This algorithm transfers the binary pattern of the measured variable to the am flags of the specified range of blocks. The block specified by sw is regarded as the least significant bit in the word, and is represented by 1 in the measured variable, the next block by 2 then by 4 etc. Should a bit be set in the measured variable then the corresponding output block will be set auto, otherwise it will be set manual.


    Control Block

    ABACUS_CTT32.gif







    The control block is a multifunction block capable of many different types of calculation, control functions, accumulation functions, and list processing. The ab algorithm b parameter specifies the manner in which the control block is processed. The algorithms are divided into three main categories.
     
     
    0 to
    9
    Calculation and control
    10 to
    13
    List calculation
    14 to
    19
    Accumulation
    20 to
    25
    List processing

    The block has the following parameters in addition to the common parameters tg sn ra am vn and ss.
     
     
    ia
    analogue input
    block_number or indirect_block_number
    aa
    algorithm a
    0 - 3
    ab
    algorithm b
    0 - 32
    sp
    setpoint
    floating_point_value
    is
    inout setpoint
    block_number or indirect_block_number
    in
    increment
    floating_point_value
    er
    error
    floating_point_value
    eq
    equalisation flag
    e/n
    ds
    data scratchpad
    block_number or indirect_block_number
    hl
    high limit
    floating_point_value
    ll
    low limit
    floating_point_value
    k1
    constant k1
    floating_point_value
    k2
    constant k2
    floating_point_value
    ca
    cascade a
    block_number cascade_type or indirect_block_number cascade_type 
    cb
    cascade b
    block_number cascade_type or indirect_block_number cascade_type 

    Calculation and control algorithms ab 0 - 9

    if is 0 then sp = ra of is block

    result is the intermediate analogue result which is then process by aa.



     
    ab description calculation(s) performed
    0 simple transfer result = sp + in - mv (mv = ra of ia block) 
    1 simple ratio result = (sp + in - mv) * k1
    2 flow rate conditioning result = k2 * sqrt( (mv - k1)*(sp + hl)/(in + ll) )
    3 general mathamatics result = k2 * (mv - k1) * (sp + hl)/(in + ll)
    4,5,6 two term control error = sp + in - mv (error is an internal variable)
    result* = k1 * ( error - k2 * er ) 
    er = error
    *Note
    4 er is used for aa calculation 
    5 ra is used for aa calculation 
    6 sp is used for aa calculation 
    7 natural logarithm result = k2 * ln( (mv - k1)*(sp + hl)/(in + ll) )
    8 exponential result = k2 * exp( (mv - k1)*(sp + hl)/(in + ll) )

    List Calculation algorithms ab 10 - 13

    if is 0 then sp = ra of is block

    These algorithms operate on the contiguous list of block defined as those blocks between is and ia. ia should refer to a block with higher block number than that pointed to by is.

    result is the intermediate analogue result which is then process by aa.



     
    ab description calculation performed
    10 sum result = k2 + k1 * sum ( is .. ia ) 
    11 ave result = k2 + k1 * ave ( is .. ia ) 
    12 max result = k2 + k1 * max ( is .. ia ) 
    13 min result = k2 + k1 * min ( is .. ia ) 

    Accumulation algorithms ab 14 - 19

    These algorithms include integration, averaging and standard deviation functions. They can only operate in a satisfactory manner when the block is the recipient of a double precision ( dp) cascade (from another control block or blocks).

    Algorithms are either pre-initialised or post-initialised.

    Pre-initialisation involves resetting the accumulations and setting the result to zero the first time the block is processed after having been set auto.

    Post-initialisation processes the accumulations to obtain the result prior to resetting the accumulations, on the first scan after the block has been set auto. This having the advantage that the block may be left manual whilst accumulating, it only being necessary to set it auto to obtain the result, and to reset the accumulations for the next period. (It is normal to set the single shot flag ss in this case.)

    acc is an internal floating point variable which is incremented by the cascading block with its result each time it cascades.



     
    ab description calculation(s) performed
    14 integration pre-initialised result = acc / ( k1 * 100 )
    15 integration post-initialised result = acc / ( k1 * 100 )
    16 average pre-initialised result = acc / er (er = number of samples) 
    17 average post-initialised result = acc / er (er = number of samples) 
    18 std. deviation pre-initialised result = k1 * standard deviation (-1 if less than 10 samples)
    19 std. deviation post-initialised result = k1 * standard deviation (-1 if less than 10 samples)

    List processing algorithms ab 20 - 25

    The list processing algorithms permit data input from a range of blocks to be processed, and output to a range of blocks.

    ia

    The input a ia parameter specifies the first of a range of blocks to be used as inputs to these algorithms. During processing, the algorithm steps from the first input block to next, then the next etc., and at each step reads the result of the input block which then becomes the measured variable mv. If the input a ia parameter is zero then the measured variable is regarded as zero for all steps.



     
    ca cascade a block_number cascade_type or indirect_block_number cascade_type 
    cb cascade b block_number cascade_type or indirect_block_number cascade_type 

    The ca parameter specifies the first block to receive a cascade from the list processing algorithm. The cb parameter specifies the last block to receive a cascade. The number of target blocks is thus defined by the cascades, and therefore the number of input blocks is the same. cb must point to a higher block number than ca.

    The cascade type must be the same for both ca and cb, and must be consistent with the type of target block.

    When the algorithm is processed the measured variable is taken from each input block in turn. The measured variable is processed and the result cascaded to the corresponding target block.

    For each item of data thus processed, limits are checked to produce an intermediate violation status, and result which are taken into account by the cascades.

    When the block specified by cb has been processed the algorithm is finished and the last calculated result and violation status are the final values for the block.



     
    ab description calculation(s) performed
    20 simple transfer result = mv
    21 multiplication result = (sp - mv) * k1
    22 division result = (sp - mv) / k1
    23 division result = k1 / (sp - mv)
    24 multiply input result = mv * A / k1 A = initial value of by output result to be cascaded to
    25 simple transfer result = mv mv = result of block whoseindirect input number is the result of the current input. If this number is invalid no cascade is performed at this stage.

    aa Logical algorithm

    The analogue result of the block calculated by the various algorithms ( ab) is further processed by aa. (In the case where ab = 4 it is the value of er, where ab = 6 it is the value of sp) The list processing algorithms have the result processed by aa at each step.

    These parameters are related to the final ra and vn values as in the following table.



     
    aa
    ra hl
    ll <= ra <= hl
    ll ra
    ra
    vn
    ra
    vn
    ra
    vn
    0
    result
    n
    result
    n
    result
    n
    1
    result
    v
    result
    n
    result
    v
    2
    hl
    v
    result
    n
    ll
    v
    3
    result
    v
    0
    n
    result
    v

    Cascades

    These parameters specify two independent cascades, which consist of a block number and a cascade type, and represent the final stage of processing for the control block. sp - Setpoint add The recipient block may be of any type, but if it is a control block, pulse duration output block, or an analogue output block it is the sp of that block which is affected, otherwise the analogue result ra which is affected. (In the case of the PID block the ra and sp parameters are equivalent). The affected parameter has the result of this block added to it. so - Setpoint overwrite The recipient block may be of any type, but if it is a control block, pulse duration output block, or an analogue output block it is the sp of that block which is affected, otherwise the analogue result ra which is affected. (In the case of the PID block the ra and sp parameters are equivalent). The affected parameter has the analogue result of this block written to it, overwriting its previous value. k1 - k1 overwrite The recipient block must be a control block whose k1 parameter is overwritten by this blocks analogue result. k2 - k2 overwrite The recipient block must be a control block whose k2 parameter is overwritten by this blocks analogue result. hl - hl overwrite The recipient block must be a control block whose hl parameter is overwritten by this blocks analogue result. ll - ll overwrite The recipient block must be a control block whose ll parameter is overwritten by this blocks analogue result. in - in add The recipient block must be a control block whose in parameter is added onto by this blocks analogue result. dp - double precision add The recipient block must be a control block whose accumulator parameter is added into by this blocks analogue result. If the recipient block has ab of 16 or 17 (averaging) the er parameter is also incremented by one each time. sv - Switch if violated The recipient block will by switched on (its am made auto) if this block is in violation. sn - Switch if not violated The recipient block will by switched on (its am made auto) if this block is not in violation. rv - Reset if violated The recipient block will by switched off (its am made manual) if this block is in violation. rn - Reset if not violated The recipient block will by switched off (its am made manual) if this block is not in violation.

    Sequence Block

    This block is not implemented at this time

    Scratchpad Block

    ABACUS_CTT34.gif







    All blocks which have no other block type are classified as scratchpad blocks. Scratchpad blocks are nonexecutable, and only have the common parameters. They are used for analogue and data storage only.

    Operator Communications

    This section of the ABACUS block language manual describes the alarm code block and the programs used in connection with the Tck/tk language to produce displays in the X windows system.

    The operator communicates with ABACUS4 via an operators station. This is usually a PC running the Linux operating system and the X window system. This may be the same system that is running ABACUS4 or another reachable over a network.

    Text based operator interfaces may be constructed, but these are not documented here.

    All normal operator access to the ABACUS4 block database is done via the Alarm Code Block, although all blocks are accesable to the Tck/tk language.

    The Alarm Code Block permits the monitoring of the analogue or logical results of blocks, and the generation of process alarms when limits are violated, or upon changes.

    The block permits the operator access to its high and low process limits, and to the result, setpoint, auto/manual and auto/manual request flags of the loop being monitored.


    Alarm Code Block

    ABACUS_OC.gif

    The Alarm Code Block permits the monitoring of the analogue or logical results of blocks, and the generation of process alarms when limits are violated, or upon changes.

    The block permits the operator access to its high and low process limits, and to the result, setpoint, auto/manual and auto/manual request of the loop being monitored.

    In addition to the common parameters the Alarm Code Block has the following
     
    si
    signal name text
    text_string (24 bytes)
    ia
    input a
    block_number
    is
    input setpoint
    block_number
    ar
    auto/man request
    block_number
    st
    status
    block_number
    hl
    high limit
    floating_point_value
    ll
    low limit
    floating_point_value
    xh
    high high limit
    floating_point_value
    xl
    low low limit
    floating_point_value
    aa
    algorithm
    integer
    hy
    hysteresis value
    floating_point_value
    un
    units text
    text_string (9 bytes)
    mo
    ok text
    text_string (9 bytes)
    ml
    high alarm text
    text_string (9 bytes)
    mh
    low alarm text
    text_string (9 bytes)
    lx
    low low alarm text
    text_string (9 bytes)
    hx
    high high alarm text
    text_string (9 bytes)
    ma
    auto text
    text_string (9 bytes)
    mm
    hand text
    text_string (9 bytes)
    ur
    auto request text
    text_string (9 bytes)
    hr
    hand request text
    text_string (9 bytes)
    s1
    status 1 (alarm)
    text text_string (9 bytes) (read only)
    s2
    status 2 (hand/auto)
    text text_string (9 bytes) (read only)
    fn
    file name text
    text_string (9 bytes)

    This is the loop name which is used in all ABACUS journaling. (It should be copied over the the descriptor of any Enterprise Server database points - see later.)

    ia input a

    The ra of the block referenced here is taken as the measured variable of the loop. Subject to the value of aa it is this block's result which is tested against the various limits.

    is input setpoint

    The ra of the block referenced here is taken as the setpoint of the loop.

    ar auto/man request

    The vn of the block referenced here is taken as the auto/man request flag of the loop. That is to say the flag which is set when the operator requests that the loop go into auto.

    st status

    The vn of the block referenced here is taken as the auto/man status flag of the loop. That is to say the flag which is set when the loop is in auto.

    hl high limit

    ll low limit

    xh high high limit

    xl low low limit

    hy hysteresis value

    Each time the measured variable passes one of the above limit values a message is recorded in the journal file, excepting that if the hy value is nonzero a return excursion is ignored until a movement greater than hy occurs. This can be used to avoid multiple alarms when a noisy signal goes into alarm.

    For correct functioning of the limit test facilities the following should be true

    xh > hl > ll > xl, and hy should be positive and less than the difference between any two of the other limits.

    s1 status 1 (alarm) text

    The text string written by the block processor here depends on the relationship between the measured value and the alarm limits. If hy = 0 the following is carried out

    if ( measured value < xl ) then s1 = lx (low low alarm text)

    else if ( measured value < ll ) then s1 = ml (low alarm text)

    else if ( measured value > hl ) then s1 = mh (high alarm text)

    else if ( measured value > xh ) then s1 = hx (high high alarm text)

    else s1 = mo (ok text)

    If however the hy value is nonzero the above becomes

    if ( measured value < xl ) then s1 = lx (low low alarm text)

    else if ( (xl + hy ) > measured value >= xl ) then s1 is unchanged

    else if ( measured value < ll ) then s1 = ml (low alarm text)

    else if ( (ll + hy ) > measured value >= ll ) then s1 is unchanged

    else if ( measured value > hl ) then s1 = mh (high alarm text)

    else if ( (hl - hy ) < measured value <= hl ) then s1 is unchanged

    else if ( measured value > xh ) then s1 = hx (high high alarm text)

    else if ( (xh - hy ) < measured value <= xh ) then s1 is unchanged

    else s1 = mo (ok text)

    aa algorithm

    This algorithm determines which limits are used to define an alarm state and set the vn of the Alarm Code Block or if the st logical value should be used. The following tables shows the effects of alternative aa values. (aa values greater than 9 inhibit the printing/logging of setpoint changes)

    Analogue alarms
     
    aa mv<xl mv < ll mv>ll&mv<hl mv>hl mv>xh
    0,10 vn = n n n n n
    1,11 vn = v v n v v
    4,14 vn = v n n n v

    Digital alarms
     
    aa st -> vn = n st -> vn = v
    2,12 vn = n v
    3,13 vn = v n

    s2 status 2 (hand/auto) text

    The text string written by the block processor here depends on the vn of the block referred to in st. If this block's vn = v then ma is copied, otherwise mm.

    All changes to limit values, setpoint value, auto/manual request flag, and status changes are logged in the journal file if the Alarm Code Block is auto. One line is written for each event, and has the following format

    YYMMDD HH:MM:SS signal_name message ( old_value ) new_value

    where

    signal_name is the si of the Alarm Code Block

    YYMMDD HH:MM:SS is the time and date of the event

    message = "setpoint changed"

    "hi-hi lim changed"

    " high lim changed"

    " low lim changed"

    or "lo-lo lim changed" as appropriate.

    When an alarm limit is exceeded the format is as follows

    YYMMDD HH:MM:SS signal_name alarm_text value units

    where alarm_text is the appropriate alarm text string ( ml mh lx hx or ok ), value is the measured value, and units is the text un.

    The ABACUS4 - Tcl/tk interface

    It is beyond the scope of this manual to describe the opperation of the Tcl/tk language, the reader is refered to the many man pages in the section n, 'Tcl and the Tk Toolkit" (Addison-Wesly, 1994) or "Practical Programming in Tcl and Tk" (Brent B .Welch).

    The easiest way to get to see a list of the commands is to run the xman program, and look into the n section.

    Displays are stored in a subdirectory of /home/abacus. This directory has the name of the application, and also contains the datadump.bin file(s). If the project is called smith for example then these files will reside in the directory /home/abacus/smith. It is usual to have one 'display manager' file which includes code for menus and a frame for the displays to be shown in, and a number of 'display files'. In addition the background diagrams and dynamic symbols are stored in gif format.

    ABACUS4 programs which Tcl/tk programs may use

    am ra3 si ss vn2

    eng2

    This is a non-interactive version of the eng program (se eng ). eng2 is called with all the block access parameters on the command line, i.e.

    eng2 b1234,p

    Here block 1234 is printed, something like the following being returned:

    Block 1234 () type index 
    
    B1234 () 
    nn 
    de 
    nm 
    tg spare;sn 0(0.0 sec);ra 0.000 
    am m ;vn n ;ss r 
    en 0;dd 0 
    aa 0;ia 0( 0.000 m-rn) 
    ab 0;ib 0( 0.000 m-rn) 
    k1 0;hl 0;ll 0 
    
    
    The tcl program is responsible for extracting the part of this printout which is of interest.

    eng2 requires that all the commands come together in its first argument, so that if spaces are part of the syntax, then the argument should be enclosed in quote marks. If you use this program from an xterm window for example beware of the command line conversions that your shell does, in particular the * character is translated into all the filename of files in the current area, before being passed onto, in this case, the eng2 program. This can be avoided either by enclosing you arguments in quotes, or by prefixing the * with the \ character.

    eng2 is sometimes used with a unix pipe. For example if we ish to list all the si paramters of the blocks in the range 8001 to 8099 we could use

    eng2 b8001,xb8099,.si

    This would produce something like

    Block 8001 () type alarm code 
    Xblock 8099 type alarm code 
    b 8001;si 
    b 8002;si# 02 GAUGE ON/OFF SHEET 
    b 8003;si# 03 GAUGE STATIC 
    b 8004;si# 04 GAUGE STANDARDIZE 
    b 8005;si
    ...
    Whereas we might not want the 'b ' parts or the ';si'. We could use the unix program sed in a pipe like this
    eng2 b8001,xb8099,.si | sed -e "s/^b //" -e "s/;si/ /" 
    
    
    which would produce something like
    Block 8001 () type alarm code 
    
    Xblock 8099 type alarm code 
    8001 
    8002 # 02 GAUGE ON/OFF SHEET 
    8003 # 03 GAUGE STATIC 
    8004 # 04 GAUGE STANDARDIZE 
    8005 
    8006 
    
    
    Removal of the top two lines could be achieved with grep
    eng2 b8001,xb8099,.si | sed -e "s/^b //" -e "s/;si/ /" | \ 
    grep -v "^B\|^X" 
    
    
    The possibilities are endless, however there are some other programs to do the simpler things easier.

    eng4

    eng4 is a very simple socket program which accepts the two letter nmuemonic followed by the block number, and returns the appropriate paramter value. Not every parameter is available, however everything on an alarm code block, including the acknowledged flag (not accessable any other way). eng4 in common with the other 'socket' programs always responds with exactly one line for each line it recieves.

    ra

    This returns the result of the block whose number is given as the first argument. An optional second argument may specify the format (in C syntax) for the returned value.
    frank(abacus):~$ ra 14
    0.250931frank(abacus):~$
    frank(abacus):~$
    Note that there was no end of line printed.

    ra2

    This returns the results of one or more blocks as listed after the ra2.
    frank(abacus):~$ ra2 14 51 52 53
    0.15332 0.1 0.2 0.58
    frank(abacus):~$
    The values are returned in the same order as the block numbers apear in the command line.

    ra3

    This program is designed for use at the end of a socket connection. A list of one or more block numbers is sent into the program which then responds with the results of those blocks, and then waits for the next input.

    vn

    This returns the violtation status of the block whose number is given as the first argument. Two further arguments may define the returned text for respectively the nonviolated and violated states of the block.

    vn2

    This returns the violtation status of one or more blocks whose numbers are given as the arguments. vn2 return either 0 for nonviolated or 1 for violated.

    vn3

    This is the socket version, returning 0 or 1 for each block sent it.

    am

    This returns the auto/manual status of the block whose number is given as the first argument. Two further arguments may define the returned text for respectively the auto and manual states of the block.

    si

    This returns the signal identity of the Alarm Code block whose number is given as the first argument. If the block number specified is not an Alarm Code block blanks are returned.

    si3

    This is the socket version of si. For each line of input exactly one line of output is returned.

    sp3

    This socket program returns the analog result (ra) of the block whose number is stored as the is parameter of the Alarm Code block whose number is inputted to this program.

    s13

    This socket program returns the s1 value of the Alarm Code block whose number is inputted to this program.

    s23

    This socket program returns the s2 value of the Alarm Code block whose number is inputted to this program.

    un3

    This socket program returns the un value of the Alarm Code block whose number is inputted to this program.

    Display Manager example

    The following example is taken from a simulation example, and is followed by notes on the contents of the file.
    #!/bin/sh
    # ./display.sh  - ABACUS operator interface 
    # the next line restarts using wish \
    exec wish "$0" "$@" -geometry +0+0
    ########################################################################
    #
    # Simulation 
    #
    # Initial version 990603
    # 
    # 
    ########################################################################
    
    ####### Edit this bit, because there should bot be any more direct
    ####### path references refering to the local machine.
    #cd /home/abacus/
    set w .page
    
    set NODE        ast
    set NODE        localhost
    #set NODE       inco
    
    ############## TITLE set here ##########################################
    #
    wm title . [format "ABACUS4 Display Manager - Teachers ABACUS" ]
    wm resizable . 0 0
    
    ############## BACKGROUND IMAGES DONE HERE ######
    frame $w 
    canvas $w.p -height 425 -width 630 
    frame $w.menu -relief raised -bd 2
    label $w.welcome -text "THE DISPLAY SYSTEM IS STARTING PLEASE WAIT"
    pack $w $w.menu $w.p  $w.welcome  -side top -fill x
    update idletasks
    
    ############## GIF Files defined here ###################################
    
    # valves pumps and blobs
    foreach blob { 
     vent_t_green   vent_t_gray     vent_t_red  
     vent_r_green   vent_r_gray     vent_r_red  
     vent_l_green   vent_l_gray     vent_l_red  
     pump_u_green   pump_u_gray     pump_u_red
     pump_d_green   pump_d_gray     pump_d_red
     pump_l_green    pump_l_gray    pump_l_red      
     pump_r_green   pump_r_gray     pump_r_red
     blob_green     blob_gray       blob_red   
     fan_u_gray     fan_u_green     fan_u_red
     fan_d_gray     fan_d_green     fan_d_red
     LEFTFLAME      NOLEFTFLAME     NORIGHTFLAME RIGHTFLAME
     reel
    } {
    $w.welcome configure -text "doing $blob"
    update idletasks
    image create photo $blob  -file   ./$blob.gif
    }
    $w.welcome configure -text "done the vent & pumps"
    update idletasks
    
    # Data pages
    #image create photo Pic_opcodes -file   ./opcodes.gif
    image create photo Pic_dp3 -file   ./datapage_3.gif
    image create photo Pic_dp2 -file   ./datapage_2.gif
    image create photo Pic_dp1 -file   ./datapage_1.gif
    $w.welcome configure -text "done the datapage images"
    update idletasks
    
    # Mimic pages
    foreach nr {1 2 3 4 5} {
    $w.welcome configure -text "starting ./process_$nr.gif"
    update idletasks
    catch { image create photo Pic_mp$nr -file   ./process_$nr.gif}
    }
    
    $w.welcome configure -text "done the mimic images"
    update idletasks
    
    image create photo Pic
    $w.p create image 0 0 -image Pic -anchor nw
    
    
    ############################################################################
    
    #cd /home/abacus/
    if { [info exists NODE] } {
      
      } else {
        set NODE localhost
      }
    ############## Symbols defined here ####################################
    ##
    ## Create an image for each symbol file that is in the directory
    
    #foreach fil [ glob /home/abacus/shapes-ppm/*.ppm ] {
    ## catch { foreach fil [ glob /home/abacus/shapes/*.ppm ] {
    ##      catch { set pic [file rootname [file tail $fil]] }
    ##      catch { image create photo $pic }
    ##      catch { $pic configure -file $fil }
    ## 
    ##      catch { image create photo ${pic}_black -palette 3 }
    ##      catch { ${pic}_black configure -file $fil }
    ## } }
    
    
    ##################### bash-net substitute ############################
    
    proc BashNetReader { sock } {
      global BashNetforever BashNetReaderResult
      gets $sock line
      if { $line == "e17971e17971" } {
        set BashNetforever 1
        catch {close $sock}
    #puts stdout "BashNetReader: close $sock"
        catch {close $sock}
        return line
      }
      append BashNetReaderResult "$line\n"
      return line
    }
    
    proc BashNet { sock args } {
      global BashNetforever BashNetReaderResult
    
        set BashNetReaderResult ""
        set sock [socket $sock 9000]
    #puts stdout "BashNet: set sock \[socket $sock 9000\]" 
        fconfigure $sock -blocking 1 -buffering line -translation lf 
        puts $sock "[lrange $args 0 end];echo e17971e17971;"
        flush $sock
    
        fileevent $sock readable [list BashNetReader $sock]
        vwait BashNetforever
        return $BashNetReaderResult
    }
    
    
    ############## Menu stuff defined here ################################
    #frame $w.menu -relief raised -bd 2
    
    set m $w.menu.file.m
    menubutton $w.menu.file -text "DISPLAY" -menu $m -underline 0
    menu $m -tearoff 0
    proc clearup {} {
      global w xx
      foreach xx [split [after info] " "] { after cancel $xx }
      foreach xx [winfo children $w.p] { destroy $xx }
     }
    
    proc change_to_org { the_page } {
      global w xx the_page_
    
      $w.p configure -height 425 -width 630 
      set the_page_ $the_page
      foreach i  [array names bar] { unset bar($i) }
      foreach xx [split [after info] " "] { after cancel $xx }
      foreach xx [winfo children $w.p] { destroy $xx }
      catch { uplevel #0 { source ./${the_page_}.tcl } }
      catch { update0 }
      timdat
     }
    
    proc change_to { the_page } {
      global w xx the_page_ bar
    
    #  $w.p configure -width 630 -height 425 
      set the_page_ $the_page
      foreach i  [array names bar] { unset bar($i) }
      foreach xx [split [after info] " "] { after cancel $xx }
    
      destroy $w.p 
      canvas $w.p -width 630 -height 425
    
      $w.p create image 0 0 -image Pic -anchor nw
      pack $w $w.menu $w.p -side top -fill x
    
      catch { uplevel #0 { source ./${the_page_}.tcl } }
    #  catch { update0 }
      timdat
     }
    
    proc mem_test {} {
      #set self [open /proc/self/status r]
      #puts stdout "[read $self]"
      #close $self
      #puts stdout "info cmdcount [info cmdcount]"
    }
    
    
    
    ## $m add separator
    ## $m add command -label "C2_1   Vällkomstbild" -command { 
    ##      clearup
    ##      catch { source ./C2_1.tcl }
    ##   } 
    $m add separator
    $m add command -label "Data Page 1 " -command { change_to dp1 }
    $m add command -label "Data Page 2 " -command { change_to dp2 }
    $m add command -label "Data Page 3 " -command { change_to dp3 }
    $m add separator
    $m add command -label "Reel Report " -command { change_to reel }
    $m add separator
    $m add command -label "Mimic 1 Machine Overview" -command { change_to mp1 }
    $m add command -label "Mimic 2 Machine Overview and PIDs" -command { change_to mp2 }
    $m add command -label "Mimic 3 " -command { change_to mp3 }
    $m add command -label "Mimic 4 " -command { change_to mp4 }
    $m add command -label "Mimic 5 " -command { change_to mp5 }
    $m add separator
    
    $m add command -label "Journal display " -command { 
     catch { exec  ./kill.journal.sh }
     exec nice ./journal_updating.sh { } &
        }
       
    $m add command -label "Alarm display" -command {
     catch { exec  ./kill.alarm_banner.sh }
     exec ./alarm_banner.sh & }
    
    
    
    set m $w.menu.file2.m
    menubutton $w.menu.file2 -text "DISPLAYS 2 " -menu $m -underline 0
    menu $m -tearoff 0
    
    $m add command -label "phtrend1" -command {phtrend1} 
    $m add separator
    $m add command -label "Start a new Engineers Mode" -command { exec xterm -e /home/abacus/bashnet $NODE /home/abacus/eng & } -underline 0
    $m add separator
    
    
    
    ########## Start other displays with these routines ##########
    proc start_abapic {} {\
            global NODE
            catch { exec ./kill.abapic.sh }
            catch { exec nice ./abapic.sh  -geometry +0+0 & }
            after 1000 exit
            }
    
    
    proc C2_1 {} {\
            global NODE
            foreach xx [split [after info] " "] { after cancel $xx }
            exec nice ./C2_1.sh  -geometry +0+0 &
            after 1000 exit
            }
    
    proc C2_tvatsek_1 {} {\
            global NODE
            exec nice ./C2_tvättsekvens_1.sh  -geometry +0+0 &
            }
    
    proc jump_button { name  x  y  text } {
            global NODE
      global        w lfont bg fg 
    ##   button  $w.p.ti$name -font $lfont -background $bg -foreground $fg \
    ##     -activebackground $bg -activeforeground $fg \
    ##      -pady 0 -padx 3 -relief flat -borderwidth 0 -text $text -command $name
      button  $w.p.ti$name -text $text -command $name -pady 0 -padx 0
      place $w.p.ti$name -x $x -y $y -anchor sw
      }
    
    #proc bgerror { message } { 
    #  bell
    #}
    
    
    ############## Blinking stuff ########################
    set fulltime    1000
    set fulltime    500
    set blinkon 0
    proc blink_change { } {
            global NODE
            global blinkon fulltime
            after cancel blink_change
            if { $blinkon  } then {
                    set blinkon 0
                    } else {
                    set blinkon 1
                    }
    #       after $fulltime blink_change
    }
    ##blink_change
    
    ############## Time/Date stuff defined here ################################
    button $w.menu.timdat -borderwidth 0 -width 20 -justify right
     
    proc timdat {} {
            global w blinkon fulltime
            global NODE
            catch { $w.menu.timdat configure -text [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S" ] }
            catch { after cancel timdat }
            mem_test
            blink_change
            after $fulltime  timdat
            }
    
    ############## X/Y display stuff defined here ################################
    
    #button $w.menu.xycontb  -borderwidth 0 -width 15 -command bell
    button $w.menu.xycontb  -borderwidth 0 -width 25 -command bell
    
    proc show_time { time_taken } {
            global NODE
      global w smoothed_time  
      
      set mili_secs [ expr [lindex $time_taken 0] / 1000]
    ##   set smoothing_factor 0.80
    ##   if { [info exists smoothed_time] == 0 } then {
         set smoothed_time $mili_secs
    ##     } else {
    ##     set new_smoothed_time [expr ( $smoothed_time * $smoothing_factor ) + \
    ##      $mili_secs * ( 1.0 - $smoothing_factor ) ]
    ##     set smoothed_time $new_smoothed_time
    ##     }
      
      ## get newest unack alarm if there is one
      set alarm_1 "[BashNet $NODE  /home/abacus/alarm | sort -r | head -1 ]"
    
      if { [string index $alarm_1 0] == "U" } then {
          $w.menu.xycontb configure -text [string range $alarm_1 28 46] \
           -background red -activebackground red
        } else {
          $w.menu.xycontb configure -text [format "%5.0f mS" $smoothed_time ] \
           -background grey85 -activebackground grey95
        }
    
    
      }
    
    
    entry $w.menu.xycont  -borderwidth 0 -width 15
    ##$w.menu.xycont insert end "Very new pumps proc !"
    bind .   {   $w.menu.xycont delete 0 end
                         $w.menu.xycont insert end "x = %x y = %y"
                         set destination_display {}
                          }
    
    ############## X/Y display button 3 puts text available for copying ################################
    button $w.menu.xy3b -width 25 -borderwidth 0 -command bell
    entry $w.menu.xy3 -width 20 -borderwidth 0
    bind .  <3> {  $w.menu.xy3 delete 0 end
                           $w.menu.xy3 insert end "-x %x -y %y %x %y" }
    bind .  <2> {  $w.menu.xy3 delete 0 end }
    $w.menu.xy3b configure -text "([clock format [clock seconds] -format "%Y-%m-%d %H:%M" ])"
    
                            
    ############## This stuff puts the above defined stuff on the screen ##########
    #pack $w $w.menu $w.p   -side top -fill x
    ######## Choose between the following for the x/y coordinate display ####
    #pack $w.menu.file $w.menu.special $w.menu.xycont $w.menu.xy3  $w.menu.timdat -side left
    #pack $w.menu.file $w.menu.xycont $w.menu.xy3  $w.menu.timdat -side left
    #pack $w.menu.file $w.menu.xycontb $w.menu.xycont $w.menu.xy3  $w.menu.timdat -side left
    #pack $w.menu.file $w.menu.xycontb $w.menu.timdat -side left
    pack $w.menu.file $w.menu.xycontb $w.menu.xy3  $w.menu.timdat -side left
    
    ###pack $w.menu.file $w.menu.special $w.menu.xycontb $w.menu.xy3b  $w.menu.timdat -side left
    ##pack $w.menu.file $w.menu.xy3b  $w.menu.timdat -side left
    
    pack $w $w.p -side top
    update idletasks
    
    timdat
    
    ############## Set font and colour variables ################################## 
    set bg  black
    set fg  yellow
    
    set bg  white
    set fg  black
    
    set lfont  -adobe-helvetica-medium-r-*-*-10-*-*-*-*-*-*-*
    set lfont  -adobe-helvetica-medium-r-*-*-8-*-*-*-*-*-*-*
    set lfont  "-windows-small fonts-medium-r-normal-*-*-100-*-*-p-*-ansi-*"
    set lfont  -*-clean-medium-r-normal--8-*-*-*-*-*-*-*
    set lfont  -adobe-courier-medium-r-*-*-10-*-*-*-*-*-*-*
    set lfont {courier 10 normal}
    
    ############## Entry stuff defined here ################################
    
    set logged_in 0
    proc log_in {} {
      global NODE logged_in
      
    }
    
    proc entry_open_shut { bno acb digo } {
       global NODE
       global w blkno
          
       set blkno $bno   
       
       if { $acb > 8000 && $acb < 8999 } {
         set text_list [BashNet $NODE /home/abacus/eng2 b$acb,ur,hr,ma,mm,si]
         set ur [string range $text_list [expr [string first \nur $text_list] + 3] [expr [string first \nur $text_list] + 9]]
         set hr [string range $text_list [expr [string first \nhr $text_list] + 3] [expr [string first \nhr $text_list] + 9]]
         set ma [string range $text_list [expr [string first \nma $text_list] + 3] [expr [string first \nma $text_list] + 9]]
         set mm [string range $text_list [expr [string first \nmm $text_list] + 3] [expr [string first \nmm $text_list] + 9]]
         set si [string range $text_list [expr [string first \nsi $text_list] + 3] [expr [string first \nsi $text_list] + 34]]
         #after 50 { wm geometry $w.open +50+50 }
         after 50 { wm geometry $w.open +[winfo pointerx .]+[winfo pointery .] }
         catch { destroy $w.open }
         set ans [tk_dialog $w.open $si $si {} [vn $bno] $hr $ur "låt bli"]
         if { $ans == 0 } { BashNet $NODE /home/abacus/eng2 b$digo,con,amm }
         if { $ans == 1 } { BashNet $NODE /home/abacus/eng2 b$digo,con,ama }
       }
    
    }
    
    proc entry_kviterar { acb } {
       global NODE w
       
       if { $acb > 8000 && $acb < 8200 } {
         set ac [ac $acb] ; set vn [vn $acb] ; set si [si $acb] ; get_values ;
         set ac [ac $acb] ; set vn [vn $acb] ; set si [si $acb] ; get_values ;
         
         after 50 { wm geometry $w.open +[winfo pointerx .]+[winfo pointery .] }
         catch { destroy $w.open }
         set ans [tk_dialog $w.open $si $si {} $ac "låt bli" "KVITTERAR"]
         if { $ans == 1 } { BashNet $NODE /home/abacus/eng2 b$acb,con,ama }
         
       }
    
    }
    
    proc entry_error { message } {
            global w blkno
            after 50 { wm geometry $w.open +[winfo pointerx .]+[winfo pointery .] }
            catch { destroy $w.open }
            tk_dialog $w.open $message $message {} 0 "låt bli"
    }
    
    proc entry_stuff { bno str } {
            global NODE
            global w blkno
            
            set blkno $bno
            after 50 { wm geometry $w.new +[winfo pointerx .]+[winfo pointery .] }
            catch { destroy $w.new } ; toplevel $w.new 
            wm title $w.new $str
    # This is like this to stop the operator clicking on some other place
    # and leaving this entry stuff open
            update idletasks
            #grab -global $w.new
            button $w.new.b1 
            button $w.new.b3 
            $w.new.b1 configure -text "Cancel" -command {destroy $w.new}
            $w.new.b3 configure -text [format "Enter: %s" $str] \
               -command {   BashNet $NODE /home/abacus/eng2 _$blkno\;con,dd777,ra[$w.new.e1 get]
                            destroy $w.new }
            
            entry $w.new.e1
            $w.new.e1 insert end [ra $blkno]
            pack $w.new.e1 -side top
            pack $w.new.b3 $w.new.b1 -side left
            focus $w.new.e1
            bind $w.new  {  BashNet $NODE /home/abacus/eng2 _$blkno\;con,ra[$w.new.e1 get]
                            destroy $w.new }
            bind $w.new  {  BashNet $NODE /home/abacus/eng2 _$blkno\;con,ra[$w.new.e1 get]
                            destroy $w.new }
            }
    
    proc entry_stuff_2 { bno title_ str } {
            global NODE
            global w blkno
            
            set blkno $bno
            after 50 { wm geometry $w.new +[winfo pointerx .]+[winfo pointery .] }
            catch { destroy $w.new } ; toplevel $w.new 
            wm title $w.new $title_
            wm geometry $w.new +50+50
    #
            update idletasks
            #grab -global $w.new
            label $w.new.label -text $str
            button $w.new.b1 
            button $w.new.b3 
            $w.new.b1 configure -text "Cancel" -command {destroy $w.new}
            $w.new.b3 configure -text [format "ENTER: %s" ""] \
               -command {   BashNet $NODE /home/abacus/eng2 b$blkno,con,ra[$w.new.e1 get]
                            destroy $w.new }
            
            entry $w.new.e1
            $w.new.e1 insert end [BashNet $NODE /home/abacus/ra $blkno]
            pack $w.new.label $w.new.e1 -side top
            pack $w.new.b1 $w.new.b3 -side left
            focus $w.new.e1
            bind $w.new  {  BashNet $NODE /home/abacus/eng2 b$blkno,con,ra[$w.new.e1 get]
                            destroy $w.new }
            bind $w.new  {  BashNet $NODE /home/abacus/eng2 b$blkno,con,ra[$w.new.e1 get]
                            destroy $w.new }
            }
    
    
    
    proc set_output { blk pcent } {
      global NODE
    #  puts stdout "blk $blk pcent $pcent"
      BashNet $NODE /home/abacus/eng2 \'b$blk,con,ra$pcent,sp$pcent\'
    }
    
    proc entry_stuff_multi_ut { blk_acb blk_sp blk_hl blk_ll blk_ar blk_st blk_ut } {
            global NODE
            global w blkno out_val
            global str_ blk_acb_ blk_sp_ blk_hl_ blk_ll_ blk_ar_ blk_st_ mm blk_ut_ ma
    
            set blk_acb_ $blk_acb
            set blk_sp_ $blk_sp
            set blk_hl_ $blk_hl
            set blk_ll_ $blk_ll
            set blk_ar_ $blk_ar
            set blk_st_ $blk_st
            set blk_ut_ $blk_ut
            
            set eng4_pipe_multi [socket $NODE 9000]
            fconfigure $eng4_pipe_multi -buffering line
            puts $eng4_pipe_multi "/home/abacus/eng4 "
    
            puts $eng4_pipe_multi "si$blk_acb"
            catch { set str_ [gets $eng4_pipe_multi] }
            
            catch { destroy $w.new } ; toplevel $w.new 
            wm title $w.new "CHANGE $str_"
            wm geometry $w.new +50+50
    #
            after 50 { wm geometry $w.new +[winfo pointerx .]+[winfo pointery .] }
    
            update idletasks
            #grab -global $w.new
            label $w.new.label -text $str_
            
            label  $w.new.spl -text "SETPOINT  "
            label  $w.new.hll -text "HIGH ALARM"
            label  $w.new.lll -text "LOW  ALARM"
            label  $w.new.utl -text "  OUTPUT  "
    
            puts $eng4_pipe_multi ra$blk_sp ;   gets $eng4_pipe_multi
            puts $eng4_pipe_multi hl$blk_hl ;   gets $eng4_pipe_multi
            puts $eng4_pipe_multi ll$blk_ll ;   gets $eng4_pipe_multi
            puts $eng4_pipe_multi ra$blk_ut ;   gets $eng4_pipe_multi
    
            puts $eng4_pipe_multi ra$blk_sp ; entry  $w.new.spe ; catch {$w.new.spe insert end [format "%g" [ gets $eng4_pipe_multi ]]}
            puts $eng4_pipe_multi hl$blk_hl ; entry  $w.new.hle ; catch {$w.new.hle insert end [format "%g" [ gets $eng4_pipe_multi ]]}
            puts $eng4_pipe_multi ll$blk_ll ; entry  $w.new.lle ; catch {$w.new.lle insert end [format "%g" [ gets $eng4_pipe_multi ]]}
    
            scale  $w.new.ute  -orient horizontal -length 200 -from 0 -to 100 \
                            -tickinterval 20 -command "set_output $blk_ut " ;
            set out_val 50
            puts $eng4_pipe_multi ra$blk_ut ; set out_val [ gets $eng4_pipe_multi ] 
            $w.new.ute set $out_val 
    
    
            button $w.new.spb   -text [format "change SETPOINT  "]
            button $w.new.hlb   -text [format "change HIGH ALARM"]
            button $w.new.llb   -text [format "change LOW  ALARM"]
    
            label  $w.new.aml -text "AUTO/HAND"
            #set isauto [s2 $blk_acb_ ]
            puts $eng4_pipe_multi s2$blk_acb_ ; set isauto [ gets $eng4_pipe_multi ]
            
            label  $w.new.isauto -text "$isauto"
            puts $eng4_pipe_multi mm$blk_acb_ ; set mm [ gets $eng4_pipe_multi ]
            puts $eng4_pipe_multi ma$blk_acb_ ; set ma [ gets $eng4_pipe_multi ]
    
    
            button $w.new.rab   -text [format "RAISE"]
            button $w.new.lob   -text [format "LOWER"]
    
            $w.new.rab configure  -command { BashNet $NODE /home/abacus/eng2 b$blk_ut_,con,sp5 }
            $w.new.lob configure  -command { BashNet $NODE /home/abacus/eng2 b$blk_ut_,con,sp-5 }
            
            button $w.new.frab   -text [format "FAST RAISE"]
            button $w.new.flob   -text [format "FAST LOWER"]
    
            $w.new.frab configure  -command { BashNet $NODE /home/abacus/eng2 b$blk_ut_,con,sp20 }
            $w.new.flob configure  -command { BashNet $NODE /home/abacus/eng2 b$blk_ut_,con,sp-20 }
            
            $w.new.spb configure  -command { BashNet $NODE /home/abacus/eng2 b$blk_sp_,con,ra[$w.new.spe get];destroy $w.new }
            $w.new.hlb configure  -command { BashNet $NODE /home/abacus/eng2 \'b$blk_hl_,con,hl[$w.new.hle get]\';destroy $w.new }
            $w.new.llb configure  -command { BashNet $NODE /home/abacus/eng2 \'b$blk_ll_,con,ll[$w.new.lle get]\';destroy $w.new }
    
    
            button $w.new.ama -text "change to $ma" -command { BashNet $NODE /home/abacus/eng2 b$blk_ar_,con,ama ;destroy $w.new }
            button $w.new.amm -text "change to $mm" -command { BashNet $NODE /home/abacus/eng2 b$blk_ar_,con,amm ;destroy $w.new }
            button $w.new.amd -text "CLOSE" -command { destroy $w.new }
                    
            grid $w.new.label  -row 0 -column 0 -rowspan 1 -columnspan 3 -sticky news
            if { $blk_sp_ } {
               grid $w.new.spl -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.spe -row 1 -column 1 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.spb -row 1 -column 2 -rowspan 1 -columnspan 1 -sticky news
               }
    
            if { $blk_hl_ } {
               grid $w.new.hll -row 2 -column 0 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.hle -row 2 -column 1 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.hlb -row 2 -column 2 -rowspan 1 -columnspan 1 -sticky news
               }
    
            if { $blk_ll_ } {
               grid $w.new.lll -row 3 -column 0 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.lle -row 3 -column 1 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.llb -row 3 -column 2 -rowspan 1 -columnspan 1 -sticky news
               }
    
            if { $blk_ar_ } {
               grid $w.new.aml -row 4 -column 0 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.isauto -row 4 -column 1 -rowspan 1 -columnspan 1 -sticky news
               if { [string trim $isauto] != [string trim $ma] } {
                   grid $w.new.ama -row 4 -column 2 -rowspan 1 -columnspan 1 -sticky news
                 } else {
                   grid $w.new.amm -row 4 -column 2 -rowspan 1 -columnspan 1 -sticky news
                 }
               }
    
            if { $blk_ut_ } {
               if { $blk_ut_ > 6300 } {     ;# analog output
                  grid $w.new.utl -row 5 -column 0 -rowspan 1 -columnspan 1 -sticky news
                  grid $w.new.ute -row 5 -column 1 -rowspan 1 -columnspan 2 -sticky news
                  #grid $w.new.utb -row 5 -column 2 -rowspan 1 -columnspan 1 -sticky news
                  }
               if { $blk_ut_ < 6300 } {     ;# PDO
                  grid $w.new.utl -row 5 -column 0 -rowspan 1 -columnspan 1 -sticky news
                  grid $w.new.lob -row 5 -column 1 -rowspan 1 -columnspan 1 -sticky news
                  grid $w.new.rab -row 5 -column 2 -rowspan 1 -columnspan 1 -sticky news
                  grid $w.new.flob -row 6 -column 1 -rowspan 1 -columnspan 1 -sticky news
                  grid $w.new.frab -row 6 -column 2 -rowspan 1 -columnspan 1 -sticky news
                  }
               }
            grid $w.new.amd -row 9 -column 2 -rowspan 1 -columnspan 1 -sticky news
    
            
            catch { close  $eng4_pipe_multi }
    
            }
    
    proc entry_stuff_multi { str blk_acb blk_sp blk_hl blk_ll blk_ar blk_st } {
            global NODE
            global w blkno
            global str_ blk_acb_ blk_sp_ blk_hl_ blk_ll_ blk_ar_ blk_st_ mm ma
            
            set blk_acb_ $blk_acb
            set blk_sp_ $blk_sp
            set blk_hl_ $blk_hl
            set blk_ll_ $blk_ll
            set blk_ar_ $blk_ar
            set blk_st_ $blk_st
            
            set eng4_pipe_multi [socket $NODE 9000]
            fconfigure $eng4_pipe_multi -buffering line
            puts $eng4_pipe_multi "/home/abacus/eng4 "
    
            puts $eng4_pipe_multi "si$blk_acb"
            catch { set str_ [gets $eng4_pipe_multi] }
    #       catch { set str_ $str }
            
            after 50 { wm geometry $w.new +[winfo pointerx .]+[winfo pointery .] }
            catch { destroy $w.new } ; toplevel $w.new 
            wm title $w.new "BYTA $str_"
            wm geometry $w.new +50+50
    #
            update idletasks
            #grab -global $w.new
            label $w.new.label -text $str_
            
            label  $w.new.spl -text "SETPOINT  "
            label  $w.new.hll -text "HIGH ALARM"
            label  $w.new.lll -text "LOW  ALARM"
    
            puts $eng4_pipe_multi ra$blk_sp ;  catch { gets $eng4_pipe_multi }
            puts $eng4_pipe_multi hl$blk_hl ;  catch { gets $eng4_pipe_multi }
            puts $eng4_pipe_multi ll$blk_ll ;  catch { gets $eng4_pipe_multi }
    
            puts $eng4_pipe_multi ra$blk_sp ; entry  $w.new.spe ; catch {$w.new.spe insert end [format "%g" [ gets $eng4_pipe_multi ]]}
            puts $eng4_pipe_multi hl$blk_hl ; entry  $w.new.hle ; catch {$w.new.hle insert end [format "%g" [ gets $eng4_pipe_multi ]]}
            puts $eng4_pipe_multi ll$blk_ll ; entry  $w.new.lle ; catch {$w.new.lle insert end [format "%g" [ gets $eng4_pipe_multi ]]}
    
            button $w.new.spb   -text [format "change SETPOINT  "]
            button $w.new.hlb   -text [format "change HIGH ALARM"]
            button $w.new.llb   -text [format "change LOW  ALARM"]
    
            $w.new.spb configure  -command { BashNet $NODE /home/abacus/eng2 b$blk_sp_,con,ra[$w.new.spe get];destroy $w.new }
            $w.new.hlb configure  -command { BashNet $NODE /home/abacus/eng2 \'b$blk_hl_,con,hl[$w.new.hle get]\';destroy $w.new }
            $w.new.llb configure  -command { BashNet $NODE /home/abacus/eng2 \'b$blk_ll_,con,ll[$w.new.lle get]\';destroy $w.new }
    
            label  $w.new.aml -text "AUTO/HAND"
            set isauto [s2 $blk_acb_ ] ; get_values ;       set isauto [s2 $blk_acb_ ]
            label  $w.new.isauto -text "$isauto"
            puts $eng4_pipe_multi mm$blk_acb_ ; set mm [ gets $eng4_pipe_multi ]
            puts $eng4_pipe_multi ma$blk_acb_ ; set ma [ gets $eng4_pipe_multi ]
    
            button $w.new.ama -text "change to $ma" -command { BashNet $NODE /home/abacus/eng2 b$blk_ar_,con,ama ;destroy $w.new }
            button $w.new.amm -text "change to $mm" -command { BashNet $NODE /home/abacus/eng2 b$blk_ar_,con,amm ;destroy $w.new }
            button $w.new.amd -text "CLOSE" -command { destroy $w.new }
                    
            grid $w.new.label  -row 0 -column 0 -rowspan 1 -columnspan 3 -sticky news
            if { $blk_sp_ } {
               grid $w.new.spl -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.spe -row 1 -column 1 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.spb -row 1 -column 2 -rowspan 1 -columnspan 1 -sticky news
               }
    
            if { $blk_hl_ } {
               grid $w.new.hll -row 2 -column 0 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.hle -row 2 -column 1 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.hlb -row 2 -column 2 -rowspan 1 -columnspan 1 -sticky news
               }
    
            if { $blk_ll_ } {
               grid $w.new.lll -row 3 -column 0 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.lle -row 3 -column 1 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.llb -row 3 -column 2 -rowspan 1 -columnspan 1 -sticky news
               }
    
            if { $blk_ar_ } {
               grid $w.new.aml -row 4 -column 0 -rowspan 1 -columnspan 1 -sticky news
               grid $w.new.isauto -row 4 -column 1 -rowspan 1 -columnspan 1 -sticky news
               if { [string trim $isauto] != [string trim $ma] } {
                   grid $w.new.ama -row 4 -column 2 -rowspan 1 -columnspan 1 -sticky news
                 } else {
                   grid $w.new.amm -row 4 -column 2 -rowspan 1 -columnspan 1 -sticky news
                 }
               }
            grid $w.new.amd -row 5 -column 2 -rowspan 1 -columnspan 1 -sticky news
            
            catch { close  $eng4_pipe_multi }
    
            }
    
    
    ################# Alarm blob proc ######################
    
    
    proc blob3 { block name x1 y1 colour } {
            global NODE
            global w r2$name $name
            global blob_data ra_data ra_block vn_data vn_block
            
            
            set alarm 0
            if { [info exists vn_block($block)] } then {
              set alarm $vn_data($block)
              } else {
              set vn_block($block) 1
              set vn_data($block) 1
              }
            set $name $alarm
    
            if { [info exists r2$name] == 0} then {
                catch { set r2$name [$w.p create oval $x1 $y1 \
                      [expr $x1 + 10] [expr $y1 + 10] \
                      -fill #E0E4E0 -width 1  ]}
              }
            set blob_name [eval set r2$name]
    
            
            if { $alarm } { catch { $w.p itemconfigure $blob_name \
                    -fill $colour -width 1  } 
            } else { catch { $w.p itemconfigure $blob_name \
                    -fill #E0E4E0 -width 1  }       }
    
            update idletasks
    }
    proc sp { block } {
            global NODE
            global sp_data sp_block
            
            set result ?
            if { [info exists sp_block($block)] } then {
              set result $sp_data($block)
              }
            set sp_block($block) 1
            return $result
    
    }
    proc s1 { block } {
            global NODE
            global s1_data s1_block
            
            set result ?
            if { [info exists s1_block($block)] } then {
              set result $s1_data($block)
              }
            set s1_block($block) 1
            return $result
    
    }
    proc s2 { block } {
            global NODE
            global s2_data s2_block
            
            set result ?
            if { [info exists s2_block($block)] } then {
              set result $s2_data($block)
              }
            set s2_block($block) 1
            return $result
    
    }
    proc si { block } {
            global NODE
            global si_data si_block
            
            set result "data kommer"
            if { [info exists si_block($block)] } then {
              set result $si_data($block)
              }
            set si_block($block) 1
            return $result
    
    }
    proc un { block } {
            global NODE
            global un_data un_block
            
            set result ?
            if { [info exists un_block($block)] } then {
              set result $un_data($block)
              }
            set un_block($block) 1
            return $result
    
    }
    proc ra { block } {
            global NODE
            global ra_data ra_block
            
            set result ?
            if { [info exists ra_block($block)] } then {
              set result $ra_data($block)
              }
            set ra_block($block) 1
            return $result
    
    }
    proc de { block } {
            global NODE
            global de_data de_block
            
            set result "data kommer"
            if { [info exists de_block($block)] } then {
              set result $de_data($block)
              }
            set de_block($block) 1
            return $result
    
    }
    ## proc de { block } {
    ##      global NODE
    ##      global de_data de_block
    ##      
    ##      set result "???"
    ##      if { [info exists de_block($block)] } then {
    ##        set result $de_data($block)
    ##        }
    ##      set de_block($block) 1
    ##      return $result
    ## 
    ## }
    proc ac { block } {
            global NODE
            global ac_data ac_block
            
            set result 0
            if { [info exists ac_block($block)] } then {
              set result $ac_data($block)
              }
            set ac_block($block) 1
            return $result
    
    }
    proc ll { block } {
            global NODE
            global ll_data ll_block
            
            set result 0
            if { [info exists ll_block($block)] } then {
              set result $ll_data($block)
              }
            set ll_block($block) 1
            return $result
    
    }
    proc hl { block } {
            global NODE
            global hl_data hl_block
            
            set result 0
            if { [info exists hl_block($block)] } then {
              set result $hl_data($block)
              }
            set hl_block($block) 1
            return $result
    
    }
    proc am { block } {
            global NODE
            global am_data am_block
            
            set result 0
            if { [info exists am_block($block)] } then {
              set result $am_data($block)
              }
            set am_block($block) 1
            return $result
    
    }
    proc vn { block } {
            global NODE
            global vn_data vn_block
            
            set result 0
            if { [info exists vn_block($block)] } then {
              set result $vn_data($block)
              }
            set vn_block($block) 1
            return $result
    
    }
    proc bar_by_block { name block x1 y1 x2 height scale colour } {
            global NODE
            global  w fulltime halftime blinkon
            global  $name 
            
            set valx [expr [ra $block] * $height / $scale ]
            if { [expr $valx > ( $height + 5 )] || [expr $valx <  -5 ] } then {
                    if { $blinkon } then {
                     set colour red 
                     set valx $height
                     } else { 
                     set colour yellow 
                     set valx $height
                     } 
                    }
            if { $valx > $height } then { set valx $height }
            if { $valx < 0 } then { set valx 0 }
    
            if {  [info exists $name ] } then {
                    $w.p coords $name $x1 $y1 $x2 [expr {$y1 - $valx} ] 
                    } else {
                    set $name [$w.p create rectangle \
                     $x1 $y1 $x2 [expr {$y1 - $valx} ] \
                     -fill $colour  -outline $colour -tags $name]   
                    
                    }
                    
            $w.p raise $name
            
    }
    proc bar_by_value { name value x1 y1 x2 height scale colour } {
            global NODE
            global  w fulltime halftime blinkon
            global  bar
            
            set valx [expr $value * $height / $scale ]
            if { [expr $valx > ( $height + 2 )] || [expr $valx <  -5 ] } then {
                    if { $blinkon } then {
                     set colour red 
                     set valx $height
                     } else { 
                     set colour yellow 
                     set valx $height
                     } 
                    }
            if { $valx > $height } then { set valx $height }
            if { $valx < 0 } then { set valx 0 }
            
            if {  [info exists bar($name) ] } then {
                    $w.p coords bar($name) $x1 $y1 $x2 [expr {$y1 - $valx} ] 
                    } else {
                    set bar($name) [$w.p create rectangle \
                     $x1 $y1 $x2 [expr {$y1 - $valx} ] \
                     -fill $colour  -outline $colour -tags bar($name)]      
                    
                    }
    }
    
    proc text_item_org { name x y format_ value_ } {
            global NODE
      global w lfont bg fg  ti$name
      
     catch { 
      if { [winfo exists $w.p.ti$name] == 0 } {
        catch { button  $w.p.ti$name -font $lfont -background $bg -foreground $fg \
        -activebackground $bg -activeforeground $fg \
         -pady 0 -padx 0 -relief raised -borderwidth 0 -highlightthickness 0 \
          }
        place $w.p.ti$name -x $x -y $y -anchor sw
        }
      
      $w.p.ti$name configure -text [format $format_ $value_]
      }
     }
      
    proc clear_out {} {
      global w 
            global blob_data ra_data ra_block vn_data vn_block am_data am_block
            global ra3_pipe vn3_pipe sp3_pipe  sp_data sp_block
            global s13_pipe s23_pipe un3_pipe  si3_pipe
            global s1_block s2_block un_block  si_block
            global s1_data s2_data un_data  si_data
    
            foreach i [array names am_block] { unset am_data($i) ; unset am_block($i) }
            foreach i [array names vn_block] { unset vn_data($i) ; unset vn_block($i) }
            foreach i [array names ra_block] { unset ra_data($i) ; unset ra_block($i) }
            foreach i [array names sp_block] { unset sp_data($i) ; unset sp_block($i) }
            foreach i [array names s1_block] { unset s1_data($i) ; unset s1_block($i) }
            foreach i [array names s2_block] { unset s2_data($i) ; unset s2_block($i) }
            foreach i [array names si_block] { unset si_data($i) ; unset si_block($i) }
            foreach i [array names un_block] { unset un_data($i) ; unset un_block($i) }
    
      }
    
    set bg yellow
    
    proc text_item { name x y format_ value_ } {
            global NODE 
      global w lfont bg fg  ti$name
      
    #  puts stdout "text_item  name=$name x=$x y=$y format_=$format_ value_=$value_ "
      
     catch { 
        catch { button  $w.p.ti$name -font $lfont -background $bg -foreground $fg \
        -activebackground $bg -activeforeground $fg \
         -pady 0 -padx 0 -relief raised -borderwidth 0 -highlightthickness 0 \
          }
        place $w.p.ti$name -x $x -y $y -anchor sw
        }
      
      $w.p.ti$name configure -text [format $format_ $value_]
     }
      
    proc state_text_2 { name  x  y value_ text_0 fg_0 bg_0 text_1 fg_1 bg_1 } {
            global NODE
      global        w lfont bg fg  ti$name 
      
      if { [winfo exists $w.p.ti$name] == 0 } {
        
        catch { button  $w.p.ti$name -font $lfont \
         -activebackground $bg -activeforeground $fg \
         -pady 0 -padx 3 -relief flat -borderwidth 0 \
          }
        place $w.p.ti$name -x $x -y $y
        }
      
      if { $value_ } {
        $w.p.ti$name configure -background $bg_1 -foreground  $fg_1 \
            -activebackground $bg_1 -activeforeground $fg_1
        $w.p.ti$name configure -text $text_1
        } else {
        $w.p.ti$name configure -background $bg_0 -foreground  $fg_0\
            -activebackground $bg_0 -activeforeground $fg_0
        $w.p.ti$name configure -text $text_0
        }
      }
    
    proc state_text_3 { name  x  y value_0 value_1 value_2 \
     text_0 fg_0 bg_0 text_1 fg_1 bg_1  text_2 fg_2 bg_2 text_3 fg_3 bg_3 \
     text_4 fg_4 bg_4 text_5 fg_5 bg_5  text_6 fg_6 bg_6 text_7 fg_7 bg_7 \
     } {
            global NODE
      global        w lfont bg fg  ti$name 
      
      if { [winfo exists $w.p.ti$name] == 0 } {
        
        catch { button  $w.p.ti$name -font $lfont \
         -activebackground $bg -activeforeground $fg \
         -pady 0 -padx 3 -relief flat -borderwidth 0 \
          }
        place $w.p.ti$name -x $x -y $y -anchor sw
        }
      
      if { !$value_0 && !$value_1 && !$value_2 } {
        $w.p.ti$name configure -background $bg_0 -foreground  $fg_0 \
            -activebackground $bg_0 -activeforeground $fg_0
        $w.p.ti$name configure -text $text_0
        } elseif {  $value_0 && !$value_1 && !$value_2 } {
        $w.p.ti$name configure -background $bg_1 -foreground  $fg_1 \
            -activebackground $bg_1 -activeforeground $fg_1
        $w.p.ti$name configure -text $text_1
        } elseif { !$value_0 &&  $value_1 && !$value_2 } {
        $w.p.ti$name configure -background $bg_2 -foreground  $fg_2 \
            -activebackground $bg_2 -activeforeground $fg_2
        $w.p.ti$name configure -text $text_2
        } elseif {  $value_0 &&  $value_1 && !$value_2 } {
        $w.p.ti$name configure -background $bg_3 -foreground  $fg_3 \
            -activebackground $bg_3 -activeforeground $fg_3
        $w.p.ti$name configure -text $text_3
        } 
         if { !$value_0 && !$value_1 &&  $value_2 } {
        $w.p.ti$name configure -background $bg_4 -foreground  $fg_4 \
            -activebackground $bg_4 -activeforeground $fg_4
        $w.p.ti$name configure -text $text_4
        } elseif {  $value_0 && !$value_1 &&  $value_2 } {
        $w.p.ti$name configure -background $bg_5 -foreground  $fg_5 \
            -activebackground $bg_5 -activeforeground $fg_5
        $w.p.ti$name configure -text $text_5
        } elseif { !$value_0 &&  $value_1 &&  $value_2 } {
        $w.p.ti$name configure -background $bg_6 -foreground  $fg_6 \
            -activebackground $bg_6 -activeforeground $fg_6
        $w.p.ti$name configure -text $text_6
        } elseif {  $value_0 &&  $value_1 &&  $value_2 } {
        $w.p.ti$name configure -background $bg_7 -foreground  $fg_7 \
            -activebackground $bg_7 -activeforeground $fg_7
        $w.p.ti$name configure -text $text_7
        } 
       
      }
    
    proc state_text_3_r { name  x  y value_0 \
     text_0 fg_0 bg_0 text_1 fg_1 bg_1  text_2 fg_2 bg_2 text_3 fg_3 bg_3 \
     text_4 fg_4 bg_4 text_5 fg_5 bg_5  text_6 fg_6 bg_6 text_7 fg_7 bg_7 \
     } {
            global NODE
      global        w lfont bg fg  ti$name 
      
      if { [winfo exists $w.p.ti$name] == 0 } {
        
        catch { button  $w.p.ti$name -font $lfont \
         -activebackground $bg -activeforeground $fg \
         -pady 0 -padx 3 -relief flat -borderwidth 0 \
          }
        place $w.p.ti$name -x $x -y $y -anchor sw
        }
      
      switch -glob -- $value_0  {
        0* { $w.p.ti$name configure -text "$text_0" -fg $fg_0 -bg $bg_0  -activeforeground $fg_0 -activebackground $bg_0}\
        1* { $w.p.ti$name configure -text "$text_1" -fg $fg_1 -bg $bg_1  -activeforeground $fg_1 -activebackground $bg_1}\
        2* { $w.p.ti$name configure -text "$text_2" -fg $fg_2 -bg $bg_2  -activeforeground $fg_2 -activebackground $bg_2}\
        3* { $w.p.ti$name configure -text "$text_3" -fg $fg_3 -bg $bg_3  -activeforeground $fg_3 -activebackground $bg_3}\
        4* { $w.p.ti$name configure -text "$text_4" -fg $fg_4 -bg $bg_4  -activeforeground $fg_4 -activebackground $bg_4}\
        5* { $w.p.ti$name configure -text "$text_5" -fg $fg_5 -bg $bg_5  -activeforeground $fg_5 -activebackground $bg_5}\
        6* { $w.p.ti$name configure -text "$text_6" -fg $fg_6 -bg $bg_6  -activeforeground $fg_6 -activebackground $bg_6}\
        7* { $w.p.ti$name configure -text "$text_7" -fg $fg_7 -bg $bg_7  -activeforeground $fg_7 -activebackground $bg_7}\
        default { $w.p.ti$name configure -text "??????" } }
      
       
      }
    
    proc shape_2 { name x y value_0 shape_0 shape_1 } {
      global NODE
      global        w lfont bg fg  ti$name 
      
      if { [winfo exists $w.p.ti$name] == 0 } {
        
        catch { button  $w.p.ti$name -font $lfont \
         -background $bg -foreground $fg \
         -activebackground $fg -activeforeground $bg \
         -pady 0 -padx 0 -relief raised -borderwidth 0 -highlightthickness 0 
          }
    ##     set cmd ""
    ##     set cmd "$w.p.ti$name configure -command \{ entry_error \{ti$name\} \} \;"
    ##     catch { puts stdout "shape_2: $cmd" }
    ##     puts stdout "[catch { $cmd }]"
        place $w.p.ti$name -x $x -y $y -anchor sw
        }
      if { !$value_0  } {
          $w.p.ti$name configure -image $shape_0
        } else { 
          $w.p.ti$name configure -image $shape_1
        }
    }
    
    proc shape_2_flash { name x y value_0 shape_0 shape_1 } {
      global NODE
      global blinkon
      global w lfont bg fg  ti$name
      
      if { [winfo exists $w.p.ti$name] == 0 } {
        
        catch { button  $w.p.ti$name -font $lfont \
         -background $bg -foreground $fg \
         -activebackground $fg -activeforeground $bg \
         -pady 0 -padx 0 -relief raised -borderwidth 0 -highlightthickness 0 \
          }
        place $w.p.ti$name -x $x -y $y -anchor sw
        }
    
      if         { !$value_0 && !$blinkon } {
          $w.p.ti$name configure -image $shape_0 
        } elseif {  $value_0 && !$blinkon  } { 
          $w.p.ti$name configure -image $shape_1
        } elseif { !$value_0 &&  $blinkon  } { 
          $w.p.ti$name configure -image $shape_1
        } elseif {  $value_0 &&  $blinkon  } { 
          $w.p.ti$name configure -image $shape_0
        }
    }
    
    proc shape_4 { name x y value_0 value_1 shape_0 shape_1 shape_2 shape_3 } {
            global NODE
      global        w lfont bg fg  ti$name 
      
      if { [winfo exists $w.p.ti$name] == 0 } {
        
        catch { button  $w.p.ti$name -font $lfont \
         -activebackground $bg -activeforeground $fg \
         -pady 0 -padx 0 -relief raised -borderwidth 0 -highlightthickness 0 \
          }
        place $w.p.ti$name -x $x -y $y -anchor sw
        }
    
      if         { !$value_0 && !$value_1 } {
        $w.p.ti$name configure -image $shape_0
        } elseif {  $value_0 && !$value_1 } {
        $w.p.ti$name configure -image $shape_1
        } elseif { !$value_0 &&  $value_1 } {
        $w.p.ti$name configure -image $shape_2
        } elseif {  $value_0 &&  $value_1 } {
        $w.p.ti$name configure -image $shape_3
        }
    }
    
    proc state_shape_3 { name  x  y value_0 value_1 value_2 \
     shape_0 shape_1 shape_2 shape_3 \
     shape_4 shape_5 shape_6 shape_7 \
     } {
            global NODE
      global        w lfont bg fg  ti$name 
      
      if { [winfo exists $w.p.ti$name] == 0 } {
        
        catch { button  $w.p.ti$name -font $lfont \
         -activebackground $bg -activeforeground $fg \
         -pady 0 -padx 0 -relief raised -borderwidth 0 -highlightthickness 0 \
          }
        place $w.p.ti$name -x $x -y $y -anchor sw
        }
      
      if { !$value_0 && !$value_1 && !$value_2 } {
        $w.p.ti$name configure -image $shape_0
        } elseif {  $value_0 && !$value_1 && !$value_2 } {
        $w.p.ti$name configure -image $shape_1
        } elseif { !$value_0 &&  $value_1 && !$value_2 } {
        $w.p.ti$name configure -image $shape_2
        } elseif {  $value_0 &&  $value_1 && !$value_2 } {
        $w.p.ti$name configure -image $shape_3
        } elseif { !$value_0 && !$value_1 &&  $value_2 } {
        $w.p.ti$name configure -image $shape_4
        } elseif {  $value_0 && !$value_1 &&  $value_2 } {
        $w.p.ti$name configure -image $shape_5
        } elseif { !$value_0 &&  $value_1 &&  $value_2 } {
        $w.p.ti$name configure -image $shape_6
        } elseif {  $value_0 &&  $value_1 &&  $value_2 } {
        $w.p.ti$name configure -image $shape_7
        } 
       
      }
    
    proc state_shape_3_r { name  x  y value_0 \
     shape_0 shape_1 shape_2 shape_3 \
     shape_4 shape_5 shape_6 shape_7 \
     } {
            global NODE
      global        w lfont bg fg  ti$name 
      
      if { [winfo exists $w.p.ti$name] == 0 } {
        
        catch { button  $w.p.ti$name -font $lfont \
         -activebackground $bg -activeforeground $fg \
         -pady 0 -padx 0 -relief raised -borderwidth 0 -highlightthickness 0 \
          }
        place $w.p.ti$name -x $x -y $y -anchor sw
         }
      
      $w.p.ti$name configure -image question
    
      switch -glob -- $value_0  {
        0* { $w.p.ti$name configure -image $shape_0 }\
        1* { $w.p.ti$name configure -image $shape_1 }\
        2* { $w.p.ti$name configure -image $shape_2 }\
        3* { $w.p.ti$name configure -image $shape_3 }\
        4* { $w.p.ti$name configure -image $shape_4 }\
        5* { $w.p.ti$name configure -image $shape_5 }\
        6* { $w.p.ti$name configure -image $shape_6 }\
        7* { $w.p.ti$name configure -image $shape_7 }\
        default { $w.p.ti$name configure -image question } }
      
      }
    
    proc shape_update { name shape x y } {
            global NODE
            global w fulltime halftime blinkon
            global  pump_data blob_data ra_data ra_block vn_data vn_block
    
            #catch { destroy $w.p.$name }
            if [info exists pump_data($name)] {
                    if { $vn_data($trip) } then \
                            {
                              if { $blinkon  } then {$w.p.$name configure -image pumpred  -borderwidth 0
                              } else {$w.p.$name configure -image pumporg  -borderwidth 0}
                            } else {
                                    if { $vn_data($running) } then \
                                            { $w.p.$name configure -image pumpgreen  -borderwidth 0 
                                    } else  { $w.p.$name configure -image pumpyellow  -borderwidth 0}
                            }
            
                    } else {
                    label $w.p.$name -image pumporg -borderwidth 0
                    place $w.p.$name -x $x -y $y
                    set pump_data($name) init
                    set vn_block($trip) 1
                    set vn_block($running) 1
                    set vn_data($trip) 0
                    set vn_data($running) 0
                    }
            
            update idletasks
    }
    
    proc pump_update { pump trip running x y } {
            global NODE
            global w fulltime halftime blinkon
            global  pump_data blob_data ra_data ra_block vn_data vn_block
    
            #catch { destroy $w.p.$pump }
            if [info exists pump_data($pump)] {
                    if { $vn_data($trip) } then \
                            {
                              if { $blinkon  } then {$w.p.$pump configure -image pumpred  -borderwidth 0
                              } else {$w.p.$pump configure -image pumporg  -borderwidth 0}
                            } else {
                                    if { $vn_data($running) } then \
                                            { $w.p.$pump configure -image pumpgreen  -borderwidth 0 
                                    } else  { $w.p.$pump configure -image pumpyellow  -borderwidth 0}
                            }
            
                    } else {
                    label $w.p.$pump -image pumporg -borderwidth 0
                    place $w.p.$pump -x $x -y $y
                    set pump_data($pump) init
                    set vn_block($trip) 1
                    set vn_block($running) 1
                    set vn_data($trip) 0
                    set vn_data($running) 0
                    }
            
            update idletasks
    }
    
    proc omr_update { omr trip running x y } {
            global NODE
            global w fulltime halftime blinkon
            global  omr_data blob_data ra_data ra_block vn_data vn_block
    
            #catch { destroy $w.p.$omr }
            if [info exists omr_data($omr)] {
                    if { $vn_data($trip) } then \
                            {
                              $w.p.$omr configure -image omrred  -borderwidth 0
                              
                            } else {
                                    if { $vn_data($running) } then \
                                            { $w.p.$omr configure -image omrgreen  -borderwidth 0 
                                    } else  { $w.p.$omr configure -image omryellow  -borderwidth 0}
                            }
            
                    } else {
                    label $w.p.$omr -image omrorg -borderwidth 0
                    place $w.p.$omr -x $x -y $y
                    set omr_data($omr) init
                    set vn_block($trip) 1
                    set vn_block($running) 1
                    set vn_data($trip) 0
                    set vn_data($running) 0
                    }
            
            update idletasks
    }
    set OLD_NODE "MaryHadALittleLamb"
    set purge_1_0 1
    proc get_values_org { } {
            global NODE
            global blob_data ra_data ra_block
            global ac_data ac_block
            global hl_data hl_block
            global ll_data ll_block
            global vn_data vn_block
            global ra3_pipe eng4_pipe sp3_pipe  sp_data sp_block
            global s13_pipe s23_pipe un3_pipe  si3_pipe
            global s1_block s2_block un_block  si_block
            global s1_data s2_data un_data  si_data
            global OLD_NODE
            
            if { $NODE != $OLD_NODE } {
              if { [info exists eng4_pipe] } then {
                  catch { close $eng4_pipe }
                  unset eng4_pipe
                }
              }
            set OLD_NODE $NODE
            
    ##      if { [info exists eng4_pipe] } then {
    ##        
    ##        } else {
    ##        set eng4_pipe [open  "| hose $NODE 9000 -slave " {RDWR } ]
    ##        fconfigure $eng4_pipe -buffering line
    ##        puts $eng4_pipe /home/abacus/eng4
    ##        }
    
            if { [info exists eng4_pipe] } then {
              
              } else {
              set eng4_pipe [socket $NODE 9000]
              fconfigure $eng4_pipe -buffering line
              puts $eng4_pipe "/home/abacus/eng4 "
              }
            
            
            foreach i [array names ll_block] {
              if { $ll_block($i) <= 2 } then {
                    puts $eng4_pipe ac$i
                    set ll_data($i) [ gets $eng4_pipe ]
                    incr ll_block($i) 1
                    } else { unset ll_block($i) ; unset ll_data($i) }
              }
    
            foreach i [array names hl_block] {
              if { $hl_block($i) <= 2 } then {
                    puts $eng4_pipe ac$i
                    set hl_data($i) [ gets $eng4_pipe ]
                    incr hl_block($i) 1
                    } else { unset hl_block($i) ; unset hl_data($i) }
              }
    
            foreach i [array names ac_block] {
              if { $ac_block($i) <= 2 } then {
                    puts $eng4_pipe ac$i
                    set ac_data($i) [ gets $eng4_pipe ]
                    incr ac_block($i) 1
                    } else { unset ac_block($i) ; unset ac_data($i) }
              }
    
            foreach i [array names vn_block] {
              if { $vn_block($i) <= 2 } then {
                    puts $eng4_pipe vn$i
                    set vn_data($i) [ gets $eng4_pipe ]
                    incr vn_block($i) 1
                    } else { unset vn_block($i) ; unset vn_data($i) }
              }
    
            foreach i [array names ra_block] {
              if { $ra_block($i) <= 2 } then {
                    puts $eng4_pipe ra$i
                    set ra_data($i) [ gets $eng4_pipe ]
                    incr ra_block($i) 1
                    } else { unset ra_block($i) ; unset ra_data($i)  }
              }
    #; puts stdout "unset ra_($i)"
    
            foreach i [array names sp_block] {
              if { $sp_block($i) <= 2 } then {
                    puts $eng4_pipe sp$i
                    set sp_data($i) [ gets $eng4_pipe ]
                    incr sp_block($i) 1
                    } else { unset sp_block($i) ; unset sp_data($i) }
              }
    
            foreach i [array names s1_block] {
              if { $s1_block($i) <= 2 } then {
                    puts $eng4_pipe s1$i
                    set s1_data($i) [ gets $eng4_pipe ]
                    incr s1_block($i) 1
                    } else { unset s1_block($i) ; unset s1_data($i) }
              }
    
            foreach i [array names s2_block] {
              if { $s2_block($i) <= 2 } then {
                    puts $eng4_pipe s2$i
                    set s2_data($i) [ gets $eng4_pipe ]
                    incr s2_block($i) 1
                    } else { unset s2_block($i) ; unset s2_data($i) }
              }
    
            foreach i [array names si_block] {
              if { $si_block($i) <= 2 } then {
                    puts $eng4_pipe si$i
                    set si_data($i) [ gets $eng4_pipe ]
                    incr si_block($i) 1
                    } else { unset si_block($i) ; unset si_data($i) }
              }
    
            foreach i [array names un_block] {
              if { $un_block($i) <= 2 } then {
                    puts $eng4_pipe un$i
                    set un_data($i) [ gets $eng4_pipe ]
                    incr un_block($i) 1
                    } else { unset un_block($i) ; unset un_data($i) }
              }
    
    }
    
    proc get_values { } {
            global NODE
            global blob_data ra_data ra_block
            global de_data de_block
            global ac_data ac_block
            global hl_data hl_block
            global ll_data ll_block
            global am_data am_block
            global vn_data vn_block
            global ra3_pipe eng4_pipe eng4_pipe_de sp3_pipe  sp_data sp_block
            global s13_pipe s23_pipe un3_pipe  si3_pipe
            global s1_block s2_block un_block  si_block
            global s1_data s2_data un_data  si_data
            global OLD_NODE
            global purge_1_0
            
            if { $NODE != $OLD_NODE } {
              if { [info exists eng4_pipe] } then {
                  catch { close $eng4_pipe }
                  unset eng4_pipe
                }
              }
            set OLD_NODE $NODE
            
    ##      if { [info exists eng4_pipe] } then {
    ##        
    ##        } else {
    ##        set eng4_pipe [open  "| hose $NODE 9000 -slave " {RDWR } ]
    ##        fconfigure $eng4_pipe -buffering line
    ##        puts $eng4_pipe /home/abacus/eng4
    ##        }
    
            if { [info exists eng4_pipe] } then {
              
              } else {
              set eng4_pipe [socket $NODE 9000]
            #########  fconfigure $eng4_pipe -buffering line
              puts $eng4_pipe "/home/abacus/eng4 "
              puts stdout "Opening \$eng4_pipe $eng4_pipe "
              }
            
            if { [info exists eng4_pipe_de] } then {
              
              } else {
              set eng4_pipe_de [socket $NODE 9000]
            #########  fconfigure $eng4_pipe_de -buffering line
              puts $eng4_pipe_de "/home/abacus/eng4 "
              }
            
            
            foreach i [array names de_block] {
              if { $de_block($i) <= 2 } then {
                    puts $eng4_pipe_de de$i } }
            flush $eng4_pipe_de
            foreach i [array names de_block] {
              if { $de_block($i) <= 2 } then {
                    update idletasks
                    set de_data($i) [ gets $eng4_pipe_de ]
                    incr de_block($i) $purge_1_0
                    } else { unset de_block($i) ; unset de_data($i) }
              }
    
    ##      foreach i [array names de_block] {
    ##        if { $de_block($i) <= 2 } then {
    ##              puts $eng4_pipe de$i } }
    ##      flush $eng4_pipe
    ##      foreach i [array names de_block] {
    ##        if { $de_block($i) <= 2 } then {
    ##              update idletasks
    ##              set de_data($i) [ gets $eng4_pipe ]
    ##              incr de_block($i) $purge_1_0
    ##              } else { unset de_block($i) ; unset de_data($i) }
    ##        }
    
            
            foreach i [array names ll_block] {
              if { $ll_block($i) <= 2 } then {
                    puts $eng4_pipe ll$i } }
            flush $eng4_pipe
            foreach i [array names ll_block] {
              if { $ll_block($i) <= 2 } then {
                    update idletasks
                    set ll_data($i) [ gets $eng4_pipe ]
                    incr ll_block($i) $purge_1_0
                    } else { unset ll_block($i) ; unset ll_data($i) }
              }
    
            foreach i [array names hl_block] {
              if { $hl_block($i) <= 2 } then {
                    puts $eng4_pipe hl$i } }
            flush $eng4_pipe
            foreach i [array names hl_block] {
              if { $hl_block($i) <= 2 } then {
                    update idletasks
                    set hl_data($i) [ gets $eng4_pipe ]
                    incr hl_block($i) $purge_1_0
                    } else { unset hl_block($i) ; unset hl_data($i) }
              }
    
            foreach i [array names ac_block] {
              if { $ac_block($i) <= 2 } then {
                    puts $eng4_pipe ac$i } }
            flush $eng4_pipe
            foreach i [array names ac_block] {
              if { $ac_block($i) <= 2 } then {
                    update idletasks
                    set ac_data($i) [ gets $eng4_pipe ]
                    incr ac_block($i) $purge_1_0
                    } else { unset ac_block($i) ; unset ac_data($i) }
              }
    
            foreach i [array names am_block] {
              if { $am_block($i) <= 2 } then {
                    puts $eng4_pipe am$i } }
            flush $eng4_pipe
            foreach i [array names am_block] {
              if { $am_block($i) <= 2 } then {
                    update idletasks
                    set am_data($i) [ gets $eng4_pipe ]
                    incr am_block($i) $purge_1_0
                    } else { unset am_block($i) ; unset am_data($i) }
              }
    
            foreach i [array names vn_block] {
              if { $vn_block($i) <= 2 } then {
                    puts $eng4_pipe vn$i } }
            flush $eng4_pipe
            foreach i [array names vn_block] {
              if { $vn_block($i) <= 2 } then {
                    update idletasks
                    set vn_data($i) [ gets $eng4_pipe ]
                    incr vn_block($i) $purge_1_0
                    } else { unset vn_block($i) ; unset vn_data($i) }
              }
    
            foreach i [array names ra_block] {
              if { $ra_block($i) <= 2 } then {
                    puts $eng4_pipe ra$i } }
            flush $eng4_pipe
            foreach i [array names ra_block] {
              if { $ra_block($i) <= 2 } then {
                    update idletasks
                    set ra_data($i) [ gets $eng4_pipe ]
                    incr ra_block($i) $purge_1_0
                    } else { unset ra_block($i) ; unset ra_data($i)  }
              }
    #; puts stdout "unset ra_($i)"
    
            foreach i [array names sp_block] {
              if { $sp_block($i) <= 2 } then {
                    puts $eng4_pipe sp$i } }
            flush $eng4_pipe
            foreach i [array names sp_block] {
              if { $sp_block($i) <= 2 } then {
                    update idletasks
                    set sp_data($i) [ gets $eng4_pipe ]
                    incr sp_block($i) $purge_1_0
                    } else { unset sp_block($i) ; unset sp_data($i) }
              }
    
            foreach i [array names s1_block] {
              if { $s1_block($i) <= 2 } then {
                    puts $eng4_pipe s1$i } }
            flush $eng4_pipe
            foreach i [array names s1_block] {
              if { $s1_block($i) <= 2 } then {
                    update idletasks
                    set s1_data($i) [ gets $eng4_pipe ]
                    incr s1_block($i) $purge_1_0
                    } else { unset s1_block($i) ; unset s1_data($i) }
              }
    
            foreach i [array names s2_block] {
              if { $s2_block($i) <= 2 } then {
                    puts $eng4_pipe s2$i } }
            flush $eng4_pipe
            foreach i [array names s2_block] {
              if { $s2_block($i) <= 2 } then {
                    update idletasks
                    set s2_data($i) [ gets $eng4_pipe ]
                    incr s2_block($i) $purge_1_0
                    } else { unset s2_block($i) ; unset s2_data($i) }
              }
    
            foreach i [array names si_block] {
              if { $si_block($i) <= 2 } then {
                    puts $eng4_pipe si$i } }
            flush $eng4_pipe
            foreach i [array names si_block] {
              if { $si_block($i) <= 2 } then {
                    update idletasks
                    set si_data($i) [ gets $eng4_pipe ]
                    incr si_block($i) $purge_1_0
                    } else { unset si_block($i) ; unset si_data($i) }
              }
    
            foreach i [array names un_block] {
              if { $un_block($i) <= 2 } then {
                    puts $eng4_pipe un$i } }
            flush $eng4_pipe
            foreach i [array names un_block] {
              if { $un_block($i) <= 2 } then {
                    update idletasks
                    set un_data($i) [ gets $eng4_pipe ]
                    incr un_block($i) $purge_1_0
                    } else { unset un_block($i) ; unset un_data($i) }
              }
    
            set purge_1_0 1
    }
    
    destroy $w.welcome
    #source ./dp2.tcl
    source ./mp1.tcl
    
    

    Notes on the contents of the display.tcl file

    The first few lines tell the system that this is code written for the program wish (the Tcl/tk interpreter), then cd /home/abacus/ makes sure that it is run in the correct directory.

    set NODE inco sets the variable NODE to the value inco . This is the node name for the process control computer at Invercon.

    ############## GIF Files defined here ################

    marks the start of the area which must be modified if a new picture is to be added.

    Where the is a pattern to the file names a foreach construction has been used, as in the case for pump and other symbols. A line like

    image create photo $blob -file /home/abacus/invercon/$blob.gif

    must exist for each display background, and symbol. The name must be unique, and the gif file must exist, otherwise the program will fault.

    Further down the file in the section starting

    ### Menu stuff defined here ###

    the menus are defined.

    $m add separator

    $m add command -label "Data Page 1 " -command { change_to dp1 }

    $m add command -label "Data Page 2 " -command { change_to dp2 }

    $m add command -label "Data Page 3 " -command { change_to dp3 }

    $m add separator

    $m add command -label "Reel Report " -command { change_to reel }

    $m add separator

    The change_to procedure assumes that the page file to be sourced has the name /home/abacus/invercon/${the_page_}.tcl where ${the_page_} represents the argument to the procedure.

    is the second mimic display. First this program must define the background to be used - this is done in the line

    Pic copy Pic_mp2

    Then buttons used to move to other displays follow

    button $w.p.ti_hem -bitmap @/home/abacus/invercon/Fort-james.xbm \

    -command { change_to mp1 }

    place $w.p.ti_hem -x 0 -y 0

    Notice that a bitmap is used, and that the command uses the change_to procedure.

    The main updating procedure is called update0. It is here that all the dynamic points on the display are defined. It will be seen that the catch command is used for nearly every instruction. This is to ensure that the display continues even if there are programming errors, or if the called subroutines fail in any way.

    After the update0 procedure comes the definitions of enterable points. When, from within update0 procedures like text_item are called they create, if one does not all ready exist, a new widget (in this case a button) with the name given as the first argument prefixed with $w.p.ti thus

    catch { text_item b139 423 242 "%4.1f" [ra 139] }

    creates $w.p.tib139. This can then be configured to allow operator entry

    catch { $w.p.tib139 configure -command { entry_stuff_multi 8046 10046 8046 8046 15146 8046 6012 } } ;# P26

    Here entry_stuff_multi will be called if the operator presses on this item, thus allowing operator entry of setpoints etc. WARNING: This block is subject to review, and possible retirement.
     

    File Handling

    This section of the ABACUS block language manual describes the disc block. This block allows data to be transmitted from the ABACUS database to and from data files, thus permitting recipes to be stored and retrieved.

    Disc Block
     
     



    ABACUS_FH.gif







    The Disc Block controls the transfer of analogue and digital data between ABACUS blocks and data files in both directions.

    In addition to the common parameters the disc block has the following

    The block is not processed by the normal block processor and consequently the sn parameter has no meaning. The ss flag is set, and the am flag reset on execution of the disc block. The vn flag is set if the block was unable to open the desired file for any reason (i.e. if the file does not exist)

    The data file's file name is [ABACUS.ABACUS]Bnnnnn.dat;1 where nnnnn is the integer part of the ra (with leading zeros) of the Disc Block.

    The block reads from the file if aa = 0, otherwise it writes, creating a new file if one does not already exist. The previous contents of the data file are lost after a write operation.

    All of the common parameters of the blocks specified by ds and dn i.e. tg, sn, ra, am, vn and ss, are either read from the file or written to it. ds defines the first block to be either read or written, and dn specifies the number of blocks.

    ABACUS 4

    System blocks, Configuration etc.

    This section deals with System Scratchpad Blocks, Scan control Blocks, and issues concerning the configuration of ABACUS4 systems.

    Data dumping is discussed, as is logging in and out of the system.

    ABACUS4 processes are listed with a short description of their function.


    Starting and Stopping ABACUS4

    On most installations it is arranged that ABACUS is started by the /etc/rc.d/rc.local shell script file which is started automatically during system start.

    To start ABACUS manually, log in as abacus (initial password abacus) and enter the command abacus. You will be asked if you wish to stop and restart ABACUS, pressing enter with the yes field selected will first stop any running ABACUS processes, then after a short pause startup the ABACUS system.

    To stop ABACUS manually, log in as abacus (initial password abacus) and enter the command kill.abacus.

    On systems configured with the PI-Bus driver, and possibly others, you may be asked for your password before ABACUS will start. This is because the processes which access I/O ports on the PC must be run with root privileges.


    Installing ABACUS4

    README.1ST

    
    
    This is the README.1ST file for abacus-4.9807 this first public version. 
    
    
    Hello everyone, this is just a bit of information about ABACUS.
    ABACUS runs under Linux, the free Un*x operating system found on a large number
    of Internet servers around the world. 
    
    The minimal hardware requirements are an IBM compatible 386 machine or better
    with at least 4 Mb RAM (preferably at least 8 Mb). A hard-disk with 30Mb has
    been used although >200Mb is required for the display system (X). On a network
    (Ethernet or even serial) a disk-less system should be possible, although this
    cannot be considered a secure arrangement.
     
    To install ABACUS 4 on your Intel based Linux machine you will need to
    consider the following.
    
    You need a user called abacus with his/her home at /home/abacus. He/She must have sudo
    rights (I put 'abacus ALL=ALL' at the end of /etc/sudoers, but you might want
    to work out something more sophisticated, let me know what you find works)
    
    As root you can install the package from the / directory with
    
    	cd /
    	tar -xzvf where_ever_it_is/abacus-4.version.tar.gz
    	install/doinst.sh
    	
    Or (if you are using Slackware) you can use the installpkg
    
    	installpkg where_ever_it_is/abacus-4.version.tar.gz
    
    
    
    The ABACUS 4 manuals are in html format in the abacusmanual/ directory.
    
    To start the system log in as abacus, and start the command abacus. The
    file abacus is a bash shell, and is where the main configuration of
    which parts of abacus are started (i.e. which process I/O drivers, and
    which block scan processors)
    
    ABACUS 4 expects '.' to be in the PATH environment variable, other wise it
    won't find everything.
    
    Process I/O
    ===========
    
    Drivers exist is this version for pibus, indata and das08. The source for the
    das08 and a number of h files are stored in the src directory.
    
    It is expected that new drivers will appear in the not too distant future for
    MODBUS and COMLI protocols. These may however require modifications to the
    database structure (i.e. new block types).
    
    DISPLAYS
    ========
    
    The displays are made using Tcl/tk. I've used version 8.0, some however will
    work with earlier versions. The tk_openfile type widgets have been used in the
    trend page, and 8.0 is required for that.
    
    LICENSING
    =========
    
    ABACUS 4 is a commercial product and a license is required to run it. However
    it will run for an evaluation period of 24 hours before shutting down
    without a valid /home/abacus/abacus.computer.id.dat file.
    
    At the time of writing the pricing has not been fixed, so if you want to buy
    a license mail to abacusabacus@hotmail.com
    
    STARTUP
    =======
    
    
    Here is a log of how it went when I started abacus
    
    	frank(abacus):~$ abacus
    	starting kill of abacus
    	kill 8461: No such process
    	kill_smem:mem_id 0, shmctl returned 0
    	Starting abacus
    
    
    	Warning - id_check failed -1
    	The identity of your harddisk does not match that expected
    	This ABACUS system will run in demonstration only
    	The file /home/abacus/abacus.computer.id.dat contains a coded
    	version of the identity of the disk for which this software is
    	licensed. Please send the following information to the supplier
    	of your ABACUS system, and arrange for a new 
    	/home/abacus/abacus.computer.id.dat file to be sent to you.
    
    	MODEL="WDC AC31600H"
    	FW_REV="23.16U23WDC AC31600H"
    	SERIAL_NO="WD-WT2893535299"
    
    
    	This information has been saved in /home/abacus/hard.disk.id.txt
    
    	Abacus will start shortly in demonstration mode.
    	It will require restarting after a period of 24 hours
    
    
    	key = 0
    	shmget(key, TOTAL_SPACE  , IPC_PRIVATE | IPC_CREAT ) errno = 0
    	mem_id = 134
    
    	Loading from datadump file ...Database load done
    
    	          system start      1, stop     20
    	        pibus ai start    101, stop    420
    	        i/p code start    801, stop    810
    	           index start   1001, stop   1199
    	       checklist start   2001, stop   2199
    	          switch start   2501, stop   2699
    	         pattern start   4001, stop   4199
    	       sequencer start   9001, stop   9050
     	        control start   5001, stop   5999
    	          p.d.o. start   6001, stop   6080
    	    analogue out start   6301, stop   6364
    	        o/p code start   6501, stop   6550
    	      alarm code start   8000, stop   8999
    	           motor start   9501, stop   9699
    	       dig input start  14001, stop  14320
    	      dig output start  14501, stop  14820
    	            disc start  17501, stop  17510
    	       data link start  17901, stop  17910
    	    scan control start     51, stop     65
    	          p.i.d. start   7001, stop   7299
    	      indata I/O start  18001, stop  18511
    	at: pluralization is wrong
    	Job 68 will be executed using /bin/sh
    	Subroutine start   7901, stop   7999
    	Date                    Owner   Queue   Job#
    	08:52:00 07/08/98       root    c       62
    	10:10:00 07/08/98       root    c       63
    	11:24:00 07/08/98       root    c       64
    	11:28:00 07/08/98       root    c       65
    	15:02:00 07/08/98       root    c       68
    	Start journal program
    	Start dcctsk ?
    	Start X windows stuff
    	Starting sxserv
    	Starting blktsk ...
    	Starting /home/abacus/dmptsk
    	Starting /home/abacus/rep 1
    	Starting trendmem program
    	/home/abacus/trendmem /home/abacus/trenddatafiles/trend.000.002.dat update
    	datadump to be done every 300s approx 
    	/home/abacus/trendmem /home/abacus/trenddatafiles/trend.001.002.dat update
    	/home/abacus/trendmem /home/abacus/trenddatafiles/trend.002.002.dat update
    	/home/abacus/trendmem /home/abacus/trenddatafiles/trend.003.002.dat update
    	/home/abacus/trendmem /home/abacus/trenddatafiles/trend.063.002.dat update
    	/home/abacus/trendmem /home/abacus/trenddatafiles/trend.080.002.dat update
    	/home/abacus/trendmem /home/abacus/trenddatafiles/trend.080.060.dat update
    	Starting engdrawserver.sh
    
    
    
    If you get
    
    	/home/abacus/faucet: Trying again . . .
    	/home/abacus/faucet: Address 9000 in use, sleeping 10.
    	/home/abacus/faucet: Trying again . . .
    	/home/abacus/faucet: Address 9000 in use, sleeping 10.
    	/home/abacus/faucet: Trying again . . .
    	/home/abacus/faucet: Address 9000 in use, sleeping 10.
    
    then it means that there is still a faucet running in the system using
    address 9000. 
    
    	frank(abacus):~$ ps ax | grep fauc
    
    will find it, and then use kill -9 on it.
    
    
    
    
    Bugs, complaints, suggestions and complements to abacusabacus@hotmail.com
    please.
    
    
    

    System Scratchpad Block

    These scratchpad blocks have only the common parameters. The ra values of some of these are written into by the system clock, and have the following significance.

    These blocks may be used to synchronise events with the system clock.

    This block may be used for simulation applications. This block indicates the status of the datadump process. ra = size in bytes of last successful datadump, or 1 during dump process.

    am is set by the dump command in engineers mode, the datadump programme sets vn = v during the dump process. This only applies if the dmptsk line in the startup shell script ABACUS was configured with the -b 20 switch.


    Scan Control Block

    This block controls the frequency of the block processor scans. It also permits the restriction of block processing to a range of blocks in order to reduce system loading.

    In addition to the common parameters the Scan Control Block has the following

    The block processor for each scan is a separate VMS process which has the name BLKnn where nn is the scan number (1 - 15). A process called sxserv controls the execution of those block processor processes installed. sxserv uses the ra parameters of the scan control blocks as timer variables, reducing then from their sp values down to zero. When zero is reached the corresponding block processor is reactivated, and the ra of the scan control block is restored to its sp value.

    Only blocks with number from f1 to l1 inclusive are processed.


    Configuration

    The number and type of ABACUS blocks may be defined to suit a particular application's needs. The current configuration may be examined by entering the look command in engineers mode. To change configuration the file /home/abacus/abacus.sys needs to be altered, the existing application programme symbolically saved, then ABACUS stopped, and restarted.

    The format of the text file /home/abacus/abacus.sys is as follows

    20000 Max Block Number

    1 20 system

    0 0 PiBus ai

    201 250 K48

    401 410 N45

    0 0 siox3

    0 0

    0 0 pulse counter

    801 810 input code

    ..

    The first line has the highest block number in the system. Each line after that has the first and then last of each successive block type. These are in the order as shown with the look command. Subsequent text is not used and only serves as commentary.

    All blocks of a particular block type have contiguous block numbers. A block number cannot refer to two different blocks at the same time, and so first and last block ranges must not overlap. Since every block number from 1 up to the maximum block number is allocated the common parameters, and is designated a scratchpad if not of any defined block type, the higher the Max Block Number the more space consumed.

    Unused block types are marked with first and last block numbers equal to zero.

    To initiate a new block configuration the command /home/abacus/initsk i is used. This should olny be done on a system where ABACUS is NOT running.

    After initialising the database initsk spawns engineers mode to load up the applications in file /home/abacus/abacus.eng. This file usually contains engineers mode commands to set up scan rate values etc.

    The total size of the database may not exceed a predetermined amount. When INITSK initialises the database it prints the total size.

    Datadump files are stored in binary format, and are a straight copy of the memory into the file. It is therefore impossible to convert directly from one shape of ABACUS system to another without using a 'symbolic' dunp. eng2 may be used to do this before you change the block configuration of a system. Something like

    eng2 b1,xb999999,short,fp > /tmp/symbolic.dump

    will create a file called /tmp/symbolic.dump with all the eng commands required to re-establish you database. Check with an editor that the file is complete, before you destroy your old system!

    When you've got your new system up and running you might want to use something like

    eng2 xb99,@/tmp/symbolic.dump

    to resotre your data. (xb99 overides the requirement for connecting to each block's tag)

    Data dumping

    In order to preserve the applications programme the entire ABACUS database is saved periodically. This process may be automatic depending on the dmptsk line in /home/abacus/ABACUS.

    . This section covers communications between ABACUS4 and other systems, and other ABACUS4 systems.

    MODBUS Communications

    Modbus RTU communications may be set up via one or more serial ports. One process is started (in the /home/abacus/ABACUS script file) for each port.

    MODBUS Master

    'coils' and 'registers' may be read and/or written.

    Each block which is intended for use as a MODBUS signal must have its nm setup with the following format

    MODBUS <channel> <address> <R=register, or C=coil> <reg/coil reference> <IN/OUT>


    Note that this software was developed against an ABB controller (commander series) where <reg/coil reference> is one greater than the 'offset address'.

    Example: To send the ra of block 10002 to the 'Local Set Point' register (42) of a Commander 100 controller whose address is 1 on the modbus channel 1 the block should look like this...
     

    Block  10002  () type scratchpad
    nn.b.name                                
    de.descr                                                                                  
    nm.name   MODBUS 1 1 R 42 OUT      
    tg.tag       spare sn.scan     0(0.0 sec) ra.rslta      123.000 
    am.au/mn    a  vn.vltn     v  ss.sshot    r  
    en.A Entpr          0 dd.D Entpr          0
    This block may then be set to whatever value you want the controller to control to. Another block (10001 in this example) may be used to obtain the 'Process Variable Input' from the controller...
    Block  10001  () type scratchpad
    nn.b.name                                
    de.descr                                                                                  
    nm.name   MODBUS 1 1 R 2 IN        
    tg.tag       spare sn.scan     0(0.0 sec) ra.rslta      -18.000 
    am.au/mn    a  vn.vltn     n  ss.sshot    r  
    en.A Entpr          0 dd.D Entpr          0
    Not e that all numbers transferred are integer with ranges restricted by the modbus unit. Decimal point location for displays on controllers is possibly also accessible via MODBUS, see the handbook for the unit in question.
     

    The modbus program

    The modbus program is executed with the following format
    modbus port baudrate channel sys-block-number
    where port is the special file name for the port (i.e. /dev/cua1) baudrate (9600 etc.) channel is the within ABACUS unique id. for just this MODBUS channel, and sys-block-number is the system block which will give running information about the link.

    Here follows a trial run, using the above two blocks only. Note that the sys-block-number is left undefined, the default being 20+channel, i.e. 21 in this case.

    frank(abacus):~$ /home/abacus/modbus /dev/cua1 9600 1
    stty sane ispeed 9600 < /dev/cua1 
    stty -ixon -ixoff -crtscts -parenb cs8 cstopb -echo raw ispeed 9600 < /dev/cua1 
    
    Starting cycle...
    MODBUS Block 10001 (n    -18), chan 1, adr 1, creg R, cregnr   2, direction IN
    MODBUS Block 10002 (v    123), chan 1, adr 1, creg R, cregnr  42, direction OU
    Starting cycle...
    MODBUS Block 10001 (n    -18), chan 1, adr 1, creg R, cregnr   2, direction IN
    MODBUS Block 10002 (v    123), chan 1, adr 1, creg R, cregnr  42, direction OU
    The sys-block-number block is also updated with information in the de parameter. The value of ra is the number of cycles that the modbus program has executed.
    frank(abacus):~$ eng2 b21,p
    Block     21  () type scratchpad
    
    B21 ()
    nn                              
    demodbus: Port /dev/cua1 at 9600 Baud, Channel 1. Found 2 blocks of interest     
    nmmodbus: /dev/cua1       
    tg   spare;sn  0(0.0 sec);ra      7.000
    am  m ;vn  v ;ss  r 
    en          0;dd          0
    For each channel a separate serial port is required, and a unique channel number. The /home/abacus/ABACUS file should include a line like
    $ABACUS_HOME/modbus /dev/cua1 9600 1 > /dev/null 2>&1 &
    The first argument to the modbus program is the special file for the serial port, the second argument is the baud rate, and the third is the channel number. The program prints a significant amount of diagnostic information which can be useful in the setting up phase, but is usually discarded by the ' > /dev/null 2>&1 '  in the above.
     

    Warning

    The am of the sys-block-number block may be used to stop the modbus program. Use of /etc/inittab and a script file would be required to ensure that the program is restarted.

    MODBUS Slave

    BSAP CommunicationsBSAP (Bristol Synchronous/Asynchronous Protocol) is the standard protocol used by RTU's of Bristol Babcock manufacture. This is a hierarchical system where each node has a unique (within the network) address. Tools are available (from Bristol Babcock) to program the controllers which form the nodes in a BSAP network.

    Any ABACUS4 block may be connected to any one signal reachable via a connected BSAP network. Which signal is defined in the nn parameter. The nm parameter is loaded (by the system) with a series of numbers representing the global address of the node, the MSD address of the signal, the MSD version number, and two status values obtained from the signal.

    Data is transferred from the signal into the ABACUS4 block. In order to reduce the network load of continuously polling all the signals a count down timer is used on each block. This counter is stored in the en parameter, which can be seen to decrement. When either the value 0 has been reached the signal is read and the counter reset to 60.

    If it is desired to prioritize one or more particular signals then set the am flag to a. This will have the effect of putting these signals at the top of the list of signals to be collected. For this reason it is not a good idea to use blocks other than scratchpads for BSAP signal collection.

    In order to change a value at a remote signal the en parameter must be set to the magic value 777, and the result to the desired value. As confirmation that the new value has been sent to the BSAP node the value 999 is put into the en parameter.

    The following shows some blocks with appropriate nn values and their generated nm values. The node name here is BIP1.

    frank(abacus):~$ eng2 long,b10001,xb10019,.nn.nm.ra.en.am.vn
    Block  10001  (BIP1:#TIME.001.) type scratchpad
    Xblock 10019  type scratchpad
    b10001,nnBIP1:#TIME.001.               ,nm0000 0968 B53A 0000 32  ,ra  65484.000,en         59,am  m ,vn  v 
    b10002,nnBIP1:#TIME.002.               ,nm0000 0971 B53A 0000 32  ,ra   1999.000,en         59,am  m ,vn  v 
    b10003,nnBIP1:#TIME.003.               ,nm0000 097A B53A 0000 32  ,ra      6.000,en         59,am  m ,vn  v 
    b10004,nnBIP1:#TIME.004.               ,nm0000 0983 B53A 0000 32  ,ra     28.000,en         59,am  m ,vn  v 
    b10005,nnBIP1:#TIME.005.               ,nm0000 098C B53A 0000 32  ,ra     18.000,en         59,am  m ,vn  v 
    b10006,nnBIP1:#TIME.006.               ,nm0000 0995 B53A 0000 32  ,ra     11.000,en         59,am  m ,vn  v 
    b10007,nnBIP1:#TIME.007.               ,nm0000 099E B53A 0000 32  ,ra     25.000,en         59,am  m ,vn  v 
    b10008,nn                              ,nm                        ,ra      0.000,en          0,am  m ,vn  n 
    b10009,nn                              ,nm                        ,ra      0.000,en          0,am  m ,vn  n 
    b10010,nn                              ,nm                        ,ra      0.000,en          0,am  m ,vn  n 
    b10011,nnBIP1:FTEST.001.               ,nm0000 09A7 B53A 0000 02  ,ra   1105.000,en         59,am  m ,vn  v 
    b10012,nnBIP1:FTEST.002.               ,nm0000 0788 B53A 0000 00  ,ra      0.000,en         59,am  m ,vn  n 
    b10013,nnBIP1:FTEST.003.               ,nm0000 0A7E B53A F5F7 06  ,ra   1105.000,en         59,am  m ,vn  v 
    b10014,nnBIP1:FTEST.LOLO.              ,nm0000 09CA B53A 0000 02  ,ra      2.000,en         59,am  m ,vn  v 
    b10015,nnBIP1:FTEST.LOW.               ,nm0000 09D7 B53A 0000 02  ,ra      4.000,en         59,am  m ,vn  v 
    b10016,nnBIP1:FTEST.HIGH.              ,nm0000 09B0 B53A 0000 02  ,ra     95.000,en         59,am  m ,vn  v 
    b10017,nnBIP1:FTEST.HIHI.              ,nm0000 09BD B53A 0000 02  ,ra     99.000,en         59,am  m ,vn  v 
    b10018,nn                              ,nm                        ,ra      0.000,en          0,am  m ,vn  n 
    b10019,nn                              ,nm                        ,ra      0.000,en          0,am  m ,vn  n

    The bsap2 program

    This is the program which actually collects and delivers the data to/from the BSAP network. If you try to execute it you will get the following informative text.
    bsap2 [-a local_adr] [-p port] [-b baud-rate] [-L]
              [-1 startblock] [-2 stopblock] [-n node_gadr] -N node_string 
    
     scans the blocks in the range startblock to stopblock to find nn's that
     match the node_string. Then tries to update the results of those blocks
     whose en == 1, decrementing all with en > 1
    
     If -L is present the node will be interogated for signal names otherwise
     the msd adresses in the nm parameters will be used
    
            -a      bsap local node address (default 0)
            -p      port (default /dev/cua1)
            -b      baud rate (default 9600)
            -1      first block (default 20000)
            -2      last block (default 39999)
            -s      new_start (default 60)
            -n      node global adr
            -N      node_string
    Notice that this program executes one pass through the ABACUS database, and then exits. It is the responsibility of another script to repeatedly run bsap2. The standard script is called bsap-run.sh and looks something like this. It is here that you must edit so that the correct node name (in this example BIP1) and local address (1) are used.
    #!/bin/sh
    #
    # This file runs the various bsap calls for tivoli and stations under
    # sudo chmod a+w /dev/cua0
    # sudo chmod a+r /dev/cua0
    export LOGFILE=/dev/null
    export LOGFILE=/home/abacus/tivoli/bsap-run.log
    
    stty 5:0:8bd:0:0:0:0:0:0:1:1:0:0:0:0:0:0:0:0:0:0:0:0 < /dev/cua1
    
    echo "
    Detta program startar /home/abacus/bsap2 -1 10000 -2 10999 data hämntingprogrammet.
    This program starts /home/abacus/bsap2 data collection program.
    "
    
        /home/abacus/bsap2 -1 10000 -2 10999 -a 1 -p /dev/cua1  -n 0 -N BIP1:  -L >> $LOGFILE
    
    while sleep 0 
      do
        echo "Starting /home/abacus/bsap2 -1 10000 -2 10999 loop `date`"
        echo  >> $LOGFILE
        echo /home/abacus/tivoli/bsap-run.sh `date`   >> $LOGFILE
        echo /home/abacus/tivoli/bsap-run.sh Starting normal pass   >> $LOGFILE
        echo  >> $LOGFILE
    
        echo Ordinary pass on BIP1:   >> $LOGFILE
    
        /home/abacus/bsap2 -1 10000 -2 10999 -w -a 1 -p /dev/cua1  -n 0 -N BIP1:     >> $LOGFILE
        /home/abacus/bsap2 -1 10000 -2 10999 -w -a 1 -p /dev/cua1  -n 0 -N BIP1:     >> $LOGFILE
        /home/abacus/bsap2 -1 10000 -2 10999 -w -a 1 -p /dev/cua1  -n 0 -N BIP1:     >> $LOGFILE
        /home/abacus/bsap2 -1 10000 -2 10999 -w -a 1 -p /dev/cua1  -n 0 -N BIP1:     >> $LOGFILE
        /home/abacus/bsap2 -1 10000 -2 10999 -w -a 1 -p /dev/cua1  -n 0 -N BIP1:     >> $LOGFILE
    
        /home/abacus/bsap2 -1 10000 -2 10999 -a 1 -p /dev/cua1  -n 0 -N BIP1:  -L >> $LOGFILE
        
        echo /home/abacus/tivoli/bsap-run.sh `date`   >> $LOGFILE
        
        mv $LOGFILE $LOGFILE.tmp
        tail -1000 $LOGFILE.tmp > $LOGFILE
        rm $LOGFILE.tmp
        
      done
     

     
     
     
     
     
     
     

    Attention should be made to the global address, which is a hex number (in this example 0 i.e. the local node). This can be obtained from the NETFILE.DOC file produced by the Bristol Babcock tools.

    The -N argument specifies the search string used by the program when it goes through all the ABACUS blocks to find suitable candidates for communication. The normal syntax for BSAP signal names is of the form XXXX:BASENAME.EXTNTN.ATTR where XXXX is the node name, the maximum length of the remaining parts is 8.6.4.
     

    Enterprise Server Communications

    When an ABACUS4 system needs to communicate with an Enterprise Server (VAX/VMS version) the en and dd parameters are used to specify which signal in the Enterprise Server system will correspond to the block in question. It is however during start-up of the entlnk4_client

    In the Enterprise Server data base there are analog and logical signals. Each analog signal has a unique number know as its PTID (point ID), each logical also has a unique DIID (digital input ID). Each signal has a system unique name which usually is of the form XXXX:SIGNALNAME. For an ABACUS block to have its values sent to the Entprise system it must have a valid signal name in the nn parameter.

    The following is an example of an analog input block...

    frank(abacus):~$ eng2 b101,p
    Block    101  (UC71:F701..AI) type pibus ai
    
    B101 (UC71:F701..AI)
    nnUC71:F701..AI                 
    deFLODE INKOMMANDE GRUVVATTEN                                                    
    nmfms AI N:01 C:02 P:0    
    tg    F701;sn  3(1.0 sec);ra     -0.232
    am  a ;vn  n ;ss  r 
    en       2101;dd          0
    aa         91;rn          0;ds    2515513
    rw  12864.000;sp     50.000;ze      0.000
    In the case of all blocks excluding Alarm Code blocks, the system sets the en parameter to the corresponding PTID, and the dd parameter to the corresponding DIID number.

    Alarm Code Blocks (acb) may be associated with more than one Enterprise signal. If the acb is called, as in our example, UC71:A720.. then the following signals may also be present...

    frank(abacus):~$ grep UC71:A720 analog.dat 
     2880 UC71:A720..               C  1   0    0   0        0      0.000      0.000 JARNHALTSMATARE - GRUVV.
     2881 UC71:A720..HL             C  0   0    0   0        0      0.000      0.000 JARNHALTSMATARE - GRUVV. HOG
     2882 UC71:A720..LL             C  0   0    0   0        0      0.000      0.000 JARNHALTSMATARE - GRUVV. LAG
     2883 UC71:A720..XL             C  0   0    0   0        0      0.000      0.000 JARNHALTSMATARE - GRUVV. INGET STROM
    Note that even XH is permitted, but not used in this case.

    Alarms are generated by Entprise, which needs to be setup so that the base signal (console point) has the HL,LL XL etc as its alarm limits.

    frank(abacus):~$ eng2 long,b8002,p
    Block   8002  (UC71:A720..) type alarm code
    
    Block   8002  (UC71:A720..) type alarm code
    nn.b.name UC71:A720..                    
    de.descr  JARNHALTSMATARE - GRUVV.                                                        
    nm.name   NOT IN ALARM             
    tg.tag        A720 sn.scan     3(1.0 sec) ra.rslta       -5.000 
    am.au/mn    a  vn.vltn     n  ss.sshot    r  
    en.A Entpr          0 dd.D Entpr          0 
    si.name                            
    aa.algra            1 hy.hyst         0.000 
    s1.status            s2.status            
    xl.limit      -30.000 xh.limit       19.800 
    ll.limit      -25.000 hl.limit       15.000 
    is.inputsp          0(     0.000 m-rn) 
    ia.inputa         102(UC71:A720..AI    -5.000 a+rv) 
    st.status           0(     0.000 m-rn) 
    ar.amreqst          0(     0.000 m-rn) 
    un.unittx            mo.ok  tx            
    ml.low tx            mh.hi  tx            
    lx.xl  tx            hx.xh  tx            
    ma.autotx            mm.handtx            
    ur.areqtx            hr.hreqtx            
    dy.delay  0:00:30 
    
                     Signal Value Monitor        remote signal -+
                                                alarm signal -+ | +- message suppr
                                             constant value -+| | |+- group suppr
                                            manual inhibit -+|| | ||+- unacknowledge
                                          control inhibit -+||| | |||+- in alarm
                                           alarm inhibit -+|||| | ||||++- alarm prio
    Signal                     Value       questionable -+||||| | ||||||++- alm type
    ------------------------  --------- ---------------- |||||| | ||||||||  --------
    UC71:A720..               -5.000000  AV X          L.     A  .  U C     68010428
    UC71:A720..HL             15.000000  AV X          L. A      .          68014000
    UC71:A720..LL             -25.00000  AV X          L. A      .          68014000
    UC71:A720..XL             -30.00000  AV X          L. A      .          68014000
    ------------------------  --------- ||||||||       | -------- --------  --------
                              updating -+||||||+- rbe  +- logical value
                           alarm change -+||||+- manual data
                            value change -+||+- operator lock
                              user change -++- aux change
    The status of the link program (entlnk4_client in ABACUS, usl:entlnk4_server running as ENTLNK4 in the Enterprise Server) can be seen in block 30. With heavily loaded Enterprise systems the server can have problems with the database, where is can no longer extract or insert data. This usually happens during start-up, and the observed number of PTIDs and DIIDs (shown in the en and dd parameters respectively) is less than the correct number. Blocks 28 and 29 are used to define minimum nos. of DIIDs and PTIDs respectively. If fewer than these are found, then the program exits and is restarted by the system.
    frank(abacus):~$ eng2 b28,xb30,fp
    Block     28  () type scratchpad
    Xblock    30  type scratchpad
    
    B28 ()
    nn                              
    deentlnk4: minimum no of DIIDs                                                   
    nm                        
    tg entlnk4;sn  0(0.0 sec);ra    196.000
    am  m ;vn  n ;ss  r 
    en          0;dd          0
    
    B29 ()
    nn                              
    deentlnk4: minimum no of PTIDs                                                   
    nm                        
    tg entlnk4;sn  0(0.0 sec);ra    355.000
    am  m ;vn  n ;ss  r 
    en          0;dd          0
    
    B30 ()
    nn                              
    deentlnk4: Try and find all the ptid and diid numbers                            
    nm                        
    tg entlnk4;sn  0(0.0 sec);ra      0.000
    am  m ;vn  n ;ss  r 
    en        326;dd          2
    In the above example the system has not yet found all the PTIDs and DIIDs corresponding to the names in the ABACUS data base.

    Profibus-fms

    The Profibus-fms protocol is used at this time only in connection with PEP-Computer's SMART-I/O subsystem, which requires specific programming. This software allows connection directly to analog & digital inputs & outputs.
     

    Analog inputs

    frank(abacus):~$ eng2 b101,p
    Block    101  (UC71:F701..AI) type pibus ai
    
    B101 (UC71:F701..AI)
    nnUC71:F701..AI                 
    deFLODE INKOMMANDE GRUVVATTEN                                                    
    nmfms AI N:01 C:02 P:0    
    tg    F701;sn  3(1.0 sec);ra     -0.232
    am  a ;vn  n ;ss  r 
    en       2101;dd          0
    aa         91;rn          0;ds    2515513
    rw  12864.000;sp     50.000;ze      0.000
    
    
    When used with the SMART-I/O subsystem, analog inputs may use the aa of 90 or 91. aa = 90 is for 0-20mA, and 91 is for 4-20mA. With these algorithms the sp corresponds to the desired ra for 20mA in and the ze corresponds to the desired ra for 0/4 mA input. The rw is the raw signal value from the SMART-I/O subsystem, in the range 0 - 65536. The ds parameter is used by the driver software to store the previous value of rw. If the difference between rw and ds is greater than 1024 the block is not updated, this prevent false readings causing undesirable effects.

    The nm parameter shows which point the block relates to. In the above example N:01 refers to the first node, C:02 refers to the card number 2, and P:0 is point 0. Points are numbered from 0 upwards, as are slots. The nodes have a Profibus address which is shown after the N:..
     

    Analog outputs

    frank(abacus):~$ eng2 b6301,p
    Block   6301  (UC71:FV750.STYR.AO) type analogue out
    
    B6301 (UC71:FV750.STYR.AO)
    nnUC71:FV750.STYR.AO            
    deREGLERVENTIL GRUVVATTEN                                                        
    nmfms AO N:01 C:05 P:0    
    tg   FV750;sn  3(1.0 sec);ra     20.000
    am  a ;vn  v ;ss  r 
    en       2134;dd          0
    hl     40.000;ll     20.000;sp     20.000
    The nm parameter shows which point the block relates to. In the above example N:01 refers to the first node, C:05 refers to the card number 5, and P:0 is point 0. The nodes have a Profibus address which is shown after the N:..
     

    Data Link Block

     

     
     
     
     
     
     
     
     
     

    WARNING: This block is not implemented yet in ABACUS4.

    This section of the ABACUS block language manual describes the data link block. This block allows data to be transmitted from one ABACUS system to another via a variety of hardware including Ethernet and asynchronous ASCII.

    Data Link Block
     
     



    ABACUS_CO.gif










    The Data Link Block controls the transfer of both analogue and digital data between ABACUS systems, over Ethernet or asynchronous ASCII links.