Uses pycomm_helper package instead of that janky old tag package

This commit is contained in:
Patrick McDonagh
2016-10-14 19:19:02 -05:00
parent 4b9dd1bc71
commit 49c8012532
33 changed files with 794 additions and 517 deletions

14
daq/Dockerfile.rpi Normal file
View File

@@ -0,0 +1,14 @@
FROM patrickjmcd/rpi-python3:latest
# Copy source files
RUN mkdir /root/tag-logger
COPY taglogger.py /root/tag-logger/taglogger.py
COPY pycomm-master /tmp/pycomm
COPY pycomm_helper /tmp/pycomm_helper
# Install some python packages
RUN pip install requests
RUN cd /tmp/pycomm && python setup.py install && cd /
RUN cd /tmp/pycomm_helper && python setup.py install && cd /
CMD ["python", "/root/tag-logger/taglogger.py"]

14
daq/Dockerfile.ubuntu Normal file
View File

@@ -0,0 +1,14 @@
FROM python:latest
# Copy source files
RUN mkdir /root/tag-logger
COPY taglogger.py /root/tag-logger/taglogger.py
COPY pycomm-master /tmp/pycomm
COPY pycomm_helper /tmp/pycomm_helper
# Install some python packages
RUN pip install requests
RUN cd /tmp/pycomm && python setup.py install && cd /
RUN cd /tmp/pycomm_helper && python setup.py install && cd /
CMD ["python", "/root/tag-logger/taglogger.py"]

1
daq/pycomm_helper Submodule

Submodule daq/pycomm_helper added at a3b6c088c5

3
daq/tag/.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "micro800"]
path = micro800
url = http://patrickjmcd@bitbucket.poconsole.net/scm/poconsole/micro800.git

View File

View File

@@ -1,126 +0,0 @@
#! /usr/bin/python
# from datetime import datetime
import time
from pycomm.ab_comm.clx import Driver as ClxDriver
import tag.micro800.micro800 as u800
import requests
import json
# import traceback
# import pickle
web_address = "http://localhost:3000"
def readTag(addr, tag):
time.sleep(0.01)
c = ClxDriver()
if c.open(addr):
try:
v = c.read_tag(tag)
return v
except Exception:
print("ERROR RETRIEVING TAG: {} at {}".format(tag, addr))
err = c.get_status()
c.close()
print err
pass
c.close()
class AnalogAlarm():
global readTag, con
def __init__(self, name, tag, db_id, device_type='CLX', ip_address='192.168.1.10'):
self.name = name
self.tag = tag
self.alarm = False
self.warning = False
self.lastAlarmCheckVal = False
self.lastWarningCheckVal = False
self.device_type = device_type
self.readFn = readTag
self.db_id = db_id
if self.device_type == "u800":
self.readFn = u800.readTag
self.ip_address = ip_address
self.condMapFn = {
20: "Low",
21: "High",
24: "LoLo",
25: "HiHi",
32: "Input Failure",
34: "Configuration Error",
16: "Failure to Stop",
17: "Failure to Start",
18: "Drive Fault"
}
def checkStatus(self, stroke_number):
condition = ''
self.alarm = self.readFn(self.ip_address, '{}.Alarm'.format(self.tag))[0] > 0
alarmChanged = not (self.alarm == self.lastAlarmCheckVal)
self.warning = self.readFn(self.ip_address, '{}.Warning'.format(self.tag))[0] > 0
warningChanged = not (self.warning == self.lastWarningCheckVal)
if (alarmChanged and self.alarm) or (warningChanged and self.warning):
condition = self.condMapFn[int(self.readFn(self.ip_address, '{}.Alarm_Code'.format(self.tag))[0])]
value = self.readFn(self.ip_address, '{}.Alarm_Value'.format(self.tag))[0]
triggerType = "Alarm"
if warningChanged:
triggerType = 'Warning'
data = {
'alarmID': self.db_id,
'type': triggerType,
'cond': condition,
'value': value,
'stroke_number': stroke_number
}
r = requests.post('{}/event'.format(web_address), data=data)
resp = json.loads(r.text)
print("Stored Event {} at {}".format(resp['id'], resp['createdAt']))
if warningChanged:
self.lastWarningCheckVal = self.warning
if alarmChanged:
self.lastAlarmCheckVal = self.alarm
class bitAlarm():
def __init__(self, name, tag, condition, db_id, device_type='CLX', ip_address='192.168.1.10'):
self.name = name
self.tag = tag
self.condition = condition
self.status = False
self.lastStatusCheckVal = False
self.device_type = device_type
self.readFn = readTag
self.db_id = db_id
if self.device_type == "u800":
self.readFn = u800.readTag
self.ip_address = ip_address
def checkStatus(self, stroke_number):
self.status = self.readFn(self.ip_address, self.tag)[0] > 0
statusChanged = not (self.status == self.lastStatusCheckVal)
if statusChanged and self.status:
data = {
'alarmID': self.db_id,
'type': "Info",
'cond': self.condition,
'value': 0.0,
'stroke_number': stroke_number
}
r = requests.post('{}/event'.format(web_address), data=data)
resp = json.loads(r.text)
print("Stored Event {} at {}".format(resp['id'], resp['createdAt']))
if statusChanged:
self.lastStatusCheckVal = self.status

View File

@@ -1,119 +0,0 @@
from pycomm.ab_comm.clx import Driver as plcDriver
import sys
def readMicroTag(addr, tag):
addr = str(addr)
tag = str(tag)
c = plcDriver()
if c.open(addr, True):
try:
v = c.read_tag(tag)
# print(v)
return v
except Exception:
err = c.get_status()
c.close()
print("{} on reading {} from {}".format(err, tag, addr))
pass
c.close()
def getTagType(addr, tag):
addr = str(addr)
tag = str(tag)
c = plcDriver()
if c.open(addr, True):
try:
return c.read_tag(tag)[1]
except Exception:
err = c.get_status()
c.close()
print(err)
pass
c.close()
def write(addr, tag, val, t):
addr = str(addr)
tag = str(tag)
c = plcDriver()
if c.open(addr, True):
try:
wt = c.write_tag(tag, val, t)
return wt
except Exception:
err = c.get_status()
c.close()
print("Write Error: {} setting {} at {} to {} type {}".format(err, tag, addr, val, t))
return False
c.close()
def closeEnough(a, b):
return abs(a - b) <= 0.001
def writeMicroTag(addr, tag, val, handshake=None, handshake_val=None):
addr = str(addr)
tag = str(tag)
print("handshake: {}, handshake_val: {}".format(handshake, handshake_val))
chk_tag = tag
if not(handshake is None) and not(handshake == "None"):
chk_tag = str(handshake)
print("Handshake tag passed, using {}".format(chk_tag))
chk_val = val
if not (handshake_val is None) and not(handshake_val == "None"):
chk_val = handshake_val
print("Handshake value passed, using {}".format(chk_val))
attempts_allowed = 5
attempts = 1
while attempts <= attempts_allowed:
try:
attempts = attempts + 1
cv = readMicroTag(addr, tag)
print("Val Before Write: {}".format(cv))
if cv:
if cv[1] == "REAL":
val = float(val)
chk_val = float(chk_val)
else:
val = int(val)
chk_val = int(chk_val)
wt = write(addr, tag, val, cv[1])
if wt:
print("write: {}".format(wt))
chk = readMicroTag(addr, chk_tag)
if chk:
print("chk: {}, chk_val: {}".format(chk, chk_val))
if closeEnough(chk[0], chk_val):
return True
except Exception as e:
print(e)
return False
def readMicroTagList(addr, tList):
addr = str(addr)
c = plcDriver()
if c.open(addr, True):
vals = []
try:
for t in tList:
v = c.read_tag(t)
vals.append({"tag": t, "val": v[0], "type": v[1]})
# print(v)
# print("{0} - {1}".format(t, v))
except Exception:
err = c.get_status()
c.close()
print(err)
pass
c.close()
return vals
if __name__ == '__main__':
if len(sys.argv) > 2:
print(readMicroTag(sys.argv[1], sys.argv[2]))
else:
print ("Did not pass a target and tag name.")

View File

@@ -1,32 +0,0 @@
__author__ = 'Agostino Ruscito'
__version__ = "1.0.7"
__date__ = "08 03 2015"
import logging
logging.basicConfig(
filename="pycomm.log",
filemode='w',
level=logging.INFO,
format="%(name)-13s %(levelname)-10s %(asctime)s %(message)s",
# propagate=0,
)
LOGGER = logging.getLogger('pycomm')
class PycommError(Exception):
pass
def setup_logger(name, level, filename=None):
log = logging.getLogger('pycomm.'+name)
log.setLevel(level)
if filename:
fh = logging.FileHandler(filename, mode='w')
fh.setFormatter(logging.Formatter("%(levelname)-10s %(asctime)s %(message)s"))
log.addHandler(fh)
log.propagate = False
return log

View File

@@ -1,107 +0,0 @@
#! /usr/bin/python
# from datetime import datetime
import time
from pycomm.ab_comm.clx import Driver as ClxDriver
import tag.micro800.micro800 as u800
import requests
import json
# import traceback
# import pickle
web_address = "https://10.10.10.10:3000"
def readTag(addr, tag):
time.sleep(0.01)
c = ClxDriver()
if c.open(addr):
try:
v = c.read_tag(tag)
return v
except Exception:
print("ERROR RETRIEVING TAG: {} at {}".format(tag, addr))
err = c.get_status()
c.close()
print(err)
pass
c.close()
def writeTag(addr, tag, val):
time.sleep(0.01)
pv = readTag(addr, tag)
if pv:
c = ClxDriver()
if c.open(addr):
try:
v = c.write_tag(tag, val, pv[1])
return v
except Exception:
print("ERROR WRITING TAG: {} at {}".format(tag, addr))
err = c.get_status()
c.close()
print(err)
pass
c.close()
class Tag():
global readTag, writeTag, web_address
def __init__(self, name, tag, db_id, data_type, change_threshold, guarantee_sec, mapFn=None, device_type='CLX', ip_address='192.168.1.10'):
self.name = name
self.tag = tag
self.data_type = data_type
self.value = None
self.last_value = None
self.guarantee_sec = guarantee_sec
self.chg_threshold = change_threshold
self.last_send_time = 0
self.mapFn = mapFn
self.device_type = device_type
self.readFn = readTag
self.writeFn = writeTag
self.db_id = db_id
if self.device_type == "u800" or self.device_type == "Micro800":
self.readFn = u800.readMicroTag
self.writeFn = u800.writeMicroTag
self.ip_address = ip_address
def read(self, forceSend):
writeToDB = False
if self.tag:
v = self.readFn(str(self.ip_address), str(self.tag))
if v:
val = v[0]
if self.data_type == 'BOOL' or self.data_type == 'STRING':
if self.mapFn:
val = self.mapFn[val]
if (self.last_send_time == 0) or (self.value is None) or not (self.value == val) or ((time.time() - self.last_send_time) > self.guarantee_sec) or (forceSend is True):
self.last_value = self.value
self.value = val
writeToDB = True
else:
writeToDB = False
else:
if (self.last_send_time == 0) or (self.value is None) or (abs(self.value - v[0]) > self.chg_threshold) or ((time.time() - self.last_send_time) > self.guarantee_sec) or (forceSend is True):
self.last_value = self.value
self.value = v[0]
writeToDB = True
else:
writeToDB = False
if forceSend is False:
writeToDB = False
if writeToDB:
self.sendToDB()
return self.value
def write(self, value):
if self.tag:
w = self.writeFn(str(self.ip_address), str(self.tag), value)
def sendToDB(self):
data = {}
data['val'] = self.value
data['tagID'] = self.db_id
r = requests.post('{}/tag_val'.format(web_address), data=data, verify=False)
resp = json.loads(r.text)
print("Stored {} for {} at {}".format(resp['val'], self.name, resp['createdAt']))
self.last_send_time = time.time()

View File

@@ -11,10 +11,11 @@ import traceback
import time
import json
import requests
from tag.tag import Tag
from pycomm_helper.tag import Tag
# DEFAULTS
web_address = "https://10.10.10.10:3000"
db_address = "10.10.10.10:3000"
db_url = "https://{}".format(db_address)
scan_rate = 30 # seconds
save_all = "test" # use True, False, or any string
plc_handshake_tags = {}
@@ -25,11 +26,11 @@ device_types = {}
def main():
global web_address, scan_rate, save_all, tag_store, device_types, plc_handshake_tags, last_handshake_time
global db_address, scan_rate, save_all, tag_store, device_types, plc_handshake_tags, last_handshake_time
try:
# Get tags stored in database
get_tag_request_data = {'where': '{"tag_class": 5}'}
get_tag_request = requests.get('{}/tag'.format(web_address), params=get_tag_request_data, verify=False)
get_tag_request = requests.get('{}/tag'.format(db_url), params=get_tag_request_data, verify=False)
tags = json.loads(get_tag_request.text)
except Exception as e:
print("Error getting tags: {}".format(e))
@@ -39,7 +40,7 @@ def main():
try:
# Get tags stored in database
get_device_type_request = requests.get('{}/device_type'.format(web_address), verify=False)
get_device_type_request = requests.get('{}/device_type'.format(db_url), verify=False)
device_types_json = json.loads(get_device_type_request.text)
for t in device_types_json:
device_types[t['id']] = t['dType']
@@ -50,7 +51,7 @@ def main():
try:
sr_req_data = 'where={"parameter": "scan_rate"}'
sr_req = requests.get('{}/config?{}'.format(web_address, sr_req_data), verify=False)
sr_req = requests.get('{}/config?{}'.format(db_url, sr_req_data), verify=False)
sr_try = json.loads(sr_req.text)
if len(sr_try) > 0:
scan_rate = int(sr_try[0]['val'])
@@ -60,7 +61,7 @@ def main():
try:
sa_req_data = {"where": {"parameter": "save_all"}}
sa_req = requests.get('{}/config'.format(web_address), params=sa_req_data, verify=False)
sa_req = requests.get('{}/config'.format(db_url), params=sa_req_data, verify=False)
sa_try = json.loads(sa_req.text)
if len(sa_try) > 0:
if sa_try[0]['val'].lower() == "true":
@@ -74,7 +75,7 @@ def main():
try:
# Get tags stored in database
get_hs_request_data = {'where': '{"tag_class": 6}'}
get_hs_request = requests.get('{}/tag'.format(web_address), params=get_hs_request_data, verify=False)
get_hs_request = requests.get('{}/tag'.format(db_url), params=get_hs_request_data, verify=False)
hs_tags = json.loads(get_hs_request.text)
if len(hs_tags) > 0:
for hs in hs_tags:
@@ -84,7 +85,7 @@ def main():
for t in tags:
# name, tag, db_id, data_type, change_threshold, guarantee_sec, mapFn=None, device_type='CLX', ip_address='192.168.1.10'):
tag_store[t['name']] = Tag(t['name'], t['tag'], t['id'], t['data_type'], t['change_threshold'], t['guarantee_sec'], mapFn=t['map_function'], ip_address=t['deviceID']['address'], device_type=device_types[t['deviceID']['device_type']])
tag_store[t['name']] = Tag(t['name'], t['tag'], t['id'], t['data_type'], t['change_threshold'], t['guarantee_sec'], mapFn=t['map_function'], ip_address=t['deviceID']['address'], device_type=device_types[t['deviceID']['device_type']], db_address=db_address)
while True:
for tag in tag_store:

14
daq_sample/Dockerfile.rpi Normal file
View File

@@ -0,0 +1,14 @@
FROM patrickjmcd/rpi-python3:latest
# Copy source files
RUN mkdir /root/tag-logger
COPY sampleData.py /root/tag-logger/sampleData.py
COPY pycomm-master /tmp/pycomm
COPY pycomm_helper /tmp/pycomm_helper
# Install some python packages
RUN pip install requests
RUN cd /tmp/pycomm && python setup.py install && cd /
RUN cd /tmp/pycomm_helper && python setup.py install && cd /
CMD ["python", "/root/tag-logger/sampleData.py"]

View File

@@ -1,15 +1,14 @@
FROM python:latest
# Copy source files
RUN mkdir /root/tag-logger
COPY taglogger.py /root/tag-logger/taglogger.py
COPY sampleData.py /root/tag-logger/sampleData.py
COPY tag /root/tag-logger/tag
COPY pycomm-master /tmp/pycomm
COPY pycomm_helper /tmp/pycomm_helper
# RUN wget https://bootstrap.pypa.io/get-pip.py
# RUN python get-pip.py
# Install some python packages
RUN pip install requests
RUN cd /tmp/pycomm && python setup.py install && cd /
RUN cd /tmp/pycomm_helper && python setup.py install && cd /
CMD ["python", "/root/tag-logger/sampleData.py"]

View File

@@ -0,0 +1,12 @@
language: python
python:
- "2.6"
- "2.7"
- "3.2"
- "3.3"
- "3.4"
install: python setup.py install
script: nosetests

View File

@@ -0,0 +1,39 @@
CHANGES
=======
1.0.8
-----
Number 0001:
handling of raw values (hex) added to functions read_array and write_array: handling of raw values can be switched
on/off with additional parameter
Number 0002:
is a bugfix when reading the tag_list from a PLC. If one tag is of datatype bool and it is part of a bool
array within an SINT, the tag type value contains also the bit position.
Number 0003:
code is always logging into a file (pycomm.log) into working path. Code changed, so that it is possible to configure
the logging from the main application.
1.0.6
-----
- Pypi posting
1.0.0
-----
- Add support for SLC and PLC/05 plc
0.2.0
---
- Add CIP support class
- Add support for ControlLogix PLC
0.1
---
- Initial release.

View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014 Agostino Ruscito
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1 @@
include README.rst

View File

@@ -0,0 +1,171 @@
pycomm
======
pycomm is a package that includes a collection of modules used to communicate with PLCs.
At the moment the first module in the package is ab_comm.
Test
~~~~
The library is currently test on Python 2.6, 2.7.
.. image:: https://travis-ci.org/ruscito/pycomm.svg?branch=master
:target: https://travis-ci.org/ruscito/pycomm
Setup
~~~~~
The package can be installed from
GitHub:
::
git clone https://github.com/ruscito/pycomm.git
cd pycomm
sudo python setup.py install
PyPi:
::
pip install pycomm
ab_comm
~~~~~~~
ab_comm is a module that contains a set of classes used to interface Rockwell PLCs using Ethernet/IP protocol.
The "clx" class can be used to communicate with Compactlogix, Controllogix PLCs
The "slc" can be used to communicate with Micrologix or SLC PLCs
I tried to followCIP specifications volume 1 and 2 as well as `Rockwell Automation Publication 1756-PM020-EN-P - November 2012`_ .
.. _Rockwell Automation Publication 1756-PM020-EN-P - November 2012: http://literature.rockwellautomation.com/idc/groups/literature/documents/pm/1756-pm020_-en-p.pdf
See the following snippet for communication with a Controllogix PLC:
::
from pycomm.ab_comm.clx import Driver as ClxDriver
import logging
if __name__ == '__main__':
logging.basicConfig(
filename="ClxDriver.log",
format="%(levelname)-10s %(asctime)s %(message)s",
level=logging.DEBUG
)
c = ClxDriver()
if c.open('172.16.2.161'):
print(c.read_tag(['ControlWord']))
print(c.read_tag(['parts', 'ControlWord', 'Counts']))
print(c.write_tag('Counts', -26, 'INT'))
print(c.write_tag(('Counts', 26, 'INT')))
print(c.write_tag([('Counts', 26, 'INT')]))
print(c.write_tag([('Counts', -26, 'INT'), ('ControlWord', -30, 'DINT'), ('parts', 31, 'DINT')]))
# To read an array
r_array = c.read_array("TotalCount", 1750)
for tag in r_array:
print (tag)
# reset tha array to all 0
w_array = []
for i in xrange(1750):
w_array.append(0)
c.write_array("TotalCount", "SINT", w_array)
c.close()
See the following snippet for communication with a Micrologix PLC:
::
from pycomm.ab_comm.slc import Driver as SlcDriver
import logging
if __name__ == '__main__':
logging.basicConfig(
filename="SlcDriver.log",
format="%(levelname)-10s %(asctime)s %(message)s",
level=logging.DEBUG
)
c = SlcDriver()
if c.open('172.16.2.160'):
print c.read_tag('S:1/5')
print c.read_tag('S:60', 2)
print c.write_tag('N7:0', [-30, 32767, -32767])
print c.write_tag('N7:0', 21)
print c.read_tag('N7:0', 10)
print c.write_tag('F8:0', [3.1, 4.95, -32.89])
print c.write_tag('F8:0', 21)
print c.read_tag('F8:0', 3)
print c.write_tag('B3:100', [23, -1, 4, 9])
print c.write_tag('B3:100', 21)
print c.read_tag('B3:100', 4)
print c.write_tag('T4:3.PRE', 431)
print c.read_tag('T4:3.PRE')
print c.write_tag('C5:0.PRE', 501)
print c.read_tag('C5:0.PRE')
print c.write_tag('T4:3.ACC', 432)
print c.read_tag('T4:3.ACC')
print c.write_tag('C5:0.ACC', 502)
print c.read_tag('C5:0.ACC')
c.write_tag('T4:2.EN', 0)
c.write_tag('T4:2.TT', 0)
c.write_tag('T4:2.DN', 0)
print c.read_tag('T4:2.EN', 1)
print c.read_tag('T4:2.TT', 1)
print c.read_tag('T4:2.DN',)
c.write_tag('C5:0.CU', 1)
c.write_tag('C5:0.CD', 0)
c.write_tag('C5:0.DN', 1)
c.write_tag('C5:0.OV', 0)
c.write_tag('C5:0.UN', 1)
c.write_tag('C5:0.UA', 0)
print c.read_tag('C5:0.CU')
print c.read_tag('C5:0.CD')
print c.read_tag('C5:0.DN')
print c.read_tag('C5:0.OV')
print c.read_tag('C5:0.UN')
print c.read_tag('C5:0.UA')
c.write_tag('B3:100', 1)
print c.read_tag('B3:100')
c.write_tag('B3/3955', 1)
print c.read_tag('B3/3955')
c.write_tag('N7:0/2', 1)
print c.read_tag('N7:0/2')
print c.write_tag('O:0.0/4', 1)
print c.read_tag('O:0.0/4')
c.close()
The Future
~~~~~~~~~~
This package is under development.
The modules _ab_comm.clx_ and _ab_comm.slc_ are completed at moment but other drivers will be added in the future.
Thanks
~~~~~~
Thanks to patrickjmcd_ for the help with the Direct Connections and thanks in advance to anyone for feedback and suggestions.
.. _patrickjmcd: https://github.com/patrickjmcd
License
~~~~~~~
pycomm is distributed under the MIT License

View File

@@ -0,0 +1,42 @@
from pycomm.ab_comm.clx import Driver as ClxDriver
import logging
from time import sleep
if __name__ == '__main__':
logging.basicConfig(
filename="ClxDriver.log",
format="%(levelname)-10s %(asctime)s %(message)s",
level=logging.DEBUG
)
c = ClxDriver()
print c['port']
print c.__version__
if c.open('172.16.2.161'):
while 1:
try:
print(c.read_tag(['ControlWord']))
print(c.read_tag(['parts', 'ControlWord', 'Counts']))
print(c.write_tag('Counts', -26, 'INT'))
print(c.write_tag(('Counts', 26, 'INT')))
print(c.write_tag([('Counts', 26, 'INT')]))
print(c.write_tag([('Counts', -26, 'INT'), ('ControlWord', -30, 'DINT'), ('parts', 31, 'DINT')]))
sleep(1)
except Exception as e:
err = c.get_status()
c.close()
print err
pass
# To read an array
r_array = c.read_array("TotalCount", 1750)
for tag in r_array:
print (tag)
c.close()

View File

@@ -0,0 +1,72 @@
__author__ = 'agostino'
from pycomm.ab_comm.slc import Driver as SlcDriver
if __name__ == '__main__':
c = SlcDriver(True, 'delete_slc.log')
if c.open('172.16.2.160'):
while 1:
try:
print c.read_tag('S:1/5')
print c.read_tag('S:60', 2)
print c.write_tag('N7:0', [-30, 32767, -32767])
print c.write_tag('N7:0', 21)
print c.read_tag('N7:0', 10)
print c.write_tag('F8:0', [3.1, 4.95, -32.89])
print c.write_tag('F8:0', 21)
print c.read_tag('F8:0', 3)
print c.write_tag('B3:100', [23, -1, 4, 9])
print c.write_tag('B3:100', 21)
print c.read_tag('B3:100', 4)
print c.write_tag('T4:3.PRE', 431)
print c.read_tag('T4:3.PRE')
print c.write_tag('C5:0.PRE', 501)
print c.read_tag('C5:0.PRE')
print c.write_tag('T4:3.ACC', 432)
print c.read_tag('T4:3.ACC')
print c.write_tag('C5:0.ACC', 502)
print c.read_tag('C5:0.ACC')
c.write_tag('T4:2.EN', 0)
c.write_tag('T4:2.TT', 0)
c.write_tag('T4:2.DN', 0)
print c.read_tag('T4:2.EN', 1)
print c.read_tag('T4:2.TT', 1)
print c.read_tag('T4:2.DN',)
c.write_tag('C5:0.CU', 1)
c.write_tag('C5:0.CD', 0)
c.write_tag('C5:0.DN', 1)
c.write_tag('C5:0.OV', 0)
c.write_tag('C5:0.UN', 1)
c.write_tag('C5:0.UA', 0)
print c.read_tag('C5:0.CU')
print c.read_tag('C5:0.CD')
print c.read_tag('C5:0.DN')
print c.read_tag('C5:0.OV')
print c.read_tag('C5:0.UN')
print c.read_tag('C5:0.UA')
c.write_tag('B3:100', 1)
print c.read_tag('B3:100')
c.write_tag('B3/3955', 1)
print c.read_tag('B3/3955')
c.write_tag('N7:0/2', 1)
print c.read_tag('N7:0/2')
print c.write_tag('O:0.0/4', 1)
print c.read_tag('O:0.0/4')
except Exception as e:
err = c.get_status()
#c.close()
print err
pass
c.close()

View File

@@ -23,9 +23,17 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
from tag.micro800.pycomm_micro.cip.cip_base import *
from tag.micro800.pycomm_micro.common import setup_logger
from pycomm.cip.cip_base import *
import logging
try: # Python 2.7+
from logging import NullHandler
except ImportError:
class NullHandler(logging.Handler):
def emit(self, record):
pass
logger = logging.getLogger(__name__)
logger.addHandler(NullHandler())
class Driver(Base):
@@ -47,11 +55,9 @@ class Driver(Base):
- ControlLogix 5572 and 1756-EN2T Module
"""
def __init__(self, debug=False, filename=None):
if debug:
super(Driver, self).__init__(setup_logger('ab_comm.clx', logging.DEBUG, filename))
else:
super(Driver, self).__init__(setup_logger('ab_comm.clx', logging.INFO, filename))
def __init__(self):
super(Driver, self).__init__()
self._buffer = {}
self._get_template_in_progress = False
@@ -176,7 +182,7 @@ class Driver(Base):
self._byte_offset += bytes_received
else:
self._status = (1, 'unknown status {0} during _parse_template'.format(status))
self.logger.warning(self._status)
logger.warning(self._status)
self._last_instance = -1
def _parse_fragment(self, start_ptr, status):
@@ -197,12 +203,18 @@ class Driver(Base):
while idx < fragment_returned_length:
try:
typ = I_DATA_TYPE[data_type]
value = UNPACK_DATA_FUNCTION[typ](fragment_returned[idx:idx+DATA_FUNCTION_SIZE[typ]])
if self._output_raw:
value = fragment_returned[idx:idx+DATA_FUNCTION_SIZE[typ]]
else:
value = UNPACK_DATA_FUNCTION[typ](fragment_returned[idx:idx+DATA_FUNCTION_SIZE[typ]])
idx += DATA_FUNCTION_SIZE[typ]
except Exception as e:
raise DataError(e)
self._tag_list.append((self._last_position, value))
self._last_position += 1
if self._output_raw:
self._tag_list += value
else:
self._tag_list.append((self._last_position, value))
self._last_position += 1
if status == SUCCESS:
self._byte_offset = -1
@@ -350,9 +362,9 @@ class Driver(Base):
if not self._target_is_connected:
if not self.forward_open():
self._status = (6, "Target did not connected. read_tag will not be executed.")
self.logger.warning(self._status)
raise Error("Target did not connected. read_tag will not be executed.")
# multi_requests = False
logger.warning(self._status)
raise DataError("Target did not connected. read_tag will not be executed.")
if multi_requests:
rp_list = []
for t in tag:
@@ -393,13 +405,12 @@ class Driver(Base):
else:
# Get the data type
data_type = unpack_uint(self._reply[50:52])
# print I_DATA_TYPE[data_type]
try:
return UNPACK_DATA_FUNCTION[I_DATA_TYPE[data_type]](self._reply[52:]), I_DATA_TYPE[data_type]
except Exception as e:
raise DataError(e)
def read_array(self, tag, counts):
def read_array(self, tag, counts, raw=False):
""" read array of atomic data type from a connected plc
At the moment there is not a strong validation for the argument passed. The user should verify
@@ -407,18 +418,23 @@ class Driver(Base):
:param tag: the name of the tag to read
:param counts: the number of element to read
:param raw: the value should output as raw-value (hex)
:return: None is returned in case of error otherwise the tag list is returned
"""
if not self._target_is_connected:
if not self.forward_open():
self._status = (7, "Target did not connected. read_tag will not be executed.")
self.logger.warning(self._status)
raise Error("Target did not connected. read_tag will not be executed.")
logger.warning(self._status)
raise DataError("Target did not connected. read_tag will not be executed.")
self._byte_offset = 0
self._last_position = 0
self._output_raw = raw
self._tag_list = []
if self._output_raw:
self._tag_list = ''
else:
self._tag_list = []
while self._byte_offset != -1:
rp = create_tag_rp(tag)
if rp is None:
@@ -480,8 +496,8 @@ class Driver(Base):
if not self._target_is_connected:
if not self.forward_open():
self._status = (8, "Target did not connected. write_tag will not be executed.")
self.logger.warning(self._status)
raise Error("Target did not connected. write_tag will not be executed.")
logger.warning(self._status)
raise DataError("Target did not connected. write_tag will not be executed.")
if multi_requests:
rp_list = []
@@ -525,7 +541,7 @@ class Driver(Base):
rp = create_tag_rp(name)
if rp is None:
self._status = (8, "Cannot create tag {0} request packet. write_tag will not be executed.".format(tag))
self.logger.warning(self._status)
logger.warning(self._status)
return None
else:
# Creating the Message Request Packet
@@ -555,7 +571,7 @@ class Driver(Base):
raise DataError("send_unit_data returned not valid data")
return ret_val
def write_array(self, tag, data_type, values):
def write_array(self, tag, data_type, values, raw=False):
""" write array of atomic data type from a connected plc
At the moment there is not a strong validation for the argument passed. The user should verify
@@ -563,25 +579,29 @@ class Driver(Base):
:param tag: the name of the tag to read
:param data_type: the type of tag to write
:param values: the array of values to write
:param values: the array of values to write, if raw: the frame with bytes
:param raw: indicates that the values are given as raw values (hex)
"""
if not isinstance(values, list):
self._status = (9, "A list of tags must be passed to write_array.")
self.logger.warning(self._status)
logger.warning(self._status)
raise DataError("A list of tags must be passed to write_array.")
if not self._target_is_connected:
if not self.forward_open():
self._status = (9, "Target did not connected. write_array will not be executed.")
self.logger.warning(self._status)
raise Error("Target did not connected. write_array will not be executed.")
logger.warning(self._status)
raise DataError("Target did not connected. write_array will not be executed.")
array_of_values = ""
byte_size = 0
byte_offset = 0
for i, value in enumerate(values):
array_of_values += PACK_DATA_FUNCTION[data_type](value)
if raw:
array_of_values += value
else:
array_of_values += PACK_DATA_FUNCTION[data_type](value)
byte_size += DATA_FUNCTION_SIZE[data_type]
if byte_size >= 450 or i == len(values)-1:
@@ -626,8 +646,8 @@ class Driver(Base):
if not self._target_is_connected:
if not self.forward_open():
self._status = (10, "Target did not connected. get_tag_list will not be executed.")
self.logger.warning(self._status)
raise Error("Target did not connected. get_tag_list will not be executed.")
logger.warning(self._status)
raise DataError("Target did not connected. get_tag_list will not be executed.")
self._last_instance = 0
@@ -674,8 +694,8 @@ class Driver(Base):
if not self._target_is_connected:
if not self.forward_open():
self._status = (10, "Target did not connected. get_tag_list will not be executed.")
self.logger.warning(self._status)
raise Error("Target did not connected. get_tag_list will not be executed.")
logger.warning(self._status)
raise DataError("Target did not connected. get_tag_list will not be executed.")
message_request = [
pack_uint(self._get_sequence()),
@@ -708,8 +728,8 @@ class Driver(Base):
if not self._target_is_connected:
if not self.forward_open():
self._status = (10, "Target did not connected. get_tag_list will not be executed.")
self.logger.warning(self._status)
raise Error("Target did not connected. get_tag_list will not be executed.")
logger.warning(self._status)
raise DataError("Target did not connected. get_tag_list will not be executed.")
self._byte_offset = 0
self._buffer = ""
@@ -753,10 +773,10 @@ class Driver(Base):
continue
if tag['symbol_type'] & 0b0001000000000000:
continue
dimension = tag['symbol_type'] & 0b0110000000000000 >> 13
template_instance_id = tag['symbol_type'] & 0b0000111111111111
dimension = (tag['symbol_type'] & 0b0110000000000000) >> 13
if tag['symbol_type'] & 0b1000000000000000 :
template_instance_id = tag['symbol_type'] & 0b0000111111111111
tag_type = 'struct'
data_type = 'user-created'
self._tag_list.append({'instance_id': tag['instance_id'],
@@ -769,12 +789,22 @@ class Driver(Base):
'udt': {}})
else:
tag_type = 'atomic'
data_type = I_DATA_TYPE[template_instance_id]
self._tag_list.append({'instance_id': tag['instance_id'],
'tag_name': tag['tag_name'],
'dim': dimension,
'tag_type': tag_type,
'data_type': data_type})
datatype = tag['symbol_type'] & 0b0000000011111111
data_type = I_DATA_TYPE[datatype]
if datatype == 0xc1:
bit_position = (tag['symbol_type'] & 0b0000011100000000) >> 8
self._tag_list.append({'instance_id': tag['instance_id'],
'tag_name': tag['tag_name'],
'dim': dimension,
'tag_type': tag_type,
'data_type': data_type,
'bit_position' : bit_position})
else:
self._tag_list.append({'instance_id': tag['instance_id'],
'tag_name': tag['tag_name'],
'dim': dimension,
'tag_type': tag_type,
'data_type': data_type})
except Exception as e:
raise DataError(e)
@@ -841,7 +871,3 @@ class Driver(Base):
# Step 4
return self._tag_list

View File

@@ -23,11 +23,21 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
from tag.micro800.pycomm_micro.cip.cip_base import *
from tag.micro800.pycomm_micro.common import setup_logger
from pycomm.cip.cip_base import *
import re
import logging
import math
#import binascii
import logging
try: # Python 2.7+
from logging import NullHandler
except ImportError:
class NullHandler(logging.Handler):
def emit(self, record):
pass
logger = logging.getLogger(__name__)
logger.addHandler(NullHandler())
def parse_tag(tag):
@@ -45,7 +55,7 @@ def parse_tag(tag):
'write_func': '\xab',
'address_field': 3}
t = re.search(r"(?P<file_type>[FBN])(?P<file_number>\d{1,3})"
t = re.search(r"(?P<file_type>[LFBN])(?P<file_number>\d{1,3})"
r"(:)(?P<element_number>\d{1,3})"
r"(/(?P<sub_element>\d{1,2}))?",
tag, flags=re.IGNORECASE)
@@ -148,11 +158,8 @@ class Driver(Base):
"""
SLC/PLC_5 Implementation
"""
def __init__(self, debug=False, filename=None):
if debug:
super(Driver, self).__init__(setup_logger('ab_comm.slc', logging.DEBUG, filename))
else:
super(Driver, self).__init__(setup_logger('ab_comm.slc', logging.INFO, filename))
def __init__(self):
super(Driver, self).__init__()
self.__version__ = '0.1'
self._last_sequence = 0
@@ -207,6 +214,127 @@ class Driver(Base):
except Exception as e:
raise DataError(e)
def __queue_data_available(self, queue_number):
""" read the queue
Possible combination can be passed to this method:
print c.read_tag('F8:0', 3) return a list of 3 registers starting from F8:0
print c.read_tag('F8:0') return one value
It is possible to read status bit
:return: None is returned in case of error
"""
# Creating the Message Request Packet
self._last_sequence = pack_uint(Base._get_sequence())
# PCCC_Cmd_Rd_w3_Q2 = [0x0f, 0x00, 0x30, 0x00, 0xa2, 0x6d, 0x00, 0xa5, 0x02, 0x00]
message_request = [
self._last_sequence,
'\x4b',
'\x02',
CLASS_ID["8-bit"],
PATH["PCCC"],
'\x07',
self.attribs['vid'],
self.attribs['vsn'],
'\x0f',
'\x00',
self._last_sequence[1],
self._last_sequence[0],
'\xa2', # protected typed logical read with three address fields FNC
'\x6d', # Byte size to read = 109
'\x00', # File Number
'\xa5', # File Type
pack_uint(queue_number)
]
if self.send_unit_data(
build_common_packet_format(
DATA_ITEM['Connected'],
''.join(message_request),
ADDRESS_ITEM['Connection Based'],
addr_data=self._target_cid,)):
sts = int(unpack_uint(self._reply[2:4]))
if sts == 146:
return True
else:
return False
else:
raise DataError("read_queue [send_unit_data] returned not valid data")
def __save_record(self, filename):
with open(filename, "a") as csv_file:
logger.debug("SLC __save_record read:{0}".format(self._reply[61:]))
csv_file.write(self._reply[61:]+'\n')
csv_file.close()
def __get_queue_size(self, queue_number):
""" get queue size
"""
# Creating the Message Request Packet
self._last_sequence = pack_uint(Base._get_sequence())
message_request = [
self._last_sequence,
'\x4b',
'\x02',
CLASS_ID["8-bit"],
PATH["PCCC"],
'\x07',
self.attribs['vid'],
self.attribs['vsn'],
'\x0f',
'\x00',
self._last_sequence[1],
self._last_sequence[0],
# '\x30',
# '\x00',
'\xa1', # FNC to get the queue size
'\x06', # Byte size to read = 06
'\x00', # File Number
'\xea', # File Type ????
'\xff', # File Type ????
pack_uint(queue_number)
]
if self.send_unit_data(
build_common_packet_format(
DATA_ITEM['Connected'],
''.join(message_request),
ADDRESS_ITEM['Connection Based'],
addr_data=self._target_cid,)):
sts = int(unpack_uint(self._reply[65:67]))
logger.debug("SLC __get_queue_size({0}) returned {1}".format(queue_number, sts))
return sts
else:
raise DataError("read_queue [send_unit_data] returned not valid data")
def read_queue(self, queue_number, file_name):
""" read the queue
"""
if not self._target_is_connected:
if not self.forward_open():
self._status = (5, "Target did not connected. is_queue_available will not be executed.")
logger.warning(self._status)
raise DataError("Target did not connected. is_queue_available will not be executed.")
if self.__queue_data_available(queue_number):
logger.debug("SLC read_queue: Queue {0} has data".format(queue_number))
self.__save_record(file_name + str(queue_number) + ".csv")
size = self.__get_queue_size(queue_number)
if size > 0:
for i in range(0, size):
if self.__queue_data_available(queue_number):
self.__save_record(file_name + str(queue_number) + ".csv")
logger.debug("SLC read_queue: {0} record extract from queue {1}".format(size, queue_number))
else:
logger.debug("SLC read_queue: Queue {0} has no data".format(queue_number))
def read_tag(self, tag, n=1):
""" read tag from a connected plc
@@ -221,7 +349,7 @@ class Driver(Base):
res = parse_tag(tag)
if not res[0]:
self._status = (1000, "Error parsing the tag passed to read_tag({0},{1})".format(tag, n))
self.logger.warning(self._status)
logger.warning(self._status)
raise DataError("Error parsing the tag passed to read_tag({0},{1})".format(tag, n))
bit_read = False
@@ -234,8 +362,8 @@ class Driver(Base):
if not self._target_is_connected:
if not self.forward_open():
self._status = (5, "Target did not connected. read_tag will not be executed.")
self.logger.warning(self._status)
raise Error("Target did not connected. read_tag will not be executed.")
logger.warning(self._status)
raise DataError("Target did not connected. read_tag will not be executed.")
data_size = PCCC_DATA_SIZE[res[2]['file_type']]
@@ -263,7 +391,7 @@ class Driver(Base):
pack_usint(sub_element)
]
self.logger.debug("SLC read_tag({0},{1})".format(tag, n))
logger.debug("SLC read_tag({0},{1})".format(tag, n))
if self.send_unit_data(
build_common_packet_format(
DATA_ITEM['Connected'],
@@ -275,7 +403,7 @@ class Driver(Base):
if sts != 0:
sts_txt = PCCC_ERROR_CODE[sts]
self._status = (1000, "Error({0}) returned from read_tag({1},{2})".format(sts_txt, tag, n))
self.logger.warning(self._status)
logger.warning(self._status)
raise DataError("Error({0}) returned from read_tag({1},{2})".format(sts_txt, tag, n))
new_value = 61
@@ -307,7 +435,7 @@ class Driver(Base):
except Exception as e:
self._status = (1000, "Error({0}) parsing the data returned from read_tag({1},{2})".format(e, tag, n))
self.logger.warning(self._status)
logger.warning(self._status)
raise DataError("Error({0}) parsing the data returned from read_tag({1},{2})".format(e, tag, n))
else:
raise DataError("send_unit_data returned not valid data")
@@ -327,17 +455,17 @@ class Driver(Base):
res = parse_tag(tag)
if not res[0]:
self._status = (1000, "Error parsing the tag passed to read_tag({0},{1})".format(tag, value))
self.logger.warning(self._status)
logger.warning(self._status)
raise DataError("Error parsing the tag passed to read_tag({0},{1})".format(tag, value))
if isinstance(value, list) and int(res[2]['address_field'] == 3):
self._status = (1000, "Function's parameters error. read_tag({0},{1})".format(tag, value))
self.logger.warning(self._status)
logger.warning(self._status)
raise DataError("Function's parameters error. read_tag({0},{1})".format(tag, value))
if isinstance(value, list) and int(res[2]['address_field'] == 3):
self._status = (1000, "Function's parameters error. read_tag({0},{1})".format(tag, value))
self.logger.warning(self._status)
logger.warning(self._status)
raise DataError("Function's parameters error. read_tag({0},{1})".format(tag, value))
bit_field = False
@@ -357,8 +485,8 @@ class Driver(Base):
if not self._target_is_connected:
if not self.forward_open():
self._status = (1000, "Target did not connected. write_tag will not be executed.")
self.logger.warning(self._status)
raise Error("Target did not connected. write_tag will not be executed.")
logger.warning(self._status)
raise DataError("Target did not connected. write_tag will not be executed.")
try:
n = 0
@@ -390,7 +518,7 @@ class Driver(Base):
except Exception as e:
self._status = (1000, "Error({0}) packing the values to write to the"
"SLC write_tag({1},{2})".format(e, tag, value))
self.logger.warning(self._status)
logger.warning(self._status)
raise DataError("Error({0}) packing the values to write to the "
"SLC write_tag({1},{2})".format(e, tag, value))
@@ -420,7 +548,7 @@ class Driver(Base):
pack_usint(sub_element)
]
self.logger.debug("SLC write_tag({0},{1})".format(tag, value))
logger.debug("SLC write_tag({0},{1})".format(tag, value))
if self.send_unit_data(
build_common_packet_format(
DATA_ITEM['Connected'],
@@ -432,15 +560,15 @@ class Driver(Base):
if sts != 0:
sts_txt = PCCC_ERROR_CODE[sts]
self._status = (1000, "Error({0}) returned from SLC write_tag({1},{2})".format(sts_txt, tag, value))
self.logger.warning(self._status)
logger.warning(self._status)
raise DataError("Error({0}) returned from SLC write_tag({1},{2})".format(sts_txt, tag, value))
return True
except Exception as e:
self._status = (1000, "Error({0}) parsing the data returned from "
"SLC write_tag({1},{2})".format(e, tag, value))
self.logger.warning(self._status)
logger.warning(self._status)
raise DataError("Error({0}) parsing the data returned from "
"SLC write_tag({1},{2})".format(e, tag, value))
else:
raise DataError("send_unit_data returned not valid data")
raise DataError("send_unit_data returned not valid data")

View File

@@ -26,10 +26,22 @@
import struct
import socket
import random
from os import getpid
from tag.micro800.pycomm_micro.cip.cip_const import *
from tag.micro800.pycomm_micro.common import PycommError
from pycomm.cip.cip_const import *
from pycomm.common import PycommError
import logging
try: # Python 2.7+
from logging import NullHandler
except ImportError:
class NullHandler(logging.Handler):
def emit(self, record):
pass
logger = logging.getLogger(__name__)
logger.addHandler(NullHandler())
class CommError(PycommError):
@@ -55,7 +67,6 @@ def pack_int(n):
def pack_uint(n):
"""pack 16 bit into 2 bytes little endian"""
# print("N: {0}".format(n))
return struct.pack('<H', n)
@@ -75,9 +86,9 @@ def pack_lint(l):
def unpack_bool(st):
if int(struct.unpack('B', st[0])[0]) == 0:
return 0
return 1
if not (int(struct.unpack('B', st[0])[0]) == 0):
return 1
return 0
def unpack_sint(st):
@@ -107,9 +118,6 @@ def unpack_real(st):
"""unpack 4 bytes little endian to int"""
return float(struct.unpack('<f', st[0:4])[0])
def unpack_lreal(st):
"""unpack 8 bytes little endian to int"""
return float(struct.unpack('<f', st[0:8])[0])
def unpack_lint(st):
"""unpack 4 bytes little endian to int"""
@@ -126,10 +134,9 @@ PACK_DATA_FUNCTION = {
'SINT': pack_sint, # Signed 8-bit integer
'INT': pack_int, # Signed 16-bit integer
'UINT': pack_uint, # Unsigned 16-bit integer
'USINT': pack_usint, # Unsigned 8-bit integer
'USINT': pack_usint, # Unsigned Byte Integer
'DINT': pack_dint, # Signed 32-bit integer
'REAL': pack_real, # 32-bit floating point
'LREAL': pack_real, # 32-bit floating point
'LINT': pack_lint,
'BYTE': pack_sint, # byte string 8-bits
'WORD': pack_uint, # byte string 16-bits
@@ -142,12 +149,10 @@ UNPACK_DATA_FUNCTION = {
'BOOL': unpack_bool,
'SINT': unpack_sint, # Signed 8-bit integer
'INT': unpack_int, # Signed 16-bit integer
'UINT': unpack_uint, # Unsigned 16-bit
'USINT': unpack_usint, # Unsigned 8-bit integer
'UINT': unpack_uint, # Unsigned 16-bit integer
'USINT': unpack_usint, # Unsigned Byte Integer
'DINT': unpack_dint, # Signed 32-bit integer
'UDINT': unpack_dint, # Signed 32-bit integer
'REAL': unpack_real, # 32-bit floating point,
'LREAL': unpack_lreal, # 32-bit floating point,
'LINT': unpack_lint,
'BYTE': unpack_sint, # byte string 8-bits
'WORD': unpack_uint, # byte string 16-bits
@@ -159,6 +164,7 @@ UNPACK_DATA_FUNCTION = {
DATA_FUNCTION_SIZE = {
'BOOL': 1,
'SINT': 1, # Signed 8-bit integer
'USINT': 1, # Unisgned 8-bit integer
'INT': 2, # Signed 16-bit integer
'UINT': 2, # Unsigned 16-bit integer
'DINT': 4, # Signed 32-bit integer
@@ -196,7 +202,6 @@ PACK_PCCC_DATA_FUNCTION = {
'I': pack_int
}
def print_bytes_line(msg):
out = ''
for ch in msg:
@@ -474,15 +479,15 @@ class Base(object):
_sequence = 0
def __init__(self, logging):
def __init__(self):
if Base._sequence == 0:
Base._sequence = getpid()
else:
Base._sequence = Base._get_sequence()
self.logger = logging
self.__version__ = '0.1'
self.__version__ = '0.3'
self.__sock = None
self.__direct_connections = False
self._session = 0
self._connection_opened = False
self._reply = None
@@ -499,12 +504,10 @@ class Base(object):
self._last_tag_read = ()
self._last_tag_write = ()
self._status = (0, "")
self._output_raw = False # indicating value should be output as raw (hex)
# self.attribs = {'context': '_pycomm_', 'protocol version': 1, 'rpi': 5000, 'port': 0xAF12, 'timeout': 10,
# 'backplane': 1, 'cpu slot': 0, 'option': 0, 'cid': '\x27\x04\x19\x71', 'csn': '\x27\x04',
# 'vid': '\x09\x10', 'vsn': '\x09\x10\x19\x71', 'name': 'Base', 'ip address': None}
self.attribs = {'context': '_pycomm_', 'protocol version': 1, 'rpi': 5000, 'port': 0xAF12, 'timeout': 10,
'backplane': 0, 'cpu slot': 0, 'option': 0, 'cid': '\x27\x04\x19\x71', 'csn': '\x27\x04',
'backplane': 1, 'cpu slot': 0, 'option': 0, 'cid': '\x27\x04\x19\x71', 'csn': '\x27\x04',
'vid': '\x09\x10', 'vsn': '\x09\x10\x19\x71', 'name': 'Base', 'ip address': None}
def __len__(self):
@@ -554,6 +557,10 @@ class Base(object):
def __repr__(self):
return self._device_description
def generate_cid(self):
self.attribs['cid'] = '{0}{1}{2}{3}'.format(chr(random.randint(0, 255)), chr(random.randint(0, 255))
, chr(random.randint(0, 255)), chr(random.randint(0, 255)))
def description(self):
return self._device_description
@@ -646,11 +653,11 @@ class Base(object):
self._receive()
if self._check_reply():
self._session = unpack_dint(self._reply[4:8])
self.logger.debug("Session ={0} has been registered.".format(print_bytes_line(self._reply[4:8])))
logger.debug("Session ={0} has been registered.".format(print_bytes_line(self._reply[4:8])))
return self._session
self._status = 'Warning ! the session has not been registered.'
self.logger.warning(self._status)
logger.warning(self._status)
return None
def forward_open(self):
@@ -686,7 +693,6 @@ class Base(object):
pack_uint(CONNECTION_PARAMETER['Default']),
TRANSPORT_CLASS, # Transport Class
# CONNECTION_SIZE['Backplane'],
CONNECTION_SIZE['Direct Network'],
# pack_usint(self.attribs['backplane']),
# pack_usint(self.attribs['cpu slot']),
CLASS_ID["8-bit"],
@@ -694,6 +700,18 @@ class Base(object):
INSTANCE_ID["8-bit"],
pack_usint(1)
]
if self.__direct_connections:
forward_open_msg[20:1] = [
CONNECTION_SIZE['Direct Network'],
]
else:
forward_open_msg[20:3] = [
CONNECTION_SIZE['Backplane'],
pack_usint(self.attribs['backplane']),
pack_usint(self.attribs['cpu slot'])
]
if self.send_rr_data(
build_common_packet_format(DATA_ITEM['Unconnected'], ''.join(forward_open_msg), ADDRESS_ITEM['UCMM'],)):
self._target_cid = self._reply[44:48]
@@ -714,7 +732,7 @@ class Base(object):
if self._session == 0:
self._status = (5, "A session need to be registered before to call forward_close.")
raise CommError("A session need to be registered before to call forward_close.")
# print ("Backplane:{0}\nCPU:{1}".format(self.attribs['backplane'], self.attribs['cpu slot']))
forward_close_msg = [
FORWARD_CLOSE,
pack_usint(2),
@@ -727,9 +745,8 @@ class Base(object):
self.attribs['csn'],
self.attribs['vid'],
self.attribs['vsn'],
CONNECTION_SIZE['Direct Network'],
# CONNECTION_SIZE['Backplane'],
'\x00', # Reserved
# '\x00', # Reserved
# pack_usint(self.attribs['backplane']),
# pack_usint(self.attribs['cpu slot']),
CLASS_ID["8-bit"],
@@ -737,12 +754,26 @@ class Base(object):
INSTANCE_ID["8-bit"],
pack_usint(1)
]
if self.__direct_connections:
forward_close_msg[11:2] = [
CONNECTION_SIZE['Direct Network'],
'\x00'
]
else:
forward_close_msg[11:4] = [
CONNECTION_SIZE['Backplane'],
'\x00',
pack_usint(self.attribs['backplane']),
pack_usint(self.attribs['cpu slot'])
]
if self.send_rr_data(
build_common_packet_format(DATA_ITEM['Unconnected'], ''.join(forward_close_msg), ADDRESS_ITEM['UCMM'])):
self._target_is_connected = False
return True
self._status = (5, "forward_close returned False")
self.logger.warning(self._status)
logger.warning(self._status)
return False
def un_register_session(self):
@@ -759,10 +790,10 @@ class Base(object):
:return: true if no error otherwise false
"""
try:
self.logger.debug(print_bytes_msg(self._message, '-------------- SEND --------------'))
logger.debug(print_bytes_msg(self._message, '-------------- SEND --------------'))
self.__sock.send(self._message)
except Exception as e:
#self.clean_up()
# self.clean_up()
raise CommError(e)
def _receive(self):
@@ -772,18 +803,21 @@ class Base(object):
"""
try:
self._reply = self.__sock.receive()
self.logger.debug(print_bytes_msg(self._reply, '----------- RECEIVE -----------'))
logger.debug(print_bytes_msg(self._reply, '----------- RECEIVE -----------'))
except Exception as e:
#self.clean_up()
# self.clean_up()
raise CommError(e)
def open(self, ip_address):
def open(self, ip_address, direct_connection=False):
"""
socket open
:param: ip address to connect to and type of connection. By default direct connection is disabled
:return: true if no error otherwise false
"""
# handle the socket layer
# set type of connection needed
self.__direct_connections = direct_connection
# handle the socket layer
if not self._connection_opened:
try:
if self.__sock is None:
@@ -791,13 +825,16 @@ class Base(object):
self.__sock.connect(ip_address, self.attribs['port'])
self._connection_opened = True
self.attribs['ip address'] = ip_address
self.generate_cid()
if self.register_session() is None:
self._status = (13, "Session not registered")
return False
# not sure but maybe I can remove this because is used to clean up any previous unclosed connection
self.forward_close()
return True
except Exception as e:
#self.clean_up()
# self.clean_up()
raise CommError(e)
def close(self):
@@ -824,4 +861,4 @@ class Base(object):
self._connection_opened = False
def is_connected(self):
return self._connection_opened
return self._connection_opened

View File

@@ -435,6 +435,7 @@ PCCC_DATA_TYPE = {
PCCC_DATA_SIZE = {
'N': 2,
# 'L': 4,
'B': 2,
'T': 6,
'C': 6,

View File

@@ -0,0 +1,8 @@
__author__ = 'Agostino Ruscito'
__version__ = "1.0.8"
__date__ = "08 03 2015"
class PycommError(Exception):
pass

View File

@@ -0,0 +1,37 @@
from distutils.core import setup
from pycomm import common
import os
def read(file_name):
return open(os.path.join(os.path.dirname(__file__), file_name)).read()
setup(
name="pycomm",
author="Agostino Ruscito",
author_email="uscito@gmail.com",
version=common.__version__,
description="A PLC communication library for Python",
long_description=read('README.rst'),
license="MIT",
url="https://github.com/ruscito/pycomm",
packages=[
"pycomm",
"pycomm.ab_comm",
"pycomm.cip"
],
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Natural Language :: English',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Topic :: Software Development :: Libraries :: Python Modules',
],
)

Submodule daq_sample/pycomm_helper added at a3b6c088c5

View File

@@ -7,7 +7,7 @@ Created on April 7, 2016
@description: Continuously loops through a list of tags to store values from a PLC into a MySQL database
'''
from tag.tag import Tag
from pycomm_helper.tag import Tag
import traceback
import time
import random
@@ -15,7 +15,8 @@ import requests
import json
# DEFAULTS
web_address = "https://10.10.10.10:3000"
db_address = "10.10.10.10:3000"
db_url = "https://{}".format(db_address)
scan_rate = 30 # seconds
save_all = "test" # use True, False, or any string
@@ -57,11 +58,11 @@ tag_store = {}
def main():
global web_address, scan_rate, save_all
global db_address, scan_rate, save_all
try:
# Get tags stored in database
get_tag_request_data = {'where': '{"tag_class": 5}'}
get_tag_request = requests.get('{}/tag'.format(web_address), params=get_tag_request_data, verify=False)
get_tag_request = requests.get('{}/tag'.format(db_url), params=get_tag_request_data, verify=False)
tags = json.loads(get_tag_request.text)
except Exception as e:
print("Error getting tags: {}".format(e))
@@ -70,7 +71,7 @@ def main():
try:
sr_req_data = 'where={"parameter": "scan_rate"}'
sr_req = requests.get('{}/config?{}'.format(web_address, sr_req_data), verify=False)
sr_req = requests.get('{}/config?{}'.format(db_url, sr_req_data), verify=False)
sr_try = json.loads(sr_req.text)
if len(sr_try) > 0:
scan_rate = int(sr_try[0]['val'])
@@ -80,7 +81,7 @@ def main():
try:
sa_req_data = {"where": {"parameter": "save_all"}}
sa_req = requests.get('{}/config'.format(web_address), params=sa_req_data, verify=False)
sa_req = requests.get('{}/config'.format(db_url), params=sa_req_data, verify=False)
sa_try = json.loads(sa_req.text)
if len(sa_try) > 0:
if sa_try[0]['val'].lower() == "true":
@@ -93,7 +94,7 @@ def main():
for t in tags:
# name, tag, db_id, data_type, change_threshold, guarantee_sec, mapFn=None, device_type='CLX', ip_address='192.168.1.10'):
tag_store[t['name']] = Sample(t['name'], t['tag'], t['id'], t['data_type'], t['change_threshold'], t['guarantee_sec'], mapFn=t['map_function'], ip_address=t['deviceID']['address'])
tag_store[t['name']] = Sample(t['name'], t['tag'], t['id'], t['data_type'], t['change_threshold'], t['guarantee_sec'], mapFn=t['map_function'], ip_address=t['deviceID']['address'], db_address=db_address)
while True:
for tag in tag_store:

23
web_db/Dockerfile.rpi Normal file
View File

@@ -0,0 +1,23 @@
FROM hypriot/rpi-node:latest
RUN apt-get -y update && apt-get install -y apt-utils dialog vim git
COPY mysql-install.sh /tmp/mysql-install.sh
COPY taglogger_db_structure.sql /tmp/taglogger_db_structure.sql
RUN chmod +x /tmp/mysql-install.sh && /tmp/mysql-install.sh
RUN mkdir /root/tag-logger
COPY www /root/tag-logger/www
COPY startup.sh /root/startup.sh
RUN chmod +x /root/startup.sh
RUN npm install -g bower pm2 sails --silent
RUN cd /root/tag-logger/www && npm install && bower install --allow-root && cd /
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
CMD '/root/startup.sh'