467 before PlugClass.__init__, and to do the file related stuff. |
470 before PlugClass.__init__, and to do the file related stuff. |
468 """ |
471 """ |
469 def __init__(_self): |
472 def __init__(_self): |
470 # self is the parent |
473 # self is the parent |
471 _self.PlugParent = self |
474 _self.PlugParent = self |
|
475 # self is the parent |
|
476 _self.logger = self.logger |
472 # Keep track of the plugin type name |
477 # Keep track of the plugin type name |
473 _self.PlugType = PlugType |
478 _self.PlugType = PlugType |
474 # remind the help string, for more fancy display |
479 # remind the help string, for more fancy display |
475 _self.PlugHelp = PlugHelp |
480 _self.PlugHelp = PlugHelp |
476 # Call the base plugin template init - change XSD into class members |
481 # Call the base plugin template init - change XSD into class members |
477 PlugTemplate.__init__(_self) |
482 PlugTemplate.__init__(_self) |
478 # check name is unique |
483 # check name is unique |
479 NewPlugName = _self.FindNewName(PlugName, logger) |
484 NewPlugName = _self.FindNewName(PlugName) |
480 # If dir have already be made, and file exist |
485 # If dir have already be made, and file exist |
481 if os.path.isdir(_self.PlugPath(NewPlugName)): #and os.path.isfile(_self.PluginXmlFilePath(PlugName)): |
486 if os.path.isdir(_self.PlugPath(NewPlugName)): #and os.path.isfile(_self.PluginXmlFilePath(PlugName)): |
482 #Load the plugin.xml file into parameters members |
487 #Load the plugin.xml file into parameters members |
483 _self.LoadXMLParams(logger, NewPlugName) |
488 _self.LoadXMLParams(NewPlugName) |
484 # Basic check. Better to fail immediately. |
489 # Basic check. Better to fail immediately. |
485 if (_self.BaseParams.getName() != NewPlugName): |
490 if (_self.BaseParams.getName() != NewPlugName): |
486 raise Exception, "Project tree layout do not match plugin.xml %s!=%s "%(NewPlugName, _self.BaseParams.getName()) |
491 raise Exception, "Project tree layout do not match plugin.xml %s!=%s "%(NewPlugName, _self.BaseParams.getName()) |
487 |
492 |
488 # Now, self.PlugPath() should be OK |
493 # Now, self.PlugPath() should be OK |
489 |
494 |
490 # Check that IEC_Channel is not already in use. |
495 # Check that IEC_Channel is not already in use. |
491 _self.FindNewIEC_Channel(_self.BaseParams.getIEC_Channel(),logger) |
496 _self.FindNewIEC_Channel(_self.BaseParams.getIEC_Channel()) |
492 # Call the plugin real __init__ |
497 # Call the plugin real __init__ |
493 if getattr(PlugClass, "__init__", None): |
498 if getattr(PlugClass, "__init__", None): |
494 PlugClass.__init__(_self) |
499 PlugClass.__init__(_self) |
495 #Load and init all the childs |
500 #Load and init all the childs |
496 _self.LoadChilds(logger) |
501 _self.LoadChilds() |
497 #just loaded, nothing to saved |
502 #just loaded, nothing to saved |
498 _self.ChangesToSave = False |
503 _self.ChangesToSave = False |
499 else: |
504 else: |
500 # If plugin do not have corresponding file/dirs - they will be created on Save |
505 # If plugin do not have corresponding file/dirs - they will be created on Save |
501 os.mkdir(_self.PlugPath()) |
506 os.mkdir(_self.PlugPath()) |
517 PluggedChildsWithSameClass.append(newPluginOpj) |
522 PluggedChildsWithSameClass.append(newPluginOpj) |
518 |
523 |
519 return newPluginOpj |
524 return newPluginOpj |
520 |
525 |
521 |
526 |
522 def LoadXMLParams(self, logger, PlugName = None): |
527 def LoadXMLParams(self, PlugName = None): |
523 methode_name = os.path.join(self.PlugPath(PlugName), "methods.py") |
528 methode_name = os.path.join(self.PlugPath(PlugName), "methods.py") |
524 if os.path.isfile(methode_name): |
529 if os.path.isfile(methode_name): |
525 execfile(methode_name) |
530 execfile(methode_name) |
526 |
531 |
527 # Get the base xml tree |
532 # Get the base xml tree |
528 if self.MandatoryParams: |
533 if self.MandatoryParams: |
529 #try: |
534 try: |
530 basexmlfile = open(self.PluginBaseXmlFilePath(PlugName), 'r') |
535 basexmlfile = open(self.PluginBaseXmlFilePath(PlugName), 'r') |
531 basetree = minidom.parse(basexmlfile) |
536 basetree = minidom.parse(basexmlfile) |
532 self.MandatoryParams[1].loadXMLTree(basetree.childNodes[0]) |
537 self.MandatoryParams[1].loadXMLTree(basetree.childNodes[0]) |
533 basexmlfile.close() |
538 basexmlfile.close() |
534 #except Exception, e: |
539 except Exception, exc: |
535 # logger.write_error("Couldn't load plugin base parameters %s :\n %s" % (PlugName, str(e))) |
540 self.logger.write_error("Couldn't load plugin base parameters %s :\n %s" % (PlugName, str(exc))) |
536 |
541 self.logger.write_error(traceback.format_exc()) |
537 |
542 |
538 # Get the xml tree |
543 # Get the xml tree |
539 if self.PlugParams: |
544 if self.PlugParams: |
540 #try: |
545 try: |
541 xmlfile = open(self.PluginXmlFilePath(PlugName), 'r') |
546 xmlfile = open(self.PluginXmlFilePath(PlugName), 'r') |
542 tree = minidom.parse(xmlfile) |
547 tree = minidom.parse(xmlfile) |
543 self.PlugParams[1].loadXMLTree(tree.childNodes[0]) |
548 self.PlugParams[1].loadXMLTree(tree.childNodes[0]) |
544 xmlfile.close() |
549 xmlfile.close() |
545 #except Exception, e: |
550 except Exception, exc: |
546 # logger.write_error("Couldn't load plugin parameters %s :\n %s" % (PlugName, str(e))) |
551 self.logger.write_error("Couldn't load plugin parameters %s :\n %s" % (PlugName, str(exc))) |
547 |
552 self.logger.write_error(traceback.format_exc()) |
548 def LoadChilds(self, logger): |
553 |
|
554 def LoadChilds(self): |
549 # Iterate over all PlugName@PlugType in plugin directory, and try to open them |
555 # Iterate over all PlugName@PlugType in plugin directory, and try to open them |
550 for PlugDir in os.listdir(self.PlugPath()): |
556 for PlugDir in os.listdir(self.PlugPath()): |
551 if os.path.isdir(os.path.join(self.PlugPath(), PlugDir)) and \ |
557 if os.path.isdir(os.path.join(self.PlugPath(), PlugDir)) and \ |
552 PlugDir.count(NameTypeSeparator) == 1: |
558 PlugDir.count(NameTypeSeparator) == 1: |
553 pname, ptype = PlugDir.split(NameTypeSeparator) |
559 pname, ptype = PlugDir.split(NameTypeSeparator) |
554 #try: |
560 try: |
555 self.PlugAddChild(pname, ptype, logger) |
561 self.PlugAddChild(pname, ptype) |
556 #except Exception, e: |
562 except Exception, exc: |
557 # logger.write_error("Could not add child \"%s\", type %s :\n%s\n"%(pname, ptype, str(e))) |
563 self.logger.write_error("Could not add child \"%s\", type %s :\n%s\n"%(pname, ptype, str(exc))) |
|
564 self.logger.write_error(traceback.format_exc()) |
558 |
565 |
559 def EnableMethod(self, method, value): |
566 def EnableMethod(self, method, value): |
560 for d in self.PluginMethods: |
567 for d in self.PluginMethods: |
561 if d["method"]==method: |
568 if d["method"]==method: |
562 d["enabled"]=value |
569 d["enabled"]=value |
|
570 return True |
|
571 return False |
|
572 |
|
573 def ShowMethod(self, method, value): |
|
574 for d in self.PluginMethods: |
|
575 if d["method"]==method: |
|
576 d["shown"]=value |
563 return True |
577 return True |
564 return False |
578 return False |
565 |
579 |
566 def _GetClassFunction(name): |
580 def _GetClassFunction(name): |
567 def GetRootClass(): |
581 def GetRootClass(): |
617 <xsd:complexType> |
640 <xsd:complexType> |
618 <xsd:sequence> |
641 <xsd:sequence> |
619 <xsd:element name="TargetType"> |
642 <xsd:element name="TargetType"> |
620 <xsd:complexType> |
643 <xsd:complexType> |
621 <xsd:choice> |
644 <xsd:choice> |
622 <xsd:element name="Win32"> |
645 """+targets.targetchoices+""" |
623 <xsd:complexType> |
|
624 <xsd:attribute name="Priority" type="xsd:integer" use="required"/> |
|
625 </xsd:complexType> |
|
626 </xsd:element> |
|
627 <xsd:element name="Linux"> |
|
628 <xsd:complexType> |
|
629 <xsd:attribute name="Nice" type="xsd:integer" use="required"/> |
|
630 </xsd:complexType> |
|
631 </xsd:element> |
|
632 <xsd:element name="Xenomai"> |
|
633 <xsd:complexType> |
|
634 <xsd:attribute name="xeno_config" type="xsd:string" use="optional" default="/usr/xenomai/"/> |
|
635 <xsd:attribute name="Priority" type="xsd:integer" use="required"/> |
|
636 </xsd:complexType> |
|
637 </xsd:element> |
|
638 <xsd:element name="RTAI"> |
|
639 <xsd:complexType> |
|
640 <xsd:attribute name="rtai_config" type="xsd:string" use="required"/> |
|
641 <xsd:attribute name="Priority" type="xsd:integer" use="required"/> |
|
642 </xsd:complexType> |
|
643 </xsd:element> |
|
644 <xsd:element name="Library"> |
|
645 <xsd:complexType> |
|
646 <xsd:attribute name="Dynamic" type="xsd:boolean" use="optional" default="true"/> |
|
647 </xsd:complexType> |
|
648 </xsd:element> |
|
649 </xsd:choice> |
|
650 </xsd:complexType> |
|
651 </xsd:element> |
|
652 <xsd:element name="Connection"> |
|
653 <xsd:complexType> |
|
654 <xsd:choice> |
|
655 <xsd:element name="Local"/> |
|
656 <xsd:element name="TCP_IP"> |
|
657 <xsd:complexType> |
|
658 <xsd:attribute name="Host" type="xsd:string" use="required"/> |
|
659 </xsd:complexType> |
|
660 </xsd:element> |
|
661 </xsd:choice> |
646 </xsd:choice> |
662 </xsd:complexType> |
647 </xsd:complexType> |
663 </xsd:element> |
648 </xsd:element> |
664 </xsd:sequence> |
649 </xsd:sequence> |
665 <xsd:attribute name="Compiler" type="xsd:string" use="optional" default="gcc"/> |
|
666 <xsd:attribute name="CFLAGS" type="xsd:string" use="required"/> |
|
667 <xsd:attribute name="Linker" type="xsd:string" use="optional" default="ld"/> |
|
668 <xsd:attribute name="LDFLAGS" type="xsd:string" use="required"/> |
|
669 <xsd:attribute name="Sync_Align_Ratio" use="optional" default="50"> |
|
670 <xsd:simpleType> |
|
671 <xsd:restriction base="xsd:integer"> |
|
672 <xsd:minInclusive value="1"/> |
|
673 <xsd:maxInclusive value="99"/> |
|
674 </xsd:restriction> |
|
675 </xsd:simpleType> |
|
676 </xsd:attribute> |
|
677 </xsd:complexType> |
650 </xsd:complexType> |
678 </xsd:element> |
651 </xsd:element> |
679 </xsd:schema> |
652 </xsd:schema> |
680 """ |
653 """ |
681 |
654 |
682 def __init__(self, frame): |
655 def __init__(self, frame, logger): |
683 PLCControler.__init__(self) |
656 PLCControler.__init__(self) |
684 |
657 |
685 self.MandatoryParams = None |
658 self.MandatoryParams = None |
686 self.AppFrame = frame |
659 self.AppFrame = frame |
687 |
660 self.logger = logger |
688 """ |
661 self._builder = None |
689 This method are not called here... but in NewProject and OpenProject |
662 self._connector = None |
690 self._AddParamsMembers() |
663 |
691 self.PluggedChilds = {} |
664 # Setup debug information |
692 """ |
665 self.IECdebug_callables = {} |
|
666 # Timer to prevent rapid-fire when registering many variables |
|
667 self.DebugTimer=Timer(0.5,self.RegisterDebugVarToConnector) |
|
668 self.ResetIECProgramsAndVariables() |
|
669 |
|
670 |
|
671 #This method are not called here... but in NewProject and OpenProject |
|
672 #self._AddParamsMembers() |
|
673 #self.PluggedChilds = {} |
|
674 |
693 # In both new or load scenario, no need to save |
675 # In both new or load scenario, no need to save |
694 self.ChangesToSave = False |
676 self.ChangesToSave = False |
695 # root have no parent |
677 # root have no parent |
696 self.PlugParent = None |
678 self.PlugParent = None |
697 # Keep track of the plugin type name |
679 # Keep track of the plugin type name |
825 return self.ProjectPath |
814 return self.ProjectPath |
826 |
815 |
827 def PluginXmlFilePath(self, PlugName=None): |
816 def PluginXmlFilePath(self, PlugName=None): |
828 return os.path.join(self.PlugPath(PlugName), "beremiz.xml") |
817 return os.path.join(self.PlugPath(PlugName), "beremiz.xml") |
829 |
818 |
830 def PlugGenerate_C(self, buildpath, locations, logger): |
|
831 """ |
|
832 Generate C code |
|
833 @param locations: List of complete variables locations \ |
|
834 [(IEC_loc, IEC_Direction, IEC_Type, Name)]\ |
|
835 ex: [((0,0,4,5),'I','STRING','__IX_0_0_4_5'),...] |
|
836 @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND |
|
837 """ |
|
838 return [(C_file_name, self.CFLAGS) for C_file_name in self.PLCGeneratedCFiles ] , "", False |
|
839 |
|
840 def _getBuildPath(self): |
819 def _getBuildPath(self): |
841 return os.path.join(self.ProjectPath, "build") |
820 return os.path.join(self.ProjectPath, "build") |
842 |
821 |
|
822 def _getExtraFilesPath(self): |
|
823 return os.path.join(self._getBuildPath(), "extra_files") |
|
824 |
843 def _getIECcodepath(self): |
825 def _getIECcodepath(self): |
844 # define name for IEC code file |
826 # define name for IEC code file |
845 return os.path.join(self._getBuildPath(), "plc.st") |
827 return os.path.join(self._getBuildPath(), "plc.st") |
846 |
828 |
847 def _getIECgeneratedcodepath(self): |
829 def _getIECgeneratedcodepath(self): |
848 # define name for IEC generated code file |
830 # define name for IEC generated code file |
849 return os.path.join(self._getBuildPath(), "generated_plc.st") |
831 return os.path.join(self._getBuildPath(), "generated_plc.st") |
850 |
832 |
851 def _getIECrawcodepath(self): |
833 def _getIECrawcodepath(self): |
852 # define name for IEC raw code file |
834 # define name for IEC raw code file |
853 return os.path.join(self._getBuildPath(), "raw_plc.st") |
835 return os.path.join(self.PlugPath(), "raw_plc.st") |
854 |
836 |
855 def GetLocations(self): |
837 def GetLocations(self): |
856 locations = [] |
838 locations = [] |
857 filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h") |
839 filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h") |
858 if os.path.isfile(filepath): |
840 if os.path.isfile(filepath): |
875 resdict['SIZE'] = 'X' |
857 resdict['SIZE'] = 'X' |
876 # finally store into located variable list |
858 # finally store into located variable list |
877 locations.append(resdict) |
859 locations.append(resdict) |
878 return locations |
860 return locations |
879 |
861 |
880 def _Generate_SoftPLC(self, logger): |
862 def _Generate_SoftPLC(self): |
881 """ |
863 """ |
882 Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2C |
864 Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2C |
883 @param buildpath: path where files should be created |
865 @param buildpath: path where files should be created |
884 @param logger: the log pseudo file |
|
885 """ |
866 """ |
886 |
867 |
887 # Update PLCOpenEditor Plugin Block types before generate ST code |
868 # Update PLCOpenEditor Plugin Block types before generate ST code |
888 self.RefreshPluginsBlockLists() |
869 self.RefreshPluginsBlockLists() |
889 |
870 |
890 logger.write("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n") |
871 self.logger.write("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n") |
891 buildpath = self._getBuildPath() |
872 buildpath = self._getBuildPath() |
892 # ask PLCOpenEditor controller to write ST/IL/SFC code file |
873 # ask PLCOpenEditor controller to write ST/IL/SFC code file |
893 result = self.GenerateProgram(self._getIECgeneratedcodepath()) |
874 result = self.GenerateProgram(self._getIECgeneratedcodepath()) |
894 if result is not None: |
875 if result is not None: |
895 # Failed ! |
876 # Failed ! |
896 logger.write_error("Error in ST/IL/SFC code generator :\n%s\n"%result) |
877 self.logger.write_error("Error in ST/IL/SFC code generator :\n%s\n"%result) |
897 return False |
878 return False |
898 plc_file = open(self._getIECcodepath(), "w") |
879 plc_file = open(self._getIECcodepath(), "w") |
899 if os.path.isfile(self._getIECrawcodepath()): |
880 if os.path.isfile(self._getIECrawcodepath()): |
900 plc_file.write(open(self._getIECrawcodepath(), "r").read()) |
881 plc_file.write(open(self._getIECrawcodepath(), "r").read()) |
901 plc_file.write("\n") |
882 plc_file.write("\n") |
902 plc_file.write(open(self._getIECgeneratedcodepath(), "r").read()) |
883 plc_file.write(open(self._getIECgeneratedcodepath(), "r").read()) |
903 plc_file.close() |
884 plc_file.close() |
904 logger.write("Compiling IEC Program in to C code...\n") |
885 self.logger.write("Compiling IEC Program in to C code...\n") |
905 # Now compile IEC code into many C files |
886 # Now compile IEC code into many C files |
906 # files are listed to stdout, and errors to stderr. |
887 # files are listed to stdout, and errors to stderr. |
907 status, result, err_result = ProcessLogger( |
888 status, result, err_result = ProcessLogger( |
908 logger, |
889 self.logger, |
909 "\"%s\" -f \"%s\" -I \"%s\" \"%s\""%( |
890 "\"%s\" -f \"%s\" -I \"%s\" \"%s\""%( |
910 iec2c_path, |
891 iec2c_path, |
911 self._getIECcodepath(), |
892 self._getIECcodepath(), |
912 ieclib_path, buildpath), |
893 ieclib_path, buildpath), |
913 no_stdout=True).spin() |
894 no_stdout=True).spin() |
914 if status: |
895 if status: |
915 # Failed ! |
896 # Failed ! |
916 logger.write_error("Error : IEC to C compiler returned %d\n"%status) |
897 self.logger.write_error("Error : IEC to C compiler returned %d\n"%status) |
917 return False |
898 return False |
918 # Now extract C files of stdout |
899 # Now extract C files of stdout |
919 C_files = [ fname for fname in result.splitlines() if fname[-2:]==".c" or fname[-2:]==".C" ] |
900 C_files = [ fname for fname in result.splitlines() if fname[-2:]==".c" or fname[-2:]==".C" ] |
920 # remove those that are not to be compiled because included by others |
901 # remove those that are not to be compiled because included by others |
921 C_files.remove("POUS.c") |
902 C_files.remove("POUS.c") |
922 if not C_files: |
903 if not C_files: |
923 logger.write_error("Error : At least one configuration and one ressource must be declared in PLC !\n") |
904 self.logger.write_error("Error : At least one configuration and one ressource must be declared in PLC !\n") |
924 return False |
905 return False |
925 # transform those base names to full names with path |
906 # transform those base names to full names with path |
926 C_files = map(lambda filename:os.path.join(buildpath, filename), C_files) |
907 C_files = map(lambda filename:os.path.join(buildpath, filename), C_files) |
927 logger.write("Extracting Located Variables...\n") |
908 self.logger.write("Extracting Located Variables...\n") |
928 # Keep track of generated located variables for later use by self._Generate_C |
909 # Keep track of generated located variables for later use by self._Generate_C |
929 self.PLCGeneratedLocatedVars = self.GetLocations() |
910 self.PLCGeneratedLocatedVars = self.GetLocations() |
930 # Keep track of generated C files for later use by self.PlugGenerate_C |
911 # Keep track of generated C files for later use by self.PlugGenerate_C |
931 self.PLCGeneratedCFiles = C_files |
912 self.PLCGeneratedCFiles = C_files |
932 # compute CFLAGS for plc |
913 # compute CFLAGS for plc |
933 self.CFLAGS = "\"-I"+ieclib_path+"\"" |
914 self.plcCFLAGS = "\"-I"+ieclib_path+"\"" |
934 return True |
915 return True |
935 |
916 |
936 def _build(self, logger): |
917 def GetBuilder(self): |
|
918 """ |
|
919 Return a Builder (compile C code into machine code) |
|
920 """ |
|
921 # Get target, module and class name |
|
922 targetname = self.BeremizRoot.getTargetType().getcontent()["name"] |
|
923 modulename = "targets." + targetname |
|
924 classname = targetname + "_target" |
|
925 |
|
926 # Get module reference |
|
927 try : |
|
928 targetmodule = getattr(__import__(modulename), targetname) |
|
929 |
|
930 except Exception, msg: |
|
931 self.logger.write_error("Can't find module for target %s!\n"%targetname) |
|
932 self.logger.write_error(str(msg)) |
|
933 return None |
|
934 |
|
935 # Get target class |
|
936 targetclass = getattr(targetmodule, classname) |
|
937 |
|
938 # if target already |
|
939 if self._builder is None or not isinstance(self._builder,targetclass): |
|
940 # Get classname instance |
|
941 self._builder = targetclass(self) |
|
942 return self._builder |
|
943 |
|
944 def GetLastBuildMD5(self): |
|
945 builder=self.GetBuilder() |
|
946 if builder is not None: |
|
947 return builder.GetBinaryCodeMD5() |
|
948 else: |
|
949 return None |
|
950 |
|
951 ####################################################################### |
|
952 # |
|
953 # C CODE GENERATION METHODS |
|
954 # |
|
955 ####################################################################### |
|
956 |
|
957 def PlugGenerate_C(self, buildpath, locations): |
|
958 """ |
|
959 Return C code generated by iec2c compiler |
|
960 when _generate_softPLC have been called |
|
961 @param locations: ignored |
|
962 @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND |
|
963 """ |
|
964 return [(C_file_name, self.plcCFLAGS) for C_file_name in self.PLCGeneratedCFiles ] , "-lrt", False |
|
965 |
|
966 def ResetIECProgramsAndVariables(self): |
|
967 """ |
|
968 Reset variable and program list that are parsed from |
|
969 CSV file generated by IEC2C compiler. |
|
970 """ |
|
971 self._ProgramList = None |
|
972 self._VariablesList = None |
|
973 self._IECPathToIdx = None |
|
974 self._IdxToIECPath = None |
|
975 |
|
976 def GetIECProgramsAndVariables(self): |
|
977 """ |
|
978 Parse CSV-like file VARIABLES.csv resulting from IEC2C compiler. |
|
979 Each section is marked with a line staring with '//' |
|
980 list of all variables used in various POUs |
|
981 """ |
|
982 if self._ProgramList is None or self._VariablesList is None: |
|
983 try: |
|
984 csvfile = os.path.join(self._getBuildPath(),"VARIABLES.csv") |
|
985 # describes CSV columns |
|
986 ProgramsListAttributeName = ["num", "C_path", "type"] |
|
987 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"] |
|
988 self._ProgramList = [] |
|
989 self._VariablesList = [] |
|
990 self._IECPathToIdx = {} |
|
991 self._IdxToIECPath = {} |
|
992 |
|
993 # Separate sections |
|
994 ListGroup = [] |
|
995 for line in open(csvfile,'r').xreadlines(): |
|
996 strippedline = line.strip() |
|
997 if strippedline.startswith("//"): |
|
998 # Start new section |
|
999 ListGroup.append([]) |
|
1000 elif len(strippedline) > 0 and len(ListGroup) > 0: |
|
1001 # append to this section |
|
1002 ListGroup[-1].append(strippedline) |
|
1003 |
|
1004 # first section contains programs |
|
1005 for line in ListGroup[0]: |
|
1006 # Split and Maps each field to dictionnary entries |
|
1007 attrs = dict(zip(ProgramsListAttributeName,line.strip().split(';'))) |
|
1008 # Truncate "C_path" to remove conf an ressources names |
|
1009 attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:]) |
|
1010 # Push this dictionnary into result. |
|
1011 self._ProgramList.append(attrs) |
|
1012 |
|
1013 # second section contains all variables |
|
1014 for line in ListGroup[1]: |
|
1015 # Split and Maps each field to dictionnary entries |
|
1016 attrs = dict(zip(VariablesListAttributeName,line.strip().split(';'))) |
|
1017 # Truncate "C_path" to remove conf an ressources names |
|
1018 attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:]) |
|
1019 # Push this dictionnary into result. |
|
1020 self._VariablesList.append(attrs) |
|
1021 # Fill in IEC<->C translation dicts |
|
1022 IEC_path=attrs["IEC_path"] |
|
1023 Idx=int(attrs["num"]) |
|
1024 self._IECPathToIdx[IEC_path]=Idx |
|
1025 self._IdxToIECPath[Idx]=IEC_path |
|
1026 except Exception,e: |
|
1027 self.logger.write_error("Cannot open/parse VARIABLES.csv!\n") |
|
1028 self.logger.write_error(traceback.format_exc()) |
|
1029 self.ResetIECProgramsAndVariables() |
|
1030 return False |
|
1031 |
|
1032 return True |
|
1033 |
|
1034 def Generate_plc_debugger(self): |
|
1035 """ |
|
1036 Generate trace/debug code out of PLC variable list |
|
1037 """ |
|
1038 self.GetIECProgramsAndVariables() |
|
1039 |
|
1040 # prepare debug code |
|
1041 debug_code = runtime.code("plc_debug") % { |
|
1042 "programs_declarations": |
|
1043 "\n".join(["extern %(type)s %(C_path)s;"%p for p in self._ProgramList]), |
|
1044 "extern_variables_declarations":"\n".join([ |
|
1045 {"PT":"extern %(type)s *%(C_path)s;", |
|
1046 "VAR":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v |
|
1047 for v in self._VariablesList if v["C_path"].find('.')<0]), |
|
1048 "subscription_table_count": |
|
1049 len(self._VariablesList), |
|
1050 "variables_pointer_type_table_count": |
|
1051 len(self._VariablesList), |
|
1052 "variables_pointer_type_table_initializer":"\n".join([ |
|
1053 {"PT":" variable_table[%(num)s].ptrvalue = (void*)(%(C_path)s);\n", |
|
1054 "VAR":" variable_table[%(num)s].ptrvalue = (void*)(&%(C_path)s);\n"}[v["vartype"]]%v + |
|
1055 " variable_table[%(num)s].type = %(type)s_ENUM;\n"%v |
|
1056 for v in self._VariablesList if v["type"] in DebugTypes ])} |
|
1057 |
|
1058 return debug_code |
|
1059 |
|
1060 def RegisterDebugVarToConnector(self): |
|
1061 Idxs = [] |
|
1062 if self._connector is not None: |
|
1063 for IECPath,WeakCallableDict in self.IECdebug_callables: |
|
1064 if len(WeakCallableDict) == 0: |
|
1065 # Callable Dict is empty. |
|
1066 # This variable is not needed anymore! |
|
1067 self.IECdebug_callables.pop(IECPath) |
|
1068 else: |
|
1069 # Convert |
|
1070 Idx = self._IECPathToIdx.get(IECPath,None) |
|
1071 if Idx is not None: |
|
1072 Idxs.append(Idx) |
|
1073 else: |
|
1074 self.logger.write_warning("Debug : Unknown variable %s\n"%IECPath) |
|
1075 self._connector.TraceVariables(Idxs) |
|
1076 |
|
1077 def SubscribeDebugIECVariable(self, IECPath, callable, *args, **kwargs): |
|
1078 """ |
|
1079 Dispatching use a dictionnary linking IEC variable paths |
|
1080 to a WeakKeyDictionary linking |
|
1081 weakly referenced callables to optionnal args |
|
1082 """ |
|
1083 # If no entry exist, create a new one with a fresh WeakKeyDictionary |
|
1084 self.IECdebug_callables.setdefault( |
|
1085 IECPath, |
|
1086 WeakKeyDictionary())[callable]=(args, kwargs) |
|
1087 # Rearm anti-rapid-fire timer |
|
1088 self.DebugTimer.cancel() |
|
1089 self.DebugTimer.start() |
|
1090 |
|
1091 def Generate_plc_common_main(self): |
|
1092 """ |
|
1093 Use plugins layout given in LocationCFilesAndCFLAGS to |
|
1094 generate glue code that dispatch calls to all plugins |
|
1095 """ |
|
1096 # filter location that are related to code that will be called |
|
1097 # in retreive, publish, init, cleanup |
|
1098 locstrs = map(lambda x:"_".join(map(str,x)), |
|
1099 [loc for loc,Cfiles,DoCalls in self.LocationCFilesAndCFLAGS if loc and DoCalls]) |
|
1100 |
|
1101 # Generate main, based on template |
|
1102 plc_main_code = runtime.code("plc_common_main") % { |
|
1103 "calls_prototypes":"\n".join([( |
|
1104 "int __init_%(s)s(int argc,char **argv);\n"+ |
|
1105 "void __cleanup_%(s)s();\n"+ |
|
1106 "void __retrieve_%(s)s();\n"+ |
|
1107 "void __publish_%(s)s();")%{'s':locstr} for locstr in locstrs]), |
|
1108 "retrieve_calls":"\n ".join([ |
|
1109 "__retrieve_%s();"%locstr for locstr in locstrs]), |
|
1110 "publish_calls":"\n ".join([ #Call publish in reverse order |
|
1111 "__publish_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]), |
|
1112 "init_calls":"\n ".join([ |
|
1113 "init_level=%d; "%i+ |
|
1114 "if(res = __init_%s(argc,argv)){"%locstr + |
|
1115 #"printf(\"%s\"); "%locstr + #for debug |
|
1116 "return res;}" for i,locstr in enumerate(locstrs)]), |
|
1117 "cleanup_calls":"\n ".join([ |
|
1118 "if(init_level >= %d) "%i+ |
|
1119 "__cleanup_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]) |
|
1120 } |
|
1121 |
|
1122 target_name = self.BeremizRoot.getTargetType().getcontent()["name"] |
|
1123 plc_main_code += runtime.code("plc_%s_main"%target_name) |
|
1124 |
|
1125 return plc_main_code |
|
1126 |
|
1127 |
|
1128 def _build(self): |
937 """ |
1129 """ |
938 Method called by user to (re)build SoftPLC and plugin tree |
1130 Method called by user to (re)build SoftPLC and plugin tree |
939 """ |
1131 """ |
940 if self.PLCEditor is not None: |
1132 if self.PLCEditor is not None: |
941 self.PLCEditor.ClearErrors() |
1133 self.PLCEditor.ClearErrors() |
943 buildpath = self._getBuildPath() |
1135 buildpath = self._getBuildPath() |
944 |
1136 |
945 # Eventually create build dir |
1137 # Eventually create build dir |
946 if not os.path.exists(buildpath): |
1138 if not os.path.exists(buildpath): |
947 os.mkdir(buildpath) |
1139 os.mkdir(buildpath) |
948 |
1140 # There is something to clean |
949 logger.flush() |
|
950 logger.write("Start build in %s\n" % buildpath) |
|
951 |
|
952 self.EnableMethod("_Clean", True) |
1141 self.EnableMethod("_Clean", True) |
953 self.EnableMethod("_showIECcode", True) |
1142 |
954 |
1143 self.logger.flush() |
955 # Generate SoftPLC code |
1144 self.logger.write("Start build in %s\n" % buildpath) |
956 if not self._Generate_SoftPLC(logger): |
1145 |
957 logger.write_error("SoftPLC code generation failed !\n") |
1146 # Generate SoftPLC IEC code |
|
1147 IECGenRes = self._Generate_SoftPLC() |
|
1148 self.ShowMethod("_showIECcode", True) |
|
1149 |
|
1150 # If IEC code gen fail, bail out. |
|
1151 if not IECGenRes: |
|
1152 self.logger.write_error("IEC-61131-3 code generation failed !\n") |
958 return False |
1153 return False |
959 |
1154 |
960 |
1155 # Reset variable and program list that are parsed from |
961 #logger.write("SoftPLC code generation successfull\n") |
1156 # CSV file generated by IEC2C compiler. |
962 |
1157 self.ResetIECProgramsAndVariables() |
963 logger.write("Generating plugins code ...\n") |
|
964 |
1158 |
965 # Generate C code and compilation params from plugin hierarchy |
1159 # Generate C code and compilation params from plugin hierarchy |
|
1160 self.logger.write("Generating plugins C code\n") |
966 try: |
1161 try: |
967 LocationCFilesAndCFLAGS,LDFLAGS = self._Generate_C( |
1162 self.LocationCFilesAndCFLAGS, self.LDFLAGS, ExtraFiles = self._Generate_C( |
968 buildpath, |
1163 buildpath, |
969 self.PLCGeneratedLocatedVars, |
1164 self.PLCGeneratedLocatedVars) |
970 logger) |
|
971 except Exception, exc: |
1165 except Exception, exc: |
972 logger.write_error("Plugins code generation Failed !\n") |
1166 self.logger.write_error("Plugins code generation failed !\n") |
973 logger.write_error(traceback.format_exc()) |
1167 self.logger.write_error(traceback.format_exc()) |
974 return False |
1168 return False |
975 |
1169 |
976 |
1170 # Get temprary directory path |
977 #debug |
1171 extrafilespath = self._getExtraFilesPath() |
978 #import pprint |
1172 # Remove old directory |
979 #pp = pprint.PrettyPrinter(indent=4) |
1173 if os.path.exists(extrafilespath): |
980 #logger.write("LocationCFilesAndCFLAGS :\n"+pp.pformat(LocationCFilesAndCFLAGS)+"\n") |
1174 shutil.rmtree(extrafilespath) |
981 #logger.write("LDFLAGS :\n"+pp.pformat(LDFLAGS)+"\n") |
1175 # Recreate directory |
982 |
1176 os.mkdir(extrafilespath) |
983 # Generate main |
1177 # Then write the files |
984 locstrs = map(lambda x:"_".join(map(str,x)), [loc for loc,Cfiles,DoCalls in LocationCFilesAndCFLAGS if loc and DoCalls]) |
1178 for fname,fobject in ExtraFiles: |
985 plc_main = runtime.code("plc_common_main") % { |
1179 print fname,fobject |
986 "calls_prototypes":"\n".join( |
1180 fpath = os.path.join(extrafilespath,fname) |
987 ["int __init_%(s)s(int argc,char **argv);\nvoid __cleanup_%(s)s();\nvoid __retrieve_%(s)s();\nvoid __publish_%(s)s();"% |
1181 open(fpath, "wb").write(fobject.read()) |
988 {'s':locstr} for locstr in locstrs]), |
1182 # Now we can forget ExtraFiles (will close files object) |
989 "retrieve_calls":"\n ".join(["__retrieve_%(s)s();"%{'s':locstr} for locstr in locstrs]), |
1183 del ExtraFiles |
990 "publish_calls":"\n ".join(["__publish_%(s)s();"%{'s':locstr} for locstr in locstrs]), |
1184 |
991 "init_calls":"\n ".join(["init_level++; if(res = __init_%(s)s(argc,argv)) return res;"%{'s':locstr} for locstr in locstrs]), |
1185 # Template based part of C code generation |
992 "cleanup_calls":"\n ".join(["if(init_level-- > 0) __cleanup_%(s)s();"%{'s':locstr} for locstr in locstrs]), |
1186 # files are stacked at the beginning, as files of plugin tree root |
993 "sync_align_ratio":self.BeremizRoot.getSync_Align_Ratio()} |
1187 for generator, filename, name in [ |
994 target_name = self.BeremizRoot.TargetType.content["name"] |
1188 # debugger code |
995 plc_main += runtime.code("plc_%s_main"%target_name) |
1189 (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"), |
996 |
1190 # init/cleanup/retrieve/publish, run and align code |
997 main_path = os.path.join(buildpath, "main.c" ) |
1191 (self.Generate_plc_common_main,"plc_common_main.c","Common runtime")]: |
998 f = open(main_path,'w') |
1192 try: |
999 f.write(plc_main) |
1193 # Do generate |
1000 f.close() |
1194 code = generator() |
1001 # First element is necessarely root |
1195 code_path = os.path.join(buildpath,filename) |
1002 LocationCFilesAndCFLAGS[0][1].insert(0,(main_path, self.CFLAGS)) |
1196 open(code_path, "w").write(code) |
1003 |
1197 # Insert this file as first file to be compiled at root plugin |
1004 # Compile the resulting code into object files. |
1198 self.LocationCFilesAndCFLAGS[0][1].insert(0,(code_path, self.plcCFLAGS)) |
1005 compiler = self.BeremizRoot.getCompiler() |
1199 except Exception, exc: |
1006 _CFLAGS = self.BeremizRoot.getCFLAGS() |
1200 self.logger.write_error(name+" generation failed !\n") |
1007 linker = self.BeremizRoot.getLinker() |
1201 self.logger.write_error(traceback.format_exc()) |
1008 _LDFLAGS = self.BeremizRoot.getLDFLAGS() |
1202 return False |
1009 obns = [] |
1203 |
1010 objs = [] |
1204 self.logger.write("C code generated successfully.\n") |
1011 for Location, CFilesAndCFLAGS, DoCalls in LocationCFilesAndCFLAGS: |
1205 |
1012 if Location: |
1206 # Get current or fresh builder |
1013 logger.write("Plugin : " + self.GetChildByIECLocation(Location).GetCurrentName() + " " + str(Location)+"\n") |
1207 builder = self.GetBuilder() |
1014 else: |
1208 if builder is None: |
1015 logger.write("PLC :\n") |
1209 self.logger.write_error("Fatal : cannot get builder.\n") |
1016 |
|
1017 for CFile, CFLAGS in CFilesAndCFLAGS: |
|
1018 bn = os.path.basename(CFile) |
|
1019 obn = os.path.splitext(bn)[0]+".o" |
|
1020 obns.append(obn) |
|
1021 logger.write(" [CC] "+bn+" -> "+obn+"\n") |
|
1022 objectfilename = os.path.splitext(CFile)[0]+".o" |
|
1023 |
|
1024 status, result, err_result = ProcessLogger( |
|
1025 logger, |
|
1026 "\"%s\" -c \"%s\" -o \"%s\" %s %s"% |
|
1027 (compiler, CFile, objectfilename, _CFLAGS, CFLAGS) |
|
1028 ).spin() |
|
1029 |
|
1030 if status != 0: |
|
1031 logger.write_error("Build failed\n") |
|
1032 return False |
|
1033 objs.append(objectfilename) |
|
1034 # Link all the object files into one executable |
|
1035 logger.write("Linking :\n") |
|
1036 exe = self.GetProjectName() |
|
1037 if target_name == "Win32": |
|
1038 exe += ".exe" |
|
1039 exe_path = os.path.join(buildpath, exe) |
|
1040 logger.write(" [CC] " + ' '.join(obns)+" -> " + exe + "\n") |
|
1041 status, result, err_result = ProcessLogger( |
|
1042 logger, |
|
1043 "\"%s\" \"%s\" -o \"%s\" %s"% |
|
1044 (linker, |
|
1045 '" "'.join(objs), |
|
1046 exe_path, |
|
1047 ' '.join(LDFLAGS+[_LDFLAGS])) |
|
1048 ).spin() |
|
1049 if status != 0: |
|
1050 logger.write_error("Build failed\n") |
|
1051 self.EnableMethod("_Run", False) |
|
1052 return False |
1210 return False |
1053 |
1211 |
1054 self.EnableMethod("_Run", True) |
1212 # Build |
|
1213 try: |
|
1214 if not builder.build() : |
|
1215 self.logger.write_error("C Build failed.\n") |
|
1216 return False |
|
1217 except Exception, exc: |
|
1218 self.logger.write_error("C Build crashed !\n") |
|
1219 self.logger.write_error(traceback.format_exc()) |
|
1220 return False |
|
1221 |
|
1222 # Update GUI status about need for transfer |
|
1223 self.CompareLocalAndRemotePLC() |
1055 return True |
1224 return True |
1056 |
1225 |
1057 def ShowError(self, logger, from_location, to_location): |
1226 def ShowError(self, logger, from_location, to_location): |
1058 chunk_infos = self.GetChunkInfos(from_location, to_location) |
1227 chunk_infos = self.GetChunkInfos(from_location, to_location) |
1059 self._EditPLC(logger) |
1228 self._EditPLC(logger) |
1060 for infos, (start_row, start_col) in chunk_infos: |
1229 for infos, (start_row, start_col) in chunk_infos: |
1061 start = (from_location[0] - start_row, from_location[1] - start_col) |
1230 start = (from_location[0] - start_row, from_location[1] - start_col) |
1062 end = (to_location[0] - start_row, to_location[1] - start_col) |
1231 end = (to_location[0] - start_row, to_location[1] - start_col) |
1063 self.PLCEditor.ShowError(infos, start, end) |
1232 self.PLCEditor.ShowError(infos, start, end) |
1064 |
1233 |
1065 def _showIECcode(self, logger): |
1234 def _showIECcode(self): |
1066 plc_file = self._getIECcodepath() |
1235 plc_file = self._getIECcodepath() |
1067 new_dialog = wx.Frame(self.AppFrame) |
1236 new_dialog = wx.Frame(self.AppFrame) |
1068 ST_viewer = TextViewer(new_dialog, "", None, None) |
1237 ST_viewer = TextViewer(new_dialog, "", None, None) |
1069 #ST_viewer.Enable(False) |
1238 #ST_viewer.Enable(False) |
1070 ST_viewer.SetKeywords(IEC_KEYWORDS) |
1239 ST_viewer.SetKeywords(IEC_KEYWORDS) |
1106 self.PLCEditor.RefreshToolBar() |
1270 self.PLCEditor.RefreshToolBar() |
1107 self.PLCEditor._onclose = _onclose |
1271 self.PLCEditor._onclose = _onclose |
1108 self.PLCEditor._onsave = _onsave |
1272 self.PLCEditor._onsave = _onsave |
1109 self.PLCEditor.Show() |
1273 self.PLCEditor.Show() |
1110 |
1274 |
1111 def _Clean(self, logger): |
1275 def _Clean(self): |
1112 if os.path.isdir(os.path.join(self._getBuildPath())): |
1276 if os.path.isdir(os.path.join(self._getBuildPath())): |
1113 logger.write("Cleaning the build directory\n") |
1277 self.logger.write("Cleaning the build directory\n") |
1114 shutil.rmtree(os.path.join(self._getBuildPath())) |
1278 shutil.rmtree(os.path.join(self._getBuildPath())) |
1115 else: |
1279 else: |
1116 logger.write_error("Build directory already clean\n") |
1280 self.logger.write_error("Build directory already clean\n") |
1117 self.EnableMethod("_showIECcode", False) |
1281 self.ShowMethod("_showIECcode", False) |
1118 self.EnableMethod("_Clean", False) |
1282 self.EnableMethod("_Clean", False) |
1119 self.EnableMethod("_Run", False) |
1283 self.CompareLocalAndRemotePLC() |
1120 |
1284 |
1121 def _Run(self, logger): |
1285 ############# Real PLC object access ############# |
1122 command_start_plc = os.path.join(self._getBuildPath(),self.GetProjectName() + exe_ext) |
1286 def UpdateMethodsFromPLCStatus(self): |
1123 if os.path.isfile(command_start_plc): |
1287 # Get PLC state : Running or Stopped |
1124 has_gui_plugin = False |
1288 # TODO : use explicit status instead of boolean |
1125 for PlugChild in self.IterChilds(): |
1289 if self._connector is not None: |
1126 has_gui_plugin |= PlugChild.IsGUIPlugin() |
1290 status = self._connector.GetPLCstatus() |
1127 logger.write("Starting PLC\n") |
1291 self.logger.write("PLC is %s\n"%status) |
1128 def this_plc_finish_callback(*args): |
|
1129 if self.runningPLC is not None: |
|
1130 self.runningPLC = None |
|
1131 self.reset_finished() |
|
1132 self.runningPLC = ProcessLogger( |
|
1133 logger, |
|
1134 command_start_plc, |
|
1135 finish_callback = this_plc_finish_callback, |
|
1136 no_gui=wx.Platform != '__WXMSW__' or not has_gui_plugin) |
|
1137 self.EnableMethod("_Clean", False) |
|
1138 self.EnableMethod("_Run", False) |
|
1139 self.EnableMethod("_Stop", True) |
|
1140 self.EnableMethod("_build", False) |
|
1141 else: |
1292 else: |
1142 logger.write_error("%s doesn't exist\n" %command_start_plc) |
1293 status = "Disconnected" |
1143 |
1294 for args in { |
1144 def reset_finished(self): |
1295 "Started":[("_Run", False), |
1145 self.EnableMethod("_Clean", True) |
1296 ("_Debug", False), |
1146 self.EnableMethod("_Run", True) |
1297 ("_Stop", True)], |
1147 self.EnableMethod("_Stop", False) |
1298 "Stopped":[("_Run", True), |
1148 self.EnableMethod("_build", True) |
1299 ("_Debug", True), |
1149 |
1300 ("_Stop", False)], |
1150 def _Stop(self, logger): |
1301 "Empty": [("_Run", False), |
1151 if self.runningPLC is not None: |
1302 ("_Debug", False), |
1152 logger.write("Stopping PLC\n") |
1303 ("_Stop", False)], |
1153 was_runningPLC = self.runningPLC |
1304 "Dirty": [("_Run", True), |
1154 self.runningPLC = None |
1305 ("_Debug", True), |
1155 was_runningPLC.kill() |
1306 ("_Stop", False)], |
1156 self.reset_finished() |
1307 "Disconnected": [("_Run", False), |
|
1308 ("_Debug", False), |
|
1309 ("_Stop", False)], |
|
1310 }.get(status,[]): |
|
1311 self.ShowMethod(*args) |
|
1312 |
|
1313 def _Run(self): |
|
1314 """ |
|
1315 Start PLC |
|
1316 """ |
|
1317 if self._connector.StartPLC(): |
|
1318 self.logger.write("Starting PLC\n") |
|
1319 else: |
|
1320 self.logger.write_error("Couldn't start PLC !\n") |
|
1321 self.UpdateMethodsFromPLCStatus() |
|
1322 |
|
1323 def _Debug(self): |
|
1324 """ |
|
1325 Start PLC (Debug Mode) |
|
1326 """ |
|
1327 if self.GetIECProgramsAndVariables() and self._connector.StartPLC(): |
|
1328 self.logger.write("Starting PLC (debug mode)\n") |
|
1329 # TODO : laucnch PLCOpenEditor in Debug Mode |
|
1330 self.logger.write_warning("Debug mode for PLCopenEditor not implemented\n") |
|
1331 self.logger.write_warning("Starting alternative test GUI\n") |
|
1332 # TODO : laucnch PLCOpenEditor in Debug Mode |
|
1333 else: |
|
1334 self.logger.write_error("Couldn't start PLC debug !\n") |
|
1335 self.UpdateMethodsFromPLCStatus() |
|
1336 |
|
1337 def _Stop(self): |
|
1338 """ |
|
1339 Stop PLC |
|
1340 """ |
|
1341 if self._connector.StopPLC(): |
|
1342 self.logger.write("Stopping PLC\n") |
|
1343 else: |
|
1344 self.logger.write_error("Couldn't stop PLC !\n") |
|
1345 self.UpdateMethodsFromPLCStatus() |
|
1346 |
|
1347 def _Connect(self): |
|
1348 # don't accept re-connetion is already connected |
|
1349 if self._connector is not None: |
|
1350 self.logger.write_error("Already connected. Please disconnect\n") |
|
1351 return |
|
1352 |
|
1353 # Get connector uri |
|
1354 uri = self.\ |
|
1355 BeremizRoot.\ |
|
1356 getTargetType().\ |
|
1357 getcontent()["value"].\ |
|
1358 getConnection().\ |
|
1359 getURI_location().\ |
|
1360 strip() |
|
1361 |
|
1362 # if uri is empty launch discovery dialog |
|
1363 if uri == "": |
|
1364 # Launch Service Discovery dialog |
|
1365 dia = DiscoveryDialog(self.AppFrame) |
|
1366 dia.ShowModal() |
|
1367 uri = dia.GetResult() |
|
1368 # Nothing choosed or cancel button |
|
1369 if uri is None: |
|
1370 return |
|
1371 else: |
|
1372 self.\ |
|
1373 BeremizRoot.\ |
|
1374 getTargetType().\ |
|
1375 getcontent()["value"].\ |
|
1376 getConnection().\ |
|
1377 setURI_location(uri) |
|
1378 |
|
1379 # Get connector from uri |
|
1380 try: |
|
1381 self._connector = connectors.ConnectorFactory(uri, self) |
|
1382 except Exception, msg: |
|
1383 self.logger.write_error("Exception while connecting %s!\n"%uri) |
|
1384 self.logger.write_error(traceback.format_exc()) |
|
1385 |
|
1386 # Did connection success ? |
|
1387 if self._connector is None: |
|
1388 # Oups. |
|
1389 self.logger.write_error("Connection failed to %s!\n"%uri) |
|
1390 else: |
|
1391 self.ShowMethod("_Connect", False) |
|
1392 self.ShowMethod("_Disconnect", True) |
|
1393 self.ShowMethod("_Transfer", True) |
|
1394 |
|
1395 self.CompareLocalAndRemotePLC() |
|
1396 self.UpdateMethodsFromPLCStatus() |
|
1397 |
|
1398 def CompareLocalAndRemotePLC(self): |
|
1399 if self._connector is None: |
|
1400 return |
|
1401 # We are now connected. Update button status |
|
1402 MD5 = self.GetLastBuildMD5() |
|
1403 # Check remote target PLC correspondance to that md5 |
|
1404 if MD5 is not None: |
|
1405 if not self._connector.MatchMD5(MD5): |
|
1406 self.logger.write_warning( |
|
1407 "Latest build do not match with target, please transfer.\n") |
|
1408 self.EnableMethod("_Transfer", True) |
|
1409 else: |
|
1410 self.logger.write( |
|
1411 "Latest build match target, no transfer needed.\n") |
|
1412 self.EnableMethod("_Transfer", True) |
|
1413 #self.EnableMethod("_Transfer", False) |
|
1414 else: |
|
1415 self.logger.write_warning( |
|
1416 "Cannot compare latest build to target. Please build.\n") |
|
1417 self.EnableMethod("_Transfer", False) |
|
1418 |
|
1419 |
|
1420 def _Disconnect(self): |
|
1421 self._connector = None |
|
1422 self.ShowMethod("_Transfer", False) |
|
1423 self.ShowMethod("_Connect", True) |
|
1424 self.ShowMethod("_Disconnect", False) |
|
1425 self.UpdateMethodsFromPLCStatus() |
|
1426 |
|
1427 def _Transfer(self): |
|
1428 # Get the last build PLC's |
|
1429 MD5 = self.GetLastBuildMD5() |
|
1430 |
|
1431 # Check if md5 file is empty : ask user to build PLC |
|
1432 if MD5 is None : |
|
1433 self.logger.write_error("Failed : Must build before transfer.\n") |
|
1434 return False |
|
1435 |
|
1436 # Compare PLC project with PLC on target |
|
1437 if self._connector.MatchMD5(MD5): |
|
1438 self.logger.write( |
|
1439 "Latest build already match current target. Transfering anyway...\n") |
|
1440 |
|
1441 # Get temprary directory path |
|
1442 extrafilespath = self._getExtraFilesPath() |
|
1443 extrafiles = [(name, open(os.path.join(extrafilespath, name), |
|
1444 'rb').read()) \ |
|
1445 for name in os.listdir(extrafilespath) \ |
|
1446 if not name=="CVS"] |
|
1447 |
|
1448 for filename, unused in extrafiles: |
|
1449 print filename |
|
1450 |
|
1451 # Send PLC on target |
|
1452 builder = self.GetBuilder() |
|
1453 if builder is not None: |
|
1454 data = builder.GetBinaryCode() |
|
1455 if data is not None : |
|
1456 if self._connector.NewPLC(MD5, data, extrafiles): |
|
1457 self.logger.write("Transfer completed successfully.\n") |
|
1458 else: |
|
1459 self.logger.write_error("Transfer failed\n") |
|
1460 else: |
|
1461 self.logger.write_error("No PLC to transfer (did build success ?)\n") |
|
1462 self.UpdateMethodsFromPLCStatus() |
1157 |
1463 |
1158 PluginMethods = [ |
1464 PluginMethods = [ |
1159 {"bitmap" : os.path.join("images", "editPLC"), |
1465 {"bitmap" : opjimg("editPLC"), |
1160 "name" : "Edit PLC", |
1466 "name" : "Edit PLC", |
1161 "tooltip" : "Edit PLC program with PLCOpenEditor", |
1467 "tooltip" : "Edit PLC program with PLCOpenEditor", |
1162 "method" : "_EditPLC"}, |
1468 "method" : "_EditPLC"}, |
1163 {"bitmap" : os.path.join("images", "Build"), |
1469 {"bitmap" : opjimg("Build"), |
1164 "name" : "Build", |
1470 "name" : "Build", |
1165 "tooltip" : "Build project into build folder", |
1471 "tooltip" : "Build project into build folder", |
1166 "method" : "_build"}, |
1472 "method" : "_build"}, |
1167 {"bitmap" : os.path.join("images", "Clean"), |
1473 {"bitmap" : opjimg("Clean"), |
1168 "name" : "Clean", |
1474 "name" : "Clean", |
|
1475 "enabled" : False, |
1169 "tooltip" : "Clean project build folder", |
1476 "tooltip" : "Clean project build folder", |
1170 "method" : "_Clean"}, |
1477 "method" : "_Clean"}, |
1171 {"bitmap" : os.path.join("images", "Run"), |
1478 {"bitmap" : opjimg("Run"), |
1172 "name" : "Run", |
1479 "name" : "Run", |
1173 "enabled" : False, |
1480 "shown" : False, |
1174 "tooltip" : "Run PLC from build folder", |
1481 "tooltip" : "Start PLC", |
1175 "method" : "_Run"}, |
1482 "method" : "_Run"}, |
1176 {"bitmap" : os.path.join("images", "Stop"), |
1483 {"bitmap" : opjimg("Debug"), |
|
1484 "name" : "Debug", |
|
1485 "shown" : False, |
|
1486 "tooltip" : "Start PLC (debug mode)", |
|
1487 "method" : "_Debug"}, |
|
1488 {"bitmap" : opjimg("Stop"), |
1177 "name" : "Stop", |
1489 "name" : "Stop", |
1178 "enabled" : False, |
1490 "shown" : False, |
1179 "tooltip" : "Stop Running PLC", |
1491 "tooltip" : "Stop Running PLC", |
1180 "method" : "_Stop"}, |
1492 "method" : "_Stop"}, |
1181 {"bitmap" : os.path.join("images", "ShowIECcode"), |
1493 {"bitmap" : opjimg("Connect"), |
|
1494 "name" : "Connect", |
|
1495 "tooltip" : "Connect to the target PLC", |
|
1496 "method" : "_Connect"}, |
|
1497 {"bitmap" : opjimg("Transfer"), |
|
1498 "name" : "Transfer", |
|
1499 "shown" : False, |
|
1500 "tooltip" : "Transfer PLC", |
|
1501 "method" : "_Transfer"}, |
|
1502 {"bitmap" : opjimg("Disconnect"), |
|
1503 "name" : "Disconnect", |
|
1504 "shown" : False, |
|
1505 "tooltip" : "Disconnect from PLC", |
|
1506 "method" : "_Disconnect"}, |
|
1507 {"bitmap" : opjimg("ShowIECcode"), |
1182 "name" : "Show code", |
1508 "name" : "Show code", |
1183 "enabled" : False, |
1509 "shown" : False, |
1184 "tooltip" : "Show IEC code generated by PLCGenerator", |
1510 "tooltip" : "Show IEC code generated by PLCGenerator", |
1185 "method" : "_showIECcode"}, |
1511 "method" : "_showIECcode"}, |
1186 {"bitmap" : os.path.join("images", "editIECrawcode"), |
1512 {"bitmap" : opjimg("editIECrawcode"), |
1187 "name" : "Append code", |
1513 "name" : "Append code", |
1188 "tooltip" : "Edit raw IEC code added to code generated by PLCGenerator", |
1514 "tooltip" : "Edit raw IEC code added to code generated by PLCGenerator", |
1189 "method" : "_editIECrawcode"} |
1515 "method" : "_editIECrawcode"}, |
1190 ] |
1516 ] |