63 from runtime.typemapping import DebugTypesSize, UnpackDebugBuffer |
63 from runtime.typemapping import DebugTypesSize, UnpackDebugBuffer |
64 from ConfigTreeNode import ConfigTreeNode, XSDSchemaErrorMessage |
64 from ConfigTreeNode import ConfigTreeNode, XSDSchemaErrorMessage |
65 |
65 |
66 base_folder = paths.AbsParentDir(__file__) |
66 base_folder = paths.AbsParentDir(__file__) |
67 |
67 |
68 MATIEC_ERROR_MODEL = re.compile(".*\.st:(\d+)-(\d+)\.\.(\d+)-(\d+): (?:error)|(?:warning) : (.*)$") |
68 MATIEC_ERROR_MODEL = re.compile( |
|
69 ".*\.st:(\d+)-(\d+)\.\.(\d+)-(\d+): (?:error)|(?:warning) : (.*)$") |
69 |
70 |
70 |
71 |
71 def ExtractChildrenTypesFromCatalog(catalog): |
72 def ExtractChildrenTypesFromCatalog(catalog): |
72 children_types = [] |
73 children_types = [] |
73 for n, d, _h, c in catalog: |
74 for n, d, _h, c in catalog: |
144 buildcmd = "\"%s\" -h" % (self.getCmd()) |
148 buildcmd = "\"%s\" -h" % (self.getCmd()) |
145 options = ["-f", "-l", "-p"] |
149 options = ["-f", "-l", "-p"] |
146 |
150 |
147 buildopt = "" |
151 buildopt = "" |
148 try: |
152 try: |
149 # Invoke compiler. Output files are listed to stdout, errors to stderr |
153 # Invoke compiler. |
|
154 # Output files are listed to stdout, errors to stderr |
150 _status, result, _err_result = ProcessLogger(None, buildcmd, |
155 _status, result, _err_result = ProcessLogger(None, buildcmd, |
151 no_stdout=True, |
156 no_stdout=True, |
152 no_stderr=True).spin() |
157 no_stderr=True).spin() |
153 except Exception: |
158 except Exception: |
154 return buildopt |
159 return buildopt |
184 <xsd:complexType> |
189 <xsd:complexType> |
185 <xsd:sequence> |
190 <xsd:sequence> |
186 <xsd:element name="TargetType"> |
191 <xsd:element name="TargetType"> |
187 <xsd:complexType> |
192 <xsd:complexType> |
188 <xsd:choice minOccurs="0"> |
193 <xsd:choice minOccurs="0"> |
189 """+targets.GetTargetChoices()+""" |
194 """ + targets.GetTargetChoices() + """ |
190 </xsd:choice> |
195 </xsd:choice> |
191 </xsd:complexType> |
196 </xsd:complexType> |
192 </xsd:element>"""+((""" |
197 </xsd:element>""" + ((""" |
193 <xsd:element name="Libraries" minOccurs="0"> |
198 <xsd:element name="Libraries" minOccurs="0"> |
194 <xsd:complexType> |
199 <xsd:complexType> |
195 """+"\n".join(['<xsd:attribute name=' + |
200 """ + "\n".join(['<xsd:attribute name=' + |
196 '"Enable_' + libname + '_Library" ' + |
201 '"Enable_' + libname + '_Library" ' + |
197 'type="xsd:boolean" use="optional" default="' + |
202 'type="xsd:boolean" use="optional" default="' + |
198 ('true' if default else 'false') + '"/>' |
203 ('true' if default else 'false') + '"/>' |
199 for libname, _lib, default in features.libraries])+""" |
204 for libname, _lib, default in features.libraries]) + """ |
200 </xsd:complexType> |
205 </xsd:complexType> |
201 </xsd:element>""") if len(features.libraries) > 0 else '') + """ |
206 </xsd:element>""") if len(features.libraries) > 0 else '') + """ |
202 </xsd:sequence> |
207 </xsd:sequence> |
203 <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/> |
208 <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/> |
204 <xsd:attribute name="Disable_Extensions" type="xsd:boolean" use="optional" default="false"/> |
209 <xsd:attribute name="Disable_Extensions" type="xsd:boolean" use="optional" default="false"/> |
205 </xsd:complexType> |
210 </xsd:complexType> |
206 </xsd:element> |
211 </xsd:element> |
207 </xsd:schema> |
212 </xsd:schema> |
208 """ |
213 """ |
209 print XSD |
|
210 return XSD |
214 return XSD |
211 |
215 |
212 |
216 |
213 class ProjectController(ConfigTreeNode, PLCControler): |
217 class ProjectController(ConfigTreeNode, PLCControler): |
|
218 |
214 """ |
219 """ |
215 This class define Root object of the confnode tree. |
220 This class define Root object of the confnode tree. |
216 It is responsible of : |
221 It is responsible of : |
217 - Managing project directory |
222 - Managing project directory |
218 - Building project |
223 - Building project |
219 - Handling PLCOpenEditor controler and view |
224 - Handling PLCOpenEditor controler and view |
220 - Loading user confnodes and instanciante them as children |
225 - Loading user confnodes and instanciante them as children |
221 - ... |
226 - ... |
222 |
227 |
223 """ |
228 """ |
224 # For root object, available Children Types are modules of the confnode packages. |
229 # For root object, available Children Types are modules of the confnode |
|
230 # packages. |
225 CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog) |
231 CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog) |
226 XSD = GetProjectControllerXSD() |
232 XSD = GetProjectControllerXSD() |
227 EditorType = ProjectNodeEditor |
233 EditorType = ProjectNodeEditor |
228 iec2c_cfg = None |
234 iec2c_cfg = None |
229 |
235 |
270 self.KillDebugThread() |
276 self.KillDebugThread() |
271 |
277 |
272 def LoadLibraries(self): |
278 def LoadLibraries(self): |
273 self.Libraries = [] |
279 self.Libraries = [] |
274 TypeStack = [] |
280 TypeStack = [] |
275 for libname, clsname, default in features.libraries: |
281 for libname, clsname, _default in features.libraries: |
276 if self.BeremizRoot.Libraries is not None and getattr(self.BeremizRoot.Libraries, "Enable_"+libname+"_Library"): |
282 if self.BeremizRoot.Libraries is not None and \ |
|
283 getattr(self.BeremizRoot.Libraries, |
|
284 "Enable_" + libname + "_Library"): |
277 Lib = GetClassImporter(clsname)()(self, libname, TypeStack) |
285 Lib = GetClassImporter(clsname)()(self, libname, TypeStack) |
278 TypeStack.append(Lib.GetTypes()) |
286 TypeStack.append(Lib.GetTypes()) |
279 self.Libraries.append(Lib) |
287 self.Libraries.append(Lib) |
280 |
288 |
281 def SetAppFrame(self, frame, logger): |
289 def SetAppFrame(self, frame, logger): |
361 if target.getcontent() is None: |
369 if target.getcontent() is None: |
362 temp_root = self.Parser.CreateRoot() |
370 temp_root = self.Parser.CreateRoot() |
363 target = self.Parser.CreateElement("TargetType", "BeremizRoot") |
371 target = self.Parser.CreateElement("TargetType", "BeremizRoot") |
364 temp_root.setTargetType(target) |
372 temp_root.setTargetType(target) |
365 target_name = self.GetDefaultTargetName() |
373 target_name = self.GetDefaultTargetName() |
366 target.setcontent(self.Parser.CreateElement(target_name, "TargetType")) |
374 target.setcontent( |
|
375 self.Parser.CreateElement(target_name, "TargetType")) |
367 return target |
376 return target |
368 |
377 |
369 def GetParamsAttributes(self, path=None): |
378 def GetParamsAttributes(self, path=None): |
370 params = ConfigTreeNode.GetParamsAttributes(self, path) |
379 params = ConfigTreeNode.GetParamsAttributes(self, path) |
371 if params[0]["name"] == "BeremizRoot": |
380 if params[0]["name"] == "BeremizRoot": |
372 for child in params[0]["children"]: |
381 for child in params[0]["children"]: |
373 if child["name"] == "TargetType" and child["value"] == '': |
382 if child["name"] == "TargetType" and child["value"] == '': |
374 child.update(self.GetTarget().getElementInfos("TargetType")) |
383 child.update( |
|
384 self.GetTarget().getElementInfos("TargetType")) |
375 return params |
385 return params |
376 |
386 |
377 def SetParamsAttribute(self, path, value): |
387 def SetParamsAttribute(self, path, value): |
378 if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None: |
388 if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None: |
379 self.BeremizRoot.setTargetType(self.GetTarget()) |
389 self.BeremizRoot.setTargetType(self.GetTarget()) |
403 return False |
413 return False |
404 |
414 |
405 def _getProjectFilesPath(self, project_path=None): |
415 def _getProjectFilesPath(self, project_path=None): |
406 if project_path is not None: |
416 if project_path is not None: |
407 return os.path.join(project_path, "project_files") |
417 return os.path.join(project_path, "project_files") |
408 projectfiles_path = os.path.join(self.GetProjectPath(), "project_files") |
418 projectfiles_path = os.path.join( |
|
419 self.GetProjectPath(), "project_files") |
409 if not os.path.exists(projectfiles_path): |
420 if not os.path.exists(projectfiles_path): |
410 os.mkdir(projectfiles_path) |
421 os.mkdir(projectfiles_path) |
411 return projectfiles_path |
422 return projectfiles_path |
412 |
423 |
413 def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"): |
424 def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"): |
414 self.ProjectAddConfiguration(config_name) |
425 self.ProjectAddConfiguration(config_name) |
415 self.ProjectAddConfigurationResource(config_name, res_name) |
426 self.ProjectAddConfigurationResource(config_name, res_name) |
416 |
427 |
417 def SetProjectDefaultConfiguration(self): |
428 def SetProjectDefaultConfiguration(self): |
418 # Sets default task and instance for new project |
429 # Sets default task and instance for new project |
419 config = self.Project.getconfiguration(self.GetProjectMainConfigurationName()) |
430 config = self.Project.getconfiguration( |
|
431 self.GetProjectMainConfigurationName()) |
420 resource = config.getresource()[0].getname() |
432 resource = config.getresource()[0].getname() |
421 config = config.getname() |
433 config = config.getname() |
422 resource_tagname = ComputeConfigurationResourceName(config, resource) |
434 resource_tagname = ComputeConfigurationResourceName(config, resource) |
423 def_task = [ |
435 def_task = [ |
424 {'Priority': '0', 'Single': '', 'Interval': 'T#20ms', 'Name': 'task0', 'Triggering': 'Cyclic'}] |
436 {'Priority': '0', 'Single': '', 'Interval': 'T#20ms', 'Name': 'task0', 'Triggering': 'Cyclic'}] |
471 # Load PLCOpen file |
483 # Load PLCOpen file |
472 error = self.OpenXMLFile(plc_file) |
484 error = self.OpenXMLFile(plc_file) |
473 if error is not None: |
485 if error is not None: |
474 if self.Project is not None: |
486 if self.Project is not None: |
475 (fname_err, lnum, src) = (("PLC",) + error) |
487 (fname_err, lnum, src) = (("PLC",) + error) |
476 self.logger.write_warning(XSDSchemaErrorMessage.format(a1=fname_err, a2=lnum, a3=src)) |
488 self.logger.write_warning( |
|
489 XSDSchemaErrorMessage.format(a1=fname_err, a2=lnum, a3=src)) |
477 else: |
490 else: |
478 return error, False |
491 return error, False |
479 if len(self.GetProjectConfigNames()) == 0: |
492 if len(self.GetProjectConfigNames()) == 0: |
480 self.AddProjectDefaultConfiguration() |
493 self.AddProjectDefaultConfiguration() |
481 # Change XSD into class members |
494 # Change XSD into class members |
528 self.ResetAppFrame(None) |
541 self.ResetAppFrame(None) |
529 |
542 |
530 def CheckNewProjectPath(self, old_project_path, new_project_path): |
543 def CheckNewProjectPath(self, old_project_path, new_project_path): |
531 if old_project_path == new_project_path: |
544 if old_project_path == new_project_path: |
532 message = (_("Save path is the same as path of a project! \n")) |
545 message = (_("Save path is the same as path of a project! \n")) |
533 dialog = wx.MessageDialog(self.AppFrame, message, _("Error"), wx.OK | wx.ICON_ERROR) |
546 dialog = wx.MessageDialog( |
|
547 self.AppFrame, message, _("Error"), wx.OK | wx.ICON_ERROR) |
534 dialog.ShowModal() |
548 dialog.ShowModal() |
535 return False |
549 return False |
536 else: |
550 else: |
537 plc_file = os.path.join(new_project_path, "plc.xml") |
551 plc_file = os.path.join(new_project_path, "plc.xml") |
538 if os.path.isfile(plc_file): |
552 if os.path.isfile(plc_file): |
539 message = (_("Selected directory already contains another project. Overwrite? \n")) |
553 message = ( |
540 dialog = wx.MessageDialog(self.AppFrame, message, _("Error"), wx.YES_NO | wx.ICON_ERROR) |
554 _("Selected directory already contains another project. Overwrite? \n")) |
|
555 dialog = wx.MessageDialog( |
|
556 self.AppFrame, message, _("Error"), wx.YES_NO | wx.ICON_ERROR) |
541 answer = dialog.ShowModal() |
557 answer = dialog.ShowModal() |
542 return answer == wx.ID_YES |
558 return answer == wx.ID_YES |
543 return True |
559 return True |
544 |
560 |
545 def SaveProject(self, from_project_path=None): |
561 def SaveProject(self, from_project_path=None): |
546 if self.CheckProjectPathPerm(False): |
562 if self.CheckProjectPathPerm(False): |
547 if from_project_path is not None: |
563 if from_project_path is not None: |
548 old_projectfiles_path = self._getProjectFilesPath(from_project_path) |
564 old_projectfiles_path = self._getProjectFilesPath( |
|
565 from_project_path) |
549 if os.path.isdir(old_projectfiles_path): |
566 if os.path.isdir(old_projectfiles_path): |
550 shutil.copytree(old_projectfiles_path, |
567 shutil.copytree(old_projectfiles_path, |
551 self._getProjectFilesPath(self.ProjectPath)) |
568 self._getProjectFilesPath(self.ProjectPath)) |
552 self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) |
569 self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) |
553 result = self.CTNRequestSave(from_project_path) |
570 result = self.CTNRequestSave(from_project_path) |
558 # Ask user to choose a path with write permissions |
575 # Ask user to choose a path with write permissions |
559 if wx.Platform == '__WXMSW__': |
576 if wx.Platform == '__WXMSW__': |
560 path = os.getenv("USERPROFILE") |
577 path = os.getenv("USERPROFILE") |
561 else: |
578 else: |
562 path = os.getenv("HOME") |
579 path = os.getenv("HOME") |
563 dirdialog = wx.DirDialog(self.AppFrame, _("Choose a directory to save project"), path, wx.DD_NEW_DIR_BUTTON) |
580 dirdialog = wx.DirDialog( |
|
581 self.AppFrame, _("Choose a directory to save project"), path, wx.DD_NEW_DIR_BUTTON) |
564 answer = dirdialog.ShowModal() |
582 answer = dirdialog.ShowModal() |
565 dirdialog.Destroy() |
583 dirdialog.Destroy() |
566 if answer == wx.ID_OK: |
584 if answer == wx.ID_OK: |
567 newprojectpath = dirdialog.GetPath() |
585 newprojectpath = dirdialog.GetPath() |
568 if os.path.isdir(newprojectpath): |
586 if os.path.isdir(newprojectpath): |
582 |
600 |
583 def GetLibrariesCCode(self, buildpath): |
601 def GetLibrariesCCode(self, buildpath): |
584 if len(self.Libraries) == 0: |
602 if len(self.Libraries) == 0: |
585 return [], [], () |
603 return [], [], () |
586 self.GetIECProgramsAndVariables() |
604 self.GetIECProgramsAndVariables() |
587 LibIECCflags = '"-I%s" -Wno-unused-function' % os.path.abspath(self.GetIECLibPath()) |
605 LibIECCflags = '"-I%s" -Wno-unused-function' % os.path.abspath( |
|
606 self.GetIECLibPath()) |
588 LocatedCCodeAndFlags = [] |
607 LocatedCCodeAndFlags = [] |
589 Extras = [] |
608 Extras = [] |
590 for lib in self.Libraries: |
609 for lib in self.Libraries: |
591 res = lib.Generate_C(buildpath, self._VariablesList, LibIECCflags) |
610 res = lib.Generate_C(buildpath, self._VariablesList, LibIECCflags) |
592 LocatedCCodeAndFlags.append(res[:2]) |
611 LocatedCCodeAndFlags.append(res[:2]) |
593 if len(res) > 2: |
612 if len(res) > 2: |
594 Extras.extend(res[2:]) |
613 Extras.extend(res[2:]) |
595 return map(list, zip(*LocatedCCodeAndFlags))+[tuple(Extras)] |
614 return map(list, zip(*LocatedCCodeAndFlags)) + [tuple(Extras)] |
596 |
615 |
597 # Update PLCOpenEditor ConfNode Block types from loaded confnodes |
616 # Update PLCOpenEditor ConfNode Block types from loaded confnodes |
598 def RefreshConfNodesBlockLists(self): |
617 def RefreshConfNodesBlockLists(self): |
599 if getattr(self, "Children", None) is not None: |
618 if getattr(self, "Children", None) is not None: |
600 self.ClearConfNodeTypes() |
619 self.ClearConfNodeTypes() |
656 # Create a build path in project folder if user has permissions |
675 # Create a build path in project folder if user has permissions |
657 if CheckPathPerm(self.ProjectPath): |
676 if CheckPathPerm(self.ProjectPath): |
658 self.DefaultBuildPath = os.path.join(self.ProjectPath, "build") |
677 self.DefaultBuildPath = os.path.join(self.ProjectPath, "build") |
659 # Create a build path in temp folder |
678 # Create a build path in temp folder |
660 else: |
679 else: |
661 self.DefaultBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build") |
680 self.DefaultBuildPath = os.path.join( |
|
681 tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build") |
662 |
682 |
663 if not os.path.exists(self.DefaultBuildPath): |
683 if not os.path.exists(self.DefaultBuildPath): |
664 os.makedirs(self.DefaultBuildPath) |
684 os.makedirs(self.DefaultBuildPath) |
665 return self.DefaultBuildPath |
685 return self.DefaultBuildPath |
666 |
686 |
681 |
701 |
682 def GetLocations(self): |
702 def GetLocations(self): |
683 locations = [] |
703 locations = [] |
684 filepath = os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h") |
704 filepath = os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h") |
685 if os.path.isfile(filepath): |
705 if os.path.isfile(filepath): |
686 # IEC2C compiler generate a list of located variables : LOCATED_VARIABLES.h |
706 # IEC2C compiler generate a list of located variables : |
687 location_file = open(os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h")) |
707 # LOCATED_VARIABLES.h |
|
708 location_file = open( |
|
709 os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h")) |
688 # each line of LOCATED_VARIABLES.h declares a located variable |
710 # each line of LOCATED_VARIABLES.h declares a located variable |
689 lines = [line.strip() for line in location_file.readlines()] |
711 lines = [line.strip() for line in location_file.readlines()] |
690 # This regular expression parses the lines genereated by IEC2C |
712 # This regular expression parses the lines genereated by IEC2C |
691 LOCATED_MODEL = re.compile("__LOCATED_VAR\((?P<IEC_TYPE>[A-Z]*),(?P<NAME>[_A-Za-z0-9]*),(?P<DIR>[QMI])(?:,(?P<SIZE>[XBWDL]))?,(?P<LOC>[,0-9]*)\)") |
713 LOCATED_MODEL = re.compile( |
|
714 "__LOCATED_VAR\((?P<IEC_TYPE>[A-Z]*),(?P<NAME>[_A-Za-z0-9]*),(?P<DIR>[QMI])(?:,(?P<SIZE>[XBWDL]))?,(?P<LOC>[,0-9]*)\)") |
692 for line in lines: |
715 for line in lines: |
693 # If line match RE, |
716 # If line match RE, |
694 result = LOCATED_MODEL.match(line) |
717 result = LOCATED_MODEL.match(line) |
695 if result: |
718 if result: |
696 # Get the resulting dict |
719 # Get the resulting dict |
697 resdict = result.groupdict() |
720 resdict = result.groupdict() |
698 # rewrite string for variadic location as a tuple of integers |
721 # rewrite string for variadic location as a tuple of |
|
722 # integers |
699 resdict['LOC'] = tuple(map(int, resdict['LOC'].split(','))) |
723 resdict['LOC'] = tuple(map(int, resdict['LOC'].split(','))) |
700 # set located size to 'X' if not given |
724 # set located size to 'X' if not given |
701 if not resdict['SIZE']: |
725 if not resdict['SIZE']: |
702 resdict['SIZE'] = 'X' |
726 resdict['SIZE'] = 'X' |
703 # finally store into located variable list |
727 # finally store into located variable list |
719 """ |
743 """ |
720 |
744 |
721 # Update PLCOpenEditor ConfNode Block types before generate ST code |
745 # Update PLCOpenEditor ConfNode Block types before generate ST code |
722 self.RefreshConfNodesBlockLists() |
746 self.RefreshConfNodesBlockLists() |
723 |
747 |
724 self.logger.write(_("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n")) |
748 self.logger.write( |
|
749 _("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n")) |
725 # ask PLCOpenEditor controller to write ST/IL/SFC code file |
750 # ask PLCOpenEditor controller to write ST/IL/SFC code file |
726 _program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath()) |
751 _program, errors, warnings = self.GenerateProgram( |
|
752 self._getIECgeneratedcodepath()) |
727 if len(warnings) > 0: |
753 if len(warnings) > 0: |
728 self.logger.write_warning(_("Warnings in ST/IL/SFC code generator :\n")) |
754 self.logger.write_warning( |
|
755 _("Warnings in ST/IL/SFC code generator :\n")) |
729 for warning in warnings: |
756 for warning in warnings: |
730 self.logger.write_warning("%s\n" % warning) |
757 self.logger.write_warning("%s\n" % warning) |
731 if len(errors) > 0: |
758 if len(errors) > 0: |
732 # Failed ! |
759 # Failed ! |
733 self.logger.write_error(_("Error in ST/IL/SFC code generator :\n%s\n") % errors[0]) |
760 self.logger.write_error( |
|
761 _("Error in ST/IL/SFC code generator :\n%s\n") % errors[0]) |
734 return False |
762 return False |
735 plc_file = open(self._getIECcodepath(), "w") |
763 plc_file = open(self._getIECcodepath(), "w") |
736 # Add ST Library from confnodes |
764 # Add ST Library from confnodes |
737 plc_file.write(self.GetLibrariesSTCode()) |
765 plc_file.write(self.GetLibrariesSTCode()) |
738 if os.path.isfile(self._getIECrawcodepath()): |
766 if os.path.isfile(self._getIECrawcodepath()): |
763 iec2c_libpath, |
791 iec2c_libpath, |
764 buildpath, |
792 buildpath, |
765 self._getIECcodepath()) |
793 self._getIECcodepath()) |
766 |
794 |
767 try: |
795 try: |
768 # Invoke compiler. Output files are listed to stdout, errors to stderr |
796 # Invoke compiler. |
|
797 # Output files are listed to stdout, errors to stderr |
769 status, result, err_result = ProcessLogger(self.logger, buildcmd, |
798 status, result, err_result = ProcessLogger(self.logger, buildcmd, |
770 no_stdout=True, no_stderr=True).spin() |
799 no_stdout=True, |
|
800 no_stderr=True).spin() |
771 except Exception, e: |
801 except Exception, e: |
772 self.logger.write_error(buildcmd + "\n") |
802 self.logger.write_error(buildcmd + "\n") |
773 self.logger.write_error(repr(e) + "\n") |
803 self.logger.write_error(repr(e) + "\n") |
774 return False |
804 return False |
775 |
805 |
794 if line[0] not in '\t \r\n': |
824 if line[0] not in '\t \r\n': |
795 last_section = line |
825 last_section = line |
796 |
826 |
797 if first_line <= i <= last_line: |
827 if first_line <= i <= last_line: |
798 if last_section is not None: |
828 if last_section is not None: |
799 self.logger.write_warning("In section: " + last_section) |
829 self.logger.write_warning( |
|
830 "In section: " + last_section) |
800 last_section = None # only write section once |
831 last_section = None # only write section once |
801 self.logger.write_warning("%04d: %s" % (i, line)) |
832 self.logger.write_warning("%04d: %s" % (i, line)) |
802 |
833 |
803 f.close() |
834 f.close() |
804 |
835 |
805 self.logger.write_error(_("Error : IEC to C compiler returned %d\n") % status) |
836 self.logger.write_error( |
|
837 _("Error : IEC to C compiler returned %d\n") % status) |
806 return False |
838 return False |
807 |
839 |
808 # Now extract C files of stdout |
840 # Now extract C files of stdout |
809 C_files = [fname for fname in result.splitlines() if fname[-2:] == ".c" or fname[-2:] == ".C"] |
841 C_files = [fname for fname in result.splitlines() if fname[ |
|
842 -2:] == ".c" or fname[-2:] == ".C"] |
810 # remove those that are not to be compiled because included by others |
843 # remove those that are not to be compiled because included by others |
811 C_files.remove("POUS.c") |
844 C_files.remove("POUS.c") |
812 if not C_files: |
845 if not C_files: |
813 self.logger.write_error(_("Error : At least one configuration and one resource must be declared in PLC !\n")) |
846 self.logger.write_error( |
|
847 _("Error : At least one configuration and one resource must be declared in PLC !\n")) |
814 return False |
848 return False |
815 # transform those base names to full names with path |
849 # transform those base names to full names with path |
816 C_files = map(lambda filename: os.path.join(buildpath, filename), C_files) |
850 C_files = map( |
|
851 lambda filename: os.path.join(buildpath, filename), C_files) |
817 |
852 |
818 # prepend beremiz include to configuration header |
853 # prepend beremiz include to configuration header |
819 H_files = [fname for fname in result.splitlines() if fname[-2:] == ".h" or fname[-2:] == ".H"] |
854 H_files = [fname for fname in result.splitlines() if fname[ |
|
855 -2:] == ".h" or fname[-2:] == ".H"] |
820 H_files.remove("LOCATED_VARIABLES.h") |
856 H_files.remove("LOCATED_VARIABLES.h") |
821 H_files = map(lambda filename: os.path.join(buildpath, filename), H_files) |
857 H_files = map( |
|
858 lambda filename: os.path.join(buildpath, filename), H_files) |
822 for H_file in H_files: |
859 for H_file in H_files: |
823 with file(H_file, 'r') as original: |
860 with file(H_file, 'r') as original: |
824 data = original.read() |
861 data = original.read() |
825 with file(H_file, 'w') as modified: |
862 with file(H_file, 'w') as modified: |
826 modified.write('#include "beremiz.h"\n' + data) |
863 modified.write('#include "beremiz.h"\n' + data) |
827 |
864 |
828 self.logger.write(_("Extracting Located Variables...\n")) |
865 self.logger.write(_("Extracting Located Variables...\n")) |
829 # Keep track of generated located variables for later use by self._Generate_C |
866 # Keep track of generated located variables for later use by |
|
867 # self._Generate_C |
830 self.PLCGeneratedLocatedVars = self.GetLocations() |
868 self.PLCGeneratedLocatedVars = self.GetLocations() |
831 # Keep track of generated C files for later use by self.CTNGenerate_C |
869 # Keep track of generated C files for later use by self.CTNGenerate_C |
832 self.PLCGeneratedCFiles = C_files |
870 self.PLCGeneratedCFiles = C_files |
833 # compute CFLAGS for plc |
871 # compute CFLAGS for plc |
834 self.plcCFLAGS = '"-I%s" -Wno-unused-function' % self.iec2c_cfg.getLibCPath() |
872 self.plcCFLAGS = '"-I%s" -Wno-unused-function' % self.iec2c_cfg.getLibCPath() |
859 if builder is not None: |
897 if builder is not None: |
860 return builder.GetBinaryCodeMD5() |
898 return builder.GetBinaryCodeMD5() |
861 else: |
899 else: |
862 return None |
900 return None |
863 |
901 |
864 ####################################################################### |
902 # |
865 # |
903 # |
866 # C CODE GENERATION METHODS |
904 # C CODE GENERATION METHODS |
867 # |
905 # |
868 ####################################################################### |
906 # |
869 |
907 |
870 def CTNGenerate_C(self, buildpath, locations): |
908 def CTNGenerate_C(self, buildpath, locations): |
871 """ |
909 """ |
872 Return C code generated by iec2c compiler |
910 Return C code generated by iec2c compiler |
873 when _generate_softPLC have been called |
911 when _generate_softPLC have been called |
902 if self._ProgramList is None or self._VariablesList is None: |
940 if self._ProgramList is None or self._VariablesList is None: |
903 try: |
941 try: |
904 csvfile = os.path.join(self._getBuildPath(), "VARIABLES.csv") |
942 csvfile = os.path.join(self._getBuildPath(), "VARIABLES.csv") |
905 # describes CSV columns |
943 # describes CSV columns |
906 ProgramsListAttributeName = ["num", "C_path", "type"] |
944 ProgramsListAttributeName = ["num", "C_path", "type"] |
907 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"] |
945 VariablesListAttributeName = [ |
|
946 "num", "vartype", "IEC_path", "C_path", "type"] |
908 self._ProgramList = [] |
947 self._ProgramList = [] |
909 self._VariablesList = [] |
948 self._VariablesList = [] |
910 self._DbgVariablesList = [] |
949 self._DbgVariablesList = [] |
911 self._IECPathToIdx = {} |
950 self._IECPathToIdx = {} |
912 |
951 |
922 ListGroup[-1].append(strippedline) |
961 ListGroup[-1].append(strippedline) |
923 |
962 |
924 # first section contains programs |
963 # first section contains programs |
925 for line in ListGroup[0]: |
964 for line in ListGroup[0]: |
926 # Split and Maps each field to dictionnary entries |
965 # Split and Maps each field to dictionnary entries |
927 attrs = dict(zip(ProgramsListAttributeName, line.strip().split(';'))) |
966 attrs = dict( |
|
967 zip(ProgramsListAttributeName, line.strip().split(';'))) |
928 # Truncate "C_path" to remove conf an resources names |
968 # Truncate "C_path" to remove conf an resources names |
929 attrs["C_path"] = '__'.join(attrs["C_path"].split(".", 2)[1:]) |
969 attrs["C_path"] = '__'.join( |
|
970 attrs["C_path"].split(".", 2)[1:]) |
930 # Push this dictionnary into result. |
971 # Push this dictionnary into result. |
931 self._ProgramList.append(attrs) |
972 self._ProgramList.append(attrs) |
932 |
973 |
933 # second section contains all variables |
974 # second section contains all variables |
934 config_FBs = {} |
975 config_FBs = {} |
935 Idx = 0 |
976 Idx = 0 |
936 for line in ListGroup[1]: |
977 for line in ListGroup[1]: |
937 # Split and Maps each field to dictionnary entries |
978 # Split and Maps each field to dictionnary entries |
938 attrs = dict(zip(VariablesListAttributeName, line.strip().split(';'))) |
979 attrs = dict( |
|
980 zip(VariablesListAttributeName, line.strip().split(';'))) |
939 # Truncate "C_path" to remove conf an resources names |
981 # Truncate "C_path" to remove conf an resources names |
940 parts = attrs["C_path"].split(".", 2) |
982 parts = attrs["C_path"].split(".", 2) |
941 if len(parts) > 2: |
983 if len(parts) > 2: |
942 config_FB = config_FBs.get(tuple(parts[:2])) |
984 config_FB = config_FBs.get(tuple(parts[:2])) |
943 if config_FB: |
985 if config_FB: |
1034 "void __retrieve_%(s)s(void);\n" + |
1077 "void __retrieve_%(s)s(void);\n" + |
1035 "void __publish_%(s)s(void);") % {'s': locstr} for locstr in locstrs]), |
1078 "void __publish_%(s)s(void);") % {'s': locstr} for locstr in locstrs]), |
1036 "retrieve_calls": "\n ".join([ |
1079 "retrieve_calls": "\n ".join([ |
1037 "__retrieve_%s();" % locstr for locstr in locstrs]), |
1080 "__retrieve_%s();" % locstr for locstr in locstrs]), |
1038 "publish_calls": "\n ".join([ # Call publish in reverse order |
1081 "publish_calls": "\n ".join([ # Call publish in reverse order |
1039 "__publish_%s();" % locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]), |
1082 "__publish_%s();" % locstrs[i - 1] for i in xrange(len(locstrs), 0, -1)]), |
1040 "init_calls": "\n ".join([ |
1083 "init_calls": "\n ".join([ |
1041 "init_level=%d; " % (i+1) + |
1084 "init_level=%d; " % (i + 1) + |
1042 "if((res = __init_%s(argc,argv))){" % locstr + |
1085 "if((res = __init_%s(argc,argv))){" % locstr + |
1043 # "printf(\"%s\"); "%locstr + #for debug |
1086 # "printf(\"%s\"); "%locstr + #for debug |
1044 "return res;}" for i, locstr in enumerate(locstrs)]), |
1087 "return res;}" for i, locstr in enumerate(locstrs)]), |
1045 "cleanup_calls": "\n ".join([ |
1088 "cleanup_calls": "\n ".join([ |
1046 "if(init_level >= %d) " % i + |
1089 "if(init_level >= %d) " % i + |
1047 "__cleanup_%s();" % locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]) |
1090 "__cleanup_%s();" % locstrs[i - 1] for i in xrange(len(locstrs), 0, -1)]) |
1048 } |
1091 } |
1049 else: |
1092 else: |
1050 plc_main_code = targets.GetCode("plc_main_head.c") % { |
1093 plc_main_code = targets.GetCode("plc_main_head.c") % { |
1051 "calls_prototypes": "\n", |
1094 "calls_prototypes": "\n", |
1052 "retrieve_calls": "\n", |
1095 "retrieve_calls": "\n", |
1053 "publish_calls": "\n", |
1096 "publish_calls": "\n", |
1054 "init_calls": "\n", |
1097 "init_calls": "\n", |
1055 "cleanup_calls": "\n" |
1098 "cleanup_calls": "\n" |
1056 } |
1099 } |
1057 plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent().getLocalTag()) |
1100 plc_main_code += targets.GetTargetCode( |
|
1101 self.GetTarget().getcontent().getLocalTag()) |
1058 plc_main_code += targets.GetCode("plc_main_tail.c") |
1102 plc_main_code += targets.GetCode("plc_main_tail.c") |
1059 return plc_main_code |
1103 return plc_main_code |
1060 |
1104 |
1061 def _Build(self): |
1105 def _Build(self): |
1062 """ |
1106 """ |
1124 try: |
1168 try: |
1125 CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C( |
1169 CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C( |
1126 buildpath, |
1170 buildpath, |
1127 self.PLCGeneratedLocatedVars) |
1171 self.PLCGeneratedLocatedVars) |
1128 except Exception: |
1172 except Exception: |
1129 self.logger.write_error(_("Runtime IO extensions C code generation failed !\n")) |
1173 self.logger.write_error( |
|
1174 _("Runtime IO extensions C code generation failed !\n")) |
1130 self.logger.write_error(traceback.format_exc()) |
1175 self.logger.write_error(traceback.format_exc()) |
1131 self.ResetBuildMD5() |
1176 self.ResetBuildMD5() |
1132 return False |
1177 return False |
1133 |
1178 |
1134 # Generate C code and compilation params from liraries |
1179 # Generate C code and compilation params from liraries |
1135 try: |
1180 try: |
1136 LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath) |
1181 LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode( |
|
1182 buildpath) |
1137 except Exception: |
1183 except Exception: |
1138 self.logger.write_error(_("Runtime library extensions C code generation failed !\n")) |
1184 self.logger.write_error( |
|
1185 _("Runtime library extensions C code generation failed !\n")) |
1139 self.logger.write_error(traceback.format_exc()) |
1186 self.logger.write_error(traceback.format_exc()) |
1140 self.ResetBuildMD5() |
1187 self.ResetBuildMD5() |
1141 return False |
1188 return False |
1142 |
1189 |
1143 self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + CTNLocationCFilesAndCFLAGS |
1190 self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + \ |
|
1191 CTNLocationCFilesAndCFLAGS |
1144 self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS |
1192 self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS |
1145 ExtraFiles = CTNExtraFiles + LibExtraFiles |
1193 ExtraFiles = CTNExtraFiles + LibExtraFiles |
1146 |
1194 |
1147 # Get temporary directory path |
1195 # Get temporary directory path |
1148 extrafilespath = self._getExtraFilesPath() |
1196 extrafilespath = self._getExtraFilesPath() |
1157 open(fpath, "wb").write(fobject.read()) |
1205 open(fpath, "wb").write(fobject.read()) |
1158 # Now we can forget ExtraFiles (will close files object) |
1206 # Now we can forget ExtraFiles (will close files object) |
1159 del ExtraFiles |
1207 del ExtraFiles |
1160 |
1208 |
1161 # Header file for extensions |
1209 # Header file for extensions |
1162 open(os.path.join(buildpath, "beremiz.h"), "w").write(targets.GetHeader()) |
1210 open(os.path.join(buildpath, "beremiz.h"), "w").write( |
|
1211 targets.GetHeader()) |
1163 |
1212 |
1164 # Template based part of C code generation |
1213 # Template based part of C code generation |
1165 # files are stacked at the beginning, as files of confnode tree root |
1214 # files are stacked at the beginning, as files of confnode tree root |
1166 c_source = [ |
1215 c_source = [ |
1167 # debugger code |
1216 # debugger code |
1176 code = generator() |
1225 code = generator() |
1177 if code is None: |
1226 if code is None: |
1178 raise Exception |
1227 raise Exception |
1179 code_path = os.path.join(buildpath, filename) |
1228 code_path = os.path.join(buildpath, filename) |
1180 open(code_path, "w").write(code) |
1229 open(code_path, "w").write(code) |
1181 # Insert this file as first file to be compiled at root confnode |
1230 # Insert this file as first file to be compiled at root |
1182 self.LocationCFilesAndCFLAGS[0][1].insert(0, (code_path, self.plcCFLAGS)) |
1231 # confnode |
|
1232 self.LocationCFilesAndCFLAGS[0][1].insert( |
|
1233 0, (code_path, self.plcCFLAGS)) |
1183 except Exception: |
1234 except Exception: |
1184 self.logger.write_error(name+_(" generation failed !\n")) |
1235 self.logger.write_error(name + _(" generation failed !\n")) |
1185 self.logger.write_error(traceback.format_exc()) |
1236 self.logger.write_error(traceback.format_exc()) |
1186 self.ResetBuildMD5() |
1237 self.ResetBuildMD5() |
1187 return False |
1238 return False |
1188 self.logger.write(_("C code generated successfully.\n")) |
1239 self.logger.write(_("C code generated successfully.\n")) |
1189 return True |
1240 return True |
1190 |
1241 |
1191 def ShowError(self, logger, from_location, to_location): |
1242 def ShowError(self, logger, from_location, to_location): |
1192 chunk_infos = self.GetChunkInfos(from_location, to_location) |
1243 chunk_infos = self.GetChunkInfos(from_location, to_location) |
1193 for infos, (start_row, start_col) in chunk_infos: |
1244 for infos, (start_row, start_col) in chunk_infos: |
1194 row = 1 if from_location[0] < start_row else (from_location[0] - start_row) |
1245 row = 1 if from_location[0] < start_row else ( |
1195 col = 1 if (start_row != from_location[0]) else (from_location[1] - start_col) |
1246 from_location[0] - start_row) |
|
1247 col = 1 if (start_row != from_location[0]) else ( |
|
1248 from_location[1] - start_col) |
1196 start = (row, col) |
1249 start = (row, col) |
1197 |
1250 |
1198 row = 1 if to_location[0] < start_row else (to_location[0] - start_row) |
1251 row = 1 if to_location[0] < start_row else ( |
1199 col = 1 if (start_row != to_location[0]) else (to_location[1] - start_col) |
1252 to_location[0] - start_row) |
|
1253 col = 1 if (start_row != to_location[0]) else ( |
|
1254 to_location[1] - start_col) |
1200 end = (row, col) |
1255 end = (row, col) |
1201 |
1256 |
1202 if self.AppFrame is not None: |
1257 if self.AppFrame is not None: |
1203 self.AppFrame.ShowError(infos, start, end) |
1258 self.AppFrame.ShowError(infos, start, end) |
1204 |
1259 |
1225 def _OpenView(self, name=None, onlyopened=False): |
1280 def _OpenView(self, name=None, onlyopened=False): |
1226 if name == "IEC code": |
1281 if name == "IEC code": |
1227 if self._IECCodeView is None: |
1282 if self._IECCodeView is None: |
1228 plc_file = self._getIECcodepath() |
1283 plc_file = self._getIECcodepath() |
1229 |
1284 |
1230 self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name) |
1285 self._IECCodeView = IECCodeViewer( |
|
1286 self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name) |
1231 self._IECCodeView.SetTextSyntax("ALL") |
1287 self._IECCodeView.SetTextSyntax("ALL") |
1232 self._IECCodeView.SetKeywords(IEC_KEYWORDS) |
1288 self._IECCodeView.SetKeywords(IEC_KEYWORDS) |
1233 try: |
1289 try: |
1234 text = file(plc_file).read() |
1290 text = file(plc_file).read() |
1235 except Exception: |
1291 except Exception: |
1246 |
1302 |
1247 elif name == "IEC raw code": |
1303 elif name == "IEC raw code": |
1248 if self._IECRawCodeView is None: |
1304 if self._IECRawCodeView is None: |
1249 controler = MiniTextControler(self._getIECrawcodepath(), self) |
1305 controler = MiniTextControler(self._getIECrawcodepath(), self) |
1250 |
1306 |
1251 self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name) |
1307 self._IECRawCodeView = IECCodeViewer( |
|
1308 self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name) |
1252 self._IECRawCodeView.SetTextSyntax("ALL") |
1309 self._IECRawCodeView.SetTextSyntax("ALL") |
1253 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS) |
1310 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS) |
1254 self._IECRawCodeView.RefreshView() |
1311 self._IECRawCodeView.RefreshView() |
1255 self._IECRawCodeView.SetIcon(GetBitmap("ST")) |
1312 self._IECRawCodeView.SetIcon(GetBitmap("ST")) |
1256 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor) |
1313 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor) |
1260 |
1317 |
1261 return self._IECRawCodeView |
1318 return self._IECRawCodeView |
1262 |
1319 |
1263 elif name == "Project Files": |
1320 elif name == "Project Files": |
1264 if self._ProjectFilesView is None: |
1321 if self._ProjectFilesView is None: |
1265 self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True) |
1322 self._ProjectFilesView = FileManagementPanel( |
|
1323 self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True) |
1266 |
1324 |
1267 extensions = [] |
1325 extensions = [] |
1268 for extension, name, editor in features.file_editors: |
1326 for extension, name, editor in features.file_editors: |
1269 if extension not in extensions: |
1327 if extension not in extensions: |
1270 extensions.append(extension) |
1328 extensions.append(extension) |
1302 |
1360 |
1303 if editor_name != "": |
1361 if editor_name != "": |
1304 name = "::".join([filepath, editor_name]) |
1362 name = "::".join([filepath, editor_name]) |
1305 |
1363 |
1306 editor = editors[editor_name]() |
1364 editor = editors[editor_name]() |
1307 self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame) |
1365 self._FileEditors[filepath] = editor( |
|
1366 self.AppFrame.TabsOpened, self, name, self.AppFrame) |
1308 self._FileEditors[filepath].SetIcon(GetBitmap("FILE")) |
1367 self._FileEditors[filepath].SetIcon(GetBitmap("FILE")) |
1309 if isinstance(self._FileEditors[filepath], DebugViewer): |
1368 if isinstance(self._FileEditors[filepath], DebugViewer): |
1310 self._FileEditors[filepath].SetDataProducer(self) |
1369 self._FileEditors[filepath].SetDataProducer(self) |
1311 |
1370 |
1312 if filepath in self._FileEditors: |
1371 if filepath in self._FileEditors: |
1393 if self.previous_plcstate != status: |
1452 if self.previous_plcstate != status: |
1394 allmethods = self.DefaultMethods.copy() |
1453 allmethods = self.DefaultMethods.copy() |
1395 allmethods.update( |
1454 allmethods.update( |
1396 self.MethodsFromStatus.get(status, {})) |
1455 self.MethodsFromStatus.get(status, {})) |
1397 for method, active in allmethods.items(): |
1456 for method, active in allmethods.items(): |
1398 self.ShowMethod(method,active) |
1457 self.ShowMethod(method, active) |
1399 self.previous_plcstate = status |
1458 self.previous_plcstate = status |
1400 if self.AppFrame is not None: |
1459 if self.AppFrame is not None: |
1401 updated = True |
1460 updated = True |
1402 self.AppFrame.RefreshStatusToolBar() |
1461 self.AppFrame.RefreshStatusToolBar() |
1403 if status == "Disconnected": |
1462 if status == "Disconnected": |
1404 self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 1) |
1463 self.AppFrame.ConnectionStatusBar.SetStatusText( |
|
1464 self.GetTextStatus(status), 1) |
1405 self.AppFrame.ConnectionStatusBar.SetStatusText('', 2) |
1465 self.AppFrame.ConnectionStatusBar.SetStatusText('', 2) |
1406 else: |
1466 else: |
1407 self.AppFrame.ConnectionStatusBar.SetStatusText( |
1467 self.AppFrame.ConnectionStatusBar.SetStatusText( |
1408 _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1) |
1468 _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1) |
1409 self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 2) |
1469 self.AppFrame.ConnectionStatusBar.SetStatusText( |
|
1470 self.GetTextStatus(status), 2) |
1410 return updated |
1471 return updated |
1411 |
1472 |
1412 def GetTextStatus(self, status): |
1473 def GetTextStatus(self, status): |
1413 msgs = { |
1474 msgs = { |
1414 "Started": _("Started"), |
1475 "Started": _("Started"), |
1415 "Stopped": _("Stopped"), |
1476 "Stopped": _("Stopped"), |
1416 "Empty": _("Empty"), |
1477 "Empty": _("Empty"), |
1417 "Broken": _("Broken"), |
1478 "Broken": _("Broken"), |
1418 "Disconnected": _("Disconnected") |
1479 "Disconnected": _("Disconnected") |
1419 } |
1480 } |
1420 return msgs.get(status, status) |
1481 return msgs.get(status, status) |
1421 |
1482 |
1422 def ShowPLCProgress(self, status="", progress=0): |
1483 def ShowPLCProgress(self, status="", progress=0): |
1423 self.AppFrame.ProgressStatusBar.Show() |
1484 self.AppFrame.ProgressStatusBar.Show() |
1424 self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 1) |
1485 self.AppFrame.ConnectionStatusBar.SetStatusText( |
|
1486 self.GetTextStatus(status), 1) |
1425 self.AppFrame.ProgressStatusBar.SetValue(progress) |
1487 self.AppFrame.ProgressStatusBar.SetValue(progress) |
1426 |
1488 |
1427 def HidePLCProgress(self): |
1489 def HidePLCProgress(self): |
1428 # clear previous_plcstate to restore status |
1490 # clear previous_plcstate to restore status |
1429 # in UpdateMethodsFromPLCStatus() |
1491 # in UpdateMethodsFromPLCStatus() |
1435 self.UpdateMethodsFromPLCStatus() |
1497 self.UpdateMethodsFromPLCStatus() |
1436 |
1498 |
1437 def SnapshotAndResetDebugValuesBuffers(self): |
1499 def SnapshotAndResetDebugValuesBuffers(self): |
1438 if self._connector is not None: |
1500 if self._connector is not None: |
1439 plc_status, Traces = self._connector.GetTraceVariables() |
1501 plc_status, Traces = self._connector.GetTraceVariables() |
1440 # print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()] |
1502 # print [dict.keys() for IECPath, (dict, log, status, fvalue) in |
|
1503 # self.IECdebug_datas.items()] |
1441 if plc_status == "Started": |
1504 if plc_status == "Started": |
1442 if len(Traces) > 0: |
1505 if len(Traces) > 0: |
1443 for debug_tick, debug_buff in Traces: |
1506 for debug_tick, debug_buff in Traces: |
1444 debug_vars = UnpackDebugBuffer(debug_buff, self.TracedIECTypes) |
1507 debug_vars = UnpackDebugBuffer( |
|
1508 debug_buff, self.TracedIECTypes) |
1445 if debug_vars is not None and len(debug_vars) == len(self.TracedIECPath): |
1509 if debug_vars is not None and len(debug_vars) == len(self.TracedIECPath): |
1446 for IECPath, values_buffer, value in izip( |
1510 for IECPath, values_buffer, value in izip( |
1447 self.TracedIECPath, |
1511 self.TracedIECPath, |
1448 self.DebugValuesBuffers, |
1512 self.DebugValuesBuffers, |
1449 debug_vars): |
1513 debug_vars): |
1450 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1514 IECdebug_data = self.IECdebug_datas.get( |
|
1515 IECPath, None) |
1451 if IECdebug_data is not None and value is not None: |
1516 if IECdebug_data is not None and value is not None: |
1452 forced = IECdebug_data[2:4] == ["Forced", value] |
1517 forced = IECdebug_data[2:4] == [ |
|
1518 "Forced", value] |
1453 if not IECdebug_data[4] and len(values_buffer) > 0: |
1519 if not IECdebug_data[4] and len(values_buffer) > 0: |
1454 values_buffer[-1] = (value, forced) |
1520 values_buffer[-1] = (value, forced) |
1455 else: |
1521 else: |
1456 values_buffer.append((value, forced)) |
1522 values_buffer.append((value, forced)) |
1457 self.DebugTicks.append(debug_tick) |
1523 self.DebugTicks.append(debug_tick) |
1476 # Callable Dict is empty. |
1542 # Callable Dict is empty. |
1477 # This variable is not needed anymore! |
1543 # This variable is not needed anymore! |
1478 IECPathsToPop.append(IECPath) |
1544 IECPathsToPop.append(IECPath) |
1479 elif IECPath != "__tick__": |
1545 elif IECPath != "__tick__": |
1480 # Convert |
1546 # Convert |
1481 Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None)) |
1547 Idx, IEC_Type = self._IECPathToIdx.get( |
|
1548 IECPath, (None, None)) |
1482 if Idx is not None: |
1549 if Idx is not None: |
1483 if IEC_Type in DebugTypesSize: |
1550 if IEC_Type in DebugTypesSize: |
1484 Idxs.append((Idx, IEC_Type, fvalue, IECPath)) |
1551 Idxs.append((Idx, IEC_Type, fvalue, IECPath)) |
1485 else: |
1552 else: |
1486 self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n") % IEC_Type) |
1553 self.logger.write_warning( |
|
1554 _("Debug: Unsupported type to debug '%s'\n") % IEC_Type) |
1487 else: |
1555 else: |
1488 self.logger.write_warning(_("Debug: Unknown variable '%s'\n") % IECPath) |
1556 self.logger.write_warning( |
|
1557 _("Debug: Unknown variable '%s'\n") % IECPath) |
1489 for IECPathToPop in IECPathsToPop: |
1558 for IECPathToPop in IECPathsToPop: |
1490 self.IECdebug_datas.pop(IECPathToPop) |
1559 self.IECdebug_datas.pop(IECPathToPop) |
1491 |
1560 |
1492 if Idxs: |
1561 if Idxs: |
1493 Idxs.sort() |
1562 Idxs.sort() |
1510 # Prevent to call RegisterDebugVarToConnector when PLC is not started |
1579 # Prevent to call RegisterDebugVarToConnector when PLC is not started |
1511 # If an output location var is forced it's leads to segmentation fault in runtime |
1580 # If an output location var is forced it's leads to segmentation fault in runtime |
1512 # Links between PLC located variables and real variables are not ready |
1581 # Links between PLC located variables and real variables are not ready |
1513 if self.IsPLCStarted(): |
1582 if self.IsPLCStarted(): |
1514 # Timer to prevent rapid-fire when registering many variables |
1583 # Timer to prevent rapid-fire when registering many variables |
1515 # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead |
1584 # use wx.CallAfter use keep using same thread. TODO : use wx.Timer |
1516 self.DebugTimer = Timer(0.5, wx.CallAfter, args=[self.RegisterDebugVarToConnector]) |
1585 # instead |
|
1586 self.DebugTimer = Timer( |
|
1587 0.5, wx.CallAfter, args=[self.RegisterDebugVarToConnector]) |
1517 # Rearm anti-rapid-fire timer |
1588 # Rearm anti-rapid-fire timer |
1518 self.DebugTimer.start() |
1589 self.DebugTimer.start() |
1519 |
1590 |
1520 def GetDebugIECVariableType(self, IECPath): |
1591 def GetDebugIECVariableType(self, IECPath): |
1521 _Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None)) |
1592 _Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None)) |
1615 debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers() |
1686 debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers() |
1616 start_time = time.time() |
1687 start_time = time.time() |
1617 if len(self.TracedIECPath) == len(buffers): |
1688 if len(self.TracedIECPath) == len(buffers): |
1618 for IECPath, values in izip(self.TracedIECPath, buffers): |
1689 for IECPath, values in izip(self.TracedIECPath, buffers): |
1619 if len(values) > 0: |
1690 if len(values) > 0: |
1620 self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values) |
1691 self.CallWeakcallables( |
|
1692 IECPath, "NewValues", debug_ticks, values) |
1621 if len(debug_ticks) > 0: |
1693 if len(debug_ticks) > 0: |
1622 self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks) |
1694 self.CallWeakcallables( |
|
1695 "__tick__", "NewDataAvailable", debug_ticks) |
1623 |
1696 |
1624 delay = time.time() - start_time |
1697 delay = time.time() - start_time |
1625 next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay) |
1698 next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay) |
1626 if self.DispatchDebugValuesTimer is not None: |
1699 if self.DispatchDebugValuesTimer is not None: |
1627 self.DispatchDebugValuesTimer.Start( |
1700 self.DispatchDebugValuesTimer.Start( |
1737 if self.previous_plcstate in ["Started", "Stopped"]: |
1812 if self.previous_plcstate in ["Started", "Stopped"]: |
1738 if self.DebugAvailable() and self.GetIECProgramsAndVariables(): |
1813 if self.DebugAvailable() and self.GetIECProgramsAndVariables(): |
1739 self.logger.write(_("Debugger ready\n")) |
1814 self.logger.write(_("Debugger ready\n")) |
1740 self._connect_debug() |
1815 self._connect_debug() |
1741 else: |
1816 else: |
1742 self.logger.write_warning(_("Debug does not match PLC - stop/transfert/start to re-enable\n")) |
1817 self.logger.write_warning( |
|
1818 _("Debug does not match PLC - stop/transfert/start to re-enable\n")) |
1743 |
1819 |
1744 def CompareLocalAndRemotePLC(self): |
1820 def CompareLocalAndRemotePLC(self): |
1745 if self._connector is None: |
1821 if self._connector is None: |
1746 return |
1822 return |
1747 # We are now connected. Update button status |
1823 # We are now connected. Update button status |
1748 MD5 = self.GetLastBuildMD5() |
1824 MD5 = self.GetLastBuildMD5() |
1749 # Check remote target PLC correspondance to that md5 |
1825 # Check remote target PLC correspondance to that md5 |
1750 if MD5 is not None: |
1826 if MD5 is not None: |
1751 if not self._connector.MatchMD5(MD5): |
1827 if not self._connector.MatchMD5(MD5): |
1752 # self.logger.write_warning( |
1828 # self.logger.write_warning( |
1753 # _("Latest build does not match with target, please transfer.\n")) |
1829 # _("Latest build does not match with target, please |
|
1830 # transfer.\n")) |
1754 self.EnableMethod("_Transfer", True) |
1831 self.EnableMethod("_Transfer", True) |
1755 else: |
1832 else: |
1756 # self.logger.write( |
1833 # self.logger.write( |
1757 # _("Latest build matches target, no transfer needed.\n")) |
1834 # _("Latest build matches target, no transfer needed.\n")) |
1758 self.EnableMethod("_Transfer", True) |
1835 self.EnableMethod("_Transfer", True) |