├── LICENSE ├── README.md ├── export.py └── load.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 18thCentury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeSys 2 | 1. 使用方法: 3 | 在CodeSys 软件内执行脚本 4 | 2. 脚本说明: 5 | 6 | Export.py: 7 | 将Codesys 内的ST语言的文本数据和Global_var,Textlist 和TaskConfiguration,library 备份到 Save_Folder 文件夹内.如果文件夹内存在.git 文件,则将文件夹更新到HEAD. 8 | 9 | Load.py: 10 | 将上述文件夹内的导入到一个新工程内. 11 | 12 | 3.问题: 13 | a. 除ST语言的文本外,其他如:Visu,imagePool,VisuConfiguration,Project Settings,Project Infomation 没有导出. 14 | b. GlobalTextList 会丢失ID Column 的数据. 15 | -------------------------------------------------------------------------------- /export.py: -------------------------------------------------------------------------------- 1 | 2 | # encoding:utf-8 3 | # We enable the new python 3 print syntax 4 | from __future__ import print_function 5 | import os 6 | import shutil 7 | import time 8 | import subprocess 9 | ''' 10 | prop_method = Guid('792f2eb6-721e-4e64-ba20-bc98351056db') 11 | tp = Guid('2db5746d-d284-4425-9f7f-2663a34b0ebc') #dut 12 | libm = Guid('adb5cb65-8e1d-4a00-b70a-375ea27582f3') 13 | method_no_ret = Guid('f89f7675-27f1-46b3-8abb-b7da8e774ffd') 14 | act = Guid('8ac092e5-3128-4e26-9e7e-11016c6684f2') 15 | fb = Guid('6f9dac99-8de1-4efc-8465-68ac443b7d08') 16 | itf = Guid('6654496c-404d-479a-aad2-8551054e5f1e') 17 | folder = Guid('738bea1e-99bb-4f04-90bb-a7a567e74e3a') 18 | gvl = Guid('ffbfa93a-b94d-45fc-a329-229860183b1d') 19 | prop = Guid('5a3b8626-d3e9-4f37-98b5-66420063d91e') 20 | textlist = Guid('2bef0454-1bd3-412a-ac2c-af0f31dbc40f') 21 | global_textlist = Guid('63784cbb-9ba0-45e6-9d69-babf3f040511') 22 | Device = Guid('225bfe47-7336-4dbc-9419-4105a7c831fa') 23 | task_config = Guid('ae1de277-a207-4a28-9efb-456c06bd52f3') 24 | method = Guid('f8a58466-d7f6-439f-bbb8-d4600e41d099') 25 | gvl_Persistent = Guid('261bd6e6-249c-4232-bb6f-84c2fbeef430') 26 | Project_Settings =Guid('8753fe6f-4a22-4320-8103-e553c4fc8e04') 27 | Plc_Logic =Guid('40b404f9-e5dc-42c6-907f-c89f4a517386') 28 | Application =Guid('639b491f-5557-464c-af91-1471bac9f549') 29 | Task =Guid('98a2708a-9b18-4f31-82ed-a1465b24fa2d') 30 | Task_pou =Guid('413e2a7d-adb1-4d2c-be29-6ae6e4fab820') 31 | Visualization =Guid('f18bec89-9fef-401d-9953-2f11739a6808') 32 | Visualization_Manager=Guid('4d3fdb8f-ab50-4c35-9d3a-d4bb9bb9a628') 33 | TargetVisualization =Guid('bc63f5fa-d286-4786-994e-7b27e4f97bd5') 34 | WebVisualization =Guid('0fdbf158-1ae0-47d9-9269-cd84be308e9d') 35 | __VisualizationStyle=Guid('8e687a04-7ca7-42d3-be06-fcbda676c5ef') 36 | ImagePool =Guid('bb0b9044-714e-4614-ad3e-33cbdf34d16b') 37 | Project_Information =Guid('085afe48-c5d8-4ea5-ab0d-b35701fa6009') 38 | SoftMotion_General_Axis_Pool=Guid('e9159722-55bc-49e5-8034-fbd278ef718f') 39 | 40 | ''' 41 | 42 | print("--- Saving files in the project: ---") 43 | 44 | # git 45 | has_repo=False 46 | 47 | save_folder=r'D:\Gitlab\codesys\Yao' 48 | 49 | if not os.path.exists(save_folder): 50 | os.makedirs(save_folder) 51 | else: 52 | #非空文件夹 删除多余 53 | a=os.listdir(save_folder) 54 | for f in a: 55 | if not f.startswith("."): #保留 svn,git 目录 56 | sub_path= os.path.join(save_folder,f) 57 | if os.path.isdir(sub_path): 58 | shutil.rmtree(sub_path) 59 | else: 60 | os.remove(sub_path) 61 | elif f==".git": 62 | has_repo=True 63 | 64 | info={} 65 | 66 | type_dist={ 67 | '792f2eb6-721e-4e64-ba20-bc98351056db':'pm', #property method 68 | '2db5746d-d284-4425-9f7f-2663a34b0ebc':'dut', #dut 69 | 'adb5cb65-8e1d-4a00-b70a-375ea27582f3':'lib', #lib manager 70 | 'f89f7675-27f1-46b3-8abb-b7da8e774ffd':'m', #method no ret 71 | '8ac092e5-3128-4e26-9e7e-11016c6684f2':'act', #action 72 | '6f9dac99-8de1-4efc-8465-68ac443b7d08':'pou', #pou 73 | '6654496c-404d-479a-aad2-8551054e5f1e':'itf', #interface 74 | '738bea1e-99bb-4f04-90bb-a7a567e74e3a':'', # folder 75 | 'ffbfa93a-b94d-45fc-a329-229860183b1d':'gvl', #global var 76 | '5a3b8626-d3e9-4f37-98b5-66420063d91e':'prop', #property 77 | '2bef0454-1bd3-412a-ac2c-af0f31dbc40f':'tl', #textlist 78 | '63784cbb-9ba0-45e6-9d69-babf3f040511':'gtl', #global textlist 79 | '225bfe47-7336-4dbc-9419-4105a7c831fa':'dev', #device 80 | 'ae1de277-a207-4a28-9efb-456c06bd52f3':'tc', #task configuration 81 | 'f8a58466-d7f6-439f-bbb8-d4600e41d099':'m', #method with ret 82 | '261bd6e6-249c-4232-bb6f-84c2fbeef430':'gvl', #gvl_Persistent 83 | '98a2708a-9b18-4f31-82ed-a1465b24fa2d':'task' 84 | }; 85 | 86 | def save(text,path,name,tp): 87 | if not tp: 88 | tp='' 89 | else: 90 | tp='.'+tp 91 | with open(os.path.join(path,name+tp),'w') as f: 92 | f.write(text.encode('utf-8')) 93 | ''' 94 | def get_mtype(a): 95 | b=a.text 96 | b=b.split("\n") 97 | for a in b: 98 | if a.find('FUNCTION_BLOCK ') >=0 : 99 | return "fb" 100 | elif a.find("FUNCTION ") >=0: 101 | return "fct" 102 | elif a.find('METHOD ')>=0 : 103 | return "m" 104 | elif a.find("INTERFACE ")>=0: 105 | return "itf" 106 | elif a.find("TYPE ")>=0: 107 | # 108 | return "tp" 109 | elif a.find("PROPERTY ")>=0 or a.find("PROPERTY\r\n")>=0: 110 | return "prop" 111 | elif a.find("PROGRAM ")>=0 or a.find("PROGRAM\r\n")>=0: 112 | return "prg" 113 | elif a.find("VAR_GLOBAL")>=0 or a.find("VAR_CONFIG") >=0: 114 | return 'gvl' 115 | return "" 116 | ''' 117 | 118 | def print_tree(treeobj, depth,path): 119 | global info 120 | #record current Path 121 | curpath=path 122 | isfolder=False 123 | 124 | t='' #text 125 | tp='' #type 126 | 127 | # get object name 128 | name = treeobj.get_name(False) 129 | id = treeobj.type.ToString() 130 | 131 | if id in type_dist: 132 | tp = type_dist[treeobj.type.ToString()] 133 | else: 134 | info[id]=name 135 | 136 | 137 | if treeobj.is_device: 138 | deviceid = treeobj.get_device_identification() 139 | t = 'type='+str(deviceid.type) +'\nid=' +str(deviceid.id) + '\nver='+ str(deviceid.version) 140 | 141 | try: 142 | if treeobj.is_folder : 143 | #system.ui.prompt('folder:'+u, PromptChoice.YesNo, PromptResult.Yes) 144 | isfolder=true 145 | pass 146 | except: 147 | pass 148 | 149 | if treeobj.has_textual_declaration : 150 | t=t+'(*#-#-#-#-#-#-#-#-#-#---Declaration---#-#-#-#-#-#-#-#-#-#-#-#-#*)\r\n' 151 | a=treeobj.textual_declaration 152 | t=t+a.text 153 | 154 | if treeobj.has_textual_implementation: 155 | t=t+'(*#-#-#-#-#-#-#-#-#-#---Implementation---#-#-#-#-#-#-#-#-#-#-#-#-#*)\r\n' 156 | a=treeobj.textual_implementation 157 | t=t+a.text 158 | 159 | ''' 160 | if treeobj.is_task_configuration: 161 | exports=[treeobj] 162 | projects.primary.export_native(exports,os.path.join(curpath,name+'.tc')) 163 | 164 | ''' 165 | 166 | if treeobj.is_task : 167 | exports=[treeobj] 168 | projects.primary.export_native(exports,os.path.join(curpath,name+'.task')) 169 | 170 | 171 | if treeobj.is_libman: 172 | exports=[treeobj] 173 | projects.primary.export_native(exports,os.path.join(curpath,name+'.lib')) 174 | 175 | if treeobj.is_textlist: 176 | treeobj.export(os.path.join(curpath,name+'.tl')) 177 | 178 | children = treeobj.get_children(False) 179 | 180 | if children or isfolder: 181 | if tp: 182 | curpath=os.path.join(curpath,name+'.'+tp) 183 | else: 184 | curpath=os.path.join(curpath,name) 185 | 186 | if not os.path.exists(curpath): 187 | os.makedirs(curpath) 188 | 189 | if t: 190 | save(t,curpath,name,tp) 191 | 192 | for child in treeobj.get_children(False): 193 | print_tree(child, depth+1,curpath) 194 | 195 | 196 | for obj in projects.primary.get_children(): 197 | print_tree(obj,0,save_folder) 198 | 199 | with open(os.path.join(save_folder,'s.txt'),'w') as f: 200 | f.write(str(info)) 201 | 202 | if has_repo: 203 | os.chdir(save_folder) 204 | si = subprocess.STARTUPINFO() 205 | si.dwFlags |= subprocess.STARTF_USESHOWWINDOW 206 | subprocess.call('"D:\\Program Files\\Git\\bin\\git.exe" add .', startupinfo=si) 207 | subprocess.call('"D:\\Program Files\\Git\\bin\\git.exe" commit -m "'+time.strftime('%Y-%m-%d %H:%M',time.localtime(time.time()))+'"', startupinfo=si) 208 | else: 209 | os.chdir(save_folder) 210 | si = subprocess.STARTUPINFO() 211 | si.dwFlags |= subprocess.STARTF_USESHOWWINDOW 212 | subprocess.call('"D:\\Program Files\\Git\\bin\\git.exe" init', startupinfo=si)#'cd '+ save_folder + " && " + 'git init') 213 | subprocess.call('"D:\\Program Files\\Git\\bin\\git.exe" add .', startupinfo=si) 214 | 215 | subprocess.call('"D:\\Program Files\\Git\\bin\\git.exe" commit -m "'+time.strftime('%Y-%m-%d %H:%M',time.localtime(time.time()))+'"', startupinfo=si) 216 | print("--- Script finished. ---") 217 | system.ui.info('save ok') -------------------------------------------------------------------------------- /load.py: -------------------------------------------------------------------------------- 1 | # encoding:utf-8 2 | from __future__ import print_function 3 | import os 4 | import shutil 5 | PROJECT = r"D:\project\CoDeSys\HMI\a.project" 6 | 7 | from_folder=r'D:\Gitlab\codesys\Yao' 8 | 9 | 10 | def check(func): 11 | def wrapper(proj,path,name): # 指定宇宙无敌参数 12 | #call func 13 | func(proj,path,name) 14 | #check 15 | name=name.split(".")[0] 16 | found = proj.find(name, False) 17 | assert(found is not None and len(found) == 1, 'No object with the name {0} found '.format(name)) 18 | #assert(found[0].is_folder, 'Found object is not a folder') 19 | item = found[0] 20 | return item 21 | return wrapper # 返回 22 | 23 | 24 | def insert_text(proj,path,name): 25 | with open(path,'r') as f: 26 | text = f.read() 27 | index = text.find('(*#-#-#-#-#-#-#-#-#-#---Implementation---#-#-#-#-#-#-#-#-#-#-#-#-#*)\r\n') 28 | 29 | if index >=0: 30 | t1= text[index:].replace('(*#-#-#-#-#-#-#-#-#-#---Implementation---#-#-#-#-#-#-#-#-#-#-#-#-#*)\r\n','') 31 | proj.textual_implementation.replace(t1) 32 | #print(name+"*** t1") 33 | try: 34 | t1= text[:index].replace('(*#-#-#-#-#-#-#-#-#-#---Declaration---#-#-#-#-#-#-#-#-#-#-#-#-#*)\r\n','') 35 | proj.textual_declaration.replace(t1) 36 | except: 37 | pass 38 | 39 | else: 40 | t1= text.replace('(*#-#-#-#-#-#-#-#-#-#---Declaration---#-#-#-#-#-#-#-#-#-#-#-#-#*)\r\n','') 41 | proj.textual_declaration.replace(t1) 42 | 43 | 44 | def export_visu(projects): 45 | proj = projects.primary 46 | device = proj.find('_3S_Testspec_Device')[0] 47 | device.export_xml(reporter, ExportFileName, recursive = True) 48 | 49 | def create_taskconfig(proj,path,name): 50 | proj = proj.create_task_configuration() 51 | return proj 52 | #projects.primary.import_native(path) 53 | 54 | 55 | def create_task(proj,path,name): 56 | name=name.split('.')[0] 57 | proj.import_native(path) 58 | 59 | 60 | def create_task_old(proj,path,name): 61 | name=name.split('.')[0] 62 | proj=proj.create_task(name) 63 | 64 | pou_names=[] 65 | interval='' 66 | priority='' 67 | kindOftask='' 68 | try: 69 | 70 | with open(path,'r') as f: 71 | t=f.readline() 72 | pou_names=t.split("=")[1].replace("\r","").replace("\n","").split(',') 73 | t=f.readline() 74 | interval=t.split("=")[1].replace("\r","").replace("\n","") 75 | t=f.readline() 76 | kindOftask=t.split("=")[1].replace("\r","").replace("\n","") 77 | except: 78 | system.ui.info('open file:\n{0} \nfailed!'.format(path)) 79 | return 80 | try: 81 | proj.interval=interval 82 | proj.priority=priority 83 | 84 | for i in pou_names: 85 | if i: 86 | try: 87 | proj.pous.add(i) 88 | except: 89 | pass 90 | 91 | proj.kind_of_task= kindOftask 92 | except: 93 | pass 94 | 95 | 96 | @check 97 | def create_dev(proj,path,name): 98 | type=0 99 | id='' 100 | ver='' 101 | 102 | with open(os.path.join(path,name),'r') as f: 103 | type=f.readline() 104 | type=int(type.split("=")[1].replace("\r\n","")) 105 | id=f.readline() 106 | id=id.split("=")[1].replace("\r","").replace("\n","") 107 | ver=f.readline() 108 | ver=ver.split("=")[1].replace("\r","").replace("\n","") 109 | 110 | #system.ui.info("create device:\ntype:{0}\nid:{1}\nver:{2}".format(type,id,ver)) 111 | 112 | devId = device_repository.create_device_identification(type, id, ver) 113 | devDesc = device_repository.get_device(devId) 114 | 115 | if devDesc is None: 116 | system.ui.info("create device ERR:\n{0}\n{1}\n{2}".format(type,id,ver)) 117 | raise Exception('No WinV3 PLC available in device repo') 118 | 119 | name=name.split('.')[0] 120 | # Add PLC to an empty project using a DeviceId instance 121 | proj.add(name, devId) 122 | 123 | 124 | 125 | def create_app(proj,path,name): 126 | pass 127 | 128 | @check 129 | def create_pou(proj,path,name): 130 | name=name.split('.')[0] 131 | proj.create_pou(name, PouType.Program) 132 | 133 | @check 134 | def create_gvl(proj,path,name): 135 | name=name.split(".")[0] 136 | proj.create_gvl(name) 137 | 138 | @check 139 | def create_property(proj,path,name): 140 | name=name.split(".")[0] 141 | proj.create_property(name) 142 | 143 | 144 | def create_method(proj,path,name): 145 | name=name.split(".")[0] 146 | proj = proj.create_method(name) 147 | return proj 148 | 149 | def create_act(proj,path,name): 150 | name=name.split(".")[0] 151 | try: 152 | proj = proj.create_action(name) 153 | except: 154 | system.ui.info("create action:{0}\nfailed".format(name)) 155 | 156 | return proj 157 | 158 | @check 159 | def create_folder(proj,path,name): 160 | proj.create_folder(name) 161 | 162 | @check 163 | def create_fb(proj,path,name): 164 | name=name.split('.')[0] 165 | proj.create_pou(name, PouType.FunctionBlock) 166 | 167 | @check 168 | def create_fuction(proj,path,name): 169 | name=name.split(".")[0] 170 | proj.create_pou(name,PouType.Function) 171 | 172 | @check 173 | def create_itf(proj,path,name): 174 | name=name.split(".")[0] 175 | proj.create_interface(name) 176 | found = proj.find(name, False) 177 | 178 | 179 | def create_dut(proj,path,name): 180 | name=name.split(".")[0] 181 | item=proj.create_dut(name,DutType.Union) 182 | 183 | return item 184 | 185 | def add_library(proj,path,name): 186 | name=name.split(".")[0] 187 | #system.ui.info("add library:{0}".format(name)) 188 | proj.import_native(path) 189 | 190 | 191 | def add_textlist(proj,path,name): 192 | name=name.split(".")[0] 193 | try: 194 | proj=proj.create_textlist(name) 195 | proj.importfile(path) 196 | 197 | except: 198 | pass 199 | 200 | @check 201 | def add_prop_method(proj,path,name): 202 | name = name.split(".")[0] 203 | 204 | 205 | 206 | def walk_folder(proj,path,tp): 207 | 208 | curpath=path 209 | 210 | for fi in os.listdir(curpath): 211 | sub_path = os.path.join(curpath,fi) 212 | is_folder = os.path.isdir(sub_path) 213 | stp= fi.split('.') 214 | 215 | fn=stp[0] 216 | try : 217 | stp=stp[1] 218 | except: 219 | stp='' 220 | 221 | if tp!='' and tp==stp and not is_folder: 222 | insert_text(proj,sub_path,fi) 223 | else: 224 | if stp=='dev': 225 | 226 | sub_proj= create_dev(proj,sub_path,fi) 227 | sub_path=os.path.join(sub_path,'Plc Logic') 228 | sub_proj=sub_proj.find('Plc Logic',False)[0] 229 | if not sub_proj : 230 | raise Exception('No PLC Logic in device') 231 | 232 | for sfi in os.listdir(sub_path): 233 | sub_sub_path= os.path.join(sub_path,sfi) 234 | sub_sub_proj=sub_proj.find(sfi,False)[0] 235 | if sub_sub_proj : 236 | walk_folder(sub_sub_proj,sub_sub_path,'app') 237 | else: 238 | sub_sub_proj = create_app(sub_sub_proj,sub_sub_path,sfi) 239 | walk_folder(sub_sub_proj,sub_sub_path,'app') 240 | 241 | elif stp=='pou': 242 | sub_proj= create_pou(proj,sub_path,fi) 243 | if not is_folder: 244 | insert_text(sub_proj,sub_path,fi) 245 | else: 246 | walk_folder(sub_proj,sub_path,'pou') 247 | 248 | elif stp=='itf': 249 | sub_proj= create_itf(proj,sub_path,fi) 250 | if not is_folder: 251 | insert_text(sub_proj,sub_path,fi) 252 | else: 253 | walk_folder(sub_proj,sub_path,'itf') 254 | 255 | elif stp=='gvl' : #gvl resistant 256 | sub_proj=create_gvl(proj,sub_path,fi) 257 | #system.ui.info("GVL:"+sub_path) 258 | insert_text(sub_proj,sub_path,fi) 259 | 260 | elif stp=='prop': 261 | sub_proj=create_property(proj,sub_path,fi) 262 | if not is_folder: 263 | insert_text(sub_proj,sub_path,fi) 264 | else: 265 | walk_folder(sub_proj,sub_path,'prop') 266 | 267 | elif stp=='pm': #property method 268 | sub_proj = add_prop_method(proj,sub_path,fi) 269 | if not is_folder: 270 | insert_text(sub_proj,sub_path,fi) 271 | else: 272 | walk_folder(sub_proj,sub_path,'pm') 273 | 274 | elif stp=='m': #method 275 | sub_proj=create_method(proj,sub_path,fi) 276 | if not is_folder: 277 | insert_text(sub_proj,sub_path,fi) 278 | else: 279 | walk_folder(sub_proj,sub_path,'m') 280 | 281 | elif stp=='act': #action 282 | sub_proj=create_act(proj,sub_path,fi) 283 | if not is_folder: 284 | insert_text(sub_proj,sub_path,fi) 285 | else: 286 | walk_folder(sub_proj,sub_path,'act') 287 | 288 | elif stp=='dut': 289 | sub_proj=create_dut(proj,sub_path,fi) 290 | insert_text(sub_proj,sub_path,fi) 291 | 292 | elif stp=='tc': #task configuration 293 | sub_proj=create_taskconfig(proj,sub_path,fi) 294 | if is_folder: 295 | walk_folder(sub_proj,sub_path,'tc') 296 | elif stp=='task': 297 | create_task(proj,sub_path,fi) 298 | 299 | elif stp=='': #folder 300 | sub_proj=create_folder(proj,sub_path,fi) 301 | walk_folder(sub_proj,sub_path,tp) 302 | 303 | elif stp=='lib': 304 | add_library(proj,sub_path,fi) 305 | 306 | elif stp=='tl': # textlist 307 | add_textlist(proj,sub_path,fi) 308 | 309 | elif stp=='gtl': #global_textlist 310 | add_textlist(proj,sub_path,fi) 311 | else: 312 | pass 313 | 314 | if projects.primary: 315 | projects.primary.close() 316 | 317 | proj = projects.create(PROJECT) 318 | 319 | walk_folder(proj,from_folder,'') 320 | 321 | 322 | system.ui.info("ok") --------------------------------------------------------------------------------