/pylogix

Read/Write data from Allen Bradley Compact/Control Logix PLC's

Primary LanguagePythonApache License 2.0Apache-2.0

****************************************
USAGE - Python 2.7.x or 3.5.x
****************************************

1) from eip import PLC

2) test = PLC()

3) Set the IP Address: test.IPAddress = "192.168.1.10"

4) If your processor is in a slot other than 0, set that: test.ProcessorSlot = 1

4a) For Micro8xx controllers, use test.Micro800 = True

5) Do one of the following:

    Read a value:  value = test.Read("tagname")
    x: value = test.Read("MyTimer.ACC")
        print(value)
    *Reading a unique tag can be sped up by specifying the data type
        ahead of time: test.Read("MyDINT", datatype=196)
        
    Read an array: value = test.Read("tagname", length)
    ex: value = test.Read("MyDINT", 10)
        print(value(3))
    
    Read multiple tags at once: test.MultiRead("tag1", "tag2", "tag3")
    ex: values = test.MultiRead("MyDINT", "MyTimer.ACC", "AnotherDINT")
        print values

    You can also read multiple tags now by passing a list of tags to Read()
    ex: tags = ["MyDint", "AnotherDINT', "ThisREAL"]
        values = test.Read(tags)
        print(values)
    *MultiRead still works, but will probably go away eventually.

    Write a value: test.Write("tagname", value)
    ex: test.Write("MyDINT", 1337)
    *Writing a unique tag can be sped up by specifying the data type
        ahead of time: test.Wtite("MyDINT", 1337, datatype=196)

    Write an array of values:
    vals = [1234, 56789]
    test.Write("MyDint[0]", vals)

    Get the controller scoped tags
    ex: tags = test.GetTagList()
        (returns a structure of .TagName, .DataType and .Offset)

    Get all Ethernet I/P devices on the network test.Discover()
    ex:  stuff = test.Discover()
        print(stuff[1].IPAddress) # prints the first devices IP Address

    Get properties of a module in a particular slot
        thing = test.GetModuleProperties(0)
        print(thing.ProductName, thing.SerialNumber)
        (returns LGXDevice(), though not all members apply)

    Get or Set the PLC clock: test.GetPLCTime() or test.SetPLCTime()
    Note:  SetPLCTime() will use your computers clock to set the PLC

6) Close the connection with test.Close().  This should be called when you are
    completely done reading/writing, not with each exchange of data.

So a complete script to read a tag would look like this:

from eip import PLC
test = PLC()
test.IPAddress = "192.168.1.64"
value = test.Read("MyTimer.ACC")
print value
test.Close()


Another method is by using a with statement:

from eip import PLC
with PLC() as test:
    test.IPAddress = "192.168.1.64"
    print test.Read("MyTimer.ACC")




****************************************
NOTES
****************************************

Several things take place when making a connection to a PLC.

1) Standard TCP connection
2) Session Registration, PLC will respond with a Session Handle
3) Forward Open.  Using the Session Handle, this establishes
        the parameters for connection
4) Command (read/write/etc) and payload

The initial connections (TCP, Session Registration, Forward Open)
    only need to be made once, after that, we can exchange data
    until the connection is closed or lost.

A number events that take place on the command end, they should
    be invisible to the user, but it's good to know (and a reminder for me).
    The user can be reading/writing simple tags, UDT's arrays, bits of a word,
    bools out of atomic arrays, custom length strings, array reads that
    won't fit in a single reply and so on.  Each has to be handled in a
    different way.

    It basically happens like this:  Figure out if its a single tag to be
    read/written or an array.  Open the connection to the PLC (1,2,3 above),
    parse the tag name, add the command (read/write), send the data to the PLC,
    handle the reply

In order to perform a typical read/write, the data type needs to be known, or
    more specifically, the number of bytes the tag is.  It would be pretty
    annoying for the user to have to specify this each time, so to get around
    this, we have the InitialRead function.  This is called on any data exchange
    where the tag name has never been read/written before.  It uses a different
    CIP command than a typical read (0x52 vs 0x4C) which will respond with the
    data type.  This helps us in two ways:  First, the user won't need to specify
    the data type.  Second, we can keep track of this information so the next
    time the particular tag is called, we already know it's type so we can just
    do the normal read/write (0x4C/0x4D/0x4E).