353 shutil.copy2(srcpath, dstpath) |
353 shutil.copy2(srcpath, dstpath) |
354 |
354 |
355 class LPCPluginsRoot(PluginsRoot): |
355 class LPCPluginsRoot(PluginsRoot): |
356 |
356 |
357 PluginMethods = [ |
357 PluginMethods = [ |
358 {"bitmap" : opjimg("Build"), |
358 {"bitmap" : opjimg("Debug"), |
359 "name" : _("Build"), |
359 "name" : _("Simulate"), |
360 "tooltip" : _("Build project into build folder"), |
360 "tooltip" : _("Simulate PLC"), |
361 "method" : "_build"}, |
361 "method" : "_Simulate"}, |
362 {"bitmap" : opjimg("Run"), |
362 {"bitmap" : opjimg("Run"), |
363 "name" : _("Run"), |
363 "name" : _("Run"), |
364 "shown" : False, |
364 "shown" : False, |
365 "tooltip" : _("Start PLC"), |
365 "tooltip" : _("Start PLC"), |
366 "method" : "_Run"}, |
366 "method" : "_Run"}, |
367 {"bitmap" : opjimg("Stop"), |
367 {"bitmap" : opjimg("Stop"), |
368 "name" : _("Stop"), |
368 "name" : _("Stop"), |
369 "shown" : False, |
369 "shown" : False, |
370 "tooltip" : _("Stop Running PLC"), |
370 "tooltip" : _("Stop Running PLC"), |
371 "method" : "_Stop"}, |
371 "method" : "_Stop"}, |
|
372 {"bitmap" : opjimg("Build"), |
|
373 "name" : _("Build"), |
|
374 "tooltip" : _("Build project into build folder"), |
|
375 "method" : "_build"}, |
372 {"bitmap" : opjimg("Transfer"), |
376 {"bitmap" : opjimg("Transfer"), |
373 "name" : _("Transfer"), |
377 "name" : _("Transfer"), |
374 "shown" : False, |
378 "shown" : False, |
375 "tooltip" : _("Transfer PLC"), |
379 "tooltip" : _("Transfer PLC"), |
376 "method" : "_Transfer"}, |
380 "method" : "_Transfer"}, |
381 |
385 |
382 self.PlugChildsTypes += [("LPCBus", LPCBus, "LPC bus")] |
386 self.PlugChildsTypes += [("LPCBus", LPCBus, "LPC bus")] |
383 |
387 |
384 self.OnlineMode = 0 |
388 self.OnlineMode = 0 |
385 self.OnlinePath = None |
389 self.OnlinePath = None |
|
390 |
|
391 self.BuildSimulation = False |
|
392 self.SimulationBuildPath = None |
|
393 |
|
394 self.previous_simulating = False |
386 |
395 |
387 def GetProjectName(self): |
396 def GetProjectName(self): |
388 return self.Project.getname() |
397 return self.Project.getname() |
389 |
398 |
390 def GetDefaultTarget(self): |
399 def GetDefaultTarget(self): |
391 target = self.Classes["BeremizRoot_TargetType"]() |
400 target = self.Classes["BeremizRoot_TargetType"]() |
392 target_value = self.Classes["TargetType_LPC"]() |
401 if self.BuildSimulation: |
393 target_value.setBuildPath(self.BuildPath) |
402 if wx.Platform == '__WXMSW__': |
394 target.setcontent({"name": "Makefile", "value": target_value}) |
403 target_name = "Win32" |
|
404 else: |
|
405 target_name = "Linux" |
|
406 target_value = self.Classes["TargetType_%s"%target_name]() |
|
407 else: |
|
408 target_name = "LPC" |
|
409 target_value = self.Classes["TargetType_LPC"]() |
|
410 target_value.setBuildPath(self.BuildPath) |
|
411 target.setcontent({"name": target_name, "value": target_value}) |
395 return target |
412 return target |
396 |
413 |
|
414 def _getBuildPath(self): |
|
415 if self.BuildSimulation: |
|
416 if self.SimulationBuildPath is None: |
|
417 self.SimulationBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build") |
|
418 return self.SimulationBuildPath |
|
419 else: |
|
420 PluginsRoot._getBuildPath(self) |
|
421 |
397 def SetProjectName(self, name): |
422 def SetProjectName(self, name): |
398 return self.Project.setname(name) |
423 return self.Project.setname(name) |
399 |
424 |
400 def SetOnlineMode(self, mode, path=None): |
425 def SetOnlineMode(self, mode, path=None): |
401 self.OnlineMode = mode |
426 if self.OnlineMode != mode: |
402 self.OnlinePath = path |
427 self.OnlineMode = mode |
403 self.UpdateMethodsFromPLCStatus() |
428 if self.OnLineMode != 0: |
|
429 if self._connector is None: |
|
430 uri = "LPC://%s" % path |
|
431 try: |
|
432 self._connector = connectors.ConnectorFactory(uri, self) |
|
433 except Exception, msg: |
|
434 self.logger.write_error(_("Exception while connecting %s!\n")%uri) |
|
435 self.logger.write_error(traceback.format_exc()) |
|
436 |
|
437 # Did connection success ? |
|
438 if self._connector is None: |
|
439 # Oups. |
|
440 self.logger.write_error(_("Connection failed to %s!\n")%uri) |
|
441 |
|
442 if self._connector is not None: |
|
443 |
|
444 if self.OnLineMode == 1: |
|
445 self.CompareLocalAndRemotePLC() |
|
446 |
|
447 # Init with actual PLC status and print it |
|
448 self.UpdateMethodsFromPLCStatus() |
|
449 if self.previous_plcstate is not None: |
|
450 status = _(self.previous_plcstate) |
|
451 else: |
|
452 status = "" |
|
453 self.logger.write(_("PLC is %s\n")%status) |
|
454 |
|
455 # Start the status Timer |
|
456 self.StatusTimer.Start(milliseconds=500, oneShot=False) |
|
457 |
|
458 if self.previous_plcstate=="Started": |
|
459 if self.DebugAvailable() and self.GetIECProgramsAndVariables(): |
|
460 self.logger.write(_("Debug connect matching running PLC\n")) |
|
461 self._connect_debug() |
|
462 else: |
|
463 self.logger.write_warning(_("Debug do not match PLC - stop/transfert/start to re-enable\n")) |
|
464 |
|
465 else: |
|
466 self._connector = None |
|
467 self.StatusTimer.Stop() |
|
468 |
|
469 self.OnlinePath = path |
|
470 self.UpdateMethodsFromPLCStatus() |
404 |
471 |
405 # Update a PLCOpenEditor Pou variable name |
472 # Update a PLCOpenEditor Pou variable name |
406 def UpdateProjectVariableName(self, old_name, new_name): |
473 def UpdateProjectVariableName(self, old_name, new_name): |
407 self.Project.updateElementName(old_name, new_name) |
474 self.Project.updateElementName(old_name, new_name) |
408 self.BufferProject() |
475 self.BufferProject() |
448 #Load and init all the childs |
515 #Load and init all the childs |
449 self.LoadChilds() |
516 self.LoadChilds() |
450 self.RefreshPluginsBlockLists() |
517 self.RefreshPluginsBlockLists() |
451 |
518 |
452 return None |
519 return None |
453 |
|
454 def SaveProject(self): |
|
455 self.SaveXMLFile(self.ProjectPath) |
|
456 |
520 |
457 ############# Real PLC object access ############# |
521 ############# Real PLC object access ############# |
458 def UpdateMethodsFromPLCStatus(self): |
522 def UpdateMethodsFromPLCStatus(self): |
459 # Get PLC state : Running or Stopped |
523 # Get PLC state : Running or Stopped |
460 # TODO : use explicit status instead of boolean |
524 # TODO : use explicit status instead of boolean |
|
525 simulating = False |
461 if self.OnlineMode == 0: |
526 if self.OnlineMode == 0: |
|
527 if self._connector is not None: |
|
528 simulating = self._connector.GetPLCstatus() == "Started" |
462 status = "Disconnected" |
529 status = "Disconnected" |
463 elif self.OnlineMode == 1: |
530 elif self.OnlineMode == 1: |
|
531 if self._connector is not None: |
|
532 simulating = self._connector.GetPLCstatus() == "Started" |
464 status = "Connected" |
533 status = "Connected" |
465 elif self._connector is not None: |
|
466 status = self._connector.GetPLCstatus() |
|
467 else: |
534 else: |
468 status = "Disconnected" |
535 if self._connector is not None: |
469 if(self.previous_plcstate != status): |
536 status = self._connector.GetPLCstatus() |
|
537 else: |
|
538 status = "Disconnected" |
|
539 if self.previous_plcstate != status or self.previous_simulating != simulating: |
470 for args in { |
540 for args in { |
471 "Started" : [("_build", False), |
541 "Started" : [("_Simulate", False), |
472 ("_Run", False), |
542 ("_Run", False), |
473 ("_Stop", True), |
543 ("_Stop", True), |
|
544 ("_build", False), |
474 ("_Transfer", False)], |
545 ("_Transfer", False)], |
475 "Stopped" : [("_build", False), |
546 "Stopped" : [("_Simulate", False), |
476 ("_Run", True), |
547 ("_Run", True), |
477 ("_Stop", False), |
548 ("_Stop", False), |
|
549 ("_build", False), |
478 ("_Transfer", False)], |
550 ("_Transfer", False)], |
479 "Connected" : [("_build", False), |
551 "Connected" : [("_Simulate", not simulating), |
480 ("_Run", False), |
552 ("_Run", False), |
481 ("_Stop", False), |
553 ("_Stop", simulating), |
|
554 ("_build", False), |
482 ("_Transfer", True)], |
555 ("_Transfer", True)], |
483 "Disconnected" :[("_build", True), |
556 "Disconnected" :[("_Simulate", not simulating), |
484 ("_Run", False), |
557 ("_Run", False), |
485 ("_Stop", False), |
558 ("_Stop", simulating), |
|
559 ("_build", True), |
486 ("_Transfer", False)], |
560 ("_Transfer", False)], |
487 }.get(status,[]): |
561 }.get(status,[]): |
488 self.ShowMethod(*args) |
562 self.ShowMethod(*args) |
489 self.previous_plcstate = status |
563 self.previous_plcstate = status |
|
564 self.previous_simulating = simulating |
490 return True |
565 return True |
491 return False |
566 return False |
492 |
567 |
|
568 def Generate_plc_declare_locations(self): |
|
569 """ |
|
570 Declare used locations in order to simulatePLC in a black box |
|
571 """ |
|
572 return """#include "iec_types_all.h" |
|
573 |
|
574 #define __LOCATED_VAR(type, name, ...) \ |
|
575 type beremiz_##name;\ |
|
576 type *name = &beremiz_##name; |
|
577 |
|
578 #include "LOCATED_VARIABLES.h" |
|
579 |
|
580 #undef __LOCATED_VAR |
|
581 |
|
582 """ |
|
583 |
|
584 def _Simulate(self): |
|
585 """ |
|
586 Method called by user to Simulate PLC |
|
587 """ |
|
588 uri = "LOCAL://" |
|
589 try: |
|
590 self._connector = connectors.ConnectorFactory(uri, self) |
|
591 except Exception, msg: |
|
592 self.logger.write_error(_("Exception while connecting %s!\n")%uri) |
|
593 self.logger.write_error(traceback.format_exc()) |
|
594 |
|
595 # Did connection success ? |
|
596 if self._connector is None: |
|
597 # Oups. |
|
598 self.logger.write_error(_("Connection failed to %s!\n")%uri) |
|
599 return False |
|
600 |
|
601 self.BuildSimulation = True |
|
602 |
|
603 |
|
604 buildpath = self._getBuildPath() |
|
605 |
|
606 # Eventually create build dir |
|
607 if not os.path.exists(buildpath): |
|
608 os.makedirs(buildpath) |
|
609 |
|
610 # Generate SoftPLC IEC code |
|
611 IECGenRes = self._Generate_SoftPLC() |
|
612 |
|
613 # If IEC code gen fail, bail out. |
|
614 if not IECGenRes: |
|
615 self.logger.write_error(_("IEC-61131-3 code generation failed !\n")) |
|
616 self.BuildSimulation = False |
|
617 return False |
|
618 |
|
619 # Reset variable and program list that are parsed from |
|
620 # CSV file generated by IEC2C compiler. |
|
621 self.ResetIECProgramsAndVariables() |
|
622 |
|
623 gen_result = self.PlugGenerate_C(buildpath, self.PLCGeneratedLocatedVars) |
|
624 PlugCFilesAndCFLAGS, PlugLDFLAGS, DoCalls = gen_result[:3] |
|
625 # if some files have been generated put them in the list with their location |
|
626 if PlugCFilesAndCFLAGS: |
|
627 self.LocationCFilesAndCFLAGS = [(self.GetCurrentLocation(), PlugCFilesAndCFLAGS, DoCalls)] |
|
628 else: |
|
629 self.LocationCFilesAndCFLAGS = [] |
|
630 |
|
631 # plugin asks for some LDFLAGS |
|
632 if PlugLDFLAGS: |
|
633 # LDFLAGS can be either string |
|
634 if type(PlugLDFLAGS)==type(str()): |
|
635 self.LDFLAGS=[PlugLDFLAGS] |
|
636 #or list of strings |
|
637 elif type(PlugLDFLAGS)==type(list()): |
|
638 self.LDFLAGS=PlugLDFLAGS[:] |
|
639 else: |
|
640 self.LDFLAGS=[] |
|
641 |
|
642 # Template based part of C code generation |
|
643 # files are stacked at the beginning, as files of plugin tree root |
|
644 for generator, filename, name in [ |
|
645 # debugger code |
|
646 (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"), |
|
647 # init/cleanup/retrieve/publish, run and align code |
|
648 (self.Generate_plc_common_main,"plc_common_main.c","Common runtime"), |
|
649 # declare located variables for simulate in a black box |
|
650 (self.Generate_plc_declare_locations,"plc_declare_locations.c","Declare Locations")]: |
|
651 try: |
|
652 # Do generate |
|
653 code = generator() |
|
654 if code is None: |
|
655 raise |
|
656 code_path = os.path.join(buildpath,filename) |
|
657 open(code_path, "w").write(code) |
|
658 # Insert this file as first file to be compiled at root plugin |
|
659 self.LocationCFilesAndCFLAGS[0][1].insert(0,(code_path, self.plcCFLAGS)) |
|
660 except Exception, exc: |
|
661 self.logger.write_error(name+_(" generation failed !\n")) |
|
662 self.logger.write_error(traceback.format_exc()) |
|
663 self.BuildSimulation = False |
|
664 return False |
|
665 |
|
666 # Get simulation builder |
|
667 builder = self.GetBuilder() |
|
668 if builder is None: |
|
669 self.logger.write_error(_("Fatal : cannot get builder.\n")) |
|
670 self.BuildSimulation = False |
|
671 return False |
|
672 |
|
673 # Build |
|
674 try: |
|
675 if not builder.build() : |
|
676 self.logger.write_error(_("C Build failed.\n")) |
|
677 self.BuildSimulation = False |
|
678 return False |
|
679 except Exception, exc: |
|
680 self.logger.write_error(_("C Build crashed !\n")) |
|
681 self.logger.write_error(traceback.format_exc()) |
|
682 self.BuildSimulation = False |
|
683 return False |
|
684 |
|
685 data = builder.GetBinaryCode() |
|
686 if data is not None : |
|
687 if self._connector.NewPLC(builder.GetBinaryCodeMD5(), data, []): |
|
688 if self.AppFrame is not None: |
|
689 self.AppFrame.CloseDebugTabs() |
|
690 self.AppFrame.RefreshInstancesTree() |
|
691 self.UnsubscribeAllDebugIECVariable() |
|
692 self.ProgramTransferred() |
|
693 self.logger.write(_("Transfer completed successfully.\n")) |
|
694 else: |
|
695 self.logger.write_error(_("Transfer failed\n")) |
|
696 self.BuildSimulation = False |
|
697 return False |
|
698 |
|
699 self._Run() |
|
700 |
|
701 self.BuildSimulation = False |
|
702 |
|
703 # Start the status Timer |
|
704 self.StatusTimer.Start(milliseconds=500, oneShot=False) |
|
705 |
493 #------------------------------------------------------------------------------- |
706 #------------------------------------------------------------------------------- |
494 # LPCBeremiz Class |
707 # LPCBeremiz Class |
495 #------------------------------------------------------------------------------- |
708 #------------------------------------------------------------------------------- |
496 |
709 |
497 class LPCBeremiz(Beremiz): |
710 class LPCBeremiz(Beremiz): |