#!/bin/more

        README file for PLC.pm v0.14 Copyright (c) 2004 by Dan Baker

	Licensed under the Artistic License (See the file 'Artistic')

   ###############################################################
   # You must edit the PLC rc file (usually /plc/PLCproc.rc)     #
   # with information on your PLC's before using this software.  #
   # (See the example PLCproc.rc file and PLC.pm)                #
   ###############################################################

## For best results, use the included program 'tns_server' to prevent
## conflicts between programs with the TNS parameter in Allen-Bradley
## communication packets.
I use the following line (in /etc/inittab) to automatically run tns_server:
T0:2345:respawn:/root/bin/tns_server > /dev/null 2>&1

Currently only Allen-Bradley(tm) PLC's and SLC's are supported
See PLC.pm for more information.

#  Send questions, ideas, etc. to Dan Baker (dan.baker@bigfoot.com)

This program and the PLC.pm module are extremely experimental.
If you think the following warnings are too extreme,
you need to gain more experience with industrial controllers
before using this program.

This is experimental software, not to be used for mission-critical
applications. You assume all responsibility for its use.
Your production lines can grind to a halt, causing lost revenue,
or worse, equipment can be destroyed or people injured or killed.
You use this program at your own risk.

DANGER: DON'T write to a PLC unless you are certain it is safe to do so!!!
It is assumed that you are experienced in PLC programming/troubleshooting
and that you know EXACTLY what you are doing. PLC's are used to control
industrial processes, motors, steam valves, hydraulic presses, etc.
You are ABSOLUTELY RESPONSIBLE for ensuring that NO-ONE is in danger
of being injured or killed because you affected the operation
of a running PLC (with this software or any other).

Files:

################------------------------------------------------------------
QUICKSTART	The short version, for the very, very impatient.

################------------------------------------------------------------
PLCproc.rc	Make a directory called /plc/, put this configuration
		file in it (/plc/PLCproc.rc), and edit PLCproc.rc
		with information on your PLC's and Interface modules.
		See the example entries and comments in the included
		PLCproc.rc file for details.

################------------------------------------------------------------
tns_server	The TNS is a transaction number used to identify PCCC packets
		to/from a PLC or SLC. Each time you send a command
		(with the current TNS), you increment the TNS for next time.
		If you send a command to a PLC using the same TNS you used
		the last time, it may ignore your command.
		If the program exits and then restarts, it might use the
		same TNS again, and then your command would be ignored.
		Or in the case of ethernet or CL-gateway, you might have
		several programs communicating with a PLC or PLC's
		all at once. For this reason, it is good practice
		to have a 'TNS server' that will dispense a different
		(ascending) number each time.
		See the included program 'tns_server'.
		If tns_server is running, we just connect to it
		on port 56283 on the local machine (or change the host
		name like this:
 $PLC::tns_server_host = 'tns_servername'; # name of PC running tns_server
		if you are running tns_server on another machine),
		 and read our TNS.
		Otherwise, we fall back to our own tns counter.
		(Also see sub PLC::tns() in PLC.pm)

################------------------------------------------------------------
PLC.pm		Perl module for communicating with PLC's (Programmable
		Logic Controllers). You can read/write data using several
		types of communication.
		Currently supports:

		RS232 serial connection using DF1 full duplex protocol:
		To a PLC5, SLC503, SLC504, (probably SLC505), 1770-KF2,
		or 1770-KF3. If you have a KF2 RS232 to Data Highway Plus
		interface, you can go through that to any PLC5 or SLC504
		on Data Highway Plus. If you have a KF3 RS232 to DH485
		interface, and one or more 1747-AIC DH485 to SLC500
		link couplers, you can go through that to multiple
		SLC500's through the DH485 jack on SLC500's,
		SLC501's, SLC502's, and SLC503's.

		Ethernet protocol to an Ethernet PLC5, and (probably)
		to a SLC505.

		ControlLogix Gateway communication protocol
		using Ethernet module(s) and DHRIO Data Highway Plus
		modules. Typically, you would have a 1756-ENET ethernet
		module in a ControlLogix Gateway, and one or more
		1756-DHRIO modules in the same rack. Each DHRIO module
		has two DH+ channels, so with (as an example) a rack
		with one ENET module and four DHRIO modules,
		you could communicate with PLC's on up to eight
		separate Data Highway Plus links simultaneously.

		So far, I have been testing this software using
		an Ethernet PLC5, several SLC500's through a KF3
		module on one serial port, several PLC5's and SLC504's
		through the ControlLogix Gateway, and PLC's and SLC504's
		through a KF2 on another serial port. These connections
		can all be maintained simultaneously.

		I haven't yet put modem support into this version,
		but I will add that after more testing.

################------------------------------------------------------------
plcx		An example perl script that can be used to read and write
		data to/from PLC's and SLC's. The PLC's and SLC's
		use different commands to transfer data, so this program
		selects the correct command depending on whether
		the processor is a PLC or SLC. If you don't give a
		data address to read or write, plcx will display
		diagnostic status data from the selected PLC or SLC.
		You can also get diagnostic status data from a KF2, KF3,
		or DHRIO module.


# Usage:
# plcx [-r] [-D] [-x] [-b] [-c CMD] [-f FNC] [-W writedata] PLCNAME [ADDRESS]

# options are:
#  -D		debug mode (lots of information you never wanted to know)
#  -q		quiet mode
#  -x		PLC::printreturndata prints returned data in hexadecimal
#  -b		PLC::printreturndata prints returned data in binary
#  -c CMD	CMD is in hex (usually 6 or F).
#  -f FNC	FNC is in hex
#  -W DATA[,DATA] Data to write to PLC
#  -r		raw print mode (don't use PLC::printreturndata, just print)
#  -s SEPCHAR	separation character for printing (if using -r)

example: plcx -cF -f29 LINE_1 N7
 sends a Read Section Size request (F-29) to PLC LINE_1 for data file N7

example: plcx -cF -f68 LINE_1 N7:5
 sends a PLC Typed Data Read request (F-68) to PLC LINE_1 for data word N7:5

If ADDRESS isn't specified, CMD-FNC defaults to 6-3 (Get Diagnostic Status)
Otherwise,
 For PLC's, CMD-FNC defaults to F-68 (PLC5 Typed Read) if no option -W
                             or F-67 (PLC5 Typed Write) if -W DATA (writing)
 For SLC's, CMD-FNC defaults to F-A2 (SLC Typed Read) if no option -W
                             or F-AA (SLC Typed Write) if -W DATA (writing)
example: plcx LINE_2 T4:0.ACC
   sends a PLC Typed Data Read request (F-68) if LINE_2 is a PLC,
or sends a SLC Typed Data Read request (F-A2) if LINE_2 is a SLC.

example: plcx LINE_1 PD77:6.KP -W0.5
   sets the Proportional Constant for PID element PD77 to 0.5
   using a PLC Typed Data Write request (F-67) (good for PLC5's only)

example: plcx A_PLC -DD (or plcx -DD A_PLC)
   sends a 'Get Diagnostic Status' request to 'A_PLC',
   with a debugging level of 2 (-DD)

plcx accepts command-line options before or after normal parameters.

################------------------------------------------------------------

There is no warrantee for this software, but then you didn't pay
for it either, so we're even.

################------------------------------------------------------------

To use the PLC.pm module from your own perl programs, see the example
program plcx. Mainly, you will 'use PLC;' and then transfer data
using PLC::readPLCdata or PLC::PLCcommand, which return an array of
returned data elements.

If there is an error, the variable $PLC::errorcode will hold an error
code identifying the type of error. Error messages are in the array
@PLC::errormsg, so to print the message, you would use something like:

if ($PLC::errorcode) {
 print "\n\n++ ERROR $PLC::errorcode ++\n";
 print "++ $PLC::errormsg[$PLC::errorcode] ++\n\n";
 if ($PLC::errorcode == 6) {
  print "PLC returned error code: ", PLC::ABerrormessage($PLCname),"\n";
 }
 exit(1);
}

################------------------------------------------------------------

You can call PLCcommand this way ...

 @data_array = PLC::PLCcommand (PLCname   => $PLCname,
				CMD       => $cmd,
				FNC       => $fnc,
				DataAddr  => $data_addr,
				Elements  => $elements,
				WriteData => \@writedata);
}
else {				# or this way.
 @data_array
  = PLC::PLCcommand($PLCname,$cmd,$fnc,$data_addr,$elements,@writedata);
}

### Note that in the first example, calling PLCcommand with hash-format
parameters, write data (if any) is send as a REFERENCE to an array,
but when using the second format, it is in list context.

Either way, data is returned as an array, so don't say:

$boiler_temperature = $PLC::PLCcommand("BOILER1",0x0F,0x68,"F8:0");

when you really mean:

($boiler_temperature) = $PLC::PLCcommand("BOILER1",0x0F,0x68,"F8:0");

or:

@temperatures = $PLC::PLCcommand("BOILER1",0x0F,0x68,"F8:0",10);

The last example uses Command F, Function 68 (both in hex) to read
ten floating-point values (from F8:0 to F8:9) from the PLC 'BOILER1'.

(Note that BOILER1 must be defined in the configuration file /plc/PLCproc.rc)

Depending on the type of data being read, the returned array data
may be in bytes, words, floating-point, or string format.

################------------------------------------------------------------

Note: When giving the PLCname to one of these routines, it is
the name specified in the rc file that is used, not necessarily the
name in the program. You can even use aliases for the same PLC in the
rc file. For example, if you have a heading in PLCproc.rc like:
[NORTHPRESS,SN123456,PRESS_1]
any one of the three names above can be used.
Again, see the examples in the sample PLCproc.rc file, and later in this file.


Other subroutines in PLC.pm include:

subroutine name:	example usage:
#-----------------------------------------------------------------------------
PLC::readPLCdata	@ret = PLC::readPLCdata($PLCname,$data_addr,$elements);

Reads data, automatically selecting the Command and Function numbers.
Elements defaults to 1.
#-----------------------------------------------------------------------------
PLC::read_program_name	$name = PLC::read_program_name($PLCname);

Returns the program name from the PLC identified in /plc/PLCproc.rc as
$PLCname. If the program name for the PLC in your hydraulic press is
SN123456, you might want to identify it in PLCproc.rc as NORTHPRESS,
for example.
#-----------------------------------------------------------------------------
PLC::ABerrormessage	print PLC::ABerrormessage($PLCname), "\n";

Prints a message for the last error code returned from the specified PLC
(if any).
#-----------------------------------------------------------------------------
PLC::printreturndata	PLC::printreturndata("MY_PLC");

Displays data returned by the last data read command or 'get status data'
#-----------------------------------------------------------------------------

Allen-Bradley DF1 protocol commands are specified as CMD-FNC,
where CMD is the command number (specified in hexadecimal), and
FNC is the function number (also in hex).

PLC::PLCcommand currently supports the following functions:

6-00	Echo Data
6-03	Get diagnostic Data
F-3A	Set Mode ( PLC5 only )
F-80	Set Mode ( SLC only )
F-00	Word Range Write ( PLC5 only )
F-01	Word Range Read ( PLC5 only )
F-29	Read Section Size ( PLC5 only )
F-67	Typed Write ( PLC5 only ) (also maybe SOME SLC504's?)
F-68	Typed Read ( PLC5 only )  (also maybe SOME SLC504's?)
F-A2	SLC Typed Logical Read ( SLC's only )
F-AA	SLC Typed Logical Write ( SLC's only )

Note: some data types below are not standard Allen-Bradley designations,
but are 'borrowed' from Ron Gage's abplc package.

Note: for SLC's, input and output addresses are not correct. I:2 is
not necessarily I:2 in the SLC. For example, if you have the following:
input cards in slots 2 and 3, output cards in slots 6 and 7,
I:0 is the first input card (actually I:2) and O:0 is actually O:6 (I think).
In order to do the translation, I probably need to decode the I/O
configuration data (wherever that is). Don't hold your breath. (:-(]

Data Types Tested: (some only work with PLC5's, so far)

PN	Program Name
RG	Rung Data
I	Input
O	Output
S	Status Data
B	Binary
T	Timer
C	Counter
R	Control Block
N	Integer Data
F	Floating Point
SC	SfcStatus
PD	PID Blocks
BT	Block Transfer Blocks
MG	Message Blocks (used in MSG instructions)
ST	String Data (only read/write one string at a time, for now)
XA	Section 3 Data
XB	Section 4 Data
XC	Section 5 Data
XD	Section 6 Data
A	ASCII data (not the same as STring)
D	BCD/Hex data (4 digits, each element)

See PLC.pm for supported subelements (i.e. .ACC, .MAXO, .KI, .KD, etc.) 
and bits (i.e. /DN, /TT, /SWM, /ER, etc.).

#-----------------------------------------------------------------------------

I originally wrote a module for ControlLogix Gateway to DH+ only,
then another module for RS232 DF1, and then one for straight Ethernet.
I have been using the ControlLogix Gateway stuff since around mid-1999
continuously, but I haven't used the RS232 stuff very much, and the
Ethernet-PLC support is very recent. This module is a Frankensteinian
conglomeration of all three, so I really need some feedback from
users/testers.

For PLC areas other than Section 0 (Data Table Files),
I have adopted Ron Gage's designations (XA,XB,XC,XD for Sections 3,4,5,&6,
PN for Program Name, RG for Rung Data, etc.)

Note: For PLC5's Input and Output Data is specified in OCTAL
i.e. I:10/17 for the top bit in decimal word 8 (decimal I:8/15)
All other addresses, and all addresses for SLC's are specified in decimal.

Allen-Bradley PLC5's and SLC's supported
## using RS232: (direct to PLC/SLC or via DF2/DF3 module) -----
Computer <-RS232-> (PLC5 or SLC503/SLC504/SLC505)
Computer <-RS232-> KF2 <- DH+ -> (PLC5 or SLC504)
Computer <-RS232-> KF3 <-DH485-> 1747AIC <----> (SLC500)

A typical entry in /plc/PLCproc.rc for an RS232 connected PLC is:

[MYPLC]
CommType = RS232
PLCtype = PLC5
RS232device = /dev/ttyS1
RS232bps = 9600      # 9600 bits/second
RS232parity = 'N'    # no parity
RS232crc_bcc = 'CRC' # using CRC error detection

## using Ethernet: to ControlLogix Gateway Ethernet module, then through ---
                DHRIO module(s) to PLC/SLC on DataHighwayPlus
Computer <-Ethernet-> (ControlLogix Gateway) <- DH+ -> (PLC5-Ethernet/SLC505)
The ControlLogix Gateway needs a 1756-ENET ethernet module,
a ControlLogix Gateway Rack (i.e. 1756-A7A), a Power Supply,
and at least one 1756-DHRIO module.


You should have a working setup (using RSLinx, etc.)
before trying this software for the first time.

#-----------------------------------------------------------------------------
A typical entry in /plc/PLCproc.rc for a CL-Gateway connected SLC504 is:

[MY504]
CommType = ControlLogixGateway
PLCtype = SLC504
GatewayIP = 192.168.0.10 # IP address of the ENET module in the Gateway
GatewaySlot = 1 # the DHRIO module is in slot 1 (slot 0 is first slot)
GatewayChan = 1 # each DHRIO has 2 DH+ channels (1=A,2=B)
DHnode = 005    # the SLC504's DH+ node address

(At work, I have a 1756-ENET in slot 0, and DHRIO's in slots 1-4)

## or directly to Ethernet PLC/SLC -----------------------------------------
Computer <-Ethernet-> (PLC5/Ethernet or SLC505)

A typical entry in /plc/PLCproc.rc for an Ethernet-connected PLC is:

[ETHPLC] # (these 4 lines would be uncommented in the rc file)
CommType = EthernetPLC
PLCtype = PLC5
PLC_IP = 192.168.0.20 # IP address of the Ethernet PLC

###########################################################################
#                                                                         #
#  NOTE: (ControlLogixGateway, DataHighwayPlus, PLC5, and SLC are         #
#  registered trademarks of Allen-Bradley Company Incorporated.           #
#  Allen-Bradley has no connection at all with this software,             #
#  and I have no connection with Allen-Bradley,                           #
#  so don't expect them to give you free technical support.               #
#                                                                         #
#  Information on Allen-Bradley's DF1 protocol can be found at:           #
# 		http://www.ab.com/manuals/cn/protocol.html                #
#  and in:	http://www.ab.com/manuals/cn/17706516.pdf                 #
#  Everything I know about these communication protocols comes from       #
#  the above AB manual (1770-6.5.16), from analysing RS232 and Ethernet   #
#  packets, and (for ethernet PLC stuff) from Ron Gage's abplc package.   #
#                                                                         #
###########################################################################

End of README for module PLC.pm -------------------------------------------

