Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ Visualisations export as `<name>.vis.xml`. Earlier versions wrote `<name>.xml`,

If the target folder is locked and cannot be swapped, the staged files are synced into it instead and the export still succeeds. If that also fails, the error dialog reports the staging folder path; the completed export is preserved there, so nothing is lost.

## Export Lib To Files

Library projects keep their POUs, DUTs, GVLs and folders directly under the project root rather than under a Device, so `Export To Files` (which walks device entrypoints) exports nothing for them. `Export Lib To Files` walks the project root instead and writes the same on-disk format as `Export To Files`, without the device and application folder levels. Service objects (Library Manager, Project Information, Project Settings, and the Task/Symbol/Visualization/Alarm/Recipe manager objects) are skipped.

Importing a library export back into a project is not yet supported.

## Project Templates

The intention of CODESCRIBE is not to export a complete copy of the project, but to only export the implementation logic of the project, enabling collaboration via git and other source control methods. An empty, but configured, underlying project file should also be committed to the repo to manage any other configuration that CODESYS provides (e.g. project level configuration, device configuration). For example, `Example Project_template_v1.project`:
Expand Down
7 changes: 7 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
"Path": "codescribe\\src\\script_export_to_files.py",
"Params": []
},
{
"Name": "Export Lib To Files",
"Desc": "Export a library project to files on disk.",
"Icon": "codescribe\\icons\\export_to_files.ico",
"Path": "codescribe\\src\\script_lib_export_to_files.py",
"Params": []
},
{
"Name": "Import From Files",
"Desc": "Import from files on disk.",
Expand Down
87 changes: 87 additions & 0 deletions src/script_lib_export_to_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# REMEMBER: this is python 2.7
from __future__ import print_function

import os

import scriptengine # type: ignore

from entrypoint import get_src_folder
from import_export import OBJECT_TYPE_TO_EXPORT_FUNCTION, write_native
from object_type import ObjectType, get_object_type
from util import *

# Service objects that are not source. The manager objects carry GUIDs that are not in
# GUID_TYPE_MAPPING, so without this list the UNKNOWN fallback below would export them
# as native xml noise.
SKIP_NAMES = [
"Library Manager",
"Project Information",
"Project Settings",
"Task Configuration",
"Symbol Configuration",
"Visualization Manager",
"Alarm Configuration",
"Recipe Manager",
"__VisualizationStyle", # auto-created in every project; native export of it fails
]


def export_child(child_obj, parent_obj, parent_folder_path):
# Keep this consistent with export_child in script_export_to_files.py. Entry
# scripts execute at import time, so it cannot be imported from there.
child_obj_type = get_object_type(child_obj)
export_fn = OBJECT_TYPE_TO_EXPORT_FUNCTION.get(child_obj_type)
if export_fn is not None:
export_fn(child_obj, parent_obj, parent_folder_path, export_child)
return

if child_obj_type == ObjectType.UNKNOWN:
# An unmapped GUID would otherwise be dropped from the export without a trace
# (seen with diagram objects such as an LD nested inside a CFC). Fall back to a
# native export and keep walking the children.
try:
write_native(child_obj, os.path.join(parent_folder_path, child_obj.get_name() + ".xml"), recursive=False)
except Exception as fallback_error:
print(
"Warning: failed to export '"
+ child_obj.get_name()
+ "' (unmapped type GUID "
+ str(child_obj.type)
+ "): "
+ str(fallback_error)
)
for c in child_obj.get_children():
export_child(c, child_obj, parent_folder_path)


try:
print_python_version()
assert_project_open()

src_folder = get_src_folder(scriptengine.projects.primary)
print("Writing to: " + src_folder)

staging_folder = begin_export_folder(src_folder)

for child_obj in scriptengine.projects.primary.get_children():
if child_obj.get_name() in SKIP_NAMES:
continue

child_obj_type = get_object_type(child_obj)
if child_obj_type != ObjectType.UNKNOWN and child_obj_type not in OBJECT_TYPE_TO_EXPORT_FUNCTION:
continue

# Passing None as parent_obj is safe at the top level: the export functions
# that use parent_obj.get_name() (METHOD, PROPERTY, ACTION, TRANSITION) only
# apply to children of POUs, and export_pou passes the POU as the parent when
# recursing into them.
export_child(child_obj, None, staging_folder)

finalize_export_folder(src_folder, staging_folder)
except Exception as e:
print(e)
ui_error_with_traceback("Export Lib To Files failed.")
raise e

print("Done!")
ui_info("Export Lib To Files complete.\n\nWrote: " + src_folder)
Loading