DataMuseum.dk

Presents historical artifacts from the history of:

RC3500

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about RC3500

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download

⟦0949a8e7a⟧ TextFileVerbose

    Length: 51456 (0xc900)
    Types: TextFileVerbose
    Names: »atcedit«

Derivation

└─⟦a41ae585a⟧ Bits:30001842 SW-save af projekt 1000, Alarm-system
    └─⟦72244f0ef⟧ 
        └─⟦this⟧ »atcedit« 

TextFileVerbose

job nla 9 200 area 10 size 100000 time 11 59 perm disc1 2000 20 
( mode list.yes
source = copy 25.1
atcsource = set 1 disc1
( i atcedit1
atcsource = edit source
end )
if ok.no
finis
outlst = set 1 disc1
outlst = indent atcsource mark lc 
templist = set 1 disc1
templist = cross outlst
clear temp outlst
o errors
pascal80 codesize.12000 alarmenv tsenvir atcsource
o c
lookup pass6code
if ok.yes
( tsatcbin = set 1 disc1
tsatcbin = move pass6code
scope user tsatcbin )
tsatclst = set 1 disc1
tsatclst = copy templist errors
scope user tsatclst
convert errors
clear temp templist
finis
)
process atconnector(
(*t1 op_sem               : sempointer; t1*)
var
main_sem             ,              (* Pointer to ATC's main semaphore.
-                                   All messages are received here *)
queue_sem            : !ts_pointer; (* Pointers to the queue semaphore, that
-                                   holds control an test messages while ATC is busy *)
var
ath_sem              ,              (* Pointer to the main semaphore of ATH *)
driver_sem           ,              (* Pointer to main semaphore of LAM driver *)
com_pool             : !sempointer; (* Pointer to the semaphore that holds
-                                   the vacant message resources of the TS *)
var
activity             : poll_activity;
var
delay                : integer;
var
node_test_frequency  : !integer;
var
traffic_counter      : integer;
var
dc_macro             ,              (* macro address of own dc *)
ts_macro             : !macroaddr;  (* macro address of own ts *)
own_addr             : !integer;    (* Micro address of this ATC inarnation *)
channel_no           : !byte );     (* i/o channel number used by this ATC incarnation *)


const
version              = "vers  3.09 /";
\f


(*********************************************************************************
* 
* description      : The purpose and function of the AT_CONNECTOR is mainly to
*                    run the protocol with an AT. The ATC is the master of 
*                    this communication.
*                    The state of the transmission line is monitored by ATC
*                    and alarms concerning the conditions of transmission are
*                    signalled to PAC or DC.
*                    Furthermore commands originating from DC and AC are executed
*                    and alarms from AT are signalled to an AC.
*                    AT_CONNECTOR participates in the module supervision in
*                    the TS.
*
* externals        : check5
*                    testopen
*                    testout
*                    receipt_message
*                    reject_message
*
* environment      : alarm environment (latest version)
*
* author           : NLA
*
*********************************************************************************)
\f


(*********************************************************************************
*
* PSEUDO CODE of AT_CONNECTOR
*
* process atconnector
*
* constant, type and variable declaration parts
*   1) shielded types
*   2) telegrams from TS to AT
*   3) telegrams from AT to TS
*   4) state telegrams from AT to TS
*   5) operation codes and formats
*   6) watch central table and management of it
*   7) management of states and protocol
*   8) supervision of transmission line
*   9) miscellaneous
*
* forward declaration part
*
* subroutine declaration part
*
*
* begin
*   
*   initialize;
*
*   restrict_protocol;
*     <* end of initialize sequence *>
*
*   repeat <* forever *>
*
*     if conversation = idle then
*      begin
*        if open( queue ) then <* preprocess queue *>
*         begin
*           wait( msg, queue );
*           initiate_conversation <* messages in the queue always involve AT *>
*         end
*        else
*         begin
*           sendtimer( delay_msg );
*           send_telegram( poll, 0 )
*         end
*      end;
*  
*     wait( msg, main );
*  
*     if ownertest( driver_pool, msg ) then
*      driver_msg :=: msg
*     else
*      case message_origin of
*  
*        ATH:
*          begin
*            if operation_code in (.control, testi1, testi2, teste.) then
*             signal( msg, queue )
*            else
*             read_write( msg ) <* AT not involved, execute at once *>
*          end;
*  
*        DRIVER:
*         if transmission_error and limit_overflow then
*           signal( call_limit_alarm, ATH )
*          else
*           begin
*             if limit_underflow then
*              signal( recall_limit_alarm, ATH );
*  
*             case AT_operation_code of
*  
*              p_ack: conversation:= idle;
*  
*              au_alarm:
*               if no_of_bytes = AVC_block_lth then
*                signal( au_alarm, AVC )
*               else
*                send_telegram( poll, 0 );
*  
*              addr:
*               begin
*                 search_address_code;
*                 send_telegram( poll, 0 )
*               end;
*  
*              state_alarm:
*               if state_byte <> current_state then
*                begin
*                  update_current_state;
*                  signal( state_alarm, PAC )
*                end;
*  
*              d_ack:
*               if no_of_bytes = block_lth then
*                signal( control_receipt, sender )
*               else
*                send_telegram( cntrl, next_cntrl_byte );
*  
*              t_ack,
*              e_ack:
*               begin
*                 signal( test_receipt, sender );
*                 conversation:= idle
*               end;
*
*             end <* case AT_operation_code *>
*
*           end
*
*     end <* case message origin *>
*
*   until forever
*
* end. <* process AT_CONNECTOR *>
*
*********************************************************************************)




\f


(*********************************************************************************
*
* ABBREVIATION LIST
*
* aac          alternative alarm centre
* ac, AC       alarm centre
* at, AT       alarm terminal
* atc, ATC     alarm terminal connector
* ath, ATH     alarm terminal (connector) handler
* au           alarm unit
* dc, DC       district centre
* driver       lam driver
* msg          reference to a message
* pac, PAC     primary alarm centre
* r_w          read_write
* sem          semaphore
* s_a..        stop activity (e.g. s_a_limit)
* t_e../..t_e  transmission error (e.g. t_e_counter)
* TS, ts       terminal station
*
*
*
*
*
*
*
*
*
*
*
*
*********************************************************************************)

\f


(*********************************************************************************
* declaration part 1: shielded types
*********************************************************************************)

var

(* pools. Allocation takes place with main_sem as answer semaphore *)

driver_pool          : pool 1 of integer; (* driver message is allocated from this pool *)
delay_pool           : pool 1;    (* delay message is allocated from this pool *)

(* reference variables *)

delay_msg,                        (* holds delay out message while it's unused *)
ath_msg,                          (* holds message from ATH while waiting for response from AT *)
driver_msg,                       (* holds message from DRIVER while while it's unused *)
alarm_msg,                        (* general use. Holds alarm message while it's updated *)
atc_msg              : reference; (* holds message from input semaphore 
while it's classified *)
\f


(*********************************************************************************
* declaration part 2: telegrams from master (TS) to slave (AT)
*
*********************************************************************************)

const
(* operation codes *)
poll_opc             = 0;
atc_cntrl            = 1;
atc_testi            = 2;
atc_teste            = 3;

(* data bytes *)
poll_byte            = 0;
testi1_byte          = 0;
testi2_byte          = 1;

type
atc_op_codes         = poll_opc..atc_teste;

atc_format           = packed record
out_data      : byte;
atc_opc       : atc_op_codes;
serial_number : boolean;
checkbits     : 0..31
end;

var
last_telegram        : atc_format   := atc_format( 0, 0, false, 0 );

\f


(*********************************************************************************
* declaration part 3: telegrams from slave (AT) to master (TS)
*
*********************************************************************************)

const
(* operation codes *)
p_ack                = 0;
state                = 1;
au_alarm             = 2;
addr                 = 3;
d_ack                = 4;
t_ack                = 5;
e_ack                = 6;
n_ack                = 7;

type
at_op_codes          = p_ack..n_ack;

at_format            = packed record
in_data   : byte;
at_opc    : at_op_codes;
checkbits : 0..31
end;
\f


(*********************************************************************************
* declaration part 4: state telegrams from slave (AT) to master (TS)
*
*********************************************************************************)

const
(* at state bits *)
unused               = 0;
at_time_out          = 1;
hs_error             = 2;
au_error             = 3;
serif_error          = 4;
restart              = 5;
batt_limit           = 6;
batt_supply          = 7;

type
state_bits           = unused..batt_supply;
state_byte           = set of state_bits;

var
state_bit            : state_bits  := unused;
\f


(*********************************************************************************
* declaration part 5: alarm net operation codes
*
*********************************************************************************)

const
(* pac log operation code *)
pac_alarm_log        = #h01;

(* garbage message *)
reject_opc           = #h12;

(* alarms *)
au_alarm_opc         = #h30;
line_alarm           = #h31;
state_alarm          = #h32;
service_alarm        = #h34;
s_a_alarm            = #h35;

(* control *)
ts_cntrl             = #h40;
group_cntrl          = #h44;

(* tests *)
ts_testi1            = #h80;
ts_testi2            = #h82;
ts_teste             = #h84;

(* change of activity *)
ts_newactivity       = #h90;

connect_test         = #h92;

(* updating of alarm centre table *)
upd_ac_table         = #ha0;

(* read package counter *)
read_package_count   = #hb4;

(* read or update of parameter *)
r_w_tec              = #hb2;
r_w_service_limit    = #hb6;
r_w_s_a_limit        = #hba;
r_w_max_succ_t_e     = #hbc;
\f


node_test            = #hc0;

dummy_alarm          = #hc8;

break_proc_end       = #hce;
\f


(*********************************************************************************
* declaration part 6: alarm centre table and the management of it
*
*********************************************************************************)

var
ac_tbl               : ac_address_table;

actual_ac_index      : ac_table_range       := pac_index;
top_aac_index        : upper_ac_tbl_index   := 0;
\f


(*********************************************************************************
* declaration part 7: management of states and protocol
*
*********************************************************************************)

type
type_of_conversation = (
idle,                       (* no telegram pending *)
busy,                       (* poll pending *)
control,                    (* control pending *)
testi,                      (* testi1 or testi2 telegram pending *)
teste,                      (* test extern pending *)
coll_alarm1 ,               (* ATC is collecting multi byte alarm *)
coll_alarm2 );              (* a poll is pending to look for a new alarm *) 

var
reject_code          : result_range         := not_ready;

conversation         : type_of_conversation := idle;
\f


(*********************************************************************************
* declaration part 8: supervision of transmission line
*
*********************************************************************************)

type
valid_set            = set of at_op_codes;

t_e_kind_type        = (
no_error,
time_excess,                (* reported from DRIVER *)
not_ack,                    (* n_ack operation code received *)
bit_error,                  (* error in checksum *)
ill_opc,                    (* unexpected operation code in response *)
channel_error );            (* reported from DRIVER *)

var
t_e_step             : integer := trans_err_rate;   (* increment of t_e_counter 
when a transmission error occurs *)
service_limit        : integer := service_lim;      (* t_e_counter service_limit. *)
s_a_limit            : integer := stop_poll_lim;    (* t_e_counter stop activity limit. *)
max_succ_t_e         : integer := max_succ_lin_err; (* number of successive 
transmission errors before line alarm *)

t_e_counter,                                        (* transmission error counter *)
no_succ_t_e          : integer           := 0;

valid_response       : array( busy..coll_alarm2 ) of valid_set;

t_e_kind             :  t_e_kind_type    := no_error;

line_state           : (
low,                        (* t_e_counter below service limit *)
serv_lim_excess,            (* service_limit exceeded *)
s_a_lim_excess ) := low;    (* stop activity limit exceeded *)



\f


(*********************************************************************************
* declaration part 9: miscellaneous
*
*********************************************************************************)

type
receipt_route        = array( netc_route..netc_route1 ) of byte;

const
route_vect           = receipt_route( at_route, at_route1 );
override             = false;
dummy                = 0;

type
create_ch_format     = array( 0..1 ) of byte;

var
package_count        : integer           := 0;      (* is counted circular from 0 
through 32766 = ( max_int - 1) *)

delay3, delay4       : byte;                        (* contents u3 and u4 values of delay message *)

at_op_code           : at_op_codes       := 0;
at_data              : byte              := 0;

(*t1 z                    : zone; t1*)
\f


(*********************************************************************************
* forward declaration part
*
*********************************************************************************)

procedure restrict_protocol;
forward;


procedure exception( excode: integer );
forward;
\f


<*t procedure print_telegram( var msg: reference; transmit: boolean; param: byte );

type 
hexa                 = array( 0..15 ) of char;

const
hex_convert          = hexa( "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"a", "b", "c", "d", "e", "f" );

var
string               : alfa   := alfa( "            " );

begin

if transmit then
lock msg as locvar: atc_format do
with locvar do
begin

string(  1 ):= "T";
string(  4 ):= hex_convert( atc_opc );
string(  6 ):= hex_convert( ord( serial_number ) );
string(  8 ):= hex_convert( checkbits div 16 );
string(  9 ):= hex_convert( checkbits mod 16 );
string( 11 ):= hex_convert( out_data div 16 );
string( 12 ):= hex_convert( out_data mod 16 )

end (* lock msg *)
else
lock msg as locvar: at_format do
with locvar do
begin

string(  1 ):= "R";
string(  6 ):= hex_convert( at_opc );
string(  8 ):= hex_convert( checkbits div 16 );
string(  9 ):= hex_convert( checkbits mod 16 );
string( 11 ):= hex_convert( in_data div 16 );
string( 12 ):= hex_convert( in_data mod 16 )

end; (* lock msg *)

testout( z, string, param )

end;(* procedure print_telegram *) t*>
\f


procedure create_channel;

(*********************************************************************************
* description      : Updates and sends a create channel message to DRIVER
*
* globals          : driver_msg
*                    conversation
*********************************************************************************)

begin

lock driver_msg as locvar: create_ch_format do
with driver_msg^ do
begin

u1:= create_at_ch;
u2:= channel_no;
u3:= at_route;

locvar( 0 ):= at_control;
locvar( 1 ):= con_lam_time

end; (* lock *)

conversation:= busy;

signal( driver_msg, driver_sem^ )

end; (* procedure create_channel *)
\f


procedure send_telegram( 
new_opc              : atc_op_codes;
data_byte            : byte;
new_serial_no        ,
transmission_error   : boolean
);

(*********************************************************************************
* description      : Updates and sends a telegram to AT (through DRIVER) and
*                    requests a delay message from TIMER
* 
* call value       : new_opc            = operation code of telegram or unsignificant
*                    data_byte          = data byte of telegram or unsignificant
*                    new_serial_no      = indicates whether serial number of
*                                         telegram is to be alternated. Significant
*                                         only if a transmission error is detected.
*                    transmision_error  = indicates whether last telegram is to be
*                                         repeated.
* return value     :                      all are unchanged
* globals          : driver_msg         = nil after the call
*                    last_telegram      = updated
*********************************************************************************)

begin

if transmission_error and ( not new_serial_no ) then      (* repeat last telegram *)
lock driver_msg as locvar: atc_format do
locvar:= last_telegram (* end lock driver_msg *)
else
with last_telegram do
begin

if not transmission_error then         (* set up new telegram *)
begin

atc_opc:= new_opc;
serial_number:= not serial_number;
out_data:= data_byte

end
else
if new_serial_no then 
serial_number:= not serial_number;

lock driver_msg as locvar: atc_format do
locvar:= last_telegram; (* end lock driver_msg *)

if check5( driver_msg, generate ) then;

lock driver_msg as locvar: atc_format do
last_telegram:= locvar (* end lock driver_msg *)

end;

<*t print_telegram( driver_msg, true, ord( conversation ) ); t*>

if not nil( delay_msg ) then
with delay_msg^ do
begin

u3:= delay3;
u4:= delay4;

sendtimer( delay_msg )

end;

driver_msg^.u2:= channel_no;

signal( driver_msg, driver_sem^ )

end; (* procedure send_telegram *)
\f


procedure handle_queue(
var
msg                  : reference;
reject_cause         : result_range
);

(*********************************************************************************
* description    : Selects a message from the queue with a legal operation
*                  code. Messages illegal for the moment are receipted
*                  with result = reject_cause.
* call value     : msg          = nil or reference to a uncompleted message
* return value   : reject_cause = cause of rejection
*                  msg          = nil or a reference to a legal message
* globals        : ath_msg, queue_sem
*********************************************************************************)

begin

if not nil( msg ) or open( queue_sem.w^ ) then
repeat

if nil( msg ) then
wait( msg, queue_sem.w^ );

with msg^ do
if ( u3 = dummy_route ) then
return( msg )
else
begin

if ( u4 in (.ts_cntrl, group_cntrl, ts_teste.) ) then
package_count:= ( package_count + 1 ) mod max_int;

case reject_cause of

accepted          :
;

state_hs_error    :
if ( u4 = ts_teste ) then
receipt_message( msg, ath_sem, route_vect( u3 ), 0, reject_cause )
;

state_power_error ,
transmit_error    ,
no_resources      ,
no_connection     ,
breaked           ,
not_ready         ,
passivated        :
receipt_message( msg, ath_sem, route_vect( u3 ), 0, reject_cause )
;

illegal_operation ,
state_au_error    ,
state_serif_error :
if ( u4 in (.ts_cntrl, group_cntrl, ts_teste.) ) then
receipt_message( msg, ath_sem, route_vect( u3 ), 0, reject_cause )
;

otherwise
testout( z, "reject code: ", reject_cause )

end

end

until not nil( msg ) or passive( queue_sem.w^ )

end; (* procedure handle_queue *)
\f


function get_message(
var
msg            : reference;
operation_code ,
block_lth      : byte;
rec_macro      : macroaddr;
rec_micro      ,
ac_index       : integer
): boolean;

(*********************************************************************************
* description      : Waits max delay seconds for a message from com_pool.
*                    If ATC is timed out and ath_msg or queue_sem holds
*                    resources, these are receipted and another effort is
*                    made.
*                    a alarm net label is entered in the message.
* call value       : msg          = nil
* return value     : get_message  = true if a message is received, otherwise false.
*                    msg          = nil or reference to the message.
*                    msg^.u1      = number of bytes to enter in data part
*                    msg^.u2      = 0 ( will be used for counting )
* globals            ath_msg
*                    queue_sem
*********************************************************************************)

begin

definetimer( true );

repeat

case waitsd( msg, com_pool^, delay ) of

a_semaphore:
lock msg as locvar: alarmlabel do
with msg^, locvar do
begin

u1:= block_lth;
u2:= 0;
u3:= at_route;
u4:= operation_code;

rec.macro:= rec_macro;
rec.micro:= rec_micro;
send.macro:= ts_macro;
send.micro:= own_addr;

ts_add( 0 ):= ac_index

end (* lock *)
;

otherwise
begin

trace( 0 );
handle_queue( ath_msg, no_resources );

(* obs!!! clean up main if ATC must keep trying *)

end

end

until passive( queue_sem.w^ ) or not nil( msg );

definetimer( false );

get_message:= not nil( msg )

end; (* function get_message *)
\f


function ready_byte_msg(
var
msg            : reference;
data_byte      : byte
): boolean;

(*********************************************************************************
* description      : Enters one data byte in the message data part. 
* call value       : msg            = references message to fill 
*                    msg^.u1        = number of bytes to enter
*                    msg^.u2        = last byte entered
*                    data_byte      = byte in question
* return value     : ready_byte_msg = true if filling is completed
*                    msg^.u2        = last byte entered.
*                    data_byte      = unchanged
* globals          : none
*********************************************************************************)

begin

lock msg as locvar: al_form_byte do
with msg^, locvar, al_label do
begin

if ( u1 > u2 ) then
begin

u2:= u2 + 1;
data( u2 ):= data_byte

end;

ready_byte_msg:= ( u2 = u1 )

end

end; (* function ready_byte_msg *)
\f


procedure finish_message(
var
msg            : reference;
res            : result_range;
log_to_pac     : boolean
);

(***********************************************************************************
* description      : Updates the user fields and message label part and sends  
*                    the message to ATH.
*                    A log of an alarm may be send to PAC through ATH.
* call value       : msg           = references message to be send
*                    msg^.u2       = number of bytes except for alarmlabel.
*                    res           = message label information
*                    log_to_pac    = true if the alarm is to be logged at PAC
* return value     : msg           = nil
* globals          : none
************************************************************************************)


var
alarm_bytes        : data_bytes;
block_lth          : data_range;

begin

lock msg as locvar: al_form_byte do
with msg^, locvar, locvar.al_label do
begin

no_of_by:= label_size + u2;
result:= res;

if log_to_pac then
begin

block_lth:= u2;

for u2:= 1 to block_lth do
alarm_bytes( u2 ):= data( u2 )

end;

if u4 in (.pac_alarm_log, au_alarm_opc, line_alarm, state_alarm.) then
package_count:= ( package_count + 1 ) mod max_int

end; (* lock msg *)

signal( msg, ath_sem^ );
\f


if log_to_pac then
with ac_tbl( pac_index ) do
if get_message( msg, pac_alarm_log, block_lth, dummy_macro, dummy, sac_rac_ix ) then
begin

lock msg as locvar: al_form_0001 do
with msg^, locvar, al_label, aac_address do
begin

micro:= ac_tbl( actual_ac_index ).sac_rac_ix;

for u2:= 1 to block_lth do
data( u2 ):= alarm_bytes( u2 );

end;

finish_message( msg, res, not log_to_pac )

end

end; (* procedure finish_message *)
\f

 
function search_addr_code( table_entry: byte; var table_index: ac_table_range ): boolean;

(**********************************************************************************
* description      : Searches ac address table for the address code of a
*                    alarm centre.
* 
* call value       : table_entry      = address code to search
*                    table_index      = undefined
* return value     : search_addr_code = true if the entry is found else false
*                    table_entry      = unchanged
*                    table_index      = indices entry if it exists
* globals          : ac_tbl           = unchanged
*********************************************************************************)

begin

table_index:= pac_index;

while ( table_index < top_aac_index ) and 
( table_entry <> ac_tbl( table_index ).addr_code ) do
table_index:= table_index + 1;

search_addr_code:= ( top_aac_index > 0 ) and ( table_entry = ac_tbl( table_index ).addr_code )

end; (* function search_addr_code *)
 
 
\f


function search_ac_index( table_entry: integer; var table_index: ac_table_range ): boolean;

(*********************************************************************************
* description      : Searches ac address table for the index of a alarm centre.
*
* call value       : table_entry     = entry to search
*                    table_index     = undefined
* return value     : search_ac_index = true if the entry is found, otherwise false
*                    table_entry     = unchanged
*                    table_index     = indices entry if it exists
* globals          : ac_tbl          = unchanged
*********************************************************************************)

begin

table_index:= pac_index;

while ( table_index < top_aac_index ) and
( table_entry <> ac_tbl( table_index ).sac_rac_ix ) do
table_index:= table_index + 1;

search_ac_index:= ( top_aac_index > 0 ) and ( table_entry = ac_tbl( table_index ).sac_rac_ix )

end; (* function search_ac_index *)
\f


procedure transm_cntrl(
var at_op_code: at_op_codes;
var at_data: byte;
valid_response: valid_set );

(*********************************************************************************
* description      : Monitors the conditions of transmission and manages the
*                    sending of messages concerning the state of the transmission line.
*                    Alarms are hold back when ATC is in service_poll.
*                    The counters are not updated if AT state says no power
* call value       : at_op_code        = undefined
*                    at_data           = undefined
*                    valid_response    = the set of responses from AT (slave) which are
*                                        valid in relation to the telegram send
* return value     : at_op_code        = the operation code of the telegram
*                                        if this is found to be valid
*                    at_data           = data byte from telegram if transmission
*                                        isn't disturbed
*                    valid_response    = unchanged
* globals          : reject_code       = updated
*                    t_e_counter       = updated
*                    no_succ_line_err  = updated
*                    line_state        = updated
*                    t_e_kind          = updated acc. to state/kind of transmission
*                    max_succ_t_e, service_limit, s_a_limit
*                                      = unchanged
********************************************************************************)
\f


procedure send_line_state( op_code, data: byte );

(*********************************************************************************
* description      : Sends a message to DC or PAC concerning the state of
*                    the transmission line
* call value       : op_code         = line, service or stop_activity alarm
*                    data            = call (may be time out) or recall
* return value     :                   both are unchanged
* globals          : none
*********************************************************************************)

var
msg                  : reference;

begin

if ( activity = start_code ) then
with ac_tbl( pac_index ) do
if get_message( msg, op_code, 1, dc_macro, dc_erh_mic_addr, sac_rac_ix ) then
if ready_byte_msg( msg, data ) then
finish_message( msg, accepted, override )

end; (* procedure send_line_state *)
\f


begin (* procedure transm_cntrl *)

if ( driver_msg^.u2 = ok_result ) then       (* no error result from DRIVER *)
begin

if check5( driver_msg, check ) = false then   (* bit error *)
t_e_kind:= bit_error
else
lock driver_msg as locvar: at_format do
with locvar do
begin

if ( at_opc = n_ack ) then
t_e_kind:= not_ack
else
if at_opc in valid_response then
begin

t_e_kind:= no_error;
at_op_code:= at_opc;
at_data:= in_data

end
else
t_e_kind:= ill_opc

end (* lock driver_msg *)

end
else
begin   (* driver_msg^.u2 <> ok_result: error result from DRIVER *)

if ( driver_msg^.u2 = timeout_err ) then 
t_e_kind:= time_excess
else
t_e_kind:= channel_error

end;

<*t print_telegram( driver_msg, false, ord( t_e_kind ) ); t*>
\f


if ( reject_code <> state_power_error ) then
begin

if ( t_e_kind <> no_error ) then
begin

t_e_counter:= t_e_counter +
( ord( t_e_counter < s_a_limit ) ) * t_e_step; (* don't go too high *)

(*t1 no_succ_t_e:= no_succ_t_e +
( ord( no_succ_t_e < max_int ) ); t1*) (* don't overflow *)

if ( no_succ_t_e = max_succ_t_e ) then (* send line alarm *) 
begin

send_line_state( line_alarm,
(**)           ( ord( t_e_kind = time_excess ) * ( timeout_err - 1 ) + call ) );

if ( activity = start_code ) then
reject_code:= transmit_error

end

end
else
begin

t_e_counter:= t_e_counter - ord( t_e_counter > 0 ); (* freeze at zero point *)

if ( no_succ_t_e >= max_succ_t_e ) then (* recall line alarm *)
begin

send_line_state( line_alarm, recall );

if ( activity = start_code ) then
if reject_code = transmit_error then
reject_code:= accepted;

no_succ_t_e:= 0

end

end;
\f


case line_state of

low:
if ( t_e_counter >= service_limit ) then (* send service alarm *)
begin

send_line_state( service_alarm, call );
line_state:= serv_lim_excess

end;

serv_lim_excess:
if ( t_e_counter < service_limit ) then   (* recall service alarm *)
begin

send_line_state( service_alarm, recall );
line_state:= low

end
else
if ( t_e_counter >= s_a_limit ) then    (* send stop activity alarm *)
begin

send_line_state( s_a_alarm, call );
line_state:= s_a_lim_excess

end;

s_a_lim_excess:
if ( t_e_counter < s_a_limit ) then     (* recall stop activity alarm *)
begin

send_line_state( s_a_alarm, recall );
line_state:= serv_lim_excess

end

otherwise

end (* case line_state *)

end

end; (* procedure transm_cntrl *)
\f


procedure initiate_conversation( var request_msg: reference );

(*********************************************************************************
* description      : Initiates a conversation with the AT in accordance with
*                    the operation code in the message from ATH
* call value       : request_msg    = references message from ATH
* return value     : request_msg    = nil if the message is rejected,
*                                     otherwise u1 = number of bytes to AT.
*                                               u2 = 0 ( will be used for counting )
* globals          : conversation   = updated according to operation code
*                    t_e_kind       = unchanged
*                    ac_tbl         = unchanged
*********************************************************************************)

var
result_code          : result_range := accepted;
steering_ac_index    : ac_table_range;

begin

lock request_msg as locvar: al_form_byte do
with request_msg^, locvar, al_label do

case u4 of (* operation code *)

ts_cntrl,
group_cntrl:
if search_ac_index( ts_add( 0 ), steering_ac_index ) then
begin

if ac_tbl( steering_ac_index ).steering then
begin (* legal sender *)

u1:= no_of_by - label_size;

conversation:= control;
send_telegram( atc_cntrl, data( 1 ),
(**)         ( t_e_kind = ill_opc ), override (* ! *) )
(* ! override transmission error *)

end
else
result_code:= not_steering

end
else
result_code:= unknown_sender;

ts_testi1, ts_testi2:
if ( send.macro <> dc_macro ) then
result_code:= forbidden
else
begin

u1:= 1;
no_of_by:= no_of_by + 1;

conversation:= testi;

if ( u4 = ts_testi1 ) then
send_telegram( atc_testi, testi1_byte,
(**)         ( t_e_kind = ill_opc ), override (* ! *) )
(* ! override transmission error *)
else
send_telegram( atc_testi, testi2_byte,
(**)         ( t_e_kind = ill_opc ), override (* ! *) )
(* ! override transmission error *)

end;

ts_teste:
begin

u1:= no_of_by - label_size;

conversation:= teste;
send_telegram( atc_teste, data( 1 ),
(**)         ( t_e_kind = ill_opc ), override (* ! *) )
(* ! override transmission error *)

end;

otherwise

end; (* lock, case request_msg^.u4 *)

if ( result_code <> accepted ) then
receipt_message( request_msg, ath_sem, route_vect( request_msg^.u3 ), 0, result_code )
else
request_msg^.u2:= 0

end; (* procedure initiate_conversation *)
\f


procedure exec_conn_operation( var msg: reference );

(********************************************************************************
* description      : Executes on request from ATH those operation types,    
*                    that doesn't involve DRIVER and sends a receipt to ATH                  
* call value       : msg    = reference to message from ATH               
* return value     : msg    = nil                                             
* globals          : reject_code
*                    according to operation code:
*                      actual_ac_index, top_ac_index, ac_tbl
********************************************************************************)

var
res                  : result_range := accepted;
table_index          : ac_table_range;
\f


procedure connect_message( var msg: reference; user4: byte );

var
work_addr     : alarmnetaddr;

begin

lock msg as locvar: al_form_0902 do
with msg^, locvar, al_label do
begin

u4:= user4;

work_addr:= send;
send:= al_net_addr;
al_net_addr:= work_addr

end

end; (* procedure connect_message *)
\f


procedure compute_delay( param: integer );

(*********************************************************************************
* description      : Computes delay to enter the u3 and u4 fields of the delay
*                    message as (u3*2^u4) 
* call value       : param          = integer value of delay ( seconds )
* globals          : delay          = value of delay
*                    delay3, delay4 = altered according to formula above
*********************************************************************************)

begin

delay:= param;

delay4:= 0;

while ( param > ( max_int div time_out_unit ) ) do
begin

param:= param div 2;
delay4:= delay4 + 1

end;

param:= param * time_out_unit;

while ( param > max_byte ) do
begin

param:= param div 2;
delay4:= delay4 + 1

end;

delay3:= param

end; (* procedure compute_delay *)
\f


procedure rw_param( var parameter: integer );

(*********************************************************************************
* description      : Reads or updates a parameter according to the operation code
*                    and the update field.
* call value       : parameter      = parameter en question
* return value     : parameter      = updated if update is insert_code
* globals          : none
*********************************************************************************)

begin

lock msg as locvar: al_form_11__ do
with locvar, locvar.al_label do
if ( send.macro <> dc_macro ) then
res:= forbidden
else

case update of

read_code:
begin

params( 1 ):= parameter;
no_of_by:= no_of_by + 2;

end;

modify_code:
if ( msg^.u4 <> read_package_count ) then
parameter:= params( 1 )
else
res:= forbidden
;

otherwise
res:= unknown_update

end (* case update *)

end; (* procedure rw_param *)
\f

 
begin (* procedure exec_conn_operation *)

<*t testout( z, "exec_conn_op", msg^.u4 ); t*>

case msg^.u4 of (* operation code *)

reject_opc:
begin

return( msg );
testout( z, "garb. return", msg^.u3 )

end;

ts_newactivity:
lock msg as locvar: al_form_0900 do
with locvar, al_label do
if ( send.macro <> dc_macro ) and ( send.micro <> ath_mic_addr ) then
res:= forbidden
else
begin

case update of

stop_code:
begin

if not nil( alarm_msg ) then
with ac_tbl( actual_ac_index ) do
finish_message( alarm_msg, data_incomplete,
(**)          ( ac_tbl( pac_index ).sac_rac_ix <> sac_rac_ix ) );

if nil( driver_msg ) then
conversation:= busy
else
conversation:= idle;

activity:= stop_code;
reject_code:= passivated

end;
\f


start_code:
if ( top_aac_index < pac_index ) then
res:= not_ready
else
begin

t_e_counter:= t_e_c_init;

compute_delay( frequence );

line_state:= low;
no_succ_t_e:= 0;

activity:= start_code;
reject_code:= accepted;

valid_response( busy ):= (.p_ack, au_alarm, addr, state, n_ack.);
valid_response( control ):= (.state, d_ack, n_ack.);
valid_response( testi ):= (.state, t_ack, n_ack.);
valid_response( teste ):= (.state, e_ack, n_ack.);
valid_response( coll_alarm1 ):= (.au_alarm, addr, state, n_ack.);
valid_response( coll_alarm2 ):= (.p_ack, au_alarm, addr, state, n_ack.)

end;
\f


service_code:
begin

if not nil( alarm_msg ) then
with ac_tbl( actual_ac_index ) do
finish_message( alarm_msg, data_incomplete,
(**)          ( ac_tbl( pac_index ).sac_rac_ix <> sac_rac_ix ) );

compute_delay( frequence );

activity:= service_code;
reject_code:= illegal_operation;

valid_response( busy ):= (.p_ack, n_ack.);
valid_response( testi ):= (.t_ack, n_ack.);

if nil( driver_msg ) then
conversation:= busy
else
conversation:= idle

end;

otherwise
res:= unknown_update

end (* case update *)

end; (* lock msg *)
\f


connect_test:
connect_message( msg, dummy_alarm - receipt )
;

dummy_alarm:
;

( dummy_alarm + receipt ):
connect_message( msg, connect_test )
;

upd_ac_table:
lock msg as locvar: al_form_1000 do
with locvar, ac_addr_tbl_e, al_label do
if ( send.macro <> dc_macro ) then
res:= forbidden
else
begin

<*t testout( z, "update kind ", update );
testout( z, "address code", addr_code );
testout( z, "ac index    ", ac_index );
testout( z, "blocksize   ", block_lth );
testout( z, "steering    ", ord( steering ) ); t*>
\f


case update of (* change of ac address table *)

read_code:
if search_addr_code( addr_code, table_index ) then
ac_addr_tbl_e:= ac_tbl( table_index )
else
res:= not_found
;

insert_code: (* insert new ac or modify existing entry *)
if search_addr_code( addr_code, table_index ) then (* modify *)
ac_tbl( table_index ):= ac_addr_tbl_e
else
if ( top_aac_index < ac_table_lth ) then
begin

top_aac_index:= top_aac_index + 1;
ac_tbl( top_aac_index ):= ac_addr_tbl_e

end
else
res:= no_room (* no room in ac address table *)
;
\f


remove_code: (* delete aac *)
if search_addr_code( addr_code, table_index ) then
if ( table_index = pac_index ) then
res:= illegal_operation
else
begin

top_aac_index:= top_aac_index - 1;

if ( actual_ac_index > table_index ) then 
actual_ac_index:= actual_ac_index - 1
else
if ( actual_ac_index = table_index ) then 
actual_ac_index:= pac_index;

(* compress *)
for table_index:= table_index to top_aac_index do 
ac_tbl( table_index ):= ac_tbl( table_index + 1 )

end
else
res:= not_found (* ac not found *)
;

otherwise
res:= unknown_update

end (* case update *)

end; (* lock *)
\f


r_w_tec:
rw_param( t_e_counter )
;

read_package_count:
rw_param( package_count )
;

r_w_service_limit:
rw_param( service_limit )
;

r_w_s_a_limit:
rw_param( s_a_limit )
;

r_w_max_succ_t_e:
rw_param( max_succ_t_e )
;

node_test:
begin

return( msg );

if ( activity = stop_code ) then
if not nil( driver_msg ) then
create_channel
else <* ? *>
else <* ? *>

end
;

otherwise
reject_message( msg, ath_sem, route_vect( msg^.u3 ), ts_macro, own_addr, unknown_opcode )

end; (* case operation code *)

if not nil( msg ) then
receipt_message( msg, ath_sem, route_vect( msg^.u3 ), 0, res )

end; (* procedure exec_conn_operation *)
\f


procedure supervise;

begin

if ( traffic_counter > ( abs( node_test_frequency ) + delay ) ) then
begin (* ATH didn't clear traffic_counter in time *)

activity:= stop_code;

reject_code:= no_connection;

(* OBS!!!
. handle_queue will clean up ath_msg and queue_sem when called with
. no_connection. Modifying runtimeset in this way mskes it impossible
. to get back to the original situation.
. outstanding:
.... a feasible reaction in this situation:
...... stop poll ?
...... modify runtimeset ?
...... tell who ?
...... recovery when/if ATH comes up with traffic_counter = 0
...... does ATC find out ?
*)

end
else
traffic_counter:= traffic_counter + delay

end; (* procedure supervise *)
\f


procedure finish_conversation;

(*********************************************************************************
* description      : Takes the appropriate action in relation to a received
*                    response from DRIVER (AT)
* globals          : alarm_msg          = nil or holding a message
*                    driver_mes         = nil or holding driver message
*                    reject_code        = updated
*                    conversation       = updated
*                    ac_tbl             = unchanged
*                    actual_ac_index    = updated
*                    state_bit          = updated
*********************************************************************************)

begin

if ( t_e_kind <> no_error ) then

case conversation of 

busy:
conversation:= idle;

testi, teste, control, coll_alarm1, coll_alarm2:
if ( no_succ_t_e < max_succ_t_e ) then
(* try to get through with the last telegram *)
send_telegram( dummy, dummy, ( ( no_succ_t_e > 1 ) and ( t_e_kind = ill_opc ) ),
(**)         ( t_e_kind <> no_error ) )
else
begin

if not nil( alarm_msg ) then
with ac_tbl( actual_ac_index ) do
finish_message( alarm_msg, data_incomplete,
(**)          ( ac_tbl( pac_index ).sac_rac_ix <> sac_rac_ix ) );

if not nil( ath_msg ) then
with ath_msg^ do
receipt_message( ath_msg, ath_sem, route_vect( u3 ), u2 - u1, data_incomplete );

conversation:= idle

end;

otherwise

end (* case conversation *)
else
\f


(* not transmission error *)

case at_op_code of

p_ack:
conversation:= idle;

au_alarm:
with ac_tbl( actual_ac_index ) do
if nil( alarm_msg ) then
if get_message( alarm_msg, au_alarm_opc, block_lth, 
dummy_macro, dummy, sac_rac_ix ) then
begin

if ready_byte_msg( alarm_msg, at_data ) then
begin (* au alarm is collected *)

with ac_tbl( actual_ac_index ) do
finish_message( alarm_msg, accepted,
(**)          ( ac_tbl( pac_index ).sac_rac_ix <> sac_rac_ix ) );

if ( conversation = coll_alarm2 ) then
conversation:= idle
else
begin (* poll immediately *)

conversation:= coll_alarm2;
send_telegram( poll_opc, poll_byte,
(**)         ( t_e_kind = ill_opc ), ( t_e_kind <> no_error ) )

end

end
else
begin (* collect multi byte alarm *)

if ( conversation <> coll_alarm2 ) then
conversation:= coll_alarm1;

send_telegram( poll_opc, poll_byte,
(**)         ( t_e_kind = ill_opc ), ( t_e_kind <> no_error ) )

end

end;
\f


addr:
begin

if not nil( alarm_msg ) then (* finish not completed alarm *)
with ac_tbl( actual_ac_index ) do
finish_message( alarm_msg, data_incomplete,
(**)          ( ac_tbl( pac_index ).sac_rac_ix <> sac_rac_ix ) )
;

if not search_addr_code( at_data, actual_ac_index ) then (* troubles with block_lth !!! *)
actual_ac_index:= pac_index;

with ac_tbl( actual_ac_index ) do
if get_message( alarm_msg, au_alarm_opc, block_lth,
dummy_macro, dummy, sac_rac_ix ) then
(* send a poll immediately *)
begin

conversation:= coll_alarm1;
send_telegram( poll_opc, poll_byte,
(**)         ( t_e_kind = ill_opc ), ( t_e_kind <> no_error ) )

end

end;
\f


state:
begin

if not nil( alarm_msg ) then (* finish not completed alarm *)
with ac_tbl( actual_ac_index ) do
finish_message( alarm_msg, data_incomplete,
(**)          ( ac_tbl( pac_index ).sac_rac_ix <> sac_rac_ix ) )
;

with ac_tbl( pac_index ) do
if get_message( alarm_msg, state_alarm, 1, dummy_macro, dummy, sac_rac_ix ) then
if ready_byte_msg( alarm_msg, at_data ) then
finish_message( alarm_msg, accepted, override )
;

lock driver_msg as locvar: state_byte do
begin

if ( locvar >= (.batt_limit, batt_supply.) ) then
reject_code:= state_power_error
else
if ( serif_error in locvar ) then
reject_code:= state_serif_error
else
if ( au_error in locvar ) then
reject_code:= state_au_error
else
if ( hs_error in locvar ) then
reject_code:= state_hs_error
else
reject_code:= accepted

end; (* lock driver_msg *)

conversation:= idle

end;
\f


d_ack,
t_ack,
e_ack:
with ath_msg^ do
if ready_byte_msg( ath_msg, at_data ) then
(* send result of control, testi1, testi2, teste to ATH *)
begin

receipt_message( ath_msg, ath_sem, route_vect( u3 ), 0, accepted );

conversation:= idle

end 
else
(* multi byte control/test: send the next data_byte *)
lock ath_msg as locvar: al_form_byte do
with last_telegram, locvar, al_label do
send_telegram( atc_opc, data( u2 + 1 ),
(**)         ( t_e_kind = ill_opc ), ( t_e_kind <> no_error ) )
; (* end lock ath_msg *)

otherwise

end (* case at_op_code *)

end; (* procedure finish_conversation *)
\f


procedure restrict_protocol;

(*********************************************************************************
* description      : Handles the situation, where ATC isn't allowed to
*                    communicate with the AT.
*                    The reason being either:
*                    initiating or stop activity ordered from DC.
* globals          : all globals may be used
*********************************************************************************)

begin

repeat

handle_queue( ath_msg, reject_code );

wait( atc_msg, main_sem.w^ );

if ownertest( delay_pool, atc_msg ) then
delay_msg :=: atc_msg (* hold the message *)
else
case atc_msg^.u3 of 
(* message origin *)

dummy_route:
return( atc_msg );

netc_route,
netc_route1: (* message from ATH *)
if atc_msg^.u4 in (.ts_cntrl, group_cntrl, ts_testi1, ts_testi2, ts_teste.) then 
signal( atc_msg, queue_sem.w^ )
else (* execute the operations that doesn't involve DRIVER *)
exec_conn_operation( atc_msg );
\f


at_route: (* message from DRIVER *)
begin

driver_msg :=: atc_msg; (* hold message *)

conversation:= idle;

if ( driver_msg^.u1 <> write_read_at ) then
begin
(* check u2 to repeat create channel ??? *)

with driver_msg^ do
u1:= write_read_at (* function field isn't altered by DRIVER *)

end;

supervise

end;

otherwise (* unknown route *)
reject_message( atc_msg, ath_sem, at_route, ts_macro, own_addr, unknown_route )

end (* case message origin *)

until ( top_aac_index > 0 ) and ( activity <> stop_code )

end; (* procedure restrict_protocol *)
\f

 
procedure exception( excode: integer );

begin
trace( excode );

if not nil( alarm_msg ) then
with ac_tbl( actual_ac_index ) do
finish_message( alarm_msg, data_incomplete,
(**)          ( ac_tbl( pac_index ).sac_rac_ix <> sac_rac_ix ) );

reject_code:= breaked;

repeat

handle_queue( ath_msg, reject_code );

wait( atc_msg, main_sem.w^ );

if ownertest( delay_pool, atc_msg ) then
delay_msg :=: atc_msg
else
with atc_msg^ do
case u3 of (* route *)

dummy_route:
return( atc_msg )
;

netc_route,
netc_route1:
begin

if u4 in (.ts_cntrl, group_cntrl, ts_testi1, ts_testi2, ts_teste.) then
signal( atc_msg, queue_sem.s^ )
else
if ( u4 <> break_proc_end ) then
exec_conn_operation( atc_msg )

end
;

at_route:
driver_msg :=: atc_msg
;

otherwise
reject_message( atc_msg, ath_sem, at_route, ts_macro, own_addr, unknown_route )

end

until not nil( atc_msg ); (* break_proc_end received *)

receipt_message( atc_msg, ath_sem, route_vect( atc_msg^.u3 ), 0, accepted )

end; (* procedure exception *)
\f


(*********************************************************************************
*
*                             AT CONNECTOR : MAIN
*
*********************************************************************************)

begin

(*t1 testopen( z, own.incname, op_sem ); t1*)

(*t1 testout( z, version , ts_env_vers );
testout( z, "chann/addr  ", ( ( channel_no * 1000 ) + own_addr ) ); t1*)

(*********************************************************************************
* Set up and send a buffer create channel to DRIVER
*********************************************************************************)

alloc( driver_msg, driver_pool, main_sem.s^ );

create_channel;

(********************************************************************************
* Allocate TIMER message with main_sem as answer- and delay_pool semaphore
* as owner-semaphore
********************************************************************************)

alloc( delay_msg, delay_pool, main_sem.s^ );

(********************************************************************************
* Start initialisation sequence
********************************************************************************)

restrict_protocol;

(********************************************************************************
* End of initialization sequence
********************************************************************************)

<*t testout( z, "end init    ", ord( conversation ) ); t*>
\f


repeat
(* forever........................................................................ *)

if ( conversation = idle ) then 
begin

handle_queue( ath_msg, reject_code );

if not nil( ath_msg ) then
(* repeat a interrupted conversation or start a queued one *)
initiate_conversation( ath_msg )
else
if not nil( delay_msg ) then
begin

conversation:= busy;
send_telegram( poll_opc, poll_byte,
(**)         ( t_e_kind = ill_opc ), ( t_e_kind <> no_error ) )

end

end;
\f


wait( atc_msg, main_sem.w^ );

if ownertest( delay_pool, atc_msg ) then
delay_msg :=: atc_msg (* hold the message *)
else
case atc_msg^.u3 of (* message origin *)

dummy_route:
return( atc_msg );

netc_route,
netc_route1: (* message from ATH *)
if atc_msg^.u4 in (.ts_cntrl, group_cntrl, ts_testi1, ts_testi2, ts_teste.) then
signal( atc_msg, queue_sem.s^ )
else
(* always execute the operations that does not involve DRIVER *)
begin

exec_conn_operation( atc_msg );

if ( activity = stop_code ) then
restrict_protocol

end
;
\f


at_route: (* response message from DRIVER *)
begin

driver_msg :=: atc_msg; (* hold message *)

transm_cntrl( at_op_code, at_data, valid_response( conversation ) );
finish_conversation;

supervise;

if ( activity = stop_code ) then
restrict_protocol

end
;

otherwise (* unknown route *)
reject_message( atc_msg, ath_sem, at_route, ts_macro, own_addr, unknown_route )

end (* case message origin *)

until forever;

end. (* process atconnector*)

«eof»