Seite 1 von 1

blockierte COM Events

Verfasst: Mittwoch 23. April 2014, 14:58
von BerndM
Hallo Zusammen,

ich suche schon länger nach einer Lösung und hoffe das ich hier eine Idee zu deren Lösung bekomme.

Ich steuere CANoe über die COM Schnittstelle fern und baue mir so einige Testumgebung auf, welche über mehrere Stunden laufen soll. Ich will dabei die Events von CANoe nutzen um auf bestimmte Änderungen zu reagieren. Als beispiel sei die Änderung einer Umgebungsvariablen genannt. Das ganze funktioniert als kleine demo Applikation schon. Ich will jetzt die verwendete MessageBox durch eine time.sleep() oder sowas ersetzen.
Leider ist die blockierend und die Events werden auch nicht mehr abgefangen.

Hier mein Testprogramm dazu:

Code: Alles auswählen

import win32com.client
import os
import time
from win32gui import MessageBox as msgbox

class Tester:
	def __init__ (self):
		self.__className = 'Tester'
		print ('%s : call constructor' %self.__className)

	def __del__ (self):
		print ('%s : call destructor' %self.__className)

	def OnChange (self, value):
		print ('VariableEvents:OnChange now called with %s' %value)

def demo ():
	appl = win32com.client.Dispatch ('CANoe.Application')
	env = appl.Environment
	var = env.GetVariable ('dtc_event')
	env_event = win32com.client.WithEvents (var, Tester)
	msgbox (0, "Finished the Test", "Info", 16)

demo()
Solange die MessageBox aktive ist, funktioniert alles gut und schön. Was kann ich nutzten um diese durch ein sleep() zu ersetzen?
Ich bin bis jetzt davon ausgegangen, daß die COM Events in einem extra Task laufen, was wohl bei python nicht so ist.
Ich habe bis jetzt versucht:
- Event Handler ein extra Tast mit threading
oder
- time.sleep() durch irgendeine while true: pass ersetzen
Leider ist dann auch wieder alle blockierend, zumal ein Task aktive sein muss und die run() Funktion den rest auch blockiert.
Ich möchte aber auch darauf achten, dass die Prozessor Last nicht zu hoch wird. Mit einer while True: pass Schleife kann ich schnell 25% CPU Load erzeugen.

Irgendwie bin ich da am verzweifeln und im Internet habe ich dazu auch keine gute Lösung gefunden.

Schon jetzt danke für die Antworten

Grüße, Bernd

Re: blockierte COM Events

Verfasst: Mittwoch 23. April 2014, 15:24
von BlackJack
@BerndM: Erst einmal etwas zur `Tester`-Klasse. Implementierungsdetails werden üblicherweise durch *einen* führenden Unterstrich gekennzeichnet. Zwei führende Unterstriche sind dazu da um Namenskollisionen bei Mehrfachvererbung und tiefen Vererbungshierarchien zu vermeiden — beides Sachen die in Python äusserst selten sind.

Das Attribut selbst dürfte auch überflüssig sein wenn Du da tatsächlich den Namen der Klasse haben möchtest. Der ist nämlich über `self.__class__.__name__` erreichbar.

`__del__()` ist kein deterministischer Destruktor. Darauf sollte man sich nicht verlassen und schon gar nicht völlig ohne Sinn implementieren, denn alleine die Existenz der Methode kann unter bestimmten Umständen Speicherlecks erzeugen. In normalem Python-Code braucht man diese Methode auch nicht.

Zwischen Funktions-/Methodennamen und die öffnende Klammer bei Argumentliste oder Aufruf gehört kein Leerzeichen. Die Namenskonvention für Funktionen, Methoden, und Werte ist `klein_mit_unterstrichen`. Ausnahmen sind natürlich Namen die von externem Code anders erwartet werden.

Warum machst Du die Leerzeichensetzung beim binären ``%``-Operator so asymmetrisch? In neuem Python-Quelltext würde ich ausserdem die `format()`-Methode dem veralteten ``%`` vorziehen.

Die Lösung zum Problem ist laut Netz eine Schleife in der `pythoncom.PumpWaitingMessages()` aufgerufen wird.

Re: blockierte COM Events

Verfasst: Donnerstag 24. April 2014, 10:38
von BerndM
@BlackJack: Danke für die schnelle Antwort und den Tips zu meinem Code.
Ich habe heute damit einiges getestet.

Für die MessageBox habe ich folgendes eingefügt um 1 Minute zu warten. Ja für dieses Beispiel funktiniert es einwandfrei. :mrgreen:

Code: Alles auswählen

	t = time.time() + 60.0
	while t > time.time():
		pythoncom.PumpWaitingMessages()
		time.sleep(0.1)
Naja, jetzt wollte ich diese Lösung in meiner Applikation einbauen und siehe da es geht nicht. Ich bekomme da diesen Fehler:
pythoncom error: Python error invoking COM method.
Liegt es daran das ich mehrere Klassen habe um mein Problem zu lösen und in der main die obigen Zeilen aufrufe?

Bezüglich der __del__() Funktion hast Du mich jetzt verunsichert. Wie kann ich sicherstellen, wenn ich ein COM Object (auch Event) nicht mehr nutzen will, das dieses auch tatsächlich freigegeben ist?

Re: blockierte COM Events

Verfasst: Donnerstag 24. April 2014, 11:52
von BlackJack
@BerndM: Was würdest Du denn mit der `__del__()`-Methode sicherstellen wollen? Im gezeigten Quelltext machst Du dort doch überhaupt nichts? Um den Speicher für das Objekt kümmert sich die Laufzeitumgebung schon automatisch. Wenn man selber noch irgendwelche externen Ressourcen verwalten, also freigeben möchte, sollte man das explizit mit einer Methode tun. Und eventuell einen Contextmanager für die ``with``-Anweisung implementieren, wo diese Methode dann benutzt wird.

Re: blockierte COM Events

Verfasst: Donnerstag 24. April 2014, 13:00
von BerndM
@BlackJack: bei CANoe ist das Event Handling noch so einfach. Dieses programm unterstützt 3 unterschiedliche Event Handler (soweit ich das bis dato gesehen habe) und einen kann und muss ich sogar öfters nutzen. So habe ich 3 Event Classen und eine leite ich dann noch ab um den Zugriff auf die unterschiedlichen Informationen nutzen zu können. Jetzt will ich zur Laufzeit einige Anmelden und wieder Abmelden. Also in den Destruktor wollte ich self.close() da einfügen. Mache ich das nicht, bleibt der COM Server hängen. (ich nutze zum Testen PyScripter)
So gut kenne ich mich mit Python noch nicht aus, was da richtig wäre oder nicht. Den Contextmanager kenne ich noch nicht und werde ihn mir anschauen.

Den Fehler "pythoncom error: Python error invoking COM method." habe ich jetzt irgendwie wegbekommen. Weiss leider nicht wie das passiert ist oder warum der weg ist, was mich nicht unbedingt beruhigt.

Du hattest mir mitgeteilt das die führenden "_" in Python selten verwendet werden. Wie kann ich Variablen als Private und Protected kennzeichnen? Die Scripte, welche ich mir bis jetzt ansah nutzten dieses dafür. Oder ist es mitunter egal in Python und es gibt gar keine Unterschiede mehr? Hat sich da etwas zwischen Python 2.7 und 3.x geändert? Ich nutze im Moment Python 2.7.3 und es ist auch nicht vorgesehen auf 3.x umzusteigen.

Wie bindend ist denn die Syntax bei Pythen? Soweit ich das verstanden habe ist doch die Leerzeichen vor und nach den Klammern egal und die Schreibweise der Funktionen auch. Gibt es da einen sehr wichtigen Grund dieses so zu machen oder geht es nur darum in der Welt einen einheitlichen Standard zu etablieren?

Re: blockierte COM Events

Verfasst: Donnerstag 24. April 2014, 13:35
von BlackJack
@BerndM: Wie gesagt, die `__del__()`-Methode ist nicht deterministisch, die Sprache macht keine garantien wann sie aufgerufen wird, oder ob sie überhaupt jemals aufgerufen wird. Wenn man zu einem bestimmten Zeitpunkt etwas erledigt/aufgeräumt haben möchte, dann muss man das explizit selber tun.

Wenn diese Methode `close()` heisst, ist `contextlib.closing()` eventuell nützlich.

Tatsächlichen Zugriffsschutz für Attribute gibt es in Python nicht. Mit einem führenden Unterstrich kennzeichnet man Implementierungsdetails die nicht von aussen benutzt werden sollten. Da hat sich zwischen 2 und 3 nichts geändert. Das `__spam` ”private” sei, verbreiten die selben Tutorials und Bücher die auch `__del__()` als Destruktor vorstellen für Sachen die so gar nicht zuverlässig funktionieren.

Dem Compiler sind die Leerzeichen und die Schreibweise der Namen egal. Es gibt halt den Style Guide und damit ein bestreben einen Standard zu haben. Interessant finde ich immer das ich zum Beispiel noch nie irgend ein Java-Projekt gesehen habe was versucht die Schreibweise von Namen anders als in Java üblich zu machen. Da kommt die Frage komischerweise gar nicht erst auf, obwohl es dem Compiler da auch egal wäre. :-)

Re: blockierte COM Events

Verfasst: Donnerstag 24. April 2014, 15:14
von BerndM
Also die __del__() habe ich schon rausgeschmissen bei mir und nur noch eine Metode implementiert, welche alle close() aufruft. Ich bin doch sehr überrascht das in der Lieteratur usw. dieses so beschrieben wird.

Das ich keine Variablen als Private deklarieren kann finde ich nicht sehr schön, zumal Python schon eine objektorientierte Scriptsprache ist.
Warum viele bei Python sich nicht an die Schreibe xxx_yyy_ccc() halten liegt eher daran das sehr viele aus der C/C++ Welt kommen, wo Jahrezehnte schon Coding Styles in den Firmen exisiteren und diese einfach in Fleisch und Blut sind. Und die schreiben mit einmal Python für Tests oder Automatisierung von ihren täglichen Arbeit.
Ich habe hier im Projekt auch das Problem das einige Python verwenden, aber so richtig es auch nie gelernt haben und auch nur gerade so nutzen um ihre Aufgaben zu realisieren.

Das ist bei Java anders. Ich werde mir mal diese Rules anschauen und hier im Projekt ansprechen was sie nun wollen.

Achja, jetzt ist mir wieder eingefallen warum ich den Klassen name selber gesetzt hatte und nicht self.__class__.__name__ verwendete. Durch die Registrierung der Klassen als Event Handler erhalten alle den gleichen Namen "COMEventClass", was für Debug ausgabe nicht sehr hilfreich ist.

Re: blockierte COM Events

Verfasst: Donnerstag 24. April 2014, 16:10
von BlackJack
@BerndM: Warum willst Du etwas als `private` deklarieren? Und was hat das damit zu tun das Python objektorientiert ist? Zugriffsschutz und OO sind orthogonal. Genau wie Kapselung und Zugriffsschutz. Wenn es der Programmierer tatsächlich nicht schafft die Finger von Attributen mit einem führenden Unterstrich zu lassen, dann hat der Programmierer IMHO ein grösseres Problem als man mit Zugriffsschutz alleine lösen könnte.

Re: blockierte COM Events

Verfasst: Donnerstag 24. April 2014, 17:06
von BerndM
@BlackJack: Ja, das ist so eine dumme Angewohnheit von mir, den eventuellen zukünftigen Nutzer möglichst keine Möglichkeiten zu geben einen Fehler in der Benutzung zu verursachen. Ich hatte in der Vergangenheit deswegen schon öfters Probleme, selbst wenn man hinschreibt "NICHT NUTZEN! FINGER WEG!", wird es dennoch gemacht. Daher was, ein benutzer nicht machen kann, muss man auch nicht abtesten und sicherstellen, das da fehler passieren können.
Für mich selber ist es egal, ich komme damit auch so klar.

Ich habe jetzt mein Script fertig, Dank deine guten Hilfe hier. :D

Jetzt steuere ich über VISA einen Generator und ein Netzteil und Werte CAN Messages in CANoe aus, so daß ich Langzeittests laufen lassen kann und verschiedene Signale nacheinander abspielen kann ohne daneben zu sitzen und alles per Hand einzustellen. Das Ganze ist leicht zu erweitern, was ich auch wollte, da die Anzahl der Signale, welche getestet werden sollen nehmen immer mehr zu.

Re: blockierte COM Events

Verfasst: Donnerstag 15. Mai 2014, 18:17
von Saravanan
Dear Friends,

I don't know any German but I have the same issue from what I understood using Google translate. I'm working on implementing a COM server in Python as I have to interact between a dSPACE HIL environment & Vector CANOE environment. The idea is I change the environment variable in (CANOE) CAPL script & I would like to perform some action on the dSPACE HIL when the event happens. So right now I have not implemented any threads.
* So the first issue is when I try to use a simple sleep to make sure the test execution in CANOE is completed, the execution doesn't happen as python sleep is blocking CANoe execution. When I use the message box as "BerndM" has done it is working fine. I'd like to replace it with some other mechanism like sleep. Kindly let me know how you have solved this issue. I have pasted my script below.

Code: Alles auswählen

class EnvironmentEvents:
    def __init__ ( self ) :
        print "Initializing Environment Event Class" 

    def OnChange (self,Value) :
        StopTestExecution = Value
        print '%3.2f' %StopTestExecution
        print ('Environment Variable Value Changed')

##        ################# INITIALIZE CONTROL DESK ################################## 
        MyControlDeskHandler = Controldesk_Handler.CONTROLDESK_HANDLER()

##        ################# INITIALIZE CANOE #########################################         
        MyCAN                = Dispatch ( 'CANoe.Application' )
        MyCANMeasurement     = MyCAN.Measurement
        MyCANEnvironment     = MyCAN.Environment.GetVariable("env_COM_BatteryVoltage")
        MyEvents             = DispatchWithEvents (MyCANEnvironment,EnvironmentEvents)

        TestSetup = MyCAN.Configuration.TestSetup
        TestEnvs = TestSetup.TestEnvironments
        TestEnv = TestEnvs.Item(1)
        TestEnv.ExecuteAll()            

##        msgbox ( 0 , "Finished the test" , "info" , 16 )  ## with a message box everything is working fine

        while(StopTestExecution>0):   ##  My idea is to send the event variable value from CAPL as 0 to stop the execution. Until that time looping has to go on.But this doesn't work
            i = i + 1
            print '%3.2f' %StopTestExecution 
            Sleep(500)        
* Then the second issue is when the OnChange event occurs I'd like to change some thing in the dSPACE HIL Control Desk. For this I have to use a library provided by dSPACE. But the problem is how do I access this library & its methods in that class. I tried to pass the dSAPCE library object & the parameter as arguments for class EnvironmentEvents as you can see below. But python gives me the following error.

Code: Alles auswählen

        MyEvents             = DispatchWithEvents (MyCANEnvironment,EnvironmentEvents(MyControlDeskHandler,'Model Root/IOUserInterface/IO_PAR/POWER/VOLTAGEOUT/Value'))

class EnvironmentEvents:
    def __init__ ( self, ControlDeskhandler, Path) :
        print "Initializing Environment Event Class"
        self.MyHandler = ControlDeskhandler
        self.MyPath = Path

    def OnChange (self,Value) :
        StopTestExecution = Value
        print '%3.2f' %StopTestExecution
        self.MyHandler.Write_Variable(self.MyPath ,Value)
        print ('Environment Variable Value Changed')

Traceback (most recent call last):
  File "C:\Program Files (x86)\Common Files\dSPACE\Python25\lib\site-packages\win32com\client\__init__.py", line 304, in DispatchWithEvents
    result_class = new.classobj("COMEventClass", (disp_class, events_class, user_event_class), {"__setattr__" : _event_setattr_})
TypeError: instance() takes at most 2 arguments (3 given)
[Dbg]>>> 
Kindly help me to fix these issues. If you need any other information please let me know.
Looking forward to your reply. Thanks.