23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
24 |
24 |
25 from xml.dom import minidom |
25 from xml.dom import minidom |
26 from types import StringType, UnicodeType, TupleType |
26 from types import StringType, UnicodeType, TupleType |
27 import cPickle |
27 import cPickle |
|
28 from copy import deepcopy |
28 import os,sys,re |
29 import os,sys,re |
29 import datetime |
30 import datetime |
30 from time import localtime |
31 from time import localtime |
31 |
32 |
32 from plcopen import PLCOpenParser, LoadProject, SaveProject, QualifierList, rect |
33 from plcopen import* |
33 from plcopen.structures import * |
|
34 from graphics.GraphicCommons import * |
34 from graphics.GraphicCommons import * |
35 from PLCGenerator import * |
35 from PLCGenerator import * |
36 |
36 |
37 duration_model = re.compile("(?:([0-9]{1,2})h)?(?:([0-9]{1,2})m(?!s))?(?:([0-9]{1,2})s)?(?:([0-9]{1,3}(?:\.[0-9]*)?)ms)?") |
37 duration_model = re.compile("(?:([0-9]{1,2})h)?(?:([0-9]{1,2})m(?!s))?(?:([0-9]{1,2})s)?(?:([0-9]{1,3}(?:\.[0-9]*)?)ms)?") |
38 |
38 |
245 |
245 |
246 # Initialize the project buffer |
246 # Initialize the project buffer |
247 self.CreateProjectBuffer(False) |
247 self.CreateProjectBuffer(False) |
248 self.ProgramChunks = [] |
248 self.ProgramChunks = [] |
249 self.ProgramOffset = 0 |
249 self.ProgramOffset = 0 |
250 self.NextCompiledProject = self.Project #self.Copy(self.Project) |
250 self.NextCompiledProject = self.Copy(self.Project) |
251 self.CurrentCompiledProject = None |
251 self.CurrentCompiledProject = None |
252 self.Buffering = False |
252 self.Buffering = False |
253 |
253 |
254 # Return project data type names |
254 # Return project data type names |
255 def GetProjectDataTypeNames(self, debug = False): |
255 def GetProjectDataTypeNames(self, debug = False): |
756 errors = [] |
756 errors = [] |
757 warnings = [] |
757 warnings = [] |
758 if self.Project is not None: |
758 if self.Project is not None: |
759 try: |
759 try: |
760 self.ProgramChunks = GenerateCurrentProgram(self, self.Project, errors, warnings) |
760 self.ProgramChunks = GenerateCurrentProgram(self, self.Project, errors, warnings) |
761 self.NextCompiledProject = self.Project #self.Copy(self.Project) |
761 self.NextCompiledProject = self.Copy(self.Project) |
762 program_text = "".join([item[0] for item in self.ProgramChunks]) |
762 program_text = "".join([item[0] for item in self.ProgramChunks]) |
763 if filepath is not None: |
763 if filepath is not None: |
764 programfile = open(filepath, "w") |
764 programfile = open(filepath, "w") |
765 programfile.write(program_text.encode("utf-8")) |
765 programfile.write(program_text.encode("utf-8")) |
766 programfile.close() |
766 programfile.close() |
842 |
842 |
843 def GetPouXml(self, pou_name): |
843 def GetPouXml(self, pou_name): |
844 if self.Project is not None: |
844 if self.Project is not None: |
845 pou = self.Project.getpou(pou_name) |
845 pou = self.Project.getpou(pou_name) |
846 if pou is not None: |
846 if pou is not None: |
847 return pou.generateXMLText('pou', 0) |
847 return pou.tostring() |
848 return None |
848 return None |
849 |
849 |
850 def PastePou(self, pou_type, pou_xml): |
850 def PastePou(self, pou_type, pou_xml): |
851 ''' |
851 ''' |
852 Adds the POU defined by 'pou_xml' to the current project with type 'pou_type' |
852 Adds the POU defined by 'pou_xml' to the current project with type 'pou_type' |
853 ''' |
853 ''' |
854 try: |
854 try: |
855 tree = minidom.parseString(pou_xml.encode("utf-8")) |
855 new_pou = LoadPou(pou_xml) |
856 root = tree.childNodes[0] |
|
857 except: |
856 except: |
858 return _("Couldn't paste non-POU object.") |
857 return _("Couldn't paste non-POU object.") |
859 |
858 |
860 if root.nodeName == "pou": |
859 name = new_pou.getname() |
861 new_pou = plcopen.pous_pou() |
860 |
862 new_pou.loadXMLTree(root) |
861 idx = 0 |
863 |
862 new_name = name |
864 name = new_pou.getname() |
863 while self.Project.getpou(new_name): |
|
864 # a POU with that name already exists. |
|
865 # make a new name and test if a POU with that name exists. |
|
866 # append an incrementing numeric suffix to the POU name. |
|
867 idx += 1 |
|
868 new_name = "%s%d" % (name, idx) |
865 |
869 |
866 idx = 0 |
870 # we've found a name that does not already exist, use it |
867 new_name = name |
871 new_pou.setname(new_name) |
868 while self.Project.getpou(new_name): |
872 |
869 # a POU with that name already exists. |
873 if pou_type is not None: |
870 # make a new name and test if a POU with that name exists. |
874 orig_type = new_pou.getpouType() |
871 # append an incrementing numeric suffix to the POU name. |
875 |
872 idx += 1 |
876 # prevent violations of POU content restrictions: |
873 new_name = "%s%d" % (name, idx) |
877 # function blocks cannot be pasted as functions, |
874 |
878 # programs cannot be pasted as functions or function blocks |
875 # we've found a name that does not already exist, use it |
879 if orig_type == 'functionBlock' and pou_type == 'function' or \ |
876 new_pou.setname(new_name) |
880 orig_type == 'program' and pou_type in ['function', 'functionBlock']: |
|
881 return _('''%s "%s" can't be pasted as a %s.''') % (orig_type, name, pou_type) |
877 |
882 |
878 if pou_type is not None: |
883 new_pou.setpouType(pou_type) |
879 orig_type = new_pou.getpouType() |
884 |
880 |
885 self.Project.insertpou(-1, new_pou) |
881 # prevent violations of POU content restrictions: |
886 self.BufferProject() |
882 # function blocks cannot be pasted as functions, |
887 |
883 # programs cannot be pasted as functions or function blocks |
888 return self.ComputePouName(new_name), |
884 if orig_type == 'functionBlock' and pou_type == 'function' or \ |
|
885 orig_type == 'program' and pou_type in ['function', 'functionBlock']: |
|
886 return _('''%s "%s" can't be pasted as a %s.''') % (orig_type, name, pou_type) |
|
887 |
|
888 new_pou.setpouType(pou_type) |
|
889 |
|
890 self.Project.insertpou(-1, new_pou) |
|
891 self.BufferProject() |
|
892 |
|
893 return self.ComputePouName(new_name), |
|
894 else: |
|
895 return _("Couldn't paste non-POU object.") |
|
896 |
889 |
897 # Remove a Pou from project |
890 # Remove a Pou from project |
898 def ProjectRemovePou(self, pou_name): |
891 def ProjectRemovePou(self, pou_name): |
899 if self.Project is not None: |
892 if self.Project is not None: |
900 self.Project.removepou(pou_name) |
893 self.Project.removepou(pou_name) |
2168 return [] |
2161 return [] |
2169 |
2162 |
2170 def GetEditedElementCopy(self, tagname, debug = False): |
2163 def GetEditedElementCopy(self, tagname, debug = False): |
2171 element = self.GetEditedElement(tagname, debug) |
2164 element = self.GetEditedElement(tagname, debug) |
2172 if element is not None: |
2165 if element is not None: |
2173 name = element.__class__.__name__ |
2166 return element.tostring() |
2174 return element.generateXMLText(name.split("_")[-1], 0) |
|
2175 return "" |
2167 return "" |
2176 |
2168 |
2177 def GetEditedElementInstancesCopy(self, tagname, blocks_id = None, wires = None, debug = False): |
2169 def GetEditedElementInstancesCopy(self, tagname, blocks_id = None, wires = None, debug = False): |
2178 element = self.GetEditedElement(tagname, debug) |
2170 element = self.GetEditedElement(tagname, debug) |
2179 text = "" |
2171 text = "" |
2180 if element is not None: |
2172 if element is not None: |
2181 wires = dict([(wire, True) for wire in wires if wire[0] in blocks_id and wire[1] in blocks_id]) |
2173 wires = dict([(wire, True) |
|
2174 for wire in wires |
|
2175 if wire[0] in blocks_id and wire[1] in blocks_id]) |
|
2176 copy_body = PLCOpenParser.CreateElement("body", "pou") |
|
2177 element.append(copy_body) |
|
2178 copy_body.setcontent( |
|
2179 PLCOpenParser.CreateElement(element.getbodyType(), "body")) |
2182 for id in blocks_id: |
2180 for id in blocks_id: |
2183 instance = element.getinstance(id) |
2181 instance = element.getinstance(id) |
2184 if instance is not None: |
2182 if instance is not None: |
2185 instance_copy = self.Copy(instance) |
2183 copy_body.appendcontentInstance(self.Copy(instance)) |
|
2184 instance_copy = copy_body.getcontentInstance(id) |
2186 instance_copy.filterConnections(wires) |
2185 instance_copy.filterConnections(wires) |
2187 name = instance_copy.__class__.__name__ |
2186 text += instance_copy.tostring() |
2188 text += instance_copy.generateXMLText(name.split("_")[-1], 0) |
2187 element.remove(copy_body) |
2189 return text |
2188 return text |
2190 |
2189 |
2191 def GenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False): |
2190 def GenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False): |
2192 names = exclude.copy() |
2191 names = exclude.copy() |
2193 if tagname is not None: |
2192 if tagname is not None: |
2225 while name is None or names.get(name.upper(), False): |
2224 while name is None or names.get(name.upper(), False): |
2226 name = (format%i) |
2225 name = (format%i) |
2227 i += 1 |
2226 i += 1 |
2228 return name |
2227 return name |
2229 |
2228 |
2230 CheckPasteCompatibility = {"SFC": lambda name: True, |
|
2231 "LD": lambda name: not name.startswith("sfcObjects"), |
|
2232 "FBD": lambda name: name.startswith("fbdObjects") or name.startswith("commonObjects")} |
|
2233 |
|
2234 def PasteEditedElementInstances(self, tagname, text, new_pos, middle=False, debug=False): |
2229 def PasteEditedElementInstances(self, tagname, text, new_pos, middle=False, debug=False): |
2235 element = self.GetEditedElement(tagname, debug) |
2230 element = self.GetEditedElement(tagname, debug) |
2236 element_name, element_type = self.GetEditedElementType(tagname, debug) |
2231 element_name, element_type = self.GetEditedElementType(tagname, debug) |
2237 if element is not None: |
2232 if element is not None: |
2238 bodytype = element.getbodyType() |
2233 bodytype = element.getbodyType() |
2246 |
2241 |
2247 # Get ids already by all the instances in edited element |
2242 # Get ids already by all the instances in edited element |
2248 used_id = dict([(instance.getlocalId(), True) for instance in element.getinstances()]) |
2243 used_id = dict([(instance.getlocalId(), True) for instance in element.getinstances()]) |
2249 new_id = {} |
2244 new_id = {} |
2250 |
2245 |
2251 text = "<paste>%s</paste>"%text |
|
2252 |
|
2253 try: |
2246 try: |
2254 tree = minidom.parseString(text.encode("utf-8")) |
2247 instances = LoadPouInstances(text.encode("utf-8"), bodytype) |
|
2248 if len(instances) == 0: |
|
2249 raise ValueError |
2255 except: |
2250 except: |
2256 return _("Invalid plcopen element(s)!!!") |
2251 return _("Invalid plcopen element(s)!!!") |
2257 instances = [] |
2252 |
2258 exclude = {} |
2253 exclude = {} |
2259 for root in tree.childNodes: |
2254 for instance in instances: |
2260 if root.nodeType == tree.ELEMENT_NODE and root.nodeName == "paste": |
2255 element.addinstance(instance) |
2261 for child in root.childNodes: |
2256 instance_type = instance.getLocalTag() |
2262 if child.nodeType == tree.ELEMENT_NODE: |
2257 if instance_type == "block": |
2263 if not child.nodeName in plcopen.ElementNameToClass: |
2258 blockname = instance.getinstanceName() |
2264 return _("\"%s\" element can't be pasted here!!!")%child.nodeName |
2259 if blockname is not None: |
2265 |
2260 blocktype = instance.gettypeName() |
2266 classname = plcopen.ElementNameToClass[child.nodeName] |
2261 if element_type == "function": |
2267 if not self.CheckPasteCompatibility[bodytype](classname): |
2262 return _("FunctionBlock \"%s\" can't be pasted in a Function!!!")%blocktype |
2268 return _("\"%s\" element can't be pasted here!!!")%child.nodeName |
2263 blockname = self.GenerateNewName(tagname, |
2269 |
2264 blockname, |
2270 classobj = getattr(plcopen, classname, None) |
2265 "%s%%d"%blocktype, |
2271 if classobj is not None: |
2266 debug=debug) |
2272 instance = classobj() |
2267 exclude[blockname] = True |
2273 instance.loadXMLTree(child) |
2268 instance.setinstanceName(blockname) |
2274 if child.nodeName == "block": |
2269 self.AddEditedElementPouVar(tagname, blocktype, blockname) |
2275 blockname = instance.getinstanceName() |
2270 elif instance_type == "step": |
2276 if blockname is not None: |
2271 stepname = self.GenerateNewName(tagname, |
2277 blocktype = instance.gettypeName() |
2272 instance.getname(), |
2278 if element_type == "function": |
2273 "Step%d", |
2279 return _("FunctionBlock \"%s\" can't be pasted in a Function!!!")%blocktype |
2274 exclude=exclude, |
2280 blockname = self.GenerateNewName(tagname, |
2275 debug=debug) |
2281 blockname, |
2276 exclude[stepname] = True |
2282 "%s%%d"%blocktype, |
2277 instance.setname(stepname) |
2283 debug=debug) |
2278 localid = instance.getlocalId() |
2284 exclude[blockname] = True |
2279 if not used_id.has_key(localid): |
2285 instance.setinstanceName(blockname) |
2280 new_id[localid] = True |
2286 self.AddEditedElementPouVar(tagname, blocktype, blockname) |
|
2287 elif child.nodeName == "step": |
|
2288 stepname = self.GenerateNewName(tagname, |
|
2289 instance.getname(), |
|
2290 "Step%d", |
|
2291 exclude=exclude, |
|
2292 debug=debug) |
|
2293 exclude[stepname] = True |
|
2294 instance.setname(stepname) |
|
2295 localid = instance.getlocalId() |
|
2296 if not used_id.has_key(localid): |
|
2297 new_id[localid] = True |
|
2298 instances.append((child.nodeName, instance)) |
|
2299 |
|
2300 if len(instances) == 0: |
|
2301 return _("Invalid plcopen element(s)!!!") |
|
2302 |
2281 |
2303 idx = 1 |
2282 idx = 1 |
2304 translate_id = {} |
2283 translate_id = {} |
2305 bbox = plcopen.rect() |
2284 bbox = rect() |
2306 for name, instance in instances: |
2285 for instance in instances: |
2307 localId = instance.getlocalId() |
2286 localId = instance.getlocalId() |
2308 bbox.union(instance.getBoundingBox()) |
2287 bbox.union(instance.getBoundingBox()) |
2309 if used_id.has_key(localId): |
2288 if used_id.has_key(localId): |
2310 while used_id.has_key(idx) or new_id.has_key(idx): |
2289 while used_id.has_key(idx) or new_id.has_key(idx): |
2311 idx += 1 |
2290 idx += 1 |
2334 else: |
2313 else: |
2335 new_pos = (max(30, new_pos[0]), max(30, new_pos[1])) |
2314 new_pos = (max(30, new_pos[0]), max(30, new_pos[1])) |
2336 diff = (new_pos[0] - x, new_pos[1] - y) |
2315 diff = (new_pos[0] - x, new_pos[1] - y) |
2337 |
2316 |
2338 connections = {} |
2317 connections = {} |
2339 for name, instance in instances: |
2318 for instance in instances: |
2340 connections.update(instance.updateConnectionsId(translate_id)) |
2319 connections.update(instance.updateConnectionsId(translate_id)) |
2341 if getattr(instance, "setexecutionOrderId", None) is not None: |
2320 if getattr(instance, "setexecutionOrderId", None) is not None: |
2342 instance.setexecutionOrderId(0) |
2321 instance.setexecutionOrderId(0) |
2343 instance.translate(*diff) |
2322 instance.translate(*diff) |
2344 element.addinstance(name, instance) |
|
2345 |
2323 |
2346 return new_id, connections |
2324 return new_id, connections |
2347 |
2325 |
2348 # Return the current pou editing informations |
2326 # Return the current pou editing informations |
2349 def GetEditedElementInstanceInfos(self, tagname, id = None, exclude = [], debug = False): |
2327 def GetEditedElementInstanceInfos(self, tagname, id = None, exclude = [], debug = False): |