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: |
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="false"/>' |
202 'type="xsd:boolean" use="optional" default="' + |
198 for libname, _lib in features.libraries])+""" |
203 ('true' if default else 'false') + '"/>' |
|
204 for libname, _lib, default in features.libraries]) + """ |
199 </xsd:complexType> |
205 </xsd:complexType> |
200 </xsd:element>""") if len(features.libraries) > 0 else '') + """ |
206 </xsd:element>""") if len(features.libraries) > 0 else '') + """ |
201 </xsd:sequence> |
207 </xsd:sequence> |
202 <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/> |
208 <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/> |
203 <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"/> |
207 """ |
213 """ |
208 return XSD |
214 return XSD |
209 |
215 |
210 |
216 |
211 class ProjectController(ConfigTreeNode, PLCControler): |
217 class ProjectController(ConfigTreeNode, PLCControler): |
|
218 |
212 """ |
219 """ |
213 This class define Root object of the confnode tree. |
220 This class define Root object of the confnode tree. |
214 It is responsible of : |
221 It is responsible of : |
215 - Managing project directory |
222 - Managing project directory |
216 - Building project |
223 - Building project |
217 - Handling PLCOpenEditor controler and view |
224 - Handling PLCOpenEditor controler and view |
218 - Loading user confnodes and instanciante them as children |
225 - Loading user confnodes and instanciante them as children |
219 - ... |
226 - ... |
220 |
227 |
221 """ |
228 """ |
222 # 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. |
223 CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog) |
231 CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog) |
224 XSD = GetProjectControllerXSD() |
232 XSD = GetProjectControllerXSD() |
225 EditorType = ProjectNodeEditor |
233 EditorType = ProjectNodeEditor |
226 iec2c_cfg = None |
234 iec2c_cfg = None |
227 |
235 |
268 self.KillDebugThread() |
276 self.KillDebugThread() |
269 |
277 |
270 def LoadLibraries(self): |
278 def LoadLibraries(self): |
271 self.Libraries = [] |
279 self.Libraries = [] |
272 TypeStack = [] |
280 TypeStack = [] |
273 for libname, clsname in features.libraries: |
281 for libname, clsname, _default in features.libraries: |
274 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"): |
275 Lib = GetClassImporter(clsname)()(self, libname, TypeStack) |
285 Lib = GetClassImporter(clsname)()(self, libname, TypeStack) |
276 TypeStack.append(Lib.GetTypes()) |
286 TypeStack.append(Lib.GetTypes()) |
277 self.Libraries.append(Lib) |
287 self.Libraries.append(Lib) |
278 |
288 |
279 def SetAppFrame(self, frame, logger): |
289 def SetAppFrame(self, frame, logger): |
359 if target.getcontent() is None: |
369 if target.getcontent() is None: |
360 temp_root = self.Parser.CreateRoot() |
370 temp_root = self.Parser.CreateRoot() |
361 target = self.Parser.CreateElement("TargetType", "BeremizRoot") |
371 target = self.Parser.CreateElement("TargetType", "BeremizRoot") |
362 temp_root.setTargetType(target) |
372 temp_root.setTargetType(target) |
363 target_name = self.GetDefaultTargetName() |
373 target_name = self.GetDefaultTargetName() |
364 target.setcontent(self.Parser.CreateElement(target_name, "TargetType")) |
374 target.setcontent( |
|
375 self.Parser.CreateElement(target_name, "TargetType")) |
365 return target |
376 return target |
366 |
377 |
367 def GetParamsAttributes(self, path=None): |
378 def GetParamsAttributes(self, path=None): |
368 params = ConfigTreeNode.GetParamsAttributes(self, path) |
379 params = ConfigTreeNode.GetParamsAttributes(self, path) |
369 if params[0]["name"] == "BeremizRoot": |
380 if params[0]["name"] == "BeremizRoot": |
370 for child in params[0]["children"]: |
381 for child in params[0]["children"]: |
371 if child["name"] == "TargetType" and child["value"] == '': |
382 if child["name"] == "TargetType" and child["value"] == '': |
372 child.update(self.GetTarget().getElementInfos("TargetType")) |
383 child.update( |
|
384 self.GetTarget().getElementInfos("TargetType")) |
373 return params |
385 return params |
374 |
386 |
375 def SetParamsAttribute(self, path, value): |
387 def SetParamsAttribute(self, path, value): |
376 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: |
377 self.BeremizRoot.setTargetType(self.GetTarget()) |
389 self.BeremizRoot.setTargetType(self.GetTarget()) |
401 return False |
413 return False |
402 |
414 |
403 def _getProjectFilesPath(self, project_path=None): |
415 def _getProjectFilesPath(self, project_path=None): |
404 if project_path is not None: |
416 if project_path is not None: |
405 return os.path.join(project_path, "project_files") |
417 return os.path.join(project_path, "project_files") |
406 projectfiles_path = os.path.join(self.GetProjectPath(), "project_files") |
418 projectfiles_path = os.path.join( |
|
419 self.GetProjectPath(), "project_files") |
407 if not os.path.exists(projectfiles_path): |
420 if not os.path.exists(projectfiles_path): |
408 os.mkdir(projectfiles_path) |
421 os.mkdir(projectfiles_path) |
409 return projectfiles_path |
422 return projectfiles_path |
410 |
423 |
411 def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"): |
424 def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"): |
412 self.ProjectAddConfiguration(config_name) |
425 self.ProjectAddConfiguration(config_name) |
413 self.ProjectAddConfigurationResource(config_name, res_name) |
426 self.ProjectAddConfigurationResource(config_name, res_name) |
414 |
427 |
415 def SetProjectDefaultConfiguration(self): |
428 def SetProjectDefaultConfiguration(self): |
416 # Sets default task and instance for new project |
429 # Sets default task and instance for new project |
417 config = self.Project.getconfiguration(self.GetProjectMainConfigurationName()) |
430 config = self.Project.getconfiguration( |
|
431 self.GetProjectMainConfigurationName()) |
418 resource = config.getresource()[0].getname() |
432 resource = config.getresource()[0].getname() |
419 config = config.getname() |
433 config = config.getname() |
420 resource_tagname = ComputeConfigurationResourceName(config, resource) |
434 resource_tagname = ComputeConfigurationResourceName(config, resource) |
421 def_task = [ |
435 def_task = [ |
422 {'Priority': '0', 'Single': '', 'Interval': 'T#20ms', 'Name': 'task0', 'Triggering': 'Cyclic'}] |
436 {'Priority': '0', 'Single': '', 'Interval': 'T#20ms', 'Name': 'task0', 'Triggering': 'Cyclic'}] |
469 # Load PLCOpen file |
483 # Load PLCOpen file |
470 error = self.OpenXMLFile(plc_file) |
484 error = self.OpenXMLFile(plc_file) |
471 if error is not None: |
485 if error is not None: |
472 if self.Project is not None: |
486 if self.Project is not None: |
473 (fname_err, lnum, src) = (("PLC",) + error) |
487 (fname_err, lnum, src) = (("PLC",) + error) |
474 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)) |
475 else: |
490 else: |
476 return error, False |
491 return error, False |
477 if len(self.GetProjectConfigNames()) == 0: |
492 if len(self.GetProjectConfigNames()) == 0: |
478 self.AddProjectDefaultConfiguration() |
493 self.AddProjectDefaultConfiguration() |
479 # Change XSD into class members |
494 # Change XSD into class members |
526 self.ResetAppFrame(None) |
541 self.ResetAppFrame(None) |
527 |
542 |
528 def CheckNewProjectPath(self, old_project_path, new_project_path): |
543 def CheckNewProjectPath(self, old_project_path, new_project_path): |
529 if old_project_path == new_project_path: |
544 if old_project_path == new_project_path: |
530 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")) |
531 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) |
532 dialog.ShowModal() |
548 dialog.ShowModal() |
533 return False |
549 return False |
534 else: |
550 else: |
535 plc_file = os.path.join(new_project_path, "plc.xml") |
551 plc_file = os.path.join(new_project_path, "plc.xml") |
536 if os.path.isfile(plc_file): |
552 if os.path.isfile(plc_file): |
537 message = (_("Selected directory already contains another project. Overwrite? \n")) |
553 message = ( |
538 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) |
539 answer = dialog.ShowModal() |
557 answer = dialog.ShowModal() |
540 return answer == wx.ID_YES |
558 return answer == wx.ID_YES |
541 return True |
559 return True |
542 |
560 |
543 def SaveProject(self, from_project_path=None): |
561 def SaveProject(self, from_project_path=None): |
544 if self.CheckProjectPathPerm(False): |
562 if self.CheckProjectPathPerm(False): |
545 if from_project_path is not None: |
563 if from_project_path is not None: |
546 old_projectfiles_path = self._getProjectFilesPath(from_project_path) |
564 old_projectfiles_path = self._getProjectFilesPath( |
|
565 from_project_path) |
547 if os.path.isdir(old_projectfiles_path): |
566 if os.path.isdir(old_projectfiles_path): |
548 shutil.copytree(old_projectfiles_path, |
567 shutil.copytree(old_projectfiles_path, |
549 self._getProjectFilesPath(self.ProjectPath)) |
568 self._getProjectFilesPath(self.ProjectPath)) |
550 self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) |
569 self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) |
551 result = self.CTNRequestSave(from_project_path) |
570 result = self.CTNRequestSave(from_project_path) |
556 # Ask user to choose a path with write permissions |
575 # Ask user to choose a path with write permissions |
557 if wx.Platform == '__WXMSW__': |
576 if wx.Platform == '__WXMSW__': |
558 path = os.getenv("USERPROFILE") |
577 path = os.getenv("USERPROFILE") |
559 else: |
578 else: |
560 path = os.getenv("HOME") |
579 path = os.getenv("HOME") |
561 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) |
562 answer = dirdialog.ShowModal() |
582 answer = dirdialog.ShowModal() |
563 dirdialog.Destroy() |
583 dirdialog.Destroy() |
564 if answer == wx.ID_OK: |
584 if answer == wx.ID_OK: |
565 newprojectpath = dirdialog.GetPath() |
585 newprojectpath = dirdialog.GetPath() |
566 if os.path.isdir(newprojectpath): |
586 if os.path.isdir(newprojectpath): |
580 |
600 |
581 def GetLibrariesCCode(self, buildpath): |
601 def GetLibrariesCCode(self, buildpath): |
582 if len(self.Libraries) == 0: |
602 if len(self.Libraries) == 0: |
583 return [], [], () |
603 return [], [], () |
584 self.GetIECProgramsAndVariables() |
604 self.GetIECProgramsAndVariables() |
585 LibIECCflags = '"-I%s" -Wno-unused-function' % os.path.abspath(self.GetIECLibPath()) |
605 LibIECCflags = '"-I%s" -Wno-unused-function' % os.path.abspath( |
|
606 self.GetIECLibPath()) |
586 LocatedCCodeAndFlags = [] |
607 LocatedCCodeAndFlags = [] |
587 Extras = [] |
608 Extras = [] |
588 for lib in self.Libraries: |
609 for lib in self.Libraries: |
589 res = lib.Generate_C(buildpath, self._VariablesList, LibIECCflags) |
610 res = lib.Generate_C(buildpath, self._VariablesList, LibIECCflags) |
590 LocatedCCodeAndFlags.append(res[:2]) |
611 LocatedCCodeAndFlags.append(res[:2]) |
591 if len(res) > 2: |
612 if len(res) > 2: |
592 Extras.extend(res[2:]) |
613 Extras.extend(res[2:]) |
593 return map(list, zip(*LocatedCCodeAndFlags))+[tuple(Extras)] |
614 return map(list, zip(*LocatedCCodeAndFlags)) + [tuple(Extras)] |
594 |
615 |
595 # Update PLCOpenEditor ConfNode Block types from loaded confnodes |
616 # Update PLCOpenEditor ConfNode Block types from loaded confnodes |
596 def RefreshConfNodesBlockLists(self): |
617 def RefreshConfNodesBlockLists(self): |
597 if getattr(self, "Children", None) is not None: |
618 if getattr(self, "Children", None) is not None: |
598 self.ClearConfNodeTypes() |
619 self.ClearConfNodeTypes() |
654 # Create a build path in project folder if user has permissions |
675 # Create a build path in project folder if user has permissions |
655 if CheckPathPerm(self.ProjectPath): |
676 if CheckPathPerm(self.ProjectPath): |
656 self.DefaultBuildPath = os.path.join(self.ProjectPath, "build") |
677 self.DefaultBuildPath = os.path.join(self.ProjectPath, "build") |
657 # Create a build path in temp folder |
678 # Create a build path in temp folder |
658 else: |
679 else: |
659 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") |
660 |
682 |
661 if not os.path.exists(self.DefaultBuildPath): |
683 if not os.path.exists(self.DefaultBuildPath): |
662 os.makedirs(self.DefaultBuildPath) |
684 os.makedirs(self.DefaultBuildPath) |
663 return self.DefaultBuildPath |
685 return self.DefaultBuildPath |
664 |
686 |
679 |
701 |
680 def GetLocations(self): |
702 def GetLocations(self): |
681 locations = [] |
703 locations = [] |
682 filepath = os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h") |
704 filepath = os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h") |
683 if os.path.isfile(filepath): |
705 if os.path.isfile(filepath): |
684 # IEC2C compiler generate a list of located variables : LOCATED_VARIABLES.h |
706 # IEC2C compiler generate a list of located variables : |
685 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")) |
686 # each line of LOCATED_VARIABLES.h declares a located variable |
710 # each line of LOCATED_VARIABLES.h declares a located variable |
687 lines = [line.strip() for line in location_file.readlines()] |
711 lines = [line.strip() for line in location_file.readlines()] |
688 # This regular expression parses the lines genereated by IEC2C |
712 # This regular expression parses the lines genereated by IEC2C |
689 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]*)\)") |
690 for line in lines: |
715 for line in lines: |
691 # If line match RE, |
716 # If line match RE, |
692 result = LOCATED_MODEL.match(line) |
717 result = LOCATED_MODEL.match(line) |
693 if result: |
718 if result: |
694 # Get the resulting dict |
719 # Get the resulting dict |
695 resdict = result.groupdict() |
720 resdict = result.groupdict() |
696 # rewrite string for variadic location as a tuple of integers |
721 # rewrite string for variadic location as a tuple of |
|
722 # integers |
697 resdict['LOC'] = tuple(map(int, resdict['LOC'].split(','))) |
723 resdict['LOC'] = tuple(map(int, resdict['LOC'].split(','))) |
698 # set located size to 'X' if not given |
724 # set located size to 'X' if not given |
699 if not resdict['SIZE']: |
725 if not resdict['SIZE']: |
700 resdict['SIZE'] = 'X' |
726 resdict['SIZE'] = 'X' |
701 # finally store into located variable list |
727 # finally store into located variable list |
717 """ |
743 """ |
718 |
744 |
719 # Update PLCOpenEditor ConfNode Block types before generate ST code |
745 # Update PLCOpenEditor ConfNode Block types before generate ST code |
720 self.RefreshConfNodesBlockLists() |
746 self.RefreshConfNodesBlockLists() |
721 |
747 |
722 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")) |
723 # ask PLCOpenEditor controller to write ST/IL/SFC code file |
750 # ask PLCOpenEditor controller to write ST/IL/SFC code file |
724 _program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath()) |
751 _program, errors, warnings = self.GenerateProgram( |
|
752 self._getIECgeneratedcodepath()) |
725 if len(warnings) > 0: |
753 if len(warnings) > 0: |
726 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")) |
727 for warning in warnings: |
756 for warning in warnings: |
728 self.logger.write_warning("%s\n" % warning) |
757 self.logger.write_warning("%s\n" % warning) |
729 if len(errors) > 0: |
758 if len(errors) > 0: |
730 # Failed ! |
759 # Failed ! |
731 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]) |
732 return False |
762 return False |
733 plc_file = open(self._getIECcodepath(), "w") |
763 plc_file = open(self._getIECcodepath(), "w") |
734 # Add ST Library from confnodes |
764 # Add ST Library from confnodes |
735 plc_file.write(self.GetLibrariesSTCode()) |
765 plc_file.write(self.GetLibrariesSTCode()) |
736 if os.path.isfile(self._getIECrawcodepath()): |
766 if os.path.isfile(self._getIECrawcodepath()): |
792 if line[0] not in '\t \r\n': |
824 if line[0] not in '\t \r\n': |
793 last_section = line |
825 last_section = line |
794 |
826 |
795 if first_line <= i <= last_line: |
827 if first_line <= i <= last_line: |
796 if last_section is not None: |
828 if last_section is not None: |
797 self.logger.write_warning("In section: " + last_section) |
829 self.logger.write_warning( |
|
830 "In section: " + last_section) |
798 last_section = None # only write section once |
831 last_section = None # only write section once |
799 self.logger.write_warning("%04d: %s" % (i, line)) |
832 self.logger.write_warning("%04d: %s" % (i, line)) |
800 |
833 |
801 f.close() |
834 f.close() |
802 |
835 |
803 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) |
804 return False |
838 return False |
805 |
839 |
806 # Now extract C files of stdout |
840 # Now extract C files of stdout |
807 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"] |
808 # 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 |
809 C_files.remove("POUS.c") |
844 C_files.remove("POUS.c") |
810 if not C_files: |
845 if not C_files: |
811 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")) |
812 return False |
848 return False |
813 # transform those base names to full names with path |
849 # transform those base names to full names with path |
814 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) |
815 |
852 |
816 # prepend beremiz include to configuration header |
853 # prepend beremiz include to configuration header |
817 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"] |
818 H_files.remove("LOCATED_VARIABLES.h") |
856 H_files.remove("LOCATED_VARIABLES.h") |
819 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) |
820 for H_file in H_files: |
859 for H_file in H_files: |
821 with file(H_file, 'r') as original: |
860 with file(H_file, 'r') as original: |
822 data = original.read() |
861 data = original.read() |
823 with file(H_file, 'w') as modified: |
862 with file(H_file, 'w') as modified: |
824 modified.write('#include "beremiz.h"\n' + data) |
863 modified.write('#include "beremiz.h"\n' + data) |
825 |
864 |
826 self.logger.write(_("Extracting Located Variables...\n")) |
865 self.logger.write(_("Extracting Located Variables...\n")) |
827 # 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 |
828 self.PLCGeneratedLocatedVars = self.GetLocations() |
868 self.PLCGeneratedLocatedVars = self.GetLocations() |
829 # 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 |
830 self.PLCGeneratedCFiles = C_files |
870 self.PLCGeneratedCFiles = C_files |
831 # compute CFLAGS for plc |
871 # compute CFLAGS for plc |
832 self.plcCFLAGS = '"-I%s" -Wno-unused-function' % self.iec2c_cfg.getLibCPath() |
872 self.plcCFLAGS = '"-I%s" -Wno-unused-function' % self.iec2c_cfg.getLibCPath() |
900 if self._ProgramList is None or self._VariablesList is None: |
940 if self._ProgramList is None or self._VariablesList is None: |
901 try: |
941 try: |
902 csvfile = os.path.join(self._getBuildPath(), "VARIABLES.csv") |
942 csvfile = os.path.join(self._getBuildPath(), "VARIABLES.csv") |
903 # describes CSV columns |
943 # describes CSV columns |
904 ProgramsListAttributeName = ["num", "C_path", "type"] |
944 ProgramsListAttributeName = ["num", "C_path", "type"] |
905 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"] |
945 VariablesListAttributeName = [ |
|
946 "num", "vartype", "IEC_path", "C_path", "type"] |
906 self._ProgramList = [] |
947 self._ProgramList = [] |
907 self._VariablesList = [] |
948 self._VariablesList = [] |
908 self._DbgVariablesList = [] |
949 self._DbgVariablesList = [] |
909 self._IECPathToIdx = {} |
950 self._IECPathToIdx = {} |
910 |
951 |
920 ListGroup[-1].append(strippedline) |
961 ListGroup[-1].append(strippedline) |
921 |
962 |
922 # first section contains programs |
963 # first section contains programs |
923 for line in ListGroup[0]: |
964 for line in ListGroup[0]: |
924 # Split and Maps each field to dictionnary entries |
965 # Split and Maps each field to dictionnary entries |
925 attrs = dict(zip(ProgramsListAttributeName, line.strip().split(';'))) |
966 attrs = dict( |
|
967 zip(ProgramsListAttributeName, line.strip().split(';'))) |
926 # Truncate "C_path" to remove conf an resources names |
968 # Truncate "C_path" to remove conf an resources names |
927 attrs["C_path"] = '__'.join(attrs["C_path"].split(".", 2)[1:]) |
969 attrs["C_path"] = '__'.join( |
|
970 attrs["C_path"].split(".", 2)[1:]) |
928 # Push this dictionnary into result. |
971 # Push this dictionnary into result. |
929 self._ProgramList.append(attrs) |
972 self._ProgramList.append(attrs) |
930 |
973 |
931 # second section contains all variables |
974 # second section contains all variables |
932 config_FBs = {} |
975 config_FBs = {} |
933 Idx = 0 |
976 Idx = 0 |
934 for line in ListGroup[1]: |
977 for line in ListGroup[1]: |
935 # Split and Maps each field to dictionnary entries |
978 # Split and Maps each field to dictionnary entries |
936 attrs = dict(zip(VariablesListAttributeName, line.strip().split(';'))) |
979 attrs = dict( |
|
980 zip(VariablesListAttributeName, line.strip().split(';'))) |
937 # Truncate "C_path" to remove conf an resources names |
981 # Truncate "C_path" to remove conf an resources names |
938 parts = attrs["C_path"].split(".", 2) |
982 parts = attrs["C_path"].split(".", 2) |
939 if len(parts) > 2: |
983 if len(parts) > 2: |
940 config_FB = config_FBs.get(tuple(parts[:2])) |
984 config_FB = config_FBs.get(tuple(parts[:2])) |
941 if config_FB: |
985 if config_FB: |
1032 "void __retrieve_%(s)s(void);\n" + |
1077 "void __retrieve_%(s)s(void);\n" + |
1033 "void __publish_%(s)s(void);") % {'s': locstr} for locstr in locstrs]), |
1078 "void __publish_%(s)s(void);") % {'s': locstr} for locstr in locstrs]), |
1034 "retrieve_calls": "\n ".join([ |
1079 "retrieve_calls": "\n ".join([ |
1035 "__retrieve_%s();" % locstr for locstr in locstrs]), |
1080 "__retrieve_%s();" % locstr for locstr in locstrs]), |
1036 "publish_calls": "\n ".join([ # Call publish in reverse order |
1081 "publish_calls": "\n ".join([ # Call publish in reverse order |
1037 "__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)]), |
1038 "init_calls": "\n ".join([ |
1083 "init_calls": "\n ".join([ |
1039 "init_level=%d; " % (i+1) + |
1084 "init_level=%d; " % (i + 1) + |
1040 "if((res = __init_%s(argc,argv))){" % locstr + |
1085 "if((res = __init_%s(argc,argv))){" % locstr + |
1041 # "printf(\"%s\"); "%locstr + #for debug |
1086 # "printf(\"%s\"); "%locstr + #for debug |
1042 "return res;}" for i, locstr in enumerate(locstrs)]), |
1087 "return res;}" for i, locstr in enumerate(locstrs)]), |
1043 "cleanup_calls": "\n ".join([ |
1088 "cleanup_calls": "\n ".join([ |
1044 "if(init_level >= %d) " % i + |
1089 "if(init_level >= %d) " % i + |
1045 "__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)]) |
1046 } |
1091 } |
1047 else: |
1092 else: |
1048 plc_main_code = targets.GetCode("plc_main_head.c") % { |
1093 plc_main_code = targets.GetCode("plc_main_head.c") % { |
1049 "calls_prototypes": "\n", |
1094 "calls_prototypes": "\n", |
1050 "retrieve_calls": "\n", |
1095 "retrieve_calls": "\n", |
1051 "publish_calls": "\n", |
1096 "publish_calls": "\n", |
1052 "init_calls": "\n", |
1097 "init_calls": "\n", |
1053 "cleanup_calls": "\n" |
1098 "cleanup_calls": "\n" |
1054 } |
1099 } |
1055 plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent().getLocalTag()) |
1100 plc_main_code += targets.GetTargetCode( |
|
1101 self.GetTarget().getcontent().getLocalTag()) |
1056 plc_main_code += targets.GetCode("plc_main_tail.c") |
1102 plc_main_code += targets.GetCode("plc_main_tail.c") |
1057 return plc_main_code |
1103 return plc_main_code |
1058 |
1104 |
1059 def _Build(self): |
1105 def _Build(self): |
1060 """ |
1106 """ |
1122 try: |
1168 try: |
1123 CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C( |
1169 CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C( |
1124 buildpath, |
1170 buildpath, |
1125 self.PLCGeneratedLocatedVars) |
1171 self.PLCGeneratedLocatedVars) |
1126 except Exception: |
1172 except Exception: |
1127 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")) |
1128 self.logger.write_error(traceback.format_exc()) |
1175 self.logger.write_error(traceback.format_exc()) |
1129 self.ResetBuildMD5() |
1176 self.ResetBuildMD5() |
1130 return False |
1177 return False |
1131 |
1178 |
1132 # Generate C code and compilation params from liraries |
1179 # Generate C code and compilation params from liraries |
1133 try: |
1180 try: |
1134 LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath) |
1181 LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode( |
|
1182 buildpath) |
1135 except Exception: |
1183 except Exception: |
1136 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")) |
1137 self.logger.write_error(traceback.format_exc()) |
1186 self.logger.write_error(traceback.format_exc()) |
1138 self.ResetBuildMD5() |
1187 self.ResetBuildMD5() |
1139 return False |
1188 return False |
1140 |
1189 |
1141 self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + CTNLocationCFilesAndCFLAGS |
1190 self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + \ |
|
1191 CTNLocationCFilesAndCFLAGS |
1142 self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS |
1192 self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS |
1143 ExtraFiles = CTNExtraFiles + LibExtraFiles |
1193 ExtraFiles = CTNExtraFiles + LibExtraFiles |
1144 |
1194 |
1145 # Get temporary directory path |
1195 # Get temporary directory path |
1146 extrafilespath = self._getExtraFilesPath() |
1196 extrafilespath = self._getExtraFilesPath() |
1174 code = generator() |
1225 code = generator() |
1175 if code is None: |
1226 if code is None: |
1176 raise Exception |
1227 raise Exception |
1177 code_path = os.path.join(buildpath, filename) |
1228 code_path = os.path.join(buildpath, filename) |
1178 open(code_path, "w").write(code) |
1229 open(code_path, "w").write(code) |
1179 # Insert this file as first file to be compiled at root confnode |
1230 # Insert this file as first file to be compiled at root |
1180 self.LocationCFilesAndCFLAGS[0][1].insert(0, (code_path, self.plcCFLAGS)) |
1231 # confnode |
|
1232 self.LocationCFilesAndCFLAGS[0][1].insert( |
|
1233 0, (code_path, self.plcCFLAGS)) |
1181 except Exception: |
1234 except Exception: |
1182 self.logger.write_error(name+_(" generation failed !\n")) |
1235 self.logger.write_error(name + _(" generation failed !\n")) |
1183 self.logger.write_error(traceback.format_exc()) |
1236 self.logger.write_error(traceback.format_exc()) |
1184 self.ResetBuildMD5() |
1237 self.ResetBuildMD5() |
1185 return False |
1238 return False |
1186 self.logger.write(_("C code generated successfully.\n")) |
1239 self.logger.write(_("C code generated successfully.\n")) |
1187 return True |
1240 return True |
1188 |
1241 |
1189 def ShowError(self, logger, from_location, to_location): |
1242 def ShowError(self, logger, from_location, to_location): |
1190 chunk_infos = self.GetChunkInfos(from_location, to_location) |
1243 chunk_infos = self.GetChunkInfos(from_location, to_location) |
1191 for infos, (start_row, start_col) in chunk_infos: |
1244 for infos, (start_row, start_col) in chunk_infos: |
1192 row = 1 if from_location[0] < start_row else (from_location[0] - start_row) |
1245 row = 1 if from_location[0] < start_row else ( |
1193 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) |
1194 start = (row, col) |
1249 start = (row, col) |
1195 |
1250 |
1196 row = 1 if to_location[0] < start_row else (to_location[0] - start_row) |
1251 row = 1 if to_location[0] < start_row else ( |
1197 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) |
1198 end = (row, col) |
1255 end = (row, col) |
1199 |
1256 |
1200 if self.AppFrame is not None: |
1257 if self.AppFrame is not None: |
1201 self.AppFrame.ShowError(infos, start, end) |
1258 self.AppFrame.ShowError(infos, start, end) |
1202 |
1259 |
1223 def _OpenView(self, name=None, onlyopened=False): |
1280 def _OpenView(self, name=None, onlyopened=False): |
1224 if name == "IEC code": |
1281 if name == "IEC code": |
1225 if self._IECCodeView is None: |
1282 if self._IECCodeView is None: |
1226 plc_file = self._getIECcodepath() |
1283 plc_file = self._getIECcodepath() |
1227 |
1284 |
1228 self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name) |
1285 self._IECCodeView = IECCodeViewer( |
|
1286 self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name) |
1229 self._IECCodeView.SetTextSyntax("ALL") |
1287 self._IECCodeView.SetTextSyntax("ALL") |
1230 self._IECCodeView.SetKeywords(IEC_KEYWORDS) |
1288 self._IECCodeView.SetKeywords(IEC_KEYWORDS) |
1231 try: |
1289 try: |
1232 text = file(plc_file).read() |
1290 text = file(plc_file).read() |
1233 except Exception: |
1291 except Exception: |
1244 |
1302 |
1245 elif name == "IEC raw code": |
1303 elif name == "IEC raw code": |
1246 if self._IECRawCodeView is None: |
1304 if self._IECRawCodeView is None: |
1247 controler = MiniTextControler(self._getIECrawcodepath(), self) |
1305 controler = MiniTextControler(self._getIECrawcodepath(), self) |
1248 |
1306 |
1249 self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name) |
1307 self._IECRawCodeView = IECCodeViewer( |
|
1308 self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name) |
1250 self._IECRawCodeView.SetTextSyntax("ALL") |
1309 self._IECRawCodeView.SetTextSyntax("ALL") |
1251 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS) |
1310 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS) |
1252 self._IECRawCodeView.RefreshView() |
1311 self._IECRawCodeView.RefreshView() |
1253 self._IECRawCodeView.SetIcon(GetBitmap("ST")) |
1312 self._IECRawCodeView.SetIcon(GetBitmap("ST")) |
1254 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor) |
1313 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor) |
1258 |
1317 |
1259 return self._IECRawCodeView |
1318 return self._IECRawCodeView |
1260 |
1319 |
1261 elif name == "Project Files": |
1320 elif name == "Project Files": |
1262 if self._ProjectFilesView is None: |
1321 if self._ProjectFilesView is None: |
1263 self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True) |
1322 self._ProjectFilesView = FileManagementPanel( |
|
1323 self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True) |
1264 |
1324 |
1265 extensions = [] |
1325 extensions = [] |
1266 for extension, name, editor in features.file_editors: |
1326 for extension, name, editor in features.file_editors: |
1267 if extension not in extensions: |
1327 if extension not in extensions: |
1268 extensions.append(extension) |
1328 extensions.append(extension) |
1300 |
1360 |
1301 if editor_name != "": |
1361 if editor_name != "": |
1302 name = "::".join([filepath, editor_name]) |
1362 name = "::".join([filepath, editor_name]) |
1303 |
1363 |
1304 editor = editors[editor_name]() |
1364 editor = editors[editor_name]() |
1305 self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame) |
1365 self._FileEditors[filepath] = editor( |
|
1366 self.AppFrame.TabsOpened, self, name, self.AppFrame) |
1306 self._FileEditors[filepath].SetIcon(GetBitmap("FILE")) |
1367 self._FileEditors[filepath].SetIcon(GetBitmap("FILE")) |
1307 if isinstance(self._FileEditors[filepath], DebugViewer): |
1368 if isinstance(self._FileEditors[filepath], DebugViewer): |
1308 self._FileEditors[filepath].SetDataProducer(self) |
1369 self._FileEditors[filepath].SetDataProducer(self) |
1309 |
1370 |
1310 if filepath in self._FileEditors: |
1371 if filepath in self._FileEditors: |
1349 |
1410 |
1350 def UpdatePLCLog(self, log_count): |
1411 def UpdatePLCLog(self, log_count): |
1351 if log_count: |
1412 if log_count: |
1352 if self.AppFrame is not None: |
1413 if self.AppFrame is not None: |
1353 self.AppFrame.LogViewer.SetLogCounters(log_count) |
1414 self.AppFrame.LogViewer.SetLogCounters(log_count) |
|
1415 |
|
1416 DefaultMethods = { |
|
1417 "_Run": False, |
|
1418 "_Stop": False, |
|
1419 "_Transfer": False, |
|
1420 "_Connect": True, |
|
1421 "_Disconnect": False |
|
1422 } |
|
1423 |
|
1424 MethodsFromStatus = { |
|
1425 "Started": {"_Stop": True, |
|
1426 "_Transfer": True, |
|
1427 "_Connect": False, |
|
1428 "_Disconnect": True}, |
|
1429 "Stopped": {"_Run": True, |
|
1430 "_Transfer": True, |
|
1431 "_Connect": False, |
|
1432 "_Disconnect": True}, |
|
1433 "Empty": {"_Transfer": True, |
|
1434 "_Connect": False, |
|
1435 "_Disconnect": True}, |
|
1436 "Broken": {"_Connect": False, |
|
1437 "_Disconnect": True}, |
|
1438 "Disconnected": {}, |
|
1439 } |
1354 |
1440 |
1355 def UpdateMethodsFromPLCStatus(self): |
1441 def UpdateMethodsFromPLCStatus(self): |
1356 updated = False |
1442 updated = False |
1357 status = None |
1443 status = None |
1358 if self._connector is not None: |
1444 if self._connector is not None: |
1362 self.UpdatePLCLog(log_count) |
1448 self.UpdatePLCLog(log_count) |
1363 if status is None: |
1449 if status is None: |
1364 self._SetConnector(None, False) |
1450 self._SetConnector(None, False) |
1365 status = "Disconnected" |
1451 status = "Disconnected" |
1366 if self.previous_plcstate != status: |
1452 if self.previous_plcstate != status: |
1367 for args in { |
1453 allmethods = self.DefaultMethods.copy() |
1368 "Started": [("_Run", False), |
1454 allmethods.update( |
1369 ("_Stop", True)], |
1455 self.MethodsFromStatus.get(status, {})) |
1370 "Stopped": [("_Run", True), |
1456 for method, active in allmethods.items(): |
1371 ("_Stop", False)], |
1457 self.ShowMethod(method, active) |
1372 "Empty": [("_Run", False), |
|
1373 ("_Stop", False)], |
|
1374 "Broken": [], |
|
1375 "Disconnected": [("_Run", False), |
|
1376 ("_Stop", False), |
|
1377 ("_Transfer", False), |
|
1378 ("_Connect", True), |
|
1379 ("_Disconnect", False)], |
|
1380 }.get(status, []): |
|
1381 self.ShowMethod(*args) |
|
1382 self.previous_plcstate = status |
1458 self.previous_plcstate = status |
1383 if self.AppFrame is not None: |
1459 if self.AppFrame is not None: |
1384 updated = True |
1460 updated = True |
1385 self.AppFrame.RefreshStatusToolBar() |
1461 self.AppFrame.RefreshStatusToolBar() |
1386 if status == "Disconnected": |
1462 if status == "Disconnected": |
1387 self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 1) |
1463 self.AppFrame.ConnectionStatusBar.SetStatusText( |
|
1464 self.GetTextStatus(status), 1) |
1388 self.AppFrame.ConnectionStatusBar.SetStatusText('', 2) |
1465 self.AppFrame.ConnectionStatusBar.SetStatusText('', 2) |
1389 else: |
1466 else: |
1390 self.AppFrame.ConnectionStatusBar.SetStatusText( |
1467 self.AppFrame.ConnectionStatusBar.SetStatusText( |
1391 _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1) |
1468 _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1) |
1392 self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 2) |
1469 self.AppFrame.ConnectionStatusBar.SetStatusText( |
|
1470 self.GetTextStatus(status), 2) |
1393 return updated |
1471 return updated |
1394 |
1472 |
1395 def GetTextStatus(self, status): |
1473 def GetTextStatus(self, status): |
1396 msgs = { |
1474 msgs = { |
1397 "Started": _("Started"), |
1475 "Started": _("Started"), |
1398 "Stopped": _("Stopped"), |
1476 "Stopped": _("Stopped"), |
1399 "Empty": _("Empty"), |
1477 "Empty": _("Empty"), |
1400 "Broken": _("Broken"), |
1478 "Broken": _("Broken"), |
1401 "Disconnected": _("Disconnected") |
1479 "Disconnected": _("Disconnected") |
1402 } |
1480 } |
1403 return msgs.get(status, status) |
1481 return msgs.get(status, status) |
1404 |
1482 |
1405 def ShowPLCProgress(self, status="", progress=0): |
1483 def ShowPLCProgress(self, status="", progress=0): |
1406 self.AppFrame.ProgressStatusBar.Show() |
1484 self.AppFrame.ProgressStatusBar.Show() |
1407 self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 1) |
1485 self.AppFrame.ConnectionStatusBar.SetStatusText( |
|
1486 self.GetTextStatus(status), 1) |
1408 self.AppFrame.ProgressStatusBar.SetValue(progress) |
1487 self.AppFrame.ProgressStatusBar.SetValue(progress) |
1409 |
1488 |
1410 def HidePLCProgress(self): |
1489 def HidePLCProgress(self): |
1411 # clear previous_plcstate to restore status |
1490 # clear previous_plcstate to restore status |
1412 # in UpdateMethodsFromPLCStatus() |
1491 # in UpdateMethodsFromPLCStatus() |
1418 self.UpdateMethodsFromPLCStatus() |
1497 self.UpdateMethodsFromPLCStatus() |
1419 |
1498 |
1420 def SnapshotAndResetDebugValuesBuffers(self): |
1499 def SnapshotAndResetDebugValuesBuffers(self): |
1421 if self._connector is not None: |
1500 if self._connector is not None: |
1422 plc_status, Traces = self._connector.GetTraceVariables() |
1501 plc_status, Traces = self._connector.GetTraceVariables() |
1423 # 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()] |
1424 if plc_status == "Started": |
1504 if plc_status == "Started": |
1425 if len(Traces) > 0: |
1505 if len(Traces) > 0: |
1426 for debug_tick, debug_buff in Traces: |
1506 for debug_tick, debug_buff in Traces: |
1427 debug_vars = UnpackDebugBuffer(debug_buff, self.TracedIECTypes) |
1507 debug_vars = UnpackDebugBuffer( |
|
1508 debug_buff, self.TracedIECTypes) |
1428 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): |
1429 for IECPath, values_buffer, value in izip( |
1510 for IECPath, values_buffer, value in izip( |
1430 self.TracedIECPath, |
1511 self.TracedIECPath, |
1431 self.DebugValuesBuffers, |
1512 self.DebugValuesBuffers, |
1432 debug_vars): |
1513 debug_vars): |
1433 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1514 IECdebug_data = self.IECdebug_datas.get( |
|
1515 IECPath, None) |
1434 if IECdebug_data is not None and value is not None: |
1516 if IECdebug_data is not None and value is not None: |
1435 forced = IECdebug_data[2:4] == ["Forced", value] |
1517 forced = IECdebug_data[2:4] == [ |
|
1518 "Forced", value] |
1436 if not IECdebug_data[4] and len(values_buffer) > 0: |
1519 if not IECdebug_data[4] and len(values_buffer) > 0: |
1437 values_buffer[-1] = (value, forced) |
1520 values_buffer[-1] = (value, forced) |
1438 else: |
1521 else: |
1439 values_buffer.append((value, forced)) |
1522 values_buffer.append((value, forced)) |
1440 self.DebugTicks.append(debug_tick) |
1523 self.DebugTicks.append(debug_tick) |
1459 # Callable Dict is empty. |
1542 # Callable Dict is empty. |
1460 # This variable is not needed anymore! |
1543 # This variable is not needed anymore! |
1461 IECPathsToPop.append(IECPath) |
1544 IECPathsToPop.append(IECPath) |
1462 elif IECPath != "__tick__": |
1545 elif IECPath != "__tick__": |
1463 # Convert |
1546 # Convert |
1464 Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None)) |
1547 Idx, IEC_Type = self._IECPathToIdx.get( |
|
1548 IECPath, (None, None)) |
1465 if Idx is not None: |
1549 if Idx is not None: |
1466 if IEC_Type in DebugTypesSize: |
1550 if IEC_Type in DebugTypesSize: |
1467 Idxs.append((Idx, IEC_Type, fvalue, IECPath)) |
1551 Idxs.append((Idx, IEC_Type, fvalue, IECPath)) |
1468 else: |
1552 else: |
1469 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) |
1470 else: |
1555 else: |
1471 self.logger.write_warning(_("Debug: Unknown variable '%s'\n") % IECPath) |
1556 self.logger.write_warning( |
|
1557 _("Debug: Unknown variable '%s'\n") % IECPath) |
1472 for IECPathToPop in IECPathsToPop: |
1558 for IECPathToPop in IECPathsToPop: |
1473 self.IECdebug_datas.pop(IECPathToPop) |
1559 self.IECdebug_datas.pop(IECPathToPop) |
1474 |
1560 |
1475 if Idxs: |
1561 if Idxs: |
1476 Idxs.sort() |
1562 Idxs.sort() |
1493 # Prevent to call RegisterDebugVarToConnector when PLC is not started |
1579 # Prevent to call RegisterDebugVarToConnector when PLC is not started |
1494 # 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 |
1495 # Links between PLC located variables and real variables are not ready |
1581 # Links between PLC located variables and real variables are not ready |
1496 if self.IsPLCStarted(): |
1582 if self.IsPLCStarted(): |
1497 # Timer to prevent rapid-fire when registering many variables |
1583 # Timer to prevent rapid-fire when registering many variables |
1498 # 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 |
1499 self.DebugTimer = Timer(0.5, wx.CallAfter, args=[self.RegisterDebugVarToConnector]) |
1585 # instead |
|
1586 self.DebugTimer = Timer( |
|
1587 0.5, wx.CallAfter, args=[self.RegisterDebugVarToConnector]) |
1500 # Rearm anti-rapid-fire timer |
1588 # Rearm anti-rapid-fire timer |
1501 self.DebugTimer.start() |
1589 self.DebugTimer.start() |
1502 |
1590 |
1503 def GetDebugIECVariableType(self, IECPath): |
1591 def GetDebugIECVariableType(self, IECPath): |
1504 _Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None)) |
1592 _Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None)) |
1598 debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers() |
1686 debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers() |
1599 start_time = time.time() |
1687 start_time = time.time() |
1600 if len(self.TracedIECPath) == len(buffers): |
1688 if len(self.TracedIECPath) == len(buffers): |
1601 for IECPath, values in izip(self.TracedIECPath, buffers): |
1689 for IECPath, values in izip(self.TracedIECPath, buffers): |
1602 if len(values) > 0: |
1690 if len(values) > 0: |
1603 self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values) |
1691 self.CallWeakcallables( |
|
1692 IECPath, "NewValues", debug_ticks, values) |
1604 if len(debug_ticks) > 0: |
1693 if len(debug_ticks) > 0: |
1605 self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks) |
1694 self.CallWeakcallables( |
|
1695 "__tick__", "NewDataAvailable", debug_ticks) |
1606 |
1696 |
1607 delay = time.time() - start_time |
1697 delay = time.time() - start_time |
1608 next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay) |
1698 next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay) |
1609 if self.DispatchDebugValuesTimer is not None: |
1699 if self.DispatchDebugValuesTimer is not None: |
1610 self.DispatchDebugValuesTimer.Start( |
1700 self.DispatchDebugValuesTimer.Start( |
1703 |
1794 |
1704 # Get connector from uri |
1795 # Get connector from uri |
1705 try: |
1796 try: |
1706 self._SetConnector(connectors.ConnectorFactory(uri, self)) |
1797 self._SetConnector(connectors.ConnectorFactory(uri, self)) |
1707 except Exception: |
1798 except Exception: |
1708 self.logger.write_error(_("Exception while connecting %s!\n") % uri) |
1799 self.logger.write_error( |
|
1800 _("Exception while connecting %s!\n") % uri) |
1709 self.logger.write_error(traceback.format_exc()) |
1801 self.logger.write_error(traceback.format_exc()) |
1710 |
1802 |
1711 # Did connection success ? |
1803 # Did connection success ? |
1712 if self._connector is None: |
1804 if self._connector is None: |
1713 # Oups. |
1805 # Oups. |
1714 self.logger.write_error(_("Connection failed to %s!\n") % uri) |
1806 self.logger.write_error(_("Connection failed to %s!\n") % uri) |
1715 else: |
1807 else: |
1716 self.ShowMethod("_Connect", False) |
|
1717 self.ShowMethod("_Disconnect", True) |
|
1718 self.ShowMethod("_Transfer", True) |
|
1719 |
|
1720 self.CompareLocalAndRemotePLC() |
1808 self.CompareLocalAndRemotePLC() |
1721 |
1809 |
1722 # Init with actual PLC status and print it |
1810 # Init with actual PLC status and print it |
1723 self.UpdateMethodsFromPLCStatus() |
1811 self.UpdateMethodsFromPLCStatus() |
1724 if self.previous_plcstate in ["Started", "Stopped"]: |
1812 if self.previous_plcstate in ["Started", "Stopped"]: |
1725 if self.DebugAvailable() and self.GetIECProgramsAndVariables(): |
1813 if self.DebugAvailable() and self.GetIECProgramsAndVariables(): |
1726 self.logger.write(_("Debugger ready\n")) |
1814 self.logger.write(_("Debugger ready\n")) |
1727 self._connect_debug() |
1815 self._connect_debug() |
1728 else: |
1816 else: |
1729 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")) |
1730 |
1819 |
1731 def CompareLocalAndRemotePLC(self): |
1820 def CompareLocalAndRemotePLC(self): |
1732 if self._connector is None: |
1821 if self._connector is None: |
1733 return |
1822 return |
1734 # We are now connected. Update button status |
1823 # We are now connected. Update button status |
1735 MD5 = self.GetLastBuildMD5() |
1824 MD5 = self.GetLastBuildMD5() |
1736 # Check remote target PLC correspondance to that md5 |
1825 # Check remote target PLC correspondance to that md5 |
1737 if MD5 is not None: |
1826 if MD5 is not None: |
1738 if not self._connector.MatchMD5(MD5): |
1827 if not self._connector.MatchMD5(MD5): |
1739 # self.logger.write_warning( |
1828 # self.logger.write_warning( |
1740 # _("Latest build does not match with target, please transfer.\n")) |
1829 # _("Latest build does not match with target, please |
|
1830 # transfer.\n")) |
1741 self.EnableMethod("_Transfer", True) |
1831 self.EnableMethod("_Transfer", True) |
1742 else: |
1832 else: |
1743 # self.logger.write( |
1833 # self.logger.write( |
1744 # _("Latest build matches target, no transfer needed.\n")) |
1834 # _("Latest build matches target, no transfer needed.\n")) |
1745 self.EnableMethod("_Transfer", True) |
1835 self.EnableMethod("_Transfer", True) |