@Coder007
hatte ich auch so verstanden und macht mir dadurch leider auch weiter Hoffnung auf einen anderen besseren Weg.
Mehr als mein jetziges Vorgehen zu beschreiben, den Code zu posten und um Stellungnahme zu bitten
Aber eigentlich geht es auch nur darum, ob diese beiden Anzeigen im Code so richtig implementiert sind. Wenn mir jemand dazu etwas sagen könnte, wäre mir sehr geholfen. kann ich aber nicht. Weshalb ich jetzt aufgegeben hatte auf weitere Hilfe zu hoffen.
Meine Aussage:
C/C++ mit/ohne Linux mit/ohne Echzeitkernel scheidet von meiner Seite für diese Anwendung leider aus. ging eher in Richtung T-Virus, der darauf zweimal hinwieß.
Ich hatte halt noch gehofft, dass jemand der das TestProgramm runtergeladen hat auf Anhieb einen krassen Fehler gesehen hat, aber egal.
Also schonmal vielen Dank an alle die hier schon geduldig Zeit investiert haben oder vielleicht doch noch investieren.
Als derzeitiges Fazit scheint ja der Weg mit den zwei Timern und zwei ConcurrentStack (oder ähnlichem mit manuellen Locking) hier der beste zu sein, wenn man mit c#/.net/WPF/WES7 arbeiten muß. Wie auch hier schon frühzeitig vorgeschlagen. C/C++ mit/ohne Linux mit/ohne Echzeitkernel scheidet von meiner Seite für diese Anwendung leider aus.
Bewußt ignoriert habe ich bestimmt nichts und wenn das so rüber gekommen ist entschuldige ich mich natürlich dafür.
Gruß luciluke
Hallo,
will nicht vielleicht doch jemand noch Kritik am Code oder an der vorgehensweise zur Anzeige machen? Ich habe meinen Weg ja jetzt grob beschrieben und die Testapplikation wurde ja schon verschiedentlich herruntergeladen. Es geht nicht um "(=>100% identische Frameraten)" wie behauptet wird, sondern um möglichst performanten Code vom Zeitpunkt des Datenerhalts bis zur Anzeige.
Wenn meine Vorgehensweise so schon in Ordnung ist, wäre das zum einem gut zu erfahren, aber Verbesserungsvorschläge würde ich auch gerne ausprobieren.
Hallo Coder007,
das ist ein Problem der Simulation, da das Zeit-Daten Packet ( hier SmpteFrame ) in dem TestTimer Tick Event erzeugt/ hochgezählt wird und dieser dann ja als erstes zusammenbricht (kürzester Intervall), d.h. die "Zeit" der SmpteFrame wird nicht mehr alle ungefähr 30-40ms erhöht. Im realen Fall kommt das Zeit-Daten Objekt über Netztwerk rein und hat dann natürlich auch die "aktuelle Zeit" bzw. die Zeit die angezeigt werden soll.
Also so krasse Sachen passieren nicht, das ist eine Einstellungssache der Simulation. Wenn ich bei mir WorkOn mit SomeWork count = 20000 habe und die Pegelanzeige ein bzw. aus schalte, dann sehe ich eine etwas schleppendere Anzeige und auf dem J1900 kommt es teilweise zum Stocken.
Auch die Zeiten für die anderen Timer sind ja kürzer als nötig, um überhaupt jetzt mal zum Verständniss einen Unterschied beim umschalten zu sehen. Die zusätzliche Arbeit, die entsteht wenn die Pegel angezeigt werden sind der Punkt. Ob diese zusätzliche Arbeit, durch das Binding im Hintergrund oder weiß nicht genau was da noch passiert, oder aber durch Zusammenspiel DirectX, GPU ... weiß ich nicht.
Die Frage ist, ob das was ich von Packetempfang bis zu den Anzeigen mache so richtig bzw. am besten sogar "optimal" für die Performance ist.
Kannst Du diesen Weg den nachvollziehen, oder soll ich nochmal besser beschreiben?
ps.: rechts unterhalb von der TimeCode Anzeige wird übrigends die verstrichene Zeit je Zeitanzeige Event angezeigt. Also, wenn der Zeitanzeige Timer auf 60 steht, dann sieht man da z.B. dann im guten Fall sowas zwischen 50 und 80. Dient nur dem Verständniss.
Ich versuchs mal...
In TcpServer der _testTimer stellt die Simulation der eingehenden Packete dar, d.h. die Methode Receive ist noch fake.
_model.TcpServer_DataReceived(buffer); ist dann die erste richtige Methode, danach wird dieser buffer wieder als Antwort zurückgeschickt(fehlt jetzt hier).
In TcpServer_DataReceived(byte[] buffer) werden die Daten verarbeitet.
ProzessKbData(kbData_2);//=====> simmuliert hier zusammen mit dem kleinen Tasten Fenster die Keyboardbefehle die eigentlich in den empfangenen Daten enthalten sind.
In _at.ProzessData(atData) findet die eigentliche Arbeit statt.
_model.TimeCodeBufferString.Push(_model.RefreshTimeCodeString(RefInOutFrameCopyTcOnly)); stellt den Timecode String in den ConcurrentStack<string[]> TimeCodeBufferString.
In ProzessSmpteFrameAsync(incommingSMPTE); werden die Daten bearbeitet und nach der Bearbeitung wird in Push_FaderData() eine List<BargraphData> erstellt, die im Prinzip einfach nur die 129 Bargraph Werte enthält. BargraphData implementiert INotifyPropertyChanged.
Diese Liste wird in einen zweiten ConcurrentStack<List<BargraphData>> BargraphBuffer gestellt.
Das war es mit der Bearbeitung und die bearbeiteten Daten werden zurückgeschickt.
Jetzt gibt es noch einen Timer _ltcGuiTimer der alle x ms den letzten wert aus dem ConcurrentStack<string[]> TimeCodeBufferString holt, die weiteren Werte, falls vorhanden, verwirft und dann in RefreshTimeCodeWPF(string[]) in einem Objekt StatusBoxData : DependencyObject ein DependencyProperty ändert. An dieses DependencyProperty ist in MainWindow.xaml entsprechend ein Textblock gebunden.
Und es gibt einen Timer _faderGuiTimer der alle x ms den letzten Wert aus dem ConcurrentStack<List<BargraphData>> BargraphBuffer holt, die weiteren Werte falls vorhanden verwirft und dann in RefreshBargraphGui(List<BargraphData> faderData) eine zweite List<BargraphData> DicCurBargraphData aktuallisiert. An die BargraphData Objekte in dieser Liste sind "BargraphUserControls " in BargraphViewModel.cs gebunden. Und diese Liste von BargraphUserControls dienen als ItemsSource für eine angepasste ListBox zur Anzeige der Pegel. Leider benutze ich mal die Bezeichnung Bargraph mal Fader.
Wenn nötig beschreibe ich das auch gerne nochmal genauer.
Die zwei Fenster sind Absicht, das sind dann zwei Monitore im Vollbildmodus und auf einem kann dann noch manchmal was anderes angezeigt werden, aber das fehlt jetzt hier. Wie im Eingangsbeitrag beschrieben läuft auch sonst nichts anderes auf dem Rechner, es gibt auch keine Maus und kein Keyboard. Relevant ist das, da es vom rendern her doppelte Belastung bedeutet.
Das ist eine Time Code Anzeige und die läuft bei SMPTE30 mit 30 (Film Bildern) Frames pro Sekunde, daher die 30.
Also ich habe einen Intel Core i5-3570 (wohl eher mittel schnell und auch nicht die neuste Grafikkarte) und wenn ich bei WorkOn die Pegelanzeige ein bzw. aus schalte, dann sehe ich eine schleppendere Anzeige. Bei einem Atom mit integrierter Grafik stolpert dann alles.
@Coder007
danke, dass Du dir das mal anschaust.
Oben rechts läuft ja die Zeitanzeige, um die geht es. Wenn man in der Methode "SomeWork()" den count entsprechend einstellst, dass eine gefühlte Verlangsamung, bzw. ein anderes Muster an der letzten Stelle dieser Zeitanzeige zu erkennen ist, wenn man zwischen Pegel on/off umschaltet, dann hat man das was ich meine. Die Zeiten für die Zeit und die Pegel Anzeige können auch größer gewählt werden, aber ich denke bei der Zeitanzeige sollte es nicht mehr als 90 und bei der Pegelanzeige nicht mehr als 120 sein ( ich schreibe besser mal nicht ms ). Habe es hier jetzt nur mal relativ kurz eingestellt, um überhaupt einen Unterschied erkennen zu können, da ich nicht glaube, dass hier jemand vor ein Rechner mit Celereon oder Atom Prozessor sitzt.
Gut zu sehen ist auch, dass links oben bei der FPS Anzeige (nur für Testzwecke) der Count ziemlich unbeirrt durchläuft. Das die Zahl an den letzten zwei Stellen keinen informativen Wert haben ist klar, aber trotzdem erkennt man wohl am Muster eine vermeintliche Gleichmäßigkeit. Und um diese geht es.
Aber eigentlich geht es auch nur darum, ob diese beiden Anzeigen im Code so richtig implementiert sind. Wenn mir jemand dazu etwas sagen könnte, wäre mir sehr geholfen. Ist z.B. das erste mal, dass ich etwas mit WPF und MVVW gemacht habe.
Hallo,
da unter anderem Palin, erfreulicher Weise, noch nicht ganz aufgegeben hat, habe ich jetzt das Projekt mal auf das nötigste zusammen geschrumpft und die eingehenden Daten über einen Timer simmuliert. Die Refreshzeiten für den "Zeit anzeigen Timer (60ms)" und den "Bargraph anzeigen Timer (90ms)" können einfach ein/umgestellt werden. So wie das Program jetzt eingestellt ist läuft es auf meinem Entwicklungsrechner gleichmäßig, egal ob die Pegel unter Last angezeigt werden oder nicht, auf dem Rechner mit dem J1900 Processor jedoch nicht, oder nicht ausreichend. Es wird also ganz gut das Orginal simmuliert.
Ist jetzt nicht sonderlich kommentiert und sortiert, da ich annehme, daß man sowieso kein ganzes Projekt anhängen kann/soll, aber wie soll ich sonst die Zusammenhänge erklären. Sehr viele Codeausschnitte sind wohl auch nicht übersichtlicher, oder? (<- ernstgemeinte Frage, dann mache ich das)
ps.:
Um das doch nochmal kurz klar zustellen. Ich weiß genau, daß sowohl Windows 7 als auch WES7 keine Echtzeit Systeme sind, und ich verstehe nicht wieso mir hier teilweise das Gegenteil unterstellt wird.
Aber wenn das Program auf dem einem Windows 7 Rechner gleichmäßig genug läuft und auf dem anderen nicht, dann darf ich ja wohl behaupten, daß es nicht "nur" !!! an Windows liegt.
Das es auch an Windows und .net mit GC usw. liegt ist schon klar. Trotzdem würde ich halt gerne das beste rausholen. Es kann ja gut sein, daß ich irgendwo einen krassen Fehler mache.
Zitat aus Anfangsbeitrag:
Mit beschäftigt und ausprobiert habe ich unter anderem Windows Forms, WPF, WPF mit Windows Forms Host, RenderTargetBitmap, SharpDX/SlimDX mit/ohne WPF Host, usw. .
WPF Drawing Performance
bestätigt auch meine Tests, auf sehr unterschiedlicher Hardware (s.a.: RenderCapability.Tier, RenderOptions.ProcessRenderMode usw.) Weswegen ich auch unter anderem Windows Forms verworfen habe, da XP (XPe) keine Option mehr ist.
Gameloop scheidet wohl aus, da die Datenverarbeitung (Antwort erstellen/zurückschicken) höchste Priorität hatt. Hatte ich mich im Rahmen von DirectX usw. auch mit beschäftigt.
Hallo Abt,
ist mir jetzt nicht so ganz klar, warum Du verärgert bist, bzw. Du mir jetzt hier unterstellst Sachverhalte nicht zu verstehen, zu denen ich mich gar nicht geäußert habe.
Das Auge kann informativ nicht mehr als 3-4 verschiedene Werte pro Sekunde kurzfristig verwerten. Erkennen kann es mehr, aber nicht sachlich verarbeiten.
Wo bitte habe ich irgendwo etwas gegenteiliges behauptet. Ich rede immer von erkennbar fehlender Gleichmäßigkeit.
Wenn Du mit zwei Timern zwei Label aktuallisieren läßt und ein Timer läuft mit 60ms und einer mit 90ms, beide greifen auf die gleiche Zahl zu, die mit einem Timer alle 30ms von 1 bis 30 hochgezählt wird, dann siehst Du da einen Unterschied. Und wenn Du nun eine Anzeige hast und diese zwischen diesen beiden Geschwindigkeiten springt bzw. schwankt, dann sieht das extrem schlecht aus und ist bei dieser speziellen Anwendung nicht akzeptabel. Und wenn es mit einem leistungsfähigerem Prozessor geht, dann liegt es halt eben nicht nur an Windows oder .Net, sondern kann auch am Code liegen. Und ob ein Gerät, lüfterlos gebaut werden kann oder nicht ist auch schon ein signifikanter Unterschied. Und wenn sich heraustellen sollte, dass es nicht geht bzw. ich es nicht hinkriege, dann muß halt ein leistungsfähigerer Prozessor her.
Aber Du hast selber geschrieben:
Die höchste Gleichmäßigkeit, die er erreichen kann (denke ich), ist alle ~50-100ms die Anzeige aus einem Datenpool durch einen Timer zu aktualisieren.
Ja, das wäre doch perfekt, da würde ich halt gerne landen und deshalb habe ich hier um Hilfe gebeten.
Ich habe nur in der Problemstellung die 33ms erwähnt, sonst rede ich immer nur von Gleichmäßigkeit.
luciluke hat leider bis jetzt überhaupt nicht verstanden, oder ignoriert es, dass exakt alle 33ms etwas zu verarbeiten und dann auch noch zu zeichnen in dieser Umgebung nicht möglich ist.
wo bitte habe ich zu dieser Schlußfolgerung Anlass gegeben.
Meine Aussage.
Ja, es soll einfach nur gleichmäßig laufen und ich mache das auch im Moment schon mit zwei Timern alle 90ms die Zeitanzeige und alle 150ms die Pegelanzeige.
Aber auch das schwankt.
Was ich nicht verstehe ist das Fazit, aber nun genug zu meiner Ehrenrettung.
Hallo Abt, Hallo Palin
ich kann das jetzt nicht so stehen lassen, das ich das nicht verstanden hätte.
In meinem letzten Post habe ich lediglich die Frage von pinki beantwortet und die Pakete kommen nunmal alle 33ms +-3ms rein. Und ich habe die Problemstellung von "nicht häufig genug" auf "nicht gleichmäßig genug " richtiggestellt, da Palin danach fragte.
In meinem vorletztem Post habe ich doch auch geschrieben:
Ich versuche jetzt nochmal die Variante mit zwei Timern und zwei ConcurrentStack für jeweils Zeit- und Pegel-Anzeige, da ich ja eigentlich auch davon überzeugt bin, daß dies die beste Lösung ist.
und da bin ich dabei, ist halt etwas umfangreicher.
Und wenn ich da ein einigermaßen befriedigendes Ergebniss hinbekomme, versuche ich mal in Auszügen den gesamten Weg zu posten, um schauen zu können, wo es noch besser geht. Allein für die Anzeige der Zeit in WPF gibt es ja schon einige unterschiedlich Wege.
Der tote Gaul ist also fleissig und bestimmt nicht beratungsresistent.
Im Gegenteil ich freue mich ja, das Ihr mir helft. Und das habt Ihr jetzt schon!
@Palin Das Problem ist, daß es nicht gleichmäßig genug aktuallisiert, nicht nicht oft genug aktualisiert.
@pinki alle 33 ms +- 3ms ein Paket
Hallo Abt Hallo Coder007,
ich hatte es jetzt ja nur nochmal ohne Entkopplung getestet, da Palin meinte es mal ohne Timer zu versuchen und das dies nicht zu Erfolg geführt hat habe ich geschrieben.
@Coder007:
...solche Spielereien wie die Musik durch irgendwelche grafischen Effekte zu visualisieren
Wenn Du die grafischen Effekte mal mit einer Frameanzeige, also einer hochlaufenden Zahl ersetzt, würdest Du warscheinlich auch Unregelmäßigkeiten sehen. Bei z.B. einer Balkenanzeige meine ich auch alles flüßig zu sehen, aber anscheindend kann man Zahlen genauer wahrnehmen, da man daran gewöhnt ist zu zählen oder so, nur eine Vermutung. Und wenn ich mir z.B. die Visuallisierung vom VLC Player ansehe (VU Meter), dann sieht man, daß diese unter anderem erst ca. 500 ms später anläuft und auch genauso lange nachläuft, wenn gestoppt wird, da wird anscheinend gar nicht auf das Timing geachtet hauptsache es bewegt sich was. Das reicht bei meiner Anwendung leider nicht so ganz.
Ich versuche jetzt nochmal die Variante mit zwei Timern und zwei ConcurrentStack für jeweils Zeit- und Pegel-Anzeige, da ich ja eigentlich auch davon überzeugt bin, daß dies die beste Lösung ist.
Hallo, erstmal danke für die Antworten,
habe das ganze jetzt auch nochmal ganz schlicht ohne async, ohne Queue usw. gemacht.
Daten kommen über Netzwerk rein, Zeit wird angezeigt, Daten werden bearbeitet, Daten werden zurückgeschickt.
Wobei bei der Zeitanzeige durch Dispatcher.BeginInvoke(new Action(NewMethod), DispatcherPriority.Send); oder ähnlichem ja schon doch noch etwas asyncron ausgeführt wird um in den UI Thread zu kommen.
Aber auch hier sieht man halt ungleichmäßigkeiten in der Zeitanzeige abhängig von der Systemauslastung.
@Palin (Quellcode wäre hilfreich)
Ich wüßte nicht wo ich da anfangen sollte.
... Wie schon oben geschrieben: Wenn ich die Gui abschalte benötigt ein Durchlauf der Daten (1) ca 1- max. 5ms incl. Empfangen/ Senden ...
Ich hätte also noch gut 20ms Zeit für die Anzeige und das reicht halt nicht immer.
Ich werde wohl einen etwas leistungsstärkeren Prozessor testen müßen und mich dann leider von der lüfterlosen Variante verabschieden, das war ja eigentlich mein Ziel.
Aber die Hoffnung stirbt zu letzt, Ideen werden noch gerne getestet.
@Abt:
Ich werd auch nicht schlau draus, warum es in diesem Fall die Anforderung von Dauerhaft X FPS gibt, wenn sie höher ist als ~27 FPS.
Kann sowieso kein Mensch unterscheiden geschweige denn was mit den Mehrinformationen anfangen.
Ist mir vollstens bewust, deshalb schrieb ich ja auch "... bzw. wo es wenigstens so aus sieht".
@T-Virus:
die Datenakquisition mache ich ja wie geschrieben schon in c/c++ mit einem CE6 Modul
Ich hoffe aber immernoch, da auf einem leistungsstärkeren Rechner mit Grafikkarte die Gleichmäßigkeit gegeben ist. Trotz .net, wpf, ca.15ms timer Auflösung und und und ...
... wenn ich das wüßte, wäre es ja auch zu einfach.
Ja, es soll einfach nur gleichmäßig laufen und ich mache das auch im Moment schon mit zwei Timern alle 90ms die Zeitanzeige und alle 150ms die Pegelanzeige.
Aber auch das schwankt. Die FPS wird mit 40-50 angezeigt.
Hallo Abt,
im Prinzip brauche ich wohl eine Technik mit der ich Bereiche des Bildschirms mit unterschiedlicher Priorität zeichenen kann. Zum einen gibt es diese Zeitanzeige, die halt an der letzten Stelle alle 33ms (30fps) aktuallisiert und einen zweitrangigen Bereich in dem ca. 60 Balkenanzeigen (Pegelanzeigen) gezeigt werden.
Und diese Zeitanzeige soll halt immer gleichmässig laufen, es würden natürlich auch z.B. alle 60ms gehen, aber halt gleichmäßig. Das fängt dann immer an ein wenig zäh zu laufen, kann ich nicht anders beschreiben. Kein Echtzeitsystem ist klar, aber z.B. Cubase oder Digijay bekommen ja auch eine Zeitanzeige hin, wo die hintere Zahl gleichmäßig durchläuft bzw. wo es wenigstens so aus sieht.
Hallo,
ist zwar schon was länger her, aber da ich das Problem nur aufgeschoben habe, muß ich doch nochmal einen Versuch starten und um Hilfe/Ideen bitten.
Kurzfassung/Eingrenzung:
Also es geht eigendlich nur darum einen Text mit 9 Zeichen möglichst exact alle ca. 33ms anzuzeigen mit einem Celeron J1900 Processor mit integrieter HD Graphics (Bay Trail) unabhängig von der sonstigen Auslastung des Programes.
Die Datenbearbeitung stellt zeitlich wirklich nicht (mehr) das Problem dar und ist getrennt von der Darstellung.
Aber wann in WPF mit Databinding gezeichnet wird ist vielleicht noch nicht so optimal.
Biete unendliche Dankbarkeit für den entscheidenden Hinweis.
Gruß luciluke
Hallo FZelle,
mit "syncron erfolgen" meinte ich auch nur die Daten empfangen, bearbeiten und wieder senden. Nicht die Darstellung. Vielleicht einwenig unglücklich formuliert in meinem Anfangs post.
In meinem "TcpServer" läuft ein Thread der den anfragenden Client verbindet und dann wenn ein Paket empfangen wurde wird dieses bearbeitet und wieder zurückgeschickt.
Das wäre ja dann mein Daten Thread, oder?
Für die Anzeigen mache ich immer nach Erhalt eines Paketes eine Kopie der Daten, der Daten Thread läuft weiter.
Diese Kopie habe ich schon einmal einer BlockingCollection<T> hinzugefügt und für die Anzeige in einem extra Task der vom Hauptthread erstellt wurde consumiert, um dann bei z.B. WPF mit hilfe von MVVM usw. z.B. die Zeit anzuzeigen.
Das wäre ja vergleichbar mit SyncQueue ... .
Wobei sich für mich da die Frage stellt was da der Darstellungs Thread ist. Der Task der consumiert und das Property ändert oder das ganze interne WPF Binding danach.
Ich habe auch schon mit einem Timer gearbeitet der die Kopie alle 80ms oder so dargestellt hat. Aber eine 100% flüßige Anzeige des Timecodes unabhängig von der restlichen Programauslastung bekomme ich da einfach nicht hin.
Nach erhalt des Datenpackets erstellst du die Antwort und sendest diese.
Wenn du das in 1-5ms hinbekommst ist da schon mal alles OK.
Das bekomme ich incl. der Datenkopie hin.
Hallo gfoidl,
danke für den Hinweis. Hatte ich mich auch schon mit beschäftigt, aber das war soweit ich mich erinnere wohl eher etwas um die Bearbeitung meiner Daten noch zu beschleunigen, aber da die Zeit für die Bearbeitung nicht das Problem war und ist hatte ich das auch nicht weiter verfolgt. Wenn ich das mit der Anzeige in den Griff bekomme, ist das natürlich zu überlegen.
(vorab: leider mußte ich Echtzeit schreiben, da wirklich alle 33ms eine Antwort erfolgen muß. Möchte aber keine Diskussion über Echtzeit anregen)
Hallo,
folgendes Problem.
Rahmenbedingungen:
Die Rahmenbedingungen sind halt leider mit ausschlaggebend, da die Anwendung auf z.B. einem Intel Core i5-3570 mit entsprechender Grafikkarte ohne Probleme läuft. Zum Testen habe ich mir einen Rechner mit einem Celeron J1900 Processor geholt, da dieser im Moment wohl der leistungsstärkste lüfterlos zu betreibende ist mit der neusten HD Graphics. Dieser kommt schon an seine Grenzen und die Anzeige hinkt ab und an und dies ist leider nicht akzeptabel.
Anwendungs Beschreibung:
Die Anwendung bekommt über Tcp/Ip von einem Windows CE6 Rechner (auch eigen programierung) alle ca. 33ms (30/sec) einen Timecode Wert, je 130 Messwerte (0-1024) und Schalterstellungen (0-1) übertragen. Diese Werte müßen einzeln (130 Kanäle) in Abhängigkeit unter anderem von der zugehörigen Schalterstellung und des Timecodes bearbeitet, zwischengespeichert und wieder zurückgegeben werden. Am Ende eines Messdurchganges, max. 60 Minuten, müßen alle Werte in einer Datei gespeichert werden.
Der Windows CE6 Rechner steuert eigen entwickelte analoge Ein- Ausgabe Karten und nimmt noch einzelne Char Werte einer Tastatur (keine Pc Tastatur) entgegen und leitet diese auch über Tcp/Ip an den WES7 Rechner weiter, der diese zu Befehlen zusammen setzt. Die Eingabe über diese Tastatur ist die einzige Steuerungs möglichkeit der Anwendung. An dem ganzen System befindet sich also keine Maus und keine Pc Tastatur.
An den WES7 Rechner sind zwei Bildschirme angeschlossen zur Anzeige der Tastatureingaben für die
Bedienung, der Zeit (frame genau) sowie der Messwerte in Form eines Balkens je Kanal (Bargraph-Anzeige).
Problem(e):
Im moment läuft die Datenübertragung über eine syncrone Socket Verbindung (WES7 TcpServer, CE6 TcpClient). Das Datenpaket wird angenommen, bearbeitet und mit den neuen Daten als Antwort zurückgesendet. Während der Bearbeitung (1) wird
(2) der übermittelte Timecode angezeigt
(3) die Bargraph-Anzeige aktualisiert
(4) die Tastatureingaben ausgewertet und angezeigt.
Da es zu jedem empfangenden Daten Paket nur exakt ein Antwort Paket gibt (können ja auch aufgezeichnete Daten sein die zurück geschickt werden), kann der gesamte Ablauf der Anwendung doch nur syncron erfolgen, oder ist da schon der erste Denkfehler?
(2)(3)u.(4) müßen natürlich nicht jeden Durchlauf angezeigt werden.
Mein Problem ist die Gui Aktualisierung (2)(3)u.(4). Die Zeitanzeige (2) sollte z.B. unter allen Umständen, unabhänig von der Auslastung der Anwendung holperfrei durchlaufen, wie z.B. bei Cubase oder anderen Programen mit Timecode Anzeige. Mit beschäftigt und ausprobiert habe ich unteranderem Windows Forms, WPF, WPF mit Windows Forms Host, RenderTargetBitmap, SharpDX/SlimDX mit/ohne WPF Host, usw. .
Wenn ich die Gui abschalte benötigt ein Durchlauf der Daten (1) ca 1- max. 5ms incl. Empfangen/ Senden und die ganze Bearbeitung ist noch lange nicht optimiert.
Da ich mich mit der Anwendung nun schon sehr lange beschäfige, befürchte ich ein wenig blind für das eigentliche Problem zu sein und hoffe hier die erlösende Hilfe zu bekommen.
Ich habe versucht den Zusammenhang so umfangreich wie erstmal nötig darzustellen, aber beantworte gerne spezielle Rückfragen. Alles aufzulisten was ich schon versucht habe erschien mir nicht hilfreich.
Bin für jede Hilfe sehr dankbar.
Hallo,
zwar schon was älter aber das Program (Adressverwaltung, Rechnungen, Lieferschein, Storno usw. drucken) in dem ich das eingesetzt habe wird seit 10 Jahren benutzt ohne Probleme.
Hallo all-finder oder auch jeder der mir helfen kann,
ist zwar schon etwas her, aber ich habe aktuell das gleiche problem.
Auf dem Windows CE Gerät landen Daten mittels CDLL (auch von mir) und IntPtr in meiner C# Anwendung, die eigentlich nur dazu gedacht ist die Daten über TCP IP an einen "normalen Windows Rechner" zu schicken (TCP IP in C würde ich mir gerne ersparen). Dort werden die Daten angezeigt, bearbeitet und wieder zurückgeschickt.
Das Windows CE Gerät arbeitet dann mit den bearbeiteten Daten weiter.
Dies geschieht in etwa alle 33ms. Kommt ein GC lauf dazwischen, sei es auf dem "normalen Windows Rechner" oder auf dem Windows CE Gerät gehen Daten verloren und das ist nicht akzeptable. Vielleicht hat ja jemand eine Idee, wo und wie ich diese gradlienige Verarbeitung aufbrechen kann, um ab und an etwas Zeit für den GC zu schaffen, ohne das Daten verloren gehen.
Gruß Luciluke
geht das so in die richtige Richtung?
Ich muß für jede mögliche Kombination einen Enumwert KB_State haben, eine Methode schreiben und einer Action<T> zuweisen, diese zusammen in ein Dictionary packen und das Dictionary für den Übergang erstellen.
enum Input { Char, Subject, Nothing }// enum KB_States ...
Input _currentInput;
KB_States _currentState = KB_States.KB_Empty;
Action<short> CheckCharAction;
Action<short> CheckSubjektAction;
Dictionary<KB_States, Action<short>> _dicStateAction;
Dictionary<KeyValuePair<KB_States, Input>, KB_States> _dicUebergang;
void DataArrived(short kbData)
{
//würde dann natürlich woanders stehen...
_dicStateAction = new Dictionary<KB_States, Action<short>>();
_dicUebergang = new Dictionary<KeyValuePair<KB_States, Input>,KB_States>();
CheckCharAction = CheckChars;
CheckSubjektAction = CheckSubjekt;
_dicUebergang.Add(new KeyValuePair<KB_States, Input>(KB_States.KB_Empty, Input.Subject), KB_States.KB_Sub1);
_dicUebergang.Add(new KeyValuePair<KB_States, Input>(KB_States.KB_Empty, Input.Char), KB_States.KB_DirektBySpace);
_dicStateAction.Add(KB_States.KB_DirektBySpace, CheckCharAction);
_dicStateAction.Add(KB_States.KB_Sub1, CheckSubjektAction);
//-------
_currentInput = GetCurrentInput(kbData);
_currentState = _dicUebergang[new KeyValuePair<KB_States, Input>(_currentState, _currentInput)];
_dicStateAction[_currentState](kbData);//methode
}
private Input GetCurrentInput(short kbData)
{
if (IsKBDataChar(kbData))//irgendein Vergleich
{
return Input.Char;
}
if (IsKBDataSubject(kbData))//irgendein Vergleich
{
return Input.Subject;
}
return Input.Nothing;
}
private void CheckChars(short kbData)
{
if (kbData == 0x00)//wenn im Zusammenhang Gültig
{
//irgendwas machen
}
}
private void CheckSubjekt(short kbData)
{
if (kbData == 0x01)//wenn im Zusammenhang Gültig
{
//irgendwas machen
}
}
Gruß luciluke
und ein weiteres Hallo,
nach längerer Beschäftigung, mit dem gesamt Konzept des Projekts, bin ich nun wieder bei der Befehlszeilenauswertung gelandet, wobei sich dieser Teil der Anwendung nun auf einem XP Rechner abspielt, sodaß ich nicht mehr auf CF beschrängt bin.
Ich habe das State Pattern mal anhand von State Pattern in C# in etwas abgeänderter Form implementiert, aber da ich den Grund Aufbau der Implementation (Reflection usw.) nicht komplett verstehe würde ich lieber etwas anderes machen (obwohl es halt funktioniert und das mit den Enums wirklich praktisch ist).
Für die Zustände reicht meistens ein Enum, für die Zustandsübergangsfunktion ein Dictionary<Key, NextState>, wobei Key ein zusammengesetzter Wert aus CurrentState und CurrentInput ist.
Ich wüße nicht wo ich da den State abhänigen Code unterbringen müßte. Im Moment habe ich ja in jeder State Klasse eine Methode die prüft ob der Input im Zusammenhang gültig ist.
Wenn ich dazu ein wenig schematischen Code als Hilfestellung bekommen könnte, wäre ich sehr dankbar. Es sei denn die genannte Implementierung von CodeProjekt ist eine gute Wahl, dann würde ich halt schauen dass ich Sie komplett verstehe...
Gruß luciluke
nochmal Hallo,
und erstmal vielen Dank für die schnellen und hilfreichen Reaktionen, aufgrund derer ich mir jetzt nocheinmal 2 Stunden die Parser Generatoren angeschaut habe (hatte mich die letzten Tage schon einmal damit beschäftigt), jedoch wenn ich das richtig verstehe benötigen diese immer eine Einbindung Ihrer speziellen laufzeit.dll und selbst wenn diese in c# geschrieben sind müßen sie nicht unbedingt CF 3.5 kompatiebel sein.
Wobei z.B Coco/R wohl nur 2 Code.cs Dateien erstellt, die dann aber ja auch nicht unbedingt CF 3.5 kompatiebel sein müßen.
Ist meine Einschätzung zu den Parser Generatoren richtig oder habe ich die vollkommen falsch verstanden?
Wenn ich die Benutzung von Parser Generatoren letztendlich ausschließen könnte, wäre mein weiteres Vorgehen ja eindeutig ein vereinfachtes State Pattern oder Regex bzw. eine Mischung aus beiden.
Mit Vererbung und dem richtigen Ansatz...
Ich habe leider noch nie ein State Pattern implementiert, sodass das mit dem richtigen Ansatz nicht so leicht ist, vorallem da man im Netz unzählige Varianten der Impementierung findet.
Gruß luciluke
Hallo,
ich scheitere im moment an folgender Problemstellung:
es kommen über eine spezielle Tastatur mit vielen Schnelltasten chars rein die ausgewertet werden müßen.
Jeder char kann endweder eine feststehende Bedeutung über die Schnelltasten(jeweils ein Char) haben:
8 Subjekte(zB. Disk, Folder,...), 10 Instructions (z.B. benenne, erstelle,...), 4 Präpostionen (z.B. vor, nach), Andere...
oder aber einfach nur ein Char (abc...usw.) für die Benennung eines Subjekts (z.B. benenne Folder "freier FolderName" vor Folder "vergebener FolderName") oder die Eingabe eines Wertes. Die feststehenden Bedeutungen können nicht mit einzelnen Chars zusammengesetzt werden.
Bei jedem Char Eingang muß ich prüfen, ob der Char im Zusammenhang gültig ist, und wenn nicht einen Hinweis ausgeben und den Char verweigern.
Der Aufbau der Befehlszeilen entspricht meistens folgendem Aufbau: Instruction -> Subjekt -> "Name" -> Präpostion -> "Wert" ,
wobei halt nicht jede Instruction mit jedem Subjekt möglich ist oder bei einer bestimmten Konstellation von Instruction und Subjekt nicht jede Präpostionen erlaubt ist usw.
Jede Befehlszeile wird am Ende mit einem Char "Ausführen" vom Bediener abgeschloßen, um den entsprechenden Befehl auszuführen.
Ich habe versucht das mit if, else, switch, break, return usw. zu bewerkstelligen und gehofft solange den Überblick zu behalten bis alle möglichen Fälle erschlagen sind, aber das war wohl nichts.
Jetzt bin ich schon länger auf der Suche nach einem neuen Lösungsansatz.
Wäre da das State Pattern eine Möglichkeit? Da müßte ich doch für jeden möglichen Zustand der Befehlszeile eine eigene Klasse erstellen (dürften ziemlich viele sein), oder?
Kann man soetwas mir RegEx, Linq, Lambda oder ähnlichem bewerkstelligen?
Ich hänge da jetzt schon mehrere Tage dran und wäre wirklich für jeden hinweis sehr dankbar.
(auch, wenn wohl nicht so wichtig, ich arbeite mit dem Compact Framework 3.5 )
Gruß luciluke
Hallo Rainbird,
irgendwie hänge ich fest, bei dem Versuch meine Anwendung die im Moment auf Basis von Deinem .NET Applikationsserver aufgebaut ist auf Zyan umzustellen.
Eine Authentifizierung habe ich mit einem eigenen AuthenticationProvider in zyan hinbekommen
wobei ich hierbei leider nicht wahlweise ipc benutzen kann, da dort dann über ServerSession.CurrentSession.Identity.Name nichts zurückgegeben wird.
die Clientanwendung die auch auf dem server läuft würde ich eigentlich gerne über ipc laufen lassen, bräuchte dann aber trotzdem den Anmeldenamen dieses Clienten.
Dann habe ich auch das mit dem PermissionManager aus Rainbird Zyan: Exception versucht, aber hier kommt mein eigentliches Problem.
Wie können sich zwei Serverkomponenten untereinander unterhalten. Die Serverkomponente A möchte gerne von der Serverkomponente B wissen ob ServerSession.CurrentSession.Identity.Name Daten ändern darf. Muß dann jede Serverkomponente eine eigene zyanconnection haben mit Anmeldung, um einen proxy für die jeweilige Komponente zu erstellen?
Bei Deinem .NET Applikationsserver konnte man das ja über die ApplicationServer Klasse machen (ApplicationServer.IsInRole("")) usw.
Das gleiche gilt für eine weitere benötigte Serverkomponente Locking.
Gibt es einen Weg, dass sich zwei Komponenten direkt über ipc unterhalten können?
Was muß ich machen, das eine Komponente von unterschiedlichen Clienten wahlweise über ipc tcp oder Http erreichbar sind?
Muß bzw. kann ich eine Komponente in drei hosts registrieren als Singelton.
in etwa so:
TcpCustomServerProtocolSetup protocol1 = new TcpCustomServerProtocolSetup(9003, new ElCashAuthProvider());
HttpCustomServerProtocolSetup protocol2 = new HttpCustomServerProtocolSetup(8000, new ElCashAuthProvider(), true);
IpcBinaryServerProtocolSetup protocol3 = new IpcBinaryServerProtocolSetup("IPCElCashPrintServer");
ZyanComponentHost host1 = new ZyanComponentHost("ElCashPrintServer", protocol1);
ZyanComponentHost host2 = new ZyanComponentHost("ElCashPrintServer", protocol2);
ZyanComponentHost host3 = new ZyanComponentHost("ElCashPrintServer", protocol3);
PrintServer _printServer = new PrintServer();
host1.RegisterComponent<IPrintServer, PrintServer>(_printServer);
host2.RegisterComponent<IPrintServer, PrintServer>(_printServer);
host3.RegisterComponent<IPrintServer, PrintServer>(_printServer);
wobei in PrintServer eine SyncQueue läuft die von mehreren Clients über unterschiedliche protokolle s.o. Daten empfangen und auch einige Daten (DataTables) halten muß.
ps: Ich habe jetzt einmal versucht von KomponenteA mit einer neuen zyanConnection(tcp mit eigenem AuthProvider) auf KomponenteB zuzugreifen.
Das funktioniert auch, nur wenn der host in einer WindowsForm erstellt wird bekomme ich beim erstellen der zyanConnection in der KomponenteA eine CryptoRemotingException "Der Server benötigt eine verschlüsselte Verbindung für diesen Client" (SecureTransactionStage.Uninitialized). Die erste zyanConnection vom entfernten Client selber funktioniert. Mich würde interessieren, wo da der Unterschied ist (host in WindowsForm oder nicht).
Guten Morgen rainbird,
ich muß schon sagen, wenn alles so schnell gehen würde ...
Zyan-Ordner getauscht, Programm läuft.
dann kann ich ja direkt weitermachen.
vielen Dank
Hallo rainbird,
ich versuche gerade eine kleine Anwendung auf Zyan umzustellen, da mir Zyan doch sehr gut gefällt.
Nun bin ich auf folgendes Problem gestoßen.
Es gibt bei mir Methoden, die nicht zwingend alle Parameter brauchen, sodass es auch vorkommt, dass ein Parameter mit NULL übergeben wird.
zB.:
Console.WriteLine(proxyA.GetHelloMessage("a",null));
Dies führt aber nun zu einem Fehler bei
// Alle Parameter durchlaufen
for (int i = 0; i < args.Length; i++)
{
// Typ in Array einfügen
types[i] = args[i].GetType();
}
in Zyan ComponentInvoker.cs
Meine Frage wäre, ob dass so von Dir beabsichtigt nicht möglich ist Null zu übergeben, oder ob es Sinn macht da noch etwas zu ändern.
ps.: Allen ein gutes, glückliches, gesundes und auch erfolgreiches neues Jahr 2011
Hallo,
ich habe ähnliche Probleme mit n-Tier Datasets in VS 2010. Beim Ausführen des benutzerdefinierten Tools (MSDataSetGenerator) kommt folgende Fehlermeldung:
"Fehler in benutzerdefiniertem Tool: Es konnte kein Code generiert werden. Der Prozess kann nicht auf die Datei "...DataSet.Dataset.Designer.cs" zugreifen, da sie von einem anderen Prozess verwendet wird."
(wobei die Datei defenitiv nicht von einem anderen Process ausser halt wenn von VS verwendet wird)
Das schlimme ist, dass es manchmal geht und manchmal nicht.
Selbst wenn ich die Datei ...DataSet.Dataset.Designer.cs lösche und das Tool erneut ausführe, macht es es meistens nur einmal.
@Kimchi
bist Du bei Deinem Problem schon auf eine Lösung gestoßen?
Gruß
Hallo Rainbird,
erstmal allen schöne Weihnachten und einen guten Rutsch ins neue Jahr.
Da ich mich gerade auch mit Deinem Beispiel hier beschäftige, habe ich da mal eine Frage?
Wie Du auch in Deinem Block beschreibst, ist ja der TableAdapter das eigentliche Problem beim generieren durch den Designer.
Wenn ich aber nun nach Erzeugung einer Table durch den Designer (Table aus dem Server-Explorer ziehen), den Adapter, die erzeugte Appconfig Datei, sowie die erzeugte Settingsdatei lösche erhalte ich doch meines Erachtens das gleiche Ergebniss wie durch das Eintippen der Table im Designer.
Liege ich da falsch und es wird doch noch mehr generiert?
ps.: ist schon eine weile her, dass Du mir beim Einstieg in die Programierung sehr geholfen hast und ich hatte danach leider keine weiteren Probleme, die nicht auch selber hier lösen konnte und so wollte ich Dir jetzt halt noch einmal für Deine Hilfestellungen und Beiträge hier im Forum danken.
hallo rainbird,
vielen dank für die ausführliche hilfestellung, so kann ich diesen kompletten abschnitt des programmes mit gutem gewissen zu ende bringen.
mfg luciluke
hallo, ich hätte da mal folgende fragen, da ich auch nach längerem suchen wenig über die benutzung von transaktionscope gefunden habe(ausser bei ms).
Die Aktuallisierung meiner Datenbank (sql2005 express) mache ich wie folgt bei allen relevanten Datasets (z.B. Lieferante, Kunde usw.).
wobei die Datasets immer nur einen datensatz enthalten, mit den dazugehörigen detail tabellen. also nicht alle kunden oder alle artikel.
Model.cs:(verkürzter Code)
public void SaveArtikelChanges()
{
// Wenn Eingaben zum speichern anstehen ...
if (IsArtikelDirty)
{
DataSet _dsChanges = DsArtikel.GetChanges();
_artikelManager.SaveArtikelData(_dsChanges);
LoadDsArtikel();
//Flag zurücksetzen
_artikelDirty = false;
if (ArtikelSaved != null)
// Ereignis feuern
ArtikelSaved(this, null);
}
}
hierzu eine zwischenfrage: ist das mit _dsChanges so ok, oder sollte ich das gesamte dataset übergeben, dann könnte ich es auch wieder zurückgeben?
dieses problem hatte ich ja schon in meinem vorletzten beitrag erwähnt.
ArtikelManger.cs
public void SaveArtikelData(DataSet _dsChanges)
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
if (_dsChanges.Tables.Contains("LieferantTyp"))
{
//Die Kontrolle könnte auch SaveConfigTable übernehmen
if (_dsChanges.Tables["LieferantTyp"].Rows.Count != 0)
{
SaveArtikelTable(_dsChanges.Tables["LieferantTyp"]);
}
}
usw. für weitere Tabellen im DataSet mit Namen "X"
if (_dsChanges.Tables.Contains("X"))
{
if (_dsChanges.Tables["X"].Rows.Count != 0)
{
SaveArtikelTable(_dsChanges.Tables["X"]);
}
}
// Transaktion abschließen
scope.Complete();
}
}
private void SaveArtikelTable(DataTable table)
{
using (SqlConnection connection = new SqlConnection(DBCONNECTION))
{
connection.Open();
// Änderungen an Lieferanten speichern
DBAccess.UpdateDataTb(connection, table);
connection.Close();
}
}
dbAccess.cs
internal static void UpdateDataTb(SqlConnection connection,DataTable table)
{
// Datenbankbefehl für Schemaabruf erzeugen
SqlCommand command = new SqlCommand(string.Format("SELECT * FROM {0} WHERE 0=1", table.TableName), connection);
// Datenadapter erzeugen
SqlDataAdapter adapter = new SqlDataAdapter(command);
// Befehlsgenerator erzeugen
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
// Änderungen speichern
adapter.Update(table);
// Ressourcen freigeben
builder.Dispose();
adapter.Dispose();
command.Dispose();
}
1.:
ist das so mit using TransaktionScope und nochmal using sqlconnection in ordnung?
2.:
darf bzw. sollte ich während der TransaktionScope anweisung try catch blöcke benutzen?
daraus resultierend 3.: mehrere fragen
wie behandele ich einen fehler innerhalb der using TransaktionScope anweisung, wenn z.B. ein update nicht möglich ist, weil ein anderer user schon daten in der betreffenden zeile verändert hat, bzw. muß ich die überhaupt behandeln, da so wie ich es verstanden habe ein rollback ausgeführt wird wenn ein fehler aufgetreten ist.
ist dieser rollback 100% sicher, oder sollten noch weitere maßnahmen nach einem mißglückten update erfolgen?
wie und wo kann ich wenn diese fehler auswerten, um den user zu informieren?
eigentlich möchte ich nur sicherstellen, dass auch unvorhersehbare fehler (die eingaben des users und die zuordnung der detailtabellen usw. werden natürlich vorher geprüft) beim update nicht zu einem fehler in der datenbank führen kann, so das diese nicht mehr reagiert.
mfg luciluke
Hallo seikeinfloh
wie du sicherlich mitbekommen hast, bin ich hier nicht der experte, aber ich würde gerne helfen, wenn die fragen konkreter wären. allgemeine vorgehensweisen, wie der datenbank zugriff mit hilfe von sql parametern sind ja schon auf der ersten seite dieses Threads mit code gezeigt worden.
Auch die frage wo die eingaben überprüft werden sollten wurde hier schon ausgiebig diskutiert. mein fazit daraus war, das es keine eindeutige "vorschrift" sondern nur empfehlungen, mit für und wieder dazu gibt.
mir persönlich hat rainbirds satz
zitat:
"Was aber eigentlich zählt, ist was hinterher in der Datenbank steht. "
mit am besten geholfen meine ständigen überlegungen, ob dies nun der "beste" weg sei oder nicht, teilweise einzustellen.
grundlegende vorgehensweisen (wie z.b. sql parameter, transaktions, allgemeine regeln von MVC usw.) berücksichtige ich natürlich auch, aber wenn etwas funktioniert und schnell ist, was soll dann daran allzu verkehrt sein. den code zu perfektionieren (falls nötig) habe ich auf das ende verlegt, da ich dann auch weiß was ich überhaupt alles benötige und wärend des schreibens des programmes stösst man eh immer wieder auf neue sachen, die man dann für verbesserungen benutzen kann.
aus diesem grund hab ich auch schon länger nichts mehr gepostet, da sich vieles von selber erledigt, wenn man eine so reichhaltige einführung, wie in diesem thread bekommen hat.
wie gesagt ich würde gerne helfen, da auch mir hier sehr geholfen wird, aber als ich was schreiben wollte, habe ich festgestellt, dass eigentlich zu allen deiner fragen, so wie ich sie verstanden habe, hier schon sehr viel geschrieben wurde und ich nichts hinzufügen könnte ausser verweise auf bereits gesagtes.
in der hoffnung nichts falsches geschrieben zu haben
luciluke
Hallo Rainbird,
ich habe jetzt soweit alles mit den kunden, liefernaten, adressen usw. im griff und wollt mich erstmal nochmals herzlichst bedanken für deine immer sehr schnellen und hilfreichen tipps zu meinen fragen.
allerdings hätte ich noch zwei fragen zur zeit.
1.
ich habe ja die kunden und lieferanten in der datenbank in getrennten tabellen, da es einige unterschiede gibt und dies später für die buchhaltung wohl auch besser ist(kreditoren, debitoren).
nun habe ich dies mit zwei fast identischen datasets und den dazugehörigen, auch fast identischen editoren reallisiert.
ist das so OK, oder sollte man bei ähnlichen datenstrukturen irgendetwas mit vererbung oder so verwenden (damit hatte ich mich noch nicht beschäftigt und habe deshalb direkt obigen weg genommen)?
2.
die anwendung soll mit hilfe den noch hinzukommenden artikel, artikelgruppe, vorgang Tabellen, rechnugen lieferscheine und angebote ermöglichen.
frage:
womit erstelle bzw. drucke ich z.b. eine rechnung?
ich hab schon einiges über crystal reports usw. gesehen und habe mich auch schon kurz mit dem reportig service beschäftigt, wobei mir dieser auf den ersten blick zu aufwendig für diese aufgabe erschien.
wie archiviere ich die vorgänge, sodass sie bis zu einem gewissen zeitraum noch änderbar sind, danach aber keine beziehungen mehr zu den artikel,kunde usw. tabellen mehr haben, damit man nicht ewig alte artikel usw. nicht löschen kann?
ich habe mir das so vorgestellt, das man z.b. nach vollständigem zahlungseingang einen schluß für alle einzelvorgänge (angebot, lieferschein, rechnug) eines vorganges macht und die zu diesem zeitpunkt bestehenden formulare als pdf archiviert.
ist meine überlegung sinnvoll, oder wie sollte man es anders machen?
in den anwendungen, die ich mir testweise mal angeschaut habe, bleiben meistens die verknüpfugen bestehen, oder werden dann wahlweise irgendwie umgeschrieben, damit man überhaupt änderungen an artikeln vornehmen kann.
es würde mich sehr interessieren, wie du dieses lösen würdest bzw. schon mal gelöst hast.
-die anwendung soll auf kurze sicht nicht für bilanzen gewinn/überschuß rechnug oder ähnliches genutzt werden, sondern wirklich nur bestellungen, angebote lieferscheine rechnugen erstellen und verwalten-
frohe weihnachten schonmal an alle
von luciluke
ps.: das mit dem dataset in einem zurückgeben, habe ich noch nicht geschafft.
ich baue aber das dataset auch im model aus tabellen zusammen die ich einzeln hole (z.b. die lieferanten daten über den liefernatenmanager und die dazu gehörigen adressen über den adressenmanger), oder sollte das dataset im, z.b lieferantenmanager erstellt werden, um dann komplett abgerufen zu werden? dafür müßten jedoch die einzelnen "manager" untereinander zugriff haben.
ist dies so üblich und besser?
-die anwendung ist von der struktur her so aufgebaut wie in deinem beispiel-
Hallo Rainbird,
Ich habe das dataset nach einer neuanlegung komplett neu geladen und das klappt dann ja auch ohne probleme.
jetzt habe ich jedoch folgende verständniss frage:
mein dataset soll ja die kompletten daten für den einen ausgewählten Lieferanten halten. Adressen, Kontakte, und halt die ganzen zugehörigen zwischen Tabellen, wie LieferantAdresse usw.
Das Saven mach ich so wie du es empfohlen hast, in dem ich die einzelnen Table dem Adapter übergebe.
// Speichert Änderungen an Lieferanten und Lieferantenadressen in einer Transaktion.
public void SaveLieferantData(DataSet dsData)
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
using (SqlConnection connection = new SqlConnection(DBCONNECTION))
{
connection.Open();
// Lieferant speichern
SaveLieferant(connection, dsData.Tables["Lieferant"]);
// Adressen speichern
SaveAdresse(connection, dsData.Tables["Adresse"]);
// Adress-Verknüpfungen speichern
SaveLieferantAdresse(connection, dsData.Tables["LieferantAdresse"]);
connection.Close();
// Transaktion abschließen
scope.Complete();
}
}
}
(ist das so mit der Transaktion eigentlich korrekt?)
wobei ich halt bei neuanlegung einer Lieferanten Adresse die Verknüpfungstabellen Daten im Code erstelle .
public void CreateAdresse()
{
Guid _adresseGuid = Guid.NewGuid();
DataRow _adresseRow = AdressenTable.NewRow();
_adresseRow["AdresseID"] = _adresseGuid;
AdressenTable.Rows.Add(_adresseRow);
Guid _lieferantAdresseGuid = Guid.NewGuid();
DataRow _lieferantAdresseRow = LieferantAdresseTable.NewRow();
_lieferantAdresseRow["LieferantAdresseID"] = _lieferantAdresseGuid;
_lieferantAdresseRow["AdresseID"] = _adresseGuid;
_lieferantAdresseRow["LieferantID"] = LieferantID;
_lieferantAdresseRow["AdresseTypID"] = AdresseTypTable.Rows[0]["AdresseTypID"];
LieferantAdresseTable.Rows.Add(_lieferantAdresseRow);
// im set von AdresseID wird AdresseLoaded gefeuert
AdresseID = _adresseGuid;
}
ist das so die richtige Vorgehensweise, oder könnte das dataset diese verknüpfungen mit hilfe der Relations selber erstellen?
es stellt sich mir die Frage, wofür die relations eigentlich gut sind, wenn ich alles im code selber steuere.
in einem anderen beitrag, hast du folgendes empfohlen Verweis
also auch z.B. die child und parent abfragen selbst zu erstellen(wenn ich das richtig verstanden habe), wobei diese daten bei meinem datset ja schon geladen wären.
Frage:
Sollte bzw. kann ich verknüpfte Tabellen Daten mithilfe der Relations erstellen,bearbeiten und löschen, oder sollte ich selber dafür sorgen, dass die daten stimmig sind?
wenn ich z.B. eine Adresse aus der Dataset.Adressen Table (sind ja beliebig viele je Lieferant) löschen möchte kann ich dies ja nicht über SaveieferantData machen , da die reihenfolge der Aktuallisierung ja dann nicht stimmt.
Schreibe ich dann einfach noch eine Methode DeleteData mit der umgekehrten Reihenfolge, oder gibt es da andere Möglichkeiten?
Wie Du siehst besteht bei mir eine grundlegende Verständniss Frage, wie ich das mit den Aktuallisierungen angehe.
Mit Relations und Constraints arbeiten, oder selber die Daten "Verwalten"?
Ich hoffe, mein Problem einigermassen klar dargelegt zu haben und wäre für eine Entscheidungshilfe sehr dankbar.
mfg luciluke
ps.: ich wollte bestimmt keine kritik oder so üben, falls es sich in meinem vorherigen Beitrag so angehört haben sollte.
Hallo Rainbird,
ich muss meinem beitrag leider noch etwas erweitern.
folgendes problem:
ich erzeuge einen neuen lieferanten, gebe ein paar daten ein, speichere und schliesse den editor wieder. das funktioniert auch.
ich öffne den gleichen lieferanten, bearbeite ihn, speichere wieder und alles funktoniert wie erwartet.
nur wenn ich nach dem neu erstellen und einigen eingaben speichere, den editor nicht schliesse, weitere änderungen mache und dann versuche zu speichern, bekomme ich eine fehlermeldung "Parallelitätsverletzung".
ich hab mir jetzt schon fast alles zu diesem thema durchgelesen und komme zu keiner lösung.
vielleicht hat es ja etwas mit der fehlenden rückgabe der table beim update zu tuen, was ja auch ein problem für mich darstellt s.o. .
es wäre toll, wenn du mir da weiterhelfen könntest, da ich jetzt festgestellt habe, dass dieser fehler( wenn es denn einer ist) auch in deiner Beispielanwendung auftritt.
mfg LuciLuke
Hallo,
der verursacher war eine integer spalte in der datenbank, obwohl ich die werte in string convertiert hatte.
habe jetzt halt eine nvarchar spalte gemacht und muß später nochmal schauen, wie der fehler genau zustande kam.
mir ist nur nicht klar, warum es mal ging und mal nicht.
und vorallem warum keine fehlermeldung kam.
aber egal, lieber jetzt am anfang als später.
eine kurze frage hätte ich allerdings noch.
die speicherung erflogt bei dir im code:
(ausschnitt)
// Änderungen speichern
_supplier=_supplierManager.SaveSuppliers(_supplier);
wobei du schreibst die table zurück geben wäre wichtig bei .Net Remoting
meine Lieferanten Table befindet sich aber in einem DataSet und ich bin noch nicht drauf gekommen wie ich diese mit der zurück gegebenen table überschreibe oder aktuallisiere. ich habe es folgendermassen gemacht:
(ausschnitt)
_lieferantTable =_lieferantenManager.SaveLieferant(_dsLieferant.Tables["Lieferant"]);
_dsLieferant.Tables["Lieferant"].AcceptChanges();
wobei _lieferantTable eigentlich keine funktion hat, aber
_dsLieferant.Tables["Lieferant"] =_lieferantenManager.SaveLieferant(_dsLieferant.Tables["Lieferant"]);
geht ja nicht. Wie komme ich an die eigentliche tabelle ran?
oder ist das so wie ich es gemacht habe mit AcceptChanges schon ok?
mfg luciluke
hallo rainbird.
ich mache ja noch viele fehler, bekomme diese aber eigentlich schon recht schnell mit hilfe des debuggers behoben, doch nun habe ich folgendes verhalten der anwendung:
im prinzip, habe ich auch erst eine lieferanten liste view( wie bei dir im beispiel) und öffne den Lieferanteneditor mit doppelklick(lieferant laden usw. )
das bearbeiten mit dem editor funktioniert. löschen auch. soweit ok.
wenn ich nun einen lieferanten mit doppelklick öffne, ist ja der butten für neu enable (lieferantDirty=false).
wenn ich den editor jetzt wieder schliesse, geht das auch, ohne die meldung "änderung wirklich verwerfen?", so wie es sein sollte.
jetzt kommts.
wenn das öffnen und schliessen, wie oben beschrieben öffters mache (egal ob es der gleiche lieferant ist oder unterschiedliche) ist irgendwann ( nach ca. 10mal aber nicht exakt feststellbar) der "neu" button disable und die form lässt sich auch nicht mehr schliessen, ohne das die meldung "änderung wirklich verwerfen?" erscheint. drücke ich "ja" erscheit das dialogfeld erneut, ich drücke wieder "ja", wieder kommt das dialogfeld usw... nach ca. 20mal(auch keine genauen werte ermittelbar) "verwerfen ja" drücken schließt sich die form wie gewünscht und ich kann weiter einen lieferanten auswählen, diesen bearbeiten oder löschen als sei nichts geschehen, bis zum nächsten fehlverhalten s.o. .
ich bin ratlos!!!!????
das verhalten ist übrigens nicht nur im debugger da, sondern auch ohne ide .
vielleicht hast du da ja einen verdacht was es sein könnte?!
ein letztes mal danke für deine hilfe, werds auch nicht mehr schreiben. Du kannst Dir meines dankes für jede antwort in zukunft sicher sein!
hallo rainbird,
hab jetzt ein wenig mit databindings aus probiert und hätte da mal die ein oder andere frage.
Kann es sein, dass das arbeiten mit bindings zur Darstellung von Daten einfacher ist, aber wenn es um änderungen geht man doch besser jedes datenfeld einzeln im model veröffentlicht wie ich es auch weiter oben mit den lieferantenfeldern gemacht habe und dies dann für alle tabellen in der Anwendung?
ist es das was du mit dem strengen model meinst?
Und liegt ein weiterer Vorteil darin, dass die Daten Prüfung (wie von Dir angeregt) so wesentlich einfacher und direkter erfolgen kann?
Ich frage nur lieber nochmal nach, da es ja doch eine grundlegende entscheidung zum aufbau der anwendung ist und ich allzuviele neuanfänge oder umstrukturierungen vermeiden möchte.
Hallo,
ich habe jetzt versucht Daten aus einem Dataset mit n:1 und n:m Beziehungen darzustellen:
private void FillList()
{
// Liste leeren
listViewLieferanten.Items.Clear();
// Alle Lieferanten durchlaufen
foreach (DataRow row in _model.ListedDsLieferanten.Tables["Lieferant"].Rows)
{
// Neuer Listeneintrag
//ListViewItem item = listViewLieferanten.Items.Add(row["LieferantTypID"].ToString());
ListViewItem item = listViewLieferanten.Items.Add(row.GetParentRow("Lieferant2LieferantTyp")["Name"].ToString());
item.SubItems.Add(row["LieferantNummer"].ToString());
item.SubItems.Add(row["Matchcode"].ToString());
item.SubItems.Add(row["Name1"].ToString());
item.SubItems.Add(row["Name2"].ToString());
item.Tag = (Guid)row["LieferantID"];
}
}
private void FillList()
{
// Liste leeren
listViewAdressen.Items.Clear();
// Alle Adressen zu ausgewähltem Lieferanten durchlaufen
// wobei die eigenschaft _model.AdressenTable _dsLieferant.Tables["Adresse"] liefert
foreach (DataRow row in _model.AdressenTable.Rows)
{
// Neuer Listeneintrag
ListViewItem item = listViewAdressen.Items.Add(row["Land"].ToString());
item.SubItems.Add(row["PLZ"].ToString());
item.SubItems.Add(row["Ort"].ToString());
item.SubItems.Add(row["Strasse"].ToString());
item.SubItems.Add(row["Zusatz"].ToString());
DataRow lieferantAdresseRow = row.GetChildRows("Adresse2LieferantAdresse")[0];
item.SubItems.Add(lieferantAdresseRow.GetParentRow("LieferantAdresse2AdresseTyp")["Name"].ToString());
item.Tag = (Guid)row["AdresseID"];
}
}
ist das so der sauberste weg, oder kann man das auch mit Databinding bewerkstelligen? Ich vermute, daß dann die Datenänderungen einfacher zu handhaben sind (?).
Hatt man noch mehr Vorteile mit einem komplett aufgebautem Dataset der aktuell benötigten Daten, ausser den Zugriff über Child und Parent?
Sollte man immer nur über Eigenschaften auf das Dataset im model zugreiffen (zweiter Block), oder kann man dies auch direkt machen wie im ersten Block.
vielleicht nur ein kurzer ratschlag zu diesen Themen, wenn ich erstmal eine gerade linie in meiner vorgehensweise habe, dann wiederholt sich der Rest ja laufend.
ps.: zu den Aktualisierungen der Daten bin ich noch nicht gekommen, werde aber Deinen vorgeschlagenen Weg gehen(oder es zumindest versuchen). also schon mal danke für die Antwort.
Ist es eigendlich hier im Forum erwünscht Code zu posten, oder sollte man dies auf ganz ganz kurze Ausschnitte begrenzen?
Hallo,
erstmal möchte ich mich Icarus666 anschließen und für dieses Beispiel danken.
Es hat auch mir den Einstieg zu MVC ermöglicht.
Nun möchte ich in meiner Anwendung in einem Lieferanten Editor alle zugehörigen Daten zu einem ausgewählten Lieferanten bearbeiten oder neu erstellen können.
betroffene Tabellen in meiner MsSql2005 Datenbank wären: alle in dem angefügten Ausschnitt gezeigten. Die ID Spalten sind Guid Werte.
Erste Frage: ist an der Aufteilung der Tabellen irgendetwas auszusetzen oder auffällig schlecht?
Hauptproblem: Zugriff und aktuallisierung der Datenbank
Meine Ansatz ist alle Tabellen samt Beziehungen im model als dataset zu laden um auf alle Daten direkt in einer Form mit TabPages zugreifen zu können.
model.cs AuszÜge :
private string GetDsLieferantField(string table, string field)
{
// Wenn ein Lieferant geladen ist ...
if (IsLieferantLoaded)
// Feldinhalt zurückgeben
return "" + _dsLieferant.Tables[table].Rows[0][field].ToString();
else
// Leeren String zurückgeben
return "";
}
private void SetDsLieferantField(string table, string field, object value)
{
// Wenn ein Lieferant geladen ist ...
if (IsLieferantLoaded)
{
// Bestehenden Wert lesen
object oldValue = _dsLieferant.Tables[table].Rows[0][field];
// Feldwert festlegen
_dsLieferant.Tables[table].Rows[0][field] = value;
// Wenn der Wert geändert wurde ...
if (value.ToString() != oldValue.ToString())
{
// Änderungsflag setzen
_lieferantDirty = true;
// Wenn für SupplierChanged Ereignisprozeduren registriert sind ...
if (LieferantChanged != null)
// Ereignis feuern
LieferantChanged(this, null);
}
}
else
// Fehlermeldung erzeugen
RaiseError("Es ist kein Lieferant geladen!");
}
private void LoadDsLieferant(Guid LieferantID)
{
if (LieferantID != Guid.Empty )
{
// Dataset Lieferant mit dem zu bearbeitenden Lieferant laden
_dsLieferant = new DataSet();
_dsLieferant = _lieferantenManager.GetLieferantByID(LieferantID);
//n:1 Tabellen komplett laden, zwecks Auswahl in ComboBox Controlls
DataTable anredeTableRec = _lieferantenManager.GetAnredeTable();
//.......
//hier die Adressen, Kontakt usw. Tabellen über n:m Tabellen laden
//mit Einschränkung für den einen Lieferanten
// Tables und Relations dem Dataset hinzufügen
_dsLieferant.Tables.Add(anredeTableRec);
_dsLieferant.DataSetName = "DsLieferant";
// usw....
_dsLieferant.Relations.Add("Lieferant2Anrede", _dsLieferant.Tables["Anrede"].Columns["AnredeID"], _dsLieferant.Tables["Lieferant"].Columns["AnredeID"], true);
}
}
LieferantenManager.cs Auszug:
public DataSet GetLieferantByID(Guid lieferantID)
{
// Parameterliste erzeugen
SqlParameter[] inputParams = new SqlParameter[1];
// Parameter zur Übergabe des Lieferantenschlüssels erzeugen
inputParams[0] = new SqlParameter("@lieferantID", SqlDbType.UniqueIdentifier, 0, "LieferantID");
inputParams[0].Value = lieferantID;
// Einen Lieferanten abrufen und zurückgeben
return DBAccess.RequestDataDs(DBCONNECTION, ("SELECT * FROM Lieferant WHERE LieferantID= @lieferantID"), inputParams, "Lieferant", "Lieferant");
}
public DataTable GetAnredeTable()
{
return DBAccess.RequestDataTb(DBCONNECTION, "SELECT * FROM Anrede ORDER BY ModDate", null, "Anrede");
}
Ist es dann möglich alle betroffenen Tabellen in der Datenbank anhand des Veränderten Datasets zu aktuallisieren, oder muß ich jede Tabelle einzeln mit einem DataAdapter aktuallisieren?
Was für eine Vorgehensweise ist zu empfehlen.
ist überhaupt irgend etwas von meiner Überlegung zu gebrauchen?
ich würde auch gerne noch detailiertere Fragen stellen und mein vorgehen näher erläutern, falls mir jemand helfen möchte.
mfg
Auszug aus der Datenbank:
ok, vielen dank erstmal hebivore
werde mich ans werk machen und alles soweit ändern.
wenn ich noch weitere denkanstöße benötige, darf ich mich ja hoffentlich nochmal melden.
mfg
hallo hebivore
erst mal danke, dass man hier so schnell feedback erhält!!!
TabPages und DrawString, waren wohl die hinweise, die ich benötigte um weiter zu machen... also alles richtig verstanden. mal sehen, wie ich das umsetzen kann.
aber ist es denn grundsätzlich richtig, dass das laden oder erzeugen von buttons oder auch lables so zeit aufwendig ist, dass man sich anders behelfen muß, oder mache ich da etwas falsch?
und, sollte ich das ganze mit einer eigenen klasse wie z.B. "BaseTile" machen oder mich noch weiter mit vorhandenen controlls beschäftigen, um vielleicht ein "schnelleres" zu finden bzw. ein user controll erstellen?
mfg
Original von herbivore
Hallo luciluke,ist m.E. das gleiche Problem wie in
> .herbivore
hallo herbivore
ich habe ja auch geschrieben, dass ich das so wie in "Optimierungsideen ?" geschildert schon ausprobiert habe und es auch funktioniert.
nur fällt mir halt nichts ein, wie ich das mit den ebenen und der beschriftung hinkriegen soll.
der mousedown event ist jawohl nur eindimensional auf der form und ich weiß auch nicht wie ich die felder beschriften soll ohne selber zu zeichnen.
wenn du mir da einen tip geben könntest währe ich sehr dankbar.
mfg
hallo
es ist mein erster beitrag hier im forum und ich weiß nicht, ob er hier richtig steht, aber einen versuch ist es wert.
zum problem:
ich möchte auf einer windows form insgesamt 30 tasten(oder felder) auf 100 ebenen, also insgesamt 3000tasten zur direkt eingabe eines artikels(es wird nur eine nummer benötigt, allerdings mus der button beschriftet sein) darstellen.
ich habe das mit einem button array[,] gemacht, das ich zur laufzeit erzeuge und dann die beschriftung und den tag für jeden button aus einer datenbank setze.
im tag ist die benötigte nummer gespeichert und der text ist die beschriftung.
die ebenen werden dann über seperate tasten angewählt.
das funktioniert auch, aber das anzeigen der form also das aufbauen der buttons dauert einfach zu lange. selbst das einfache visible machen der form, ohne sie komplett neu zu instanzieren dauert noch zu lange.
ich suche jetzt schon seit längerer zeit nach einer lösung und habe auch den thread
"Optimierungsideen ?" gelesen, wo es ja um ein ähnliches problem geht.
mir einen wert über musedown usw. zu holen funktioniert auch, aber wie bekomme ich dann mehrere ebenen realiesiert und wie die beschriftung?
ich wäre über jede art hinweis dankbar, da ich ähnliche anwendungen in vb6 geschrieben gesehen habe und diese laufen auch auf rechnern unter 500Mhz noch einigermassen schnell.
mfg