COM-Port simulieren
-
Ich arbeite an einem Programm, das ein externes Gerät (Kartenleser) über den COM-Port ansteuert - d.h. es öffnet eine Verbindung per CreateFileA(...) und sendet anschließend mit WriteFile()/ReadFile() Befehle an das Gerät. Jetzt habe ich aber das Problem, daß das Gerät beim Endanwender eingebaut wurde und ich kein zweites Exemplar zur Verfügung habe, um die Abläufe zu testen.
Deshalb hatte ich den Gedanken, ein Hilfsprogramm zu schreiben, das so tut als wäre es ein Kartenleser. Das würde dann ein "Objekt" zur Verfügung stellen, mit dem ich mich per CreateFile() verbinden kann und dort nach einkommenden Anfragen lauschen, um die richtigen Antwortnachrichten zurückzuschicken.
Ist sowas überhaupt möglich? Wenn ja, wie würde es funktionieren?
-
Und du willst dies mit C# erreichen?
Oder bist du womöglich im falschen Subforum gelandet und wolltest zur WinAPI?Grüssli
-
Dravere schrieb:
Und du willst dies mit C# erreichen?
Ja, wenn es geht schon. Die Alternative, die ich zur Verfügung hätte, wäre der Gupta Team Developer.
-
Ehm, aber du willst ein Gerät emulieren. Dazu bräuchte es doch auf Windows mindestens einen UMD (User Mode Driver). Ich habe so meine Zweifel, dass dies direkt mit C# geht. Ich werde mich aber mal auf die Suche machen, da es mich gerade auch interessiert, ob man sowas hinbekommen könnte.
Ich sehe allerdings noch ein paar andere Alternativen. Wieso baust du ins eigentliche Programm nicht einen Switch ein, damit man einen Simulationsmodus aktivieren kann. Dann wird die Kommunikation über eine andere Schnittstelle durchgezogen. So könntest du grundsätzlich eine beliebige andere Schnittstelle verwenden. Ich habe erst gerade sowas ähnliches gemacht und dazu eine Named-Pipe verwendet. Normalerweise geht es über eine RS232 Schnittstelle und im Simulationsmodus über die Named-Pipe, wobei ein anderes Programm als Named-Pipe-Server agiert und somit das Gerät emuliert.
In welcher Sprache ist denn das eigentliche Programm geschrieben?
Grüssli
-
Dravere schrieb:
Ehm, aber du willst ein Gerät emulieren. Dazu bräuchte es doch auf Windows mindestens einen UMD (User Mode Driver). Ich habe so meine Zweifel, dass dies direkt mit C# geht. Ich werde mich aber mal auf die Suche machen, da es mich gerade auch interessiert, ob man sowas hinbekommen könnte.
Im Moment bin ich noch auf der Suche nach möglichen Lösungen.
Ich sehe allerdings noch ein paar andere Alternativen. Wieso baust du ins eigentliche Programm nicht einen Switch ein, damit man einen Simulationsmodus aktivieren kann. Dann wird die Kommunikation über eine andere Schnittstelle durchgezogen. So könntest du grundsätzlich eine beliebige andere Schnittstelle verwenden. Ich habe erst gerade sowas ähnliches gemacht und dazu eine Named-Pipe verwendet. Normalerweise geht es über eine RS232 Schnittstelle und im Simulationsmodus über die Named-Pipe, wobei ein anderes Programm als Named-Pipe-Server agiert und somit das Gerät emuliert.
Eine Umschaltung im Programm hatte ich auch schon probiert, aber dort vermutlich etwas falsch gemacht - und den Zugriff auf das echte Gerät gekappt.
In welcher Sprache ist denn das eigentliche Programm geschrieben?
Das ist mit Gupta TD5.2 geschrieben.
-
Ok, habe einige Informationen gefunden:
1. Mit C# alleine kannst du kein Gerät emulieren. Dazu gibt es mehrere Forenbeiträge in den MSDN Foren. Dazu braucht es eben mindestens einen UMD.
2. Es gibt allerdings verschiedene Null-Modem Emulatoren im Netz. Das sind Programme, welche einfach zwei COM-Ports zusammenschliessen. Was man dann in COM Port A reinschreibt, kann man beim COM Port B auslesen, bzw. umgekehrt. Wenn du mit so einem Programm nun einen COM Port emulierst, kannst du über ein C# Programm das Gerät emulieren.
Leider habe ich als freie Software erst dieses Programm entdeckt:
http://com0com.sourceforge.net/
Funktioniert aber anscheinend nur, wenn man Windows unter Testbetrieb startet.CStoll schrieb:
Das ist mit Gupta TD5.2 geschrieben.
Das ist eine IDE und keine Sprache, zumindest nach meinem Wissen ...
Grüssli
-
Warum abstrahierst du nicht einfach das Gerät und stellst nur ein Interface bereit. Dann brauchst du nur das Interface durch eine andere Klasse zu implementieren und kannst so Testdaten erzeugen.
-
Dravere schrieb:
Ok, habe einige Informationen gefunden:
1. Mit C# alleine kannst du kein Gerät emulieren. Dazu gibt es mehrere Forenbeiträge in den MSDN Foren. Dazu braucht es eben mindestens einen UMD.
2. Es gibt allerdings verschiedene Null-Modem Emulatoren im Netz. Das sind Programme, welche einfach zwei COM-Ports zusammenschliessen. Was man dann in COM Port A reinschreibt, kann man beim COM Port B auslesen, bzw. umgekehrt. Wenn du mit so einem Programm nun einen COM Port emulierst, kannst du über ein C# Programm das Gerät emulieren.
Leider habe ich als freie Software erst dieses Programm entdeckt:
http://com0com.sourceforge.net/
Funktioniert aber anscheinend nur, wenn man Windows unter Testbetrieb startet.Dann müsste ich mich nach anderen Möglichkeiten umsehen, wie ich die Datenherkunft umschalten kann. Zur Not setze ich auch eine Ebene höher an und schalte in der Port-Ansteuerung um auf eine named Pipe, aus der ich die Daten holen kann.
Edit: Ich hab' mir doch mal die Mühe gemacht, die CreateFile()-Funktion genauer anzusehen. Dort kann man auch Named Pipes angeben, also ist es nur eine Frage der Bezeichnung (der "Port"-Name ist konfigurierbar), den ich verwende.
CStoll schrieb:
Das ist mit Gupta TD5.2 geschrieben.
Das ist eine IDE und keine Sprache, zumindest nach meinem Wissen ...
OK, wenn du es so genau wissen willst, dann SAL. Aber für mich sind dort die Übergänge zwischen Sprache und IDE fließend.
In your face schrieb:
Warum abstrahierst du nicht einfach das Gerät und stellst nur ein Interface bereit. Dann brauchst du nur das Interface durch eine andere Klasse zu implementieren und kannst so Testdaten erzeugen.
Das wäre eine Möglichkeit, aber auch wenn es behauptet, objektorientiert zu sein, ist Polymorphie in SAL so eine Sache für sich.
-
Ich hab das Problem jetzt so gelöst, daß ich mein Programm mit einer Pipe verbinde und auf der Gegenseite einen NamedPipeServerStream verwende, der die Anfragen entgegennimmt und beantwortet.
Nun bleibt allerdings noch ein Problem: Wie halte ich diese Pipe am Leben?
Das Terminal öffnet im Sinne der Lokalität für jede Anfrage den "Port", sendet seine Daten, wartet die Antworten ab und schließt das Handle wieder. Allerdings wird beim Schließen auch die komplette Pipe geschlossen und alle weiteren Operationen auf dem ServerStream werfen eine IOException ("Die Pipe ist geschlossen").
-
Und was hindert dich daran, sie erneut zu eröffnen?
Grüssli
-
Dravere schrieb:
Und was hindert dich daran, sie erneut zu eröffnen?
Ich bin mir nicht sicher, wie ich das sauber hinbekomme.
Momentan habe ich es so gelöst:
while(!ende) { NamedPipeServerStream pipe = new NamedPipeServerStream("TestPipe"); pipe.WaitForConnection(); //Kommunikation //eine Nachricht von der Gegenstelle lesen und Antwort zusammenstellen pipe.WaitForPipeDrain(); pipe.Disconnect(); pipe.Dispose(); }
aber meine Instinkte sagen mir, daß ich da noch irgendwelche Stolperstellen übersehen habe oder mir zu viel Arbeit mache.
-
Ich würde vielleicht ein zwei Dinge anders machen:
while(!ende) using(var pipe = new NamedPipeServerStream("TestPipe")) { pipe.WaitForConnection(); //Kommunikation //eine Nachricht von der Gegenstelle lesen und Antwort zusammenstellen pipe.WaitForPipeDrain(); }
Mit dem Disconnect läufst du Gefahr, dass der Client sich bereits abgemeldet hat und dann bekommst eine Exception um die Ohren gehauen. So sollte es meiner Meinung nach sicher sein. Allerdings muss ich zugeben, dass ich auch kein Pipe-Experte bin.
Grüssli
-
Jetzt habe ich nur noch ein kleines Problem mit dem WairForConnection()-Aufruf - der blockiert die Arbeit, bis sich tatsächlich der Client verbunden hat. Gibt es denn eine Möglichkeit, das Warten nach einer Weile abzubrechen und eine Meldung zu erhalten, ob die Verbindung in dieser Zeit hergestellt werden konnte?
Dravere schrieb:
Allerdings muss ich zugeben, dass ich auch kein Pipe-Experte bin.
Kennst du denn hier einen?
-
Du kannst das APM (Asynchronous Programming Model) mit BeginXXX / EndXXX benutzen. Dazu ist die PowerThreading Library von Jeffrey Richter sehr hilfreich (aber nicht zwingend).
Bei Dir wäre das konkret BeginWaitForConnection(..) / EndWaitForConnection(..).
-
theta schrieb:
Bei Dir wäre das konkret BeginWaitForConnection(..) / EndWaitForConnection(..).
Hast du mal ein Beispiel, wie sowas aussehen könnte - ein MSDN-Link würde mir auch schon reichen, aber die Beschreibung zu den Methoden ist mir zu vage, um etwas daraus zu bauen.
PS: Und ich habe noch ein neues Problem - der Client ist zu schnell für den Server und versucht die Verbindung anscheinend schon wieder neu aufzubauen, bevor der Server die Pipe wieder neu geöffnet hat. Das Ergebnis ist, daß die meisten der Client-Anfragen schon bei einem Fehler im CreateFile() hängenbleiben.