def tag_list_equal(): with LogixDriver('10.61.50.4/10') as plc: tag_list = plc.get_tag_list() if {tag['tag_name']: tag for tag in tag_list} == plc.tags: print('They are the same!') with LogixDriver('10.61.50.4/10', init_tags=False) as plc2: plc2.get_tag_list() if plc.tags == plc2.tags: print('Calling get_tag_list() does the same thing.') else: print('Calling get_tag_list() does NOT do the same.')
def read(): try: plc_path = request.args.get('plc') tag = request.args.get('tag') if plc_path in plc_connections.keys(): plc = plc_connections[plc_path] else: plc = LogixDriver(plc_path) plc_connections[plc_path] = plc val = plc.read(tag).value return jsonify({'status': 'success', 'val': val}) except: import traceback return jsonify({'status': 'error', 'val': traceback.format_exc()})
def read_plc(ip_address, tag_name): try: with LogixDriver(ip_address) as plc: return plc.read(tag_name) except: #log error return ""
def read_all_plc(ip_address): try: with LogixDriver(ip_address) as plc: return plc.tags except: # Log error# return ""
def array_read(): plc_path = request.args.get('plc') data = json.loads(request.data) if plc_path in plc_connections.keys(): plc = plc_connections[plc_path] else: plc = LogixDriver(plc_path) plc_connections[plc_path] = plc tag = request.args.get('tag') tag_type = plc.tags[tag.split('[')[0]]['data_type_name'] value = types[tag_type](request.args.get('value')) error = plc.write((tag, value)).error if error is not None: import traceback raise Exception(traceback.format_exc()) return jsonify({'status': 'success'})
def test_connect_init_tags(): with LogixDriver(PATH) as plc: assert len(plc.tags) > 0 assert isinstance(plc.tags, dict) assert 'Pycomm3_Testing' in plc.info['programs'] assert plc.tags == {tag['tag_name']: tag for tag in plc.get_tag_list()}
def info_plc(ip_address): try: with LogixDriver(ip_address) as plc: return plc.info except: # Log error return ""
def test_connect_init_info(): with LogixDriver(PATH, init_info=True, init_tags=False) as plc: assert plc.info['name'] == plc.name assert plc.info['vendor'] == 'Rockwell Automation/Allen-Bradley' assert plc.info['keyswitch'] == 'REMOTE RUN' assert 'modules' not in plc.info assert 'tasks' not in plc.info assert 'programs' not in plc.info
def connectToPlc(ipAddress): try: myPlc = LogixDriver(ipAddress) except: log.exception('PLC connection error.') else: log.info("Connected to PLC.") return myPlc
def connect(self) -> bool: """ Connect driver. : returns: True if connection stablished False if not """ # Create connection try: self._connection = LogixDriver(self.ip) self._connection.open() except Exception as e: self.sendDebugInfo(f"Connection with {self.ip} cannot be stablished.") return False # Check connection status. if self._connection.connected: return True else: self.sendDebugInfo(f"Driver not connected.") return False
def write_plc(ip_address, tag_name, tag_value): try: with LogixDriver(ip_address) as plc: result = plc.write(tag_name, tag_value) return result except: # log error return ""
def find_pids(): with LogixDriver('10.61.50.4/10') as plc: # PIDs are structures, the data_type attribute will be a dict with data type definition. # For tag types of 'atomic' the data type will a string, we need to skip those first. # Then we can just look for tags whose data type name matches 'PID' pid_tags = [ tag for tag, _def in plc.tags.items() if _def['data_type_name'] == 'PID' ] print(pid_tags)
def discoverPLCs(): # Function to discover any PLC on the network ips, slots, progName = [], [], [] try: discovery = CIPDriver.discover() # Return list of all CIP devices on the network for device in discovery: # Go through discovery list and append any PLC#'s to a list if device['product_type'] == "Programmable Logic Controller": ips.append(device['ip_address']) if len(ips) > 0: # Print the discovered PLC's, if there are any ips.sort() # Sort the IP address in ascending order table = Table(box=box.ROUNDED) # Create table table.add_column('#', justify='center') # Add column table.add_column('Device Type', justify='center') # Add column table.add_column('IP Address', justify='center') # Add column table.add_column('Slot #', justify='center') # Add column table.add_column('Program Name', justify='center') # Add column for i, ip in enumerate(ips): # Add row for each PLC discovered slots.append('Unknown') progName.append('Unknown') for slot in range(1, 18): try: plc = LogixDriver(f'{plc}/{str(slot)}', init_tags=False) if plc.open(): slots[i] = slot progName[i] = plc.get_plc_name() plc.close() break except: continue table.add_row(str(i+1), 'Programmable Logic Controller', ip, str(slots[i]), progName[i]) print(table) else: print("No PLC's discovered on the network") except Exception: traceback.print_exc()
class LogixDriverCOMServer: _reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER _public_methods_ = ['open', 'close', 'read_tag', 'write', ] #'get_plc_info', 'get_plc_name', 'get_tag_list'] _readonlu_attrs_ = [] _public_attrs_ = [] # _readonly_attrs_ = ['tags', 'info', 'name'] # _public_attrs_ = ['path', 'large_packets', 'init_tags', 'init_program_tags' ] + _readonly_attrs_ _reg_clsid_ = CLSID _reg_desc_ = 'Pycomm3 - Python Ethernet/IP ControlLogix Library COM Server' _reg_progid_ = 'Pycomm3.COMServer' def __init__(self): self.plc: LogixDriver = None def open(self, path, init_tags=True, init_program_tags=False, init_info=True): self.plc = LogixDriver(path, init_tags=init_tags, init_program_tags=init_program_tags, init_info=init_info) self.plc.open() def close(self): self.plc.close() def read(self, tag): result = self.plc.read(tag) return result.value if result else None def write(self, *tag_values): return self.plc.write(*tag_values)
def write_pf525_parameter(): drive_path = '10.10.10.100/bp/1/enet/192.168.1.55' with LogixDriver(drive_path, init_tags=False, init_info=False) as drive: drive.generic_message( service=CommonService.set_attribute_single, class_code=b'\x93', instance=b'\x29', # (hex) Parameter 41 = Accel Time attribute=b'\x09', request_data=Pack.int(500), # 5s = seconds * 100 connected=False, unconnected_send=True, route_path=True, name='pf525_param')
def poll_plc(self): while self.run_poll: while self.ui.pushButton_poll.isChecked(): if int(self.ui.label_countdown.text()) > self.ui.spinBox_poll.value(): self.ui.label_countdown.setText(str(self.ui.spinBox_poll.value())) if int(self.ui.label_countdown.text()) < 1: self.ui.label_countdown.setText(str(self.ui.spinBox_poll.value())) try: with LogixDriver(self.ui.lineEdit_ip.text()) as plc: curr_datetime = datetime.now() date_stamp = curr_datetime.strftime("%Y-%m-%d %H:%M:%S") row = self.ui.tableWidget.rowCount() if row >= self.ui.spinBox_chart_points.value(): self.ui.tableWidget.removeRow(row-1) self.ui.tableWidget.insertRow(0) self.ui.tableWidget.setItem(0, 0, QTableWidgetItem(date_stamp)) with open(self.ui.label_log_file.text(), "a", newline="") as outfile: writer = csv.writer(outfile) for i in self.settings["tags"]: fields = [] fields.append(date_stamp) val = round(plc.read(i).value, 2) fields.append(i) fields.append(val) writer.writerow(fields) if i == self.ui.comboBox_chart_tag.currentText(): self.x.append(curr_datetime) self.y.append(val) if len(self.x) > self.ui.spinBox_chart_points.value(): self.x.pop(0) if len(self.y) > self.ui.spinBox_chart_points.value(): self.y.pop(0) self.refresh_chart() self.ui.tableWidget.setItem(0, self.settings["tags"].index(i)+1, QTableWidgetItem("%.2f" % val)) except Exception as error: self.ui.label_error.setStyleSheet("background-color: rgba(255, 0, 0, 0.5);") self.ui.label_error.setText(str(error)) time.sleep(10) else: self.ui.label_error.setStyleSheet("background-color: rgba(35, 199, 35, 1);") self.ui.label_error.setText(" ") time.sleep(1) self.ui.label_countdown.setText(str(int(self.ui.label_countdown.text()) - 1))
def write_structure(): with LogixDriver('10.61.50.4/10') as plc: recipe_data = [ True, [10, 11, 4, 20, 6, 20, 6, 30, 5, 0], [100, 500, 85, 5, 15, 10.5, 20, 0, 0, 0], [ 'Set Water Temperature', 'Heated Water', 'Start Agitator', 'Hand Add - Flavor Part 1', 'Timed Mix', 'Hand Add - Flavor Part 2', 'Timed Mix', 'Transfer to Storage Tank', 'Disable Agitator', '' ], ['°F', 'lbs', '%', 'gal', 'min', 'lbs', 'min', '', '', ''], 'Our Fictional Recipe' ] plc.write(('Example_Recipe', recipe_data))
def read_pf525_parameter(): drive_path = '10.10.10.100/bp/1/enet/192.168.1.55' with LogixDriver(drive_path, init_tags=False, init_info=False) as drive: param = drive.generic_message( service=CommonService.get_attribute_single, class_code=b'\x93', instance=b'\x29', # (hex) Parameter 41 = Accel Time attribute=b'\x09', connected=False, unconnected_send=True, route_path=True, data_format=[ ('AccelTime', 'INT'), ], name='pf525_param') print(param)
def poll_plc(self): while True: while self.ui.pushButton_poll.isChecked(): self.find_checked() try: fields = [] with LogixDriver(self.ui.comboBox_ip.currentText()) as plc: date_stamp = datetime.now().strftime( "%Y-%m-%d %H:%M:%S") fields.append(date_stamp) row = self.ui.tableWidget.rowCount() - 1 if row >= 20: self.ui.tableWidget.removeRow(row - 1) self.ui.tableWidget.insertRow(0) self.ui.tableWidget.setItem( 0, 0, QTableWidgetItem(date_stamp)) with open("test.csv", "a", newline="") as outfile: writer = csv.writer(outfile) for i in self.poll_tags: val = round( plc.read(self.poll_tags[i] + ".EuOut").value, 2) fields.append(self.poll_tags[i]) fields.append(val) writer.writerow(fields) self.ui.tableWidget.setItem( 0, i, QTableWidgetItem("%.2f" % val)) #for i in self.poll_tags: # fields.append(plc.read(self.poll_tags[i] + ".EuOut").value) except Exception as error: self.ui.label_error.setStyleSheet( "background-color: rgba(255, 0, 0, 0.5);") self.ui.label_error.setText(str(error)) time.sleep(10) else: self.ui.label_error.setStyleSheet( "background-color: rgba(35, 199, 35, 1);") self.ui.label_error.setText(" ") time.sleep(self.ui.spinBox_poll.value())
def get_plc_tags(self): self.tags.clear() self.ui.treeWidget with LogixDriver(self.ui.comboBox_ip.currentText()) as plc: self.tags = plc.get_tag_list() for i in self.tags: parent = QTreeWidgetItem(self.ui.treeWidget) parent.setText(0, "{}".format(i['tag_name'])) if type(i['data_type']) == str: parent.setText(1, i['data_type']) parent.setFlags(parent.flags() | Qt.ItemIsUserCheckable) parent.setCheckState(0, Qt.Unchecked) #for x in range(5): # child = QTreeWidgetItem(parent) # child.setFlags(child.flags() | Qt.ItemIsUserCheckable) # child.setText(0, "Child {}".format(x)) # child.setCheckState(0, Qt.Unchecked) self.ui.treeWidget.sortByColumn(0) self.ui.treeWidget.resizeColumnToContents(0)
def read_single(): with LogixDriver('10.61.50.4/10') as plc: return plc.read('DINT1')
def read_udt(): with LogixDriver('10.61.50.4/10') as plc: return plc.read('SimpleUDT1_1')
def read_strings(): with LogixDriver('10.61.50.4/10') as plc: return plc.read('STRING1', 'STRING_ARY1[2]{2}')
def read_array_slice(): with LogixDriver('10.61.50.4/10') as plc: return plc.read('DINT_ARY1[50]{5}')
#!/usr/bin/env python3 """ Python script to dump tags from AB-Logix PLC to JSON file Usage: ./fetch-tags.py <IP[PATH]> > tags.json """ import argparse try: import rapidjson as json except: import json from pycomm3 import LogixDriver ap = argparse.ArgumentParser() ap.add_argument("source", metavar="PLC", help="PLC host/ip and path") a = ap.parse_args() with LogixDriver(a.source) as plc: print(json.dumps(plc.get_tag_list()))
def write_multiple(): with LogixDriver('10.61.50.4/10') as plc: return plc.write(('REAL2', 25.2), ('STRING3', 'A test for writing to a string.'))
def write_single(): with LogixDriver('10.61.50.4/10') as plc: return plc.write(('DINT2', 100_000_000))
def read_timer(): with LogixDriver('10.61.50.4/10') as plc: return plc.read('TIMER1')
def read_multiple(): tags = ['DINT1', 'SINT1', 'REAL1'] with LogixDriver('10.61.50.4/10') as plc: return plc.read(*tags)
import os from pycomm3 import LogixDriver ip_cl = os.environ["controllogix"] try: print('\033[46;1mControlLogix 5581E PLC - ' + ip_cl + ':\033[0m') with LogixDriver(ip_cl) as plc: print(plc.get_tag_list()) except: print('\033[41;1mSomething went wrong while connecting to ' + ip_cl + '\033[0m')