410 try: |
415 try: |
411 from threading import Thread, currentThread |
416 from threading import Thread, currentThread |
412 if havewx: |
417 if havewx: |
413 from twisted.internet import wxreactor |
418 from twisted.internet import wxreactor |
414 wxreactor.install() |
419 wxreactor.install() |
415 from twisted.internet import reactor, task |
420 from twisted.internet import reactor |
416 from twisted.python import log, util |
|
417 from nevow import rend, appserver, inevow, tags, loaders, athena |
|
418 from nevow.page import renderer |
|
419 |
421 |
420 havetwisted = True |
422 havetwisted = True |
421 except: |
423 except: |
422 print "Twisted unavailable !" |
424 print "Twisted unavailable." |
423 havetwisted = False |
425 havetwisted = False |
424 |
426 |
|
427 pyruntimevars = {} |
|
428 statuschange = [] |
|
429 registerserverto = [] |
|
430 |
425 if havetwisted: |
431 if havetwisted: |
426 |
|
427 xhtml_header = '''<?xml version="1.0" encoding="utf-8"?> |
|
428 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" |
|
429 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> |
|
430 ''' |
|
431 |
|
432 class PLCHMI(athena.LiveElement): |
|
433 |
|
434 initialised = False |
|
435 |
|
436 def HMIinitialised(self, result): |
|
437 self.initialised = True |
|
438 |
|
439 def HMIinitialisation(self): |
|
440 self.HMIinitialised(None) |
|
441 |
|
442 class DefaultPLCStartedHMI(PLCHMI): |
|
443 docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ |
|
444 tags.h1["PLC IS NOW STARTED"], |
|
445 ]) |
|
446 |
|
447 class PLCStoppedHMI(PLCHMI): |
|
448 docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ |
|
449 tags.h1["PLC IS STOPPED"], |
|
450 ]) |
|
451 |
|
452 class MainPage(athena.LiveElement): |
|
453 jsClass = u"WebInterface.PLC" |
|
454 docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ |
|
455 tags.div(id='content')[ |
|
456 tags.div(render = tags.directive('PLCElement')), |
|
457 ]]) |
|
458 |
|
459 def __init__(self, *a, **kw): |
|
460 athena.LiveElement.__init__(self, *a, **kw) |
|
461 self.pcl_state = False |
|
462 self.HMI = None |
|
463 self.resetPLCStartedHMI() |
|
464 |
|
465 def setPLCState(self, state): |
|
466 self.pcl_state = state |
|
467 if self.HMI is not None: |
|
468 self.callRemote('updateHMI') |
|
469 |
|
470 def setPLCStartedHMI(self, hmi): |
|
471 self.PLCStartedHMIClass = hmi |
|
472 |
|
473 def resetPLCStartedHMI(self): |
|
474 self.PLCStartedHMIClass = DefaultPLCStartedHMI |
|
475 |
|
476 def getHMI(self): |
|
477 return self.HMI |
|
478 |
|
479 def HMIexec(self, function, *args, **kwargs): |
|
480 if self.HMI is not None: |
|
481 getattr(self.HMI, function, lambda:None)(*args, **kwargs) |
|
482 athena.expose(HMIexec) |
|
483 |
|
484 def resetHMI(self): |
|
485 self.HMI = None |
|
486 |
|
487 def PLCElement(self, ctx, data): |
|
488 return self.getPLCElement() |
|
489 renderer(PLCElement) |
|
490 |
|
491 def getPLCElement(self): |
|
492 self.detachFragmentChildren() |
|
493 if self.pcl_state: |
|
494 f = self.PLCStartedHMIClass() |
|
495 else: |
|
496 f = PLCStoppedHMI() |
|
497 f.setFragmentParent(self) |
|
498 self.HMI = f |
|
499 return f |
|
500 athena.expose(getPLCElement) |
|
501 |
|
502 def detachFragmentChildren(self): |
|
503 for child in self.liveFragmentChildren[:]: |
|
504 child.detach() |
|
505 |
|
506 class WebInterface(athena.LivePage): |
|
507 |
|
508 docFactory = loaders.stan([tags.raw(xhtml_header), |
|
509 tags.html(xmlns="http://www.w3.org/1999/xhtml")[ |
|
510 tags.head(render=tags.directive('liveglue')), |
|
511 tags.body[ |
|
512 tags.div[ |
|
513 tags.div( render = tags.directive( "MainPage" )) |
|
514 ]]]]) |
|
515 MainPage = MainPage() |
|
516 PLCHMI = PLCHMI |
|
517 |
|
518 def __init__(self, plcState=False, *a, **kw): |
|
519 super(WebInterface, self).__init__(*a, **kw) |
|
520 self.jsModules.mapping[u'WebInterface'] = util.sibpath(__file__, os.path.join('runtime', 'webinterface.js')) |
|
521 self.plcState = plcState |
|
522 self.MainPage.setPLCState(plcState) |
|
523 |
|
524 def getHMI(self): |
|
525 return self.MainPage.getHMI() |
|
526 |
|
527 def LoadHMI(self, hmi, jsmodules): |
|
528 for name, path in jsmodules.iteritems(): |
|
529 self.jsModules.mapping[name] = os.path.join(WorkingDir, path) |
|
530 self.MainPage.setPLCStartedHMI(hmi) |
|
531 |
|
532 def UnLoadHMI(self): |
|
533 self.MainPage.resetPLCStartedHMI() |
|
534 |
|
535 def PLCStarted(self): |
|
536 self.plcState = True |
|
537 self.MainPage.setPLCState(True) |
|
538 |
|
539 def PLCStopped(self): |
|
540 self.plcState = False |
|
541 self.MainPage.setPLCState(False) |
|
542 |
|
543 def renderHTTP(self, ctx): |
|
544 """ |
|
545 Force content type to fit with SVG |
|
546 """ |
|
547 req = inevow.IRequest(ctx) |
|
548 req.setHeader('Content-type', 'application/xhtml+xml') |
|
549 return super(WebInterface, self).renderHTTP(ctx) |
|
550 |
|
551 def render_MainPage(self, ctx, data): |
|
552 f = self.MainPage |
|
553 f.setFragmentParent(self) |
|
554 return ctx.tag[f] |
|
555 |
|
556 def child_(self, ctx): |
|
557 self.MainPage.detachFragmentChildren() |
|
558 return WebInterface(plcState=self.plcState) |
|
559 |
|
560 def beforeRender(self, ctx): |
|
561 d = self.notifyOnDisconnect() |
|
562 d.addErrback(self.disconnected) |
|
563 |
|
564 def disconnected(self, reason): |
|
565 self.MainPage.resetHMI() |
|
566 #print reason |
|
567 #print "We will be called back when the client disconnects" |
|
568 |
432 |
569 if havewx: |
433 if havewx: |
570 reactor.registerWxApp(app) |
434 reactor.registerWxApp(app) |
571 website = WebInterface() |
435 |
572 site = appserver.NevowSite(website) |
436 # TODO add command line switch |
573 |
437 try: |
574 website_port = 8009 |
438 import runtime.NevowServer as NS |
575 listening = False |
439 website = NS.RegisterWebsite(reactor) |
576 while not listening: |
440 pyruntimevars["website"] = website |
577 try: |
441 statuschange.append(NS.website_statuslistener_factory(website)) |
578 reactor.listenTCP(website_port, site) |
442 except: |
579 listening = True |
443 print "Nevow Web service failed." |
580 except: |
444 |
581 website_port += 1 |
|
582 print "Http interface port :",website_port |
|
583 else: |
|
584 website = None |
|
585 |
445 |
586 if havewx: |
446 if havewx: |
587 from threading import Semaphore |
447 from threading import Semaphore |
588 wx_eval_lock = Semaphore(0) |
448 wx_eval_lock = Semaphore(0) |
589 main_thread = currentThread() |
449 main_thread = currentThread() |
590 |
450 |
591 def statuschange(status): |
451 def statuschangeTskBar(status): |
592 wx.CallAfter(taskbar_instance.UpdateIcon,status) |
452 wx.CallAfter(taskbar_instance.UpdateIcon,status) |
|
453 |
|
454 statuschange.append(statuschangeTskBar) |
593 |
455 |
594 def wx_evaluator(obj, *args, **kwargs): |
456 def wx_evaluator(obj, *args, **kwargs): |
595 tocall,args,kwargs = obj.call |
457 tocall,args,kwargs = obj.call |
596 obj.res = default_evaluator(tocall, *args, **kwargs) |
458 obj.res = default_evaluator(tocall, *args, **kwargs) |
597 wx_eval_lock.release() |
459 wx_eval_lock.release() |
605 o=type('',(object,),dict(call=(tocall, args, kwargs), res=None)) |
467 o=type('',(object,),dict(call=(tocall, args, kwargs), res=None)) |
606 wx.CallAfter(wx_evaluator,o) |
468 wx.CallAfter(wx_evaluator,o) |
607 wx_eval_lock.acquire() |
469 wx_eval_lock.acquire() |
608 return o.res |
470 return o.res |
609 |
471 |
610 pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, statuschange, evaluator, website) |
472 pyroserver = Server(servicename, given_ip, port, |
|
473 WorkingDir, argv, autostart, |
|
474 statuschange, evaluator, pyruntimevars) |
|
475 |
611 taskbar_instance = BeremizTaskBarIcon(pyroserver, enablewx) |
476 taskbar_instance = BeremizTaskBarIcon(pyroserver, enablewx) |
612 else: |
477 else: |
613 pyroserver = Server(servicename, given_ip, port, WorkingDir, argv, autostart, website=website) |
478 pyroserver = Server(servicename, given_ip, port, |
|
479 WorkingDir, argv, autostart, |
|
480 statuschange, pyruntimevars=pyruntimevars) |
|
481 |
|
482 for registrar in registerserverto : |
|
483 registrar(pyroserver) |
614 |
484 |
615 # Exception hooks s |
485 # Exception hooks s |
616 import threading, traceback |
486 import threading, traceback |
617 def LogException(*exp): |
487 def LogException(*exp): |
618 if pyroserver.plcobj is not None: |
488 if pyroserver.plcobj is not None: |