Source code for peat.modules.rockwell.l5x_parse
"""
Functionality for pulling some interesting fields out of RSLogix L5X files.
Uses the ``l5x`` library for parsing, most functions return data structured
for PEAT.
Authors
- John Mulder
- Jennifer Trasti
- Christopher Goes
"""
import xml.etree.ElementTree as ET
import l5x
from peat import log
# TODO: cleanup this code to build PEAT data models directly, instead
# of returning dicts that then get used to build model objects...
[docs]
def get_ip_address(project: l5x.Project) -> str:
"""
Takes a ``l5x.Project`` object.
Returns a string of the IP communication path.
"""
comm_path = project.controller.comm_path
if comm_path:
ip_addr = comm_path.split("\\")[1]
else:
ip_addr = ""
return ip_addr
[docs]
def get_logic(project: l5x.Project) -> dict:
"""
Return a logic dict created from the project object.
"""
logic = {}
logic["id"] = project.controller.element.attrib.get("ProjectSN", "")
logic["author"] = project.doc.attrib.get("Owner", "")
logic["created_date"] = project.controller.element.attrib.get("ProjectCreationDate", "")
logic["last_modified_date"] = project.controller.element.attrib.get("LastModifiedDate", "")
logic["major_rev"] = project.controller.element.attrib.get("MajorRev", "")
logic["minor_rev"] = project.controller.element.attrib.get("MinorRev", "")
logic["software_revision"] = project.doc.attrib.get("SoftwareRevision", "")
logic["export_date"] = project.doc.attrib.get("ExportDate", "")
programs = {}
try:
for name in project.programs.names:
programs[name] = []
routines_iter = project.programs[name].element.iter("Routine")
for routine in routines_iter:
xml_str = ET.tostring(routine, encoding="utf-8", method="xml") # type: bytes
# Remove the initial xml tag that ElementTree tacks on the beginning
# start_index = xml_str.find(b"\n") # + 1
# Convert to proper string
decoded_str = xml_str.decode("utf-8").strip()
# programs[name].append(xml_str[start_index:])
programs[name].append(decoded_str)
logic["formats"] = {"routines": programs}
except Exception as ex:
log.warning(f"Failed to extract logic routines: {ex}")
return logic
[docs]
def get_modules_data(project: l5x.Project) -> list[dict]:
"""
Gets module device data from an L5X project.
"""
modules_data = []
try:
for name in project.modules.names:
# look up the module in the l5x.Project structure
module = project.modules[name]
module_dict = {}
module_dict["name"] = name
module_dict["vendor"] = module.element.attrib.get("Vendor", "")
module_dict["product_type"] = module.element.attrib.get("ProductType", "")
module_dict["product_code"] = module.element.attrib.get("ProductCode", "")
module_dict["part_number"] = module.element.attrib.get("CatalogNumber", "")
module_dict["major"] = module.element.attrib.get("Major", "")
module_dict["minor"] = module.element.attrib.get("Minor", "")
module_dict["parent_module"] = module.element.attrib.get("ParentModule", "")
ports = []
for port_num in module.ports.names:
port_obj = module.ports[port_num]
ports.append(
{
"id": port_num,
"type": port_obj.type,
"address": port_obj.address,
"upstream": port_obj.element.get("Upstream", ""),
"slot": port_obj.element.get("Slot", ""),
}
)
module_dict["ports"] = ports
modules_data.append(module_dict)
except Exception as ex:
log.warning(f"Failed to extract modules: {ex}")
return modules_data
[docs]
def get_programs(project: l5x.Project) -> dict:
"""
Takes a ``l5x.Project`` object.
Returns dict of programs where key is the program name and value is
a dict of some known interesting attributes of the program.
"""
programs = {}
for program_name in project.programs.names:
program = project.programs[program_name]
if "MainRoutineName" in program.element.attrib:
main_routine_name = program.element.attrib["MainRoutineName"]
programs[program_name] = {"MainRoutineName": main_routine_name}
return programs
[docs]
def get_comm_ports(project: l5x.Project) -> list[dict]:
comm_ports = []
for comm_ports_tag in project.controller.element.iter("CommPorts"):
for comm_port_tag in comm_ports_tag.iter("SerialPort"):
comm_port_dict = {}
comm_port_dict["baudrate"] = comm_port_tag.attrib.get("BaudRate", "")
comm_port_dict["parity"] = comm_port_tag.attrib.get("Parity", "")
comm_port_dict["stop_bits"] = comm_port_tag.attrib.get("StopBits", "")
comm_port_dict["id"] = comm_port_tag.attrib.get("Channel", "")
comm_port_dict["data_bits"] = comm_port_tag.attrib.get("DataBits", "")
comm_port_dict["application"] = comm_port_tag.attrib.get("ComDriverId", "")
comm_ports.append(comm_port_dict)
return comm_ports
[docs]
def get_project_description(project: l5x.Project) -> str:
ele = project.controller.element.find("Description")
if ele is None or not len(ele):
return ""
return next(iter(ele)).text.strip()