Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Portal
  • |
  • Mitglieder
Beiträge von Luth
Thema: Grobarchitektur Server / Datenzugriff
Am im Forum: Datentechnologien

Hallo Rainbird,

und erstmal danke für die Hinweise. Bei den meisten davon hege ich trotzdem noch leise Zweifel ob sie mir weiterhelfen.

Zitat
ADO.NET macht Standardmäßig Connection Pooling (Das ist auch beim NPGSQL Provider so: http://npgsql.projects.postgresql.org/do...UserManual.html). Die Verbindungen werden also nie wirklich geschlossen, wenn Du connection.Close() aufrufst, sondern nur an den Pool zurückgegeben.

Das benutze ich bisher auch. Der Ansatz die Connections auf Threadbasis offen zu halten entspringt einem anderen Gedanken. Der Npgsql-Provider unterstützt unter anderem Prepared Commands, bei welchen der Query Plan von der Datenbank aufgehoben wird. Insbesondere bei Selects auf einzelne Datensätze habe ich mit Prepared Statements 2-10 mal mehr Anfragen / Sekunde ausführen können als ohne. Problem ist, dass dies scheinbar nur funktioniert, solange die Connection zwischendurch nicht geschlossen wird. Sollte ich da nichts übersehen haben erlauben mir die offen gehaltenen Connections also eine wesentlich höhere Abfragerate. Da ich nun nicht für jeden Client eine Connection vorhalten kann, kam der Gedanke auf, das über die Threads mit permanent offenen Connections und die Zuteilung der Threads an die Clients zu lösen.

Falls ich das irgendwie anders bewerkstelligen kann, wäre ich sehr dankbar für weitere Tips.
Zitat
Viel bedenklicher an Deinem Konstrukt ist, dass Du DataTables abrufst und diese in Objektlisten (IList<Klasse1>) kopierst. Das ist zum einen mal eine Schleife und temporär doppelter Arbeitsspeicherverbrauch am Server. Da geht die Leistung hin. Also solltest Du entweder durchgehend mit (typisierten) DataSets/DataTables (DataRows gehen nicht, da sie nicht serialisierbar sind) arbeiten, oder direkt Objekte erzeugen (DataReader => Objektliste). Allerdings wird ein fertiger OR-Mapper wie z.B. das Entity Framework die Arbeit vermutlich effizienter und komfortabler erledigen, als ein eigenes Gebilde.

Ja. Wenn ich nicht irre, darf ich für den Entity-Framework Ansatz aber erst ein Visual Studio kaufen. Da sträube ich mich momentan noch dagegen, da ich hier an einem Hobbyprojekt rumbastel das in erster Linie dazu dient ein wenig über .NET zu lernen. Von daher wird es eher in Richtung Datareader und eigene Business-Objekte basteln gehen.
Zitat
Warum arbeitest DU mit einem eigenen Protokoll und einem eigenen ThreadPool? Du könntest Remoting oder WCF verwenden. Das ist fertig und mit Sicherheit skalierbarer als ein eigenes Konstrukt. Es ist nicht sinnvoll Infrastruktur selber zu schreiben, die im .NET Framework bereits vorhanden ist. Du schreibst Dir ja auch nicht Deinen eigenen String-Datentypn, oder?

Auch hier liegt das vielleicht nur an meiner Unwissenheit. Der Client wird wohl in C++ und ohne .NET implementiert werden, falls irgendwie machbar als Crossplattform-Client für Windows/Linux. Sollte ich da mit WCF oder anderen Bordmitteln weiter kommen, würde ich das sicherlich vorziehen. Das scheint allerdings eher unwahrscheinlich, auch wenn ich mich mit der Thematik noch nicht ausführlich beschäftigt habe.

Das mit dem Threadpool war wohl unglücklich formuliert. Es spricht sicherlich nichts gegen die Nutzung des .NET Threadpools, ich bezog mich eher darauf, die Threads zur Anfragebearbeitung auf separaten "Manager"klassen zu starten, welche die für Anfragen notwendigen Connections und Command Objekte halten. Dies scheint mir bisher wegen den in Punkt 1 erwähnten Prepared Statements nötig.
Zitat
Caching würde ich nicht so implementieren, da die Synchronisierung des Caches vermutlich mehr kostet als nützt. Das RDBMS hat bereits ausgefeilte Caching-Implementierungen, so dass Du das Caching getrost dem RDBMS überlassen kannst. Es sei denn Du hast eine Daten aus verschiedenen Datenquellen bzw. Daten die aufwändig aufbereitet werden (Zellinhalte von Resultsets 1:1 in Objekte zu schreiben, ist keine Aufbereitung), dann würde ein AppServer-seitiges Cahing wieder Sinn machen.

Das dürfte sich in meinem Fall zumindest für Teilbereiche lohnen, da auf den Daten einiges an Berechnungen und Nachverarbeitung vorgenommen wird ohne dies in die DB zurück zu schreiben. Der größte Teil dürfte sich aber mittlerweile erledigt haben, siehe letzten Kommentar.
Zitat
Wenn Datenbankzugriffe zu lahm sind, solltest Du die Indizes optimieren. Das ist in den allermeisten Fällen die Lösung.

Sind sie eigentlich. Zugriffe auf Datensätze erfolgen zumeist über eine ID-Spalte die als PRIMARY UNIQUE deklariert ist. Damit wird doch automatisch ein entsprechender Index erzeugt. Allerdings scheint ich da über einen Fehler in Npgsql gestolpert zu sein der beim Lesen von Array-Spalten auftritt. Vergrößert man die Arraylänge um den Faktor zwei, steigt die zum Schreiben benötigte Zeit ebenfalls um den Faktor 2, die zum Lesen benötigte Zeit aber um den Faktor 4. Lese ich also ein Array der Länge 1024 dauert das etwa 2-3 ms, bei einem Array der Länge 16384 allerdings schon um die 500 ms. Sollte das behoben werden relativiert sich wohl auch mein Geschwindigkeitsproblem wieder.

Thema: SQL-Datenbank Empfehlung??
Am im Forum: Datentechnologien

Zitat
.... PostgreSQL versuchen. Mal ein paar Test durchführen wie sich die DB performancetechnisch verhält.

Mach das, bei Fragen kann ich auch gerne versuchen per PM zu helfen. Bin wahrscheinlich am Wochenende ab und an mal hier. Kleiner Tip am Rande, bei dem ich anfangs etwas geknobelt habe:

Du hast nach der Installation zwei Konfigurationsdateien, die liegen unter ...\PostgreSQL\8.3\data\

1. pg_hba.conf - Legt Berechtigungen fest, wer auf die DB verbinden darf
2. postgresql.conf - Allgemeine Serveroptionen (Speichernutzung, Verzeichnisse, AutoVacuum, Kosten für verschiedene Scan-Arten auf Tabellen und etliches weiterführendes ...)

Wenn du da nicht rumschreiben willst, schau dir mal PG-Admin an. Das ist die Administrations-GUI die du mit installiert bekommst, das dürfte so was ähnliches wie das SQL Management Studio sein.

Thema: SQL-Datenbank Empfehlung??
Am im Forum: Datentechnologien

Das kommt darauf an, wie deine Applikation darauf zugreifen soll und was du unter administrativen Aufwand verstehst. PostgreSQL ist eine sehr fitte OpenSource Datenbank, die du auch im kommerziellen Umfeld verwenden kannst wie du willst. Bei uns wird die mit 5 Klicks installiert. Erst den Windows-Installer der DB ausführen, zwei Verzeichnisse angeben und OK klicken. Dann eine Batch-Datei die das Erzeugungsscript ausführt und die Tabellen erstellt bzw. mit Inhalten füllt. Die Datenbank kann fast alles was eine kommerzielle auch kann, bei vergleichbaren Geschwindigkeiten und ist in 5 Minuten installiert. Vorausgesetzt jemand hat sich mal eine Stunde hingesetzt und die Erzeugungsscripte aufgeschrieben.

NPGSQL ist ein OpenSource DataProvider für die .Net-Umgebung mit dem sich eine PostgresDB anbinden lässt. Der scheint bei manchen Dingen nicht der allerschnellste zu sein, im Vergleich zu den MS Providern. (Datentyp Array aus einer Spalte lesen bsp.) Er unterstützt aber zumindest die gängingen Sachen. (Transaktionen, Parametrisierung, Prepared Commands, Callbacks etc ...) Ein weiteres Manko hat er allerdings, da er sich von alleine nicht vollständig in die Visual Studio Umgebung integriert. Komponenten wie DataAdapter, Connection etc. darfst du dann also im Code erzeugen.

Alternativ gibt's DataProvider für PostgreSQL auch zu kaufen.

Edit: Tippfehler ...

Thema: Große Bilder (5000 x 5000 Pixel) darstellen
Am im Forum: Grafik und Sound

Das klingt unheimlich plausibel, aber ... wenn ich mit untenstehendem Code mein Bitmap (21600x10800) zusammenbastel, benötigt das Bitmap alleine, ohne die Rohdaten etwa 940 MB Ram. Die reserviere ich ja direkt am Anfang. Das ist, im Debug und Release-Mode, bestimmte einige Dutzend mal gelaufen ohne je eine Exception zu werfen.


public Bitmap RawTobitmap()
{
    Bitmap bmp = new Bitmap(rawdata.GetLength(0), rawdata.GetLength(1));
    BitmapData bmData = bmp.LockBits(new Rectangle(0,0,bmp.Width, bmp.Height),ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
    IntPtr ptr = bmData.Scan0;
    
    { ...Farbwerte setzen, Bitmap zusammenbauen .... }

    bmp.UnlockBits(bmData);
  return bmp;                    
}



Edit: Gerade gefunden und Hier auch
Zitat
Your program has a virtual memory space that is about 2 Gigabytes but it is fragmented by sections of code and data. You can typically get about 500 Megabytes of contiguous memory but beyond that you'll quickly lose. Your image is right there with about 510 MB. As John pointed out, you'll need a 64-bit operating system.


Es scheint so als ob ich nur Glück hatte weil Vista 64 für einen Nutzerprozess einfach mehr zusammenhängenden (virtuellen) Speicher adressieren kann. Da könnte das Problem von mcpd sein, das er ein 32Bit System laufen hat.

Thema: Redesign: Wie Form "schöner" machen?
Am im Forum: GUI: Windows-Forms

Ich bevorzuge die farbliche Absetzung des Hintergrundes von Beschriftungen und Eingabefeldern. Wenn du das über alle Formulare als Standard durchziehst, finden sich (unsere) Benutzer damit ganz gut zurecht. Weiterhin kannst du dann mit nur 2-3 Farbwerten bzw. vordefinierten Designs dafür sorgen, das der Nutzer sich das ein wenig nach eigenen Vorlieben zusammenbastelt.

Dann platziere ich die Beschriftungstexte immer rechtsbündig. Insbesondere wenn du längere und kürzere Beschriftungstexte zusammen in einem Formular hast, stehen die bei linksbündiger Ausrichtung manchmal zu weit vom Editfeld entfernt. Logisch zusammenhängende Eingabefelder werden auch räumlich zusammengefasst. Im Beispiel sind die Serverfelder vorgefüllt, aber editierbar, während die Logindaten wechseln. Der Fokus steht nach dem Öffnen erstmal im User-Feld.


Weiterhin benutzen wir, für unsere (Delphi) Anwendung, für jedes Grid grundsätzlich einen DBNavigator. Da ich die .Net-Komponenten bisher kaum kenne, weis ich allerdings gerade nicht ob da sowas dabei ist. Es wäre eine Schande wenn nicht ...
Diese Dinger meine ich auf jeden Fall, nur in hübsch, modern und mit weniger Buttons. Bei so kleinen Tabellen wie in deinem Bild eigentlich nur Insert, Post, Delete und Refresh, und direkt oberhalt der Tabelle angebracht. Das hat auch den Vorteil, dass der Benutzer gleich sieht was er machen darf / wofür er Rechte hat.

Falls es mehrere Formulare zu einem Themenbereich gibt, etwa Programmkonfiguration, verwenden wir für jedes Formular des Bereichs eine Abwandlung des gleichen Icons. Das dient der Wiedererkennung und Navigierbarkeit des Ganzen, insbesondere in Hauptmenüs.

Thema: Große Bilder (5000 x 5000 Pixel) darstellen
Am im Forum: Grafik und Sound

Du könntest ja einfach mal versuchen das Bild nicht im Formdesigner zu laden und zu platzieren, sondern derst zur Laufzeit einzulesen und der Picturebox zu übergeben. Ich kann mir vorstellen das der Designer da bei bestimmten Bildgrößen eher rumspinnt als die Bitmap-Klasse selbst.

Bitmap bmp = new Bitmap(FileName);
pictureBox.Image = bmp;


Das funktioniert bei mir problemlos bis mindestens 8000x6000 Bilder.

Thema: weitere Verweise zu Projekt hinzfügen
Am im Forum: Entwicklungs- und Laufzeitumgebung (Infrastruktur)

Hier mal nachschauen unter Namespaces und Using ... damit findest du fast alle Klassen auf die du hier im Forum oder in Tutorials triffst. Stopwatch ist beispielsweise in System.Diagnostics.

3.15 Namensräume (Namespaces)

Thema: Große Bilder (5000 x 5000 Pixel) darstellen
Am im Forum: Grafik und Sound

Eine Grenze gibt es, die ist aber RAM abhängig und liegt höher als die 5000x5000. Bei mir schafft er es ganz gut bis 24.000x24.000, bei 40.000x24.000 stürzt er allerdings ab weil der RAM nicht reicht. Und mach dich auf ewige Zeiten zum Neuzeichnen gefasst.

Zitat
... bekomme beim Laden des Images eine Exception: "Out Of Memory"./QUOTE]

Beim Laden oder beim Compilieren? Wann und wie lädst du denn das Bild? Was ist die Fehlermeldung?

Thema: Grobarchitektur Server / Datenzugriff
Am im Forum: Datentechnologien

verwendetes Datenbanksystem: <PostgreSQL 8.3 mit NPGSQL 2.0 Data Provider>

Hallo zusammen.

Ich überlege und experimentiere jetzt schon eine Weile mit verschiedenen Varianten eines Servers rum, stecke aber irgendwie fest und könnte ein paar Denkanstöße gebrauchen.


Rahmenbedingungen sind wie folgt:

1. TCP/IP-Server mit eigenem Protokoll und dahinter liegender Datenbank
2. Anzahl handhabbarer Clients bzw. Anfragen maximieren
3. Thin-Client - sämtliche Anfragevalidierung, Zwischenverarbeitung muss auf dem Server erfolgen
4. Geschätzes Verhältnis Lesen zu Schreiben = 100 zu 1
5. 25-50% der DB-Daten dürften sich im RAM halten lassen
6. 95% der Clientanfragen beziehen sich auf 25% des Datenbestandes
7. In festen Zeitabständen, zwischen 5 Minuten und 1 Stunde, erfolgen serverseitige Operationen auf einem größeren Teil des Datenbestandes
8. Der Server braucht (bis auf ein wenig Administrationszeug) keinen View

Bisherige Probleme:
1. SingleThreading ist zu langsam. Teils weil Anfragen länger dauern können, teils weil Prozessor und Datenbank so nicht auszulasten sind. Selbst wenn ich 10 Threads gleichzeitig Anfragen an die (lokale) DB schicken lasse, langweilt sich die DB bei 5% Prozessorleistung, während der Prozessor komplett auslgeastet ist.
Lösung: Multithreading mit (eigenem?) Pool der beim Serverstart erzeugt wird.

2. Erzeugung von Instanzen der DB-Zugriffsklassen bei Eintreffen einer Anfrage ist zu langsam, wenn man dort erst die SQL-Command Objekte erzeugt. Da kann ich keine keine PreparedStatements mehr nutzen, obwohl die Queries vorher feststehen. CommandObjekte statisch machen, Prepare nutzen und für Threadzugriffe locken war auch nicht so toll, da die Threads sich gegenseitig ausperren.
Lösung: Die Threads erzeugen beim Serverstart jeweils eine Instanz der DB-Zugriffsklassen, bereiten CommandObjekte mit Prepare vor und bei Anfragen wird nur noch parametrisiert. Wenn die Threads ihre "Arbeitsobjekte" selbst halten, dürfte das auch die Synchronisation vereinfachen.

3. Bei einigen Klassen, die jeweils nur eine Datenzeile kapseln, sind die Datasets zu langsam.
Lösung: DataReader und händisch Objekte zusammenbasteln für die betroffenen Klassen.

4. Bei vielen Anfragen wird auf Daten zugegriffen die sich nicht geändert haben (können). Die immer wieder abzufragen kostet einen Haufen Zeit. Beim Login eines Clients stehen 50% der benötigten Daten von vorneherein fest.
Lösung: Einführung eines Cache der diesen Teil der Daten im RAM vorhält und Abgleich/Nachladen bei Bearbeitung einer Anfrage.

Soweit zu meinen Vorgedanken.


Mich würde nun vor allem interessieren wo, abgesehen vom erhöhten Mehraufwand für die Implementierung und Nacharbeit bei Änderungen der Datenbankmodelle, ihr dort Schwachstellen seht. Oder ob sogar das ganze Gedankengebäude in sich zusammenbricht, weil ich was unheimlich wichtiges übersehen habe ...

Im Anhang habe ich mal versucht meine momentanen Vorstellungen in ein Gemälde zu fassen. Die rot gestrichelten Linien kennzeichnen die Stellen, an denen ich Synchronisierungsbedarf für die Thread sehe.

Thema: Disskussion bzgl. der Client-Server-Komponente
Am im Forum: Netzwerktechnologien

Zitat
Was mach ich falsch ?

Wahrscheinlich nichts.

Ich hab den Tutorial-Code vor 3-4 Tagen selber mal ausprobiert und bekam die gleiche Nachricht. Ich rate jetzt einfach mal mit, das mit den neueren Frameworks oder Vista, irgendwelche neuen Sicherheitsbestimmungen zuschlagen. Nachdem ich zu blöd / faul war rauszufinden an was es lag, hab ich einfach statt dem angegebenen Log mein eigenes, auf Textdateien basierendes Log verwendet oder in die Console geschrieben. Das ging dann auch anstandslos.

Wenn du nicht unbedingt in die Ereignisüberwachung reinschreiben willst, schmeiß es einfach raus und schreib woanders rein.


Und an tom-essen nochmal ein Dankeschön für's zur Verfügung stellen des Tutorials.

Thema: [Artikel] Meine DB ist langam weil...
Am im Forum: Datentechnologien

Bei PostgreSQL zum Beispiel mit EXPLAIN ANALYZE "Statement" ... das schlüsselt den Queryplan, verwendete Indices etc. ganz gut auf. Falls das hier jemand benutzen sollte.

Thema: Strukturen in Bildern erkennen und vermessen
Am im Forum: Grafik und Sound

Hallo EFK381 ...

Ich hatte mal mit etwas Ähnlichem zu tun. Da ging es darum in einer Höhenkarte einer Landschaft, die als Grauwert-Array gegeben war, alle zusammenhängenden Flächen zwischen bestimmten Höhenwerten zu finden. Zum Beispiel "Finde alle zusammenhängenden Flächen, die zwischen 100 und 200 Meter Höhe liegen und entferne alle Flächen, die nicht mindestens X-Pixel groß sind."

Nach etlichem hin und her habe ich das dann halbwegs gelöst bekommen und die Verarbeitungszeit lag bei etwa 30ms für ein 512x512 Bild. Wenn du die Einzelpixel mittels Filter vorher entfernst, dürfte das wahrscheinlich auch für dein Problem anwendbar sein. Ohne diese Vorverarbeitung bekommst du halt, bei etwas schlechterer Laufzeit sehr viel mehr "Regionen" der Größe 1, die du dann wegschmeißen kannst.

Die größten gefundenen Regionen in einem bestimmten Graubereich dürften dann wahrscheinlich die hellen Flecken sein die du suchst. Alternativ könnte man die gefundenen Regionen sicher auch auf eine "ungefähre Kreisform" testen. Wenn du mittlerweile eine bessere Lösung gefunden hast, lass es mich wissen. Eventuell kann ich die ja auch verwenden.

Der (relativ schlecht dokumentierte) Code ist hier zu finden:

[Erledigt] Region Growing - Performance/CodeRevision?

Thema: Mengenüberschneidungen
Am im Forum: Rund um die Programmierung

Klar, kann ich.
Ist aber noch ziemlich wüst ...
Die Versionen dürften auch noch etwa 30 Minuten mit 2000 Mengen brauchen, weil jede Menge mit jeder anderen verglichen wird. Aber wenn du M1 mit M100 verglichen hast, musst du ja M100 nicht nochmal mit M1 vergleichen. Das dürfte möglich sein, wenn du

 for (int k = 0; k < mengen; k++)  //Die anderen Mengen prüfen, ob es enthalten ist
                    { 
zu


 for (int k = i; k < mengen; k++)  //Die anderen Mengen prüfen, ob es enthalten ist  
   { 
änderst. Dann müsstest du noch das matches[i,j] suchen verändern und es müsste gehen und in der halben Zeit durchlaufen. Probiert hab ich das aber noch nicht. Und das ganze läuft noch mit shorts statt ints...das hatte ich zwischendurch mal geändert um zu schauen ob das Vergleichen dann schneller geht. Das müsstest du demnach wieder auf ints zurück setzen, wenn die Werte in deiner DB größer sind als der short-Wertebereich. Aber vielleicht hat ja auch jemand eine viel bessere Lösung.



Version 1, zum Testen ob's geht und ankucken wie's was macht


 static void Main(string[] args)
        {
       int mengen=7;       //Auf 2000 setzen
            int elemente = 0;
            int maxwert = 10;   //Das wären dann 500 

            //Liste von Mengen
            List<List<short>> lists = new List<List<short>>(mengen);
            
            //Testdaten bauen
            Random r = new Random();            
            for (int i = 0; i < mengen; i++)
            {
                //Liste für eine Menge anlegen und füllen mit Werten zwischen 
                List<short> l = new List<short>(r.Next(maxwert));
                Console.Write("Menge "+i+": ");
                for (int j = 0; j < l.Capacity; j++)
                {
                    l.Add((short)r.Next(maxwert));  //Hier stehen dann die richtigen Daten drinn
                    Console.Write(l.ElementAt(j)+",");
                }
                Console.WriteLine("");
                elemente = elemente+l.Count;
                lists.Add(l);
            }
            
            long stop = DateTime.Now.Ticks;           
            Console.WriteLine("Anzahl Mengen: " + mengen + " - Anzahl Elemente: " + elemente);
            
            //Zu jeder Menge speichern wieviele Elemente sich mit jeder anderen überschneiden
            short[,] matches = new short[lists.Count,lists.Count];
            
            //Muss man das händisch 0 setzen oder wird das beim Initialisieren gemacht? 
            for (int i = 0; i < lists.Count; i++)
                for (int j = 0; j < lists.Count; j++)
                    matches[i,j]=0;

            long stop2;

            int currcount; //Größe der Menge für die wir Vergleichsmenge suchen
            short nr;      //Zahl die wir in anderen Mengen suchen
            Console.WriteLine("\n" + "Mengen vergleichen: ");
            for (int i = 0; i< mengen; i++) //Jede Menge durchgehen
            {                
                currcount = lists.ElementAt(i).Count; //So viele Elemente hat unsere Menge
                for (int j = 0; j < currcount; j++)   //Für jedes Element in der Menge
                {
                    for (int k = 0; k < mengen; k++)  //Die anderen Mengen prüfen, ob es enthalten ist
                    {
                        if (lists.ElementAt(k).Contains(lists.ElementAt(i).ElementAt(j)) && (i!= k))
                        {
                            matches[i,k]++;           //ist enthalten, Anzahl der Übereinstimmungen erhöhen
                            Console.WriteLine(i + " und " + k + " enthalten Element " + lists.ElementAt(i).ElementAt(j));
                        }
                    }
                }
                
                stop2 = DateTime.Now.Ticks;
                //Console.WriteLine("Menge: "+i+ ", Größe: "+currcount+", Mittlere Zeit pro Menge: "+((stop2 - stop) / TimeSpan.TicksPerMillisecond)/(i+1) + "ms");
            }

            //Durch die Egebnisse laufen            
            int max;
            int menge2=-1;
            Console.WriteLine("\n"+"Beste Übereinstimmung suchen: ");
            for (int i = 0; i < mengen; i++) //Menge
            {
                max = 0; //Erstmal annehmen es gab keine Übereinstimmung  
                for (int j = 0; j < mengen; j++)
                {
                    if ((matches[i, j] > max)) //Wenn Menge1 mit Menge 2 in mehr Elementen übereinstimmt als Max
                    {
                        max = matches[i, j]; //Neue Anzahl Übereinstimmungen speichern
                        menge2 = j;              //
                    }
                    Console.Write(" M"+i+"xM"+j+" "+matches[i, j]);
                }
                if (max>0)
                    Console.WriteLine("     Beste für M"+i + " = M"+menge2);
                else
                    Console.WriteLine("     Beste für M" + i + " = keine");

                
            }

            stop2 = DateTime.Now.Ticks;
            Console.WriteLine((stop2 - stop) / TimeSpan.TicksPerMillisecond + "ms");
            Console.ReadKey();
}     


Version 2, zum laufen lassen ... aber nur mit 100 Mengen damit ich nicht so ewig warten muss.


 static void Main(string[] args)
        {
            int mengen=100;       //Auf 2000 setzen
            int elemente = 0;
            int maxwert = 500;   //Das wären dann 500 

            //Liste von Mengen
            List<List<short>> lists = new List<List<short>>(mengen);
            
            //Testdaten bauen
            Random r = new Random();            
            for (int i = 0; i < mengen; i++)
            {
                //Liste für eine Menge anlegen und füllen mit Werten zwischen 
                List<short> l = new List<short>(r.Next(maxwert+1));
                for (int j = 0; j < l.Capacity; j++)
                {
                    l.Add((short)r.Next(32768));  //Hier stehen dann die richtigen Daten drinn
                }
                elemente = elemente+l.Count;
                lists.Add(l);
            }
            
            long stop = DateTime.Now.Ticks;           
            Console.WriteLine("Anzahl Mengen: " + mengen + " - Anzahl Elemente: " + elemente);
            
            //Zu jeder Menge speichern wieviele Elemente sich mit jeder anderen überschneiden
            short[,] matches = new short[lists.Count,lists.Count];
            
            //Muss man das händisch 0 setzen oder wird das beim Initialisieren gemacht? 
            for (int i = 0; i < lists.Count; i++)
                for (int j = 0; j < lists.Count; j++)
                    matches[i,j]=0;

            long stop2;

            int currcount; //Größe der Menge für die wir Vergleichsmenge suchen
            short nr;      //Zahl die wir in anderen Mengen suchen
            Console.WriteLine("\n" + "Mengen vergleichen: ");
            for (int i = 0; i< mengen; i++) //Jede Menge durchgehen
            {                
                currcount = lists.ElementAt(i).Count; //So viele Elemente hat unsere Menge
                for (int j = 0; j < currcount; j++)   //Für jedes Element in der Menge
                {
                    for (int k = 0; k < mengen; k++)  //Die anderen Mengen prüfen, ob es enthalten ist
                    {
                        if (lists.ElementAt(k).Contains(lists.ElementAt(i).ElementAt(j)) && (i!= k))
                        {
                            matches[i,k]++;           //ist enthalten, Anzahl der Übereinstimmungen erhöhen
                        }
                    }
                }
                
                stop2 = DateTime.Now.Ticks;
                Console.WriteLine("Menge: "+i+ ", Größe: "+currcount+", Mittlere Zeit pro Menge: "+((stop2 - stop) / TimeSpan.TicksPerMillisecond)/(i+1) + "ms");
            }

            //Durch die Egebnisse laufen            
            int max;
            int menge2=-1;
            Console.WriteLine("\n"+"Beste Übereinstimmung suchen: ");
            for (int i = 0; i < mengen; i++) //Menge
            {
                max = 0; //Erstmal annehmen es gab keine Übereinstimmung  
                for (int j = 0; j < mengen; j++)
                {
                    if ((matches[i, j] > max)) //Wenn Menge1 mit Menge 2 in mehr Elementen übereinstimmt als Max
                    {
                        max = matches[i, j]; //Neue Anzahl Übereinstimmungen speichern
                        menge2 = j;              //
                    }
                }
                Console.WriteLine("Beste Übereinstimmung für M"+i+" ist M"+menge2+" mit "+max+" gleichen Elementen");
            }

            stop2 = DateTime.Now.Ticks;
            Console.WriteLine("Gesamtzeit: "+(stop2 - stop) / TimeSpan.TicksPerMillisecond + "ms");
            Console.ReadKey();           

        }

Thema: Mengenüberschneidungen
Am im Forum: Rund um die Programmierung

Hi!
Bist du schon weiter mit der DB Implementierung? Wenn ich mich nicht allzu dämlich angestellt hab, braucht man bei den angegebenen Werten mit stumpfem Iterieren und Vergleichen über Listen etwa 15 Minuten auf einem 2.6Ghz Celeron. Ausgangsbasis für den Test waren 2000 Listen mit jeweils 1..500 zufällige gewählten Elementen im Bereich zwischen 1 und 500, und ziemlich viele for-Schleifen. Geht prinzipiell, die 15 Minuten sind aber schon arg lange für sowas.

Allerdings bezweifle ich ein wenig, das es mit der Datenbank wesentlich schneller geht. Die muss genauso die ganzen Elemente zusammensuchen, Wert für Wert miteinander vergleichen und Zwischenergebnisse speichern. Zumindest falls da keine Magie im Spiel ist. Wie auch immer die Lösung und die Ergebnisse letztendlich aussehen, würde ich gern hören was dabei rausgekommen ist.

Thema: Erster versuch in Direct3D
Am im Forum: Grafik und Sound

Hallo Inek!

Was genau willst du eigentlich machen? "Einfach nur" die Bilder nehmen und mit Direct3D schneller darstellen weil die GDI-Komponenten so langsam sind, oder die 3D-Darstellung danach noch manipulieren und andere Objekte dazufügen?

Thema: [erledigt] Region Growing - Performance/CodeRevision?
Am im Forum: Rund um die Programmierung

Hah! Problem gelöst!

Ich bin von den Listen halbwegs weg und nehm ein bool[][] - Array zur Speicherung der Kandidaten. Alles was unter der gegebenen Höhe ist, bekommt ein true...der Rest ein false verpasst. Außerdem prüfe ich nicht mehr alle 8 Nachbarn eines Pixels ab, sondern nur noch 4. Damit spar ich mir viele Aufrufe von list<>.Contains() und list<>.Remove(). Das ganze ist dann unglaublich viel schneller. Jetzt brauche ich allerdings auch 20-30 MB mehr RAM, womit ich aber leben kann.

Zitat
20:50:53: Starting removal of lakes smaller then 400.
20:50:53: Found 5 different lakes with 41569 points.
20:50:53: Finished removal of lakes smaller then 400.
20:50:53: Generated terrain (513x513) in 31 milliseconds.

Hat es vorher 60+ Sekunden benötigt, ist es jetzt fast um den Faktor 2000 schneller nur weil die Listen nicht mehr so eine große Rolle spielen. Ich weis nicht ganz ob ich jetzt darüber lachen soll oder nicht. Zumal man den Code noch weiter optimieren könnte, indem man einige der Prüfbedingungen weglässt und solches Zeug. 8o

Trotzdem nochmal danke für den Tipp für größere Schleifen for(...) statt foreach(...) zu verwenden.

Der neue Code, falls jemand die Änderungen interessieren:


        private static List<List<int>> PerformRegionGrowing(float[]hm, float min, float max)
        {
            int i,j=0;
            int x, y,x1,x2,y1,y2, offx,pixelcount = 0;
            int offs = 0;

            _map = hm;
            _size = (int)System.Math.Ceiling((System.Math.Sqrt(_map.Length)));

            //Flagmap of our terrain, true where 
            bool[,] candidatemap = new bool[_size, _size];
            
            //List of all connected regions we found                                         
            List<List<int>> regions = new List<List<int>>();
            
            //Search all points in the threshold
            for (i = 0; i < _size; i++)
            {
                for (j = 0; j < _size; j++)
                    if ((hm[offs + j] ≥ min) && (hm[offs + j] ≤ max))
                    {                        
                        candidatemap[i, j] = true;
                        pixelcount++;
                    }
                
                offs += _size;
            }
            //Buffer for current region
            List<int> reg;

            bool found = true;
            int candidate;

            //Every points has to belong to a region
            while (pixelcount>0)
            {
                //Begin new region simply with the next point in candidates
                reg = new List<int>();
                bool dobreak = false;
                //Search a starting point for that region
                for (i = 0; i < _size; i++)
                {
                    for (j = 0; j < _size; j++)
                    {
                        dobreak = candidatemap[i, j];
                        if (dobreak) break;
                    }
                    if (dobreak) break;                                                   
                }
                
                //We have a start pixel
                reg.Add(i*_size+j);
                pixelcount--;
                candidatemap[i,j]=false;
                //First point of new region
                offs = 0;
                found = true;
                
                while (found)
                {
                    found = false;
                    candidate = reg.ElementAt(offs);
                
                    x = candidate / _size;                    
                    y = candidate % _size;
                    offx = x * _size;                    
                  
                    x1 = x-1;
                    x2 = x+1;
                    y1 = y-1;
                    y2 = y+1;                    
                    //Check if the 4 neighbourgs belong to region and are on the map                    
                    if ((x1≥0)&&(candidatemap[x1,y]))
                    {
                       candidatemap[x1, y] = false;
                       reg.Add(x1*_size+y);
                       pixelcount--;
                    }

                    if ((x2<_size)&&(candidatemap[x2, y]))
                    {
                        candidatemap[x2, y] = false;
                        reg.Add(x2 * _size + y);
                        pixelcount--;
                    }

                    if ((y1≥0)&&(candidatemap[x, y1]))
                    {
                        candidatemap[x, y1] = false;
                        reg.Add(x * _size + y1);
                        pixelcount--;
                    }

                    if ((y2<_size)&&(candidatemap[x, y2]))
                    {
                        candidatemap[x, y2] = false;
                        reg.Add(x * _size + y2); 
                        pixelcount--;
                    }
                   
                    if (offs < reg.Count - 1) found = true;
                    offs++;
                }
                regions.Add(reg);
            }
            return regions;
        }

Thema: [erledigt] Region Growing - Performance/CodeRevision?
Am im Forum: Rund um die Programmierung

Hallo und erstmal danke für die Antwort!

Wenn ich deinen Vorschlag richtig verstanden habe, geht das leider nicht. Da die Karte ja zufallsgeneriert ist, habe ich vor dem Durchlauf keine Ahnung wieviele Pixel unter der Wasserlinie liegen (die Candidates-Liste). Das können 200 oder 40.000 sein ... je nachdem was der Zufallsgenerator ausspuckt. Weiterhin weis ich ja noch nicht, wieviele verschiedene Regionen ich habe und wieviele Punkte zu der Region gehören, damit fällt die Initialisierung der Liste mit einem vorgegebenen Wert flach, wenn ich das richtig sehe. Aus dem gleichen Grund dürfte die Verwendung von Arrays keinen Vorteil bringen, da ich auch dort die Größe nicht zu beginn weis. Zumindest falls ich nichts übersehen habe ...

Die Foreach-Schleife habe ich mittlerweile gegen eine for( int j=0; j < 8; j++) ausgetauscht. Falls es dadurch einen Geschwindigkeitszuwachs gab, war der allerdings nicht im meßbaren Bereich.

Thema: [erledigt] Region Growing - Performance/CodeRevision?
Am im Forum: Rund um die Programmierung

Hallo zusammen!

Ich sitz gerade an einem einfachen Terraingenerator den ich mir für die Verwendung mit der Mogre-Engine (.Net-Wrapper für Ogre3D) schreibe. Bei der Generierung einer Höhenkarte lege ich fest, das bei Höhe = 0.1 die Wasserlinie verläuft. Nun habe ich das Problem, das durch die zufällige Höhenverteilung viele kleine "Seen" entstehen können. Die will ich raus haben. Lösungsansatz ist derzeit alle Seen zu finden, die aus weniger Punkten bestehen als eine gegebene Anzahl und die Höhen dieser Punkte anzupassen. Dadurch kann ich "Seen" in vernünftiger Größe behalten und den kleinen Fitzelkram rauswerfen. Die Höhendaten sind derzeit in einem einfachen float[] gespeichert.

Problem:
Finde alle Punkte die zu einem See gehören, und alle Seen

Lösungsansatz - Region Growing
1. Erstelle Liste aller Pixel mit Höhe ≤ 0.1 (Candidates)
2. Erstelle Liste die gefundene Seen enthält (Regions)
3. Nimm ersten Pixel (current) aus Candidates und erstelle neue Region (Reg)
4. Nachbarpixel auch in Candidates? Ja => Zu Reg hinzufügen, aus Candidates löschen
5. Nimm nächsten Pixel (current) aus Reg => gehe zu 4.
6. Wenn reg einmal durchlaufen ist und noch Kandidaten vorhanden sind => gehe zu 3.
7. Wenn Candidates leer ist => Ende

Das funktioniert soweit auch ganz gut ... läuft allerdings mit 40.000 See-Pixeln gute 60 Sekunden. X( Da ich von C# nur mässig Ahnung habe, würde ich mich sehr freuen wenn mal jemand ein Auge auf den Code werfen könnte um mir Tips hinsichtlich der Performance zu geben. Eventuell berechne ich ja noch viele Sachen doppelt und dreifach, an die man auch einfacher kommen könnte oder die verwendeten Datentypen sind der Aufgabe nicht angemessen oder falsch verwendet. Falls jemand mit sowas Erfahrung hat, würden mich natürlich auch alternative Ansätze oder Tips zu einer möglichen Vorverarbeitung interessieren. Alles was ich dazu bisher im Netz gefunden habe, war viel zu kompliziert um von mir in annehmbarer Zeit implementiert zu werden.

Hier der momentane Code:


      private static List<List<int>> PerformRegionGrowing(float[] hm, float min, float max)
        {
            int i=0;
            _map = hm;
            _size = (int)System.Math.Ceiling((System.Math.Sqrt(_map.Length)));                      
            
            List<int> candidates = new List<int>(); //List of points that are between min and max and have no region assigned           
            List<List<int>> regions = new List<List<int>>(); //List of all connected regions we found
            
            for (i = 0; i < hm.Length; i++)             //Search all candidates
                if ((hm[i] ≥ min) && (hm[i] ≤ max)) candidates.Add(i);              

            if (candidates.Count==0)  return null;           
           
            List<int> reg;             //Pixel-List of current region

            bool found = true;
            int x,y,offs,offx,candidate;
            int[] ind = new int[8];

            //Every point has to belong to a region
            while (candidates.Count != 0)
            {
                //Begin new region with the first point in candidates
                reg = new List<int>();
                reg.Add(candidates.ElementAt(0));
                candidates.RemoveAt(0);
                
                offs = 0;
                found = true;
                
                while (found)
                {
                    found = false;
                    current = reg.ElementAt(offs); //The pixel we check next
                    x = current / _size;                //The x-Position    
                    y = current % _size;              //The y-Position
                    offx = x * _size;                    
                   
                    //get indices of the 8 direct neighbours
                    ind[0] = (offx - _size) + (y - 1);
                    ind[1] = (offx)         + (y - 1);
                    ind[2] = (offx + _size) + (y - 1);

                    ind[3] = (offx - _size) + (y);
                    ind[4] = (offx + _size) + (y);

                    ind[5] = (offx - _size) + (y + 1);
                    ind[6] = (offx        ) + (y + 1);
                    ind[7] = (offx + _size) + (y + 1);

                    foreach(int j in ind)
                        if (candidates.Contains(j))
                            {
                                //It is a neighbour and a candidate, so it belongs to the region
                                reg.Add(j);
                                candidates.Remove(j);
                            }

                    if (offs < reg.Count - 1) found = true; //More pixels to check in this region
                    offs++;
                }
                regions.Add(reg);
            }
            return regions;
        }




Wie gesagt, bin ich für jeden Hinweis wie das zu optimieren wäre dankbar.

Bis dann

Thema: [erledigt] Design? Eventempfänger registrieren über Interfaces
Am im Forum: Rund um die Programmierung

Tja...um es kurz zu machen, du hast natürlich recht mit deinen Ausführungen. Diesmal ist angekommen was du gemeint hast und die Erklärung war einleuchtend. Ich werde es so machen empfohlen. Die Events im Manager anbieten und die Geräte die Registrierung und das Abmelden übernehmen lassen.

Vielen Dank nochmal für's auf die Sprünge helfen!

Lutz

Thema: [erledigt] Design? Eventempfänger registrieren über Interfaces
Am im Forum: Rund um die Programmierung

Guten (fast noch) Morgen...

Danke erstmal für die Antwort! Ich bin mir nur nicht sicher, dass ich diese richtig verstanden habe.

Zitat
Ereignisse dienen dazu, einer anderen Klasse irgendetwas mitzuteilen.
Wenn der Manager die Ereignisse auslöst, muss er sie auch anbieten. Jede Klasse abonniert dann nur die Events, die sie benötigt.

Das Ereignisse anderen Klassen etwas mitteilen sollen ist mir schon klar. Ich denke das tut mein erstes Vorschlag auch. Die Events sind schließlich im Manager deklariert und die Ereignisdaten werden an die registrierten Empfänger zur Verarbeitung gesendet. Ich will dabei aber vermeiden, dass eine Klasse sich aussuchen kann, welche Ereignisse sie verarbeitet. Eine Klasse die IDialogConsumer implementiert, muss auch alle Dialogereignisse verarbeiten können. Daher die Vorgabe der Methoden zum Handling der Ereignisse durch das Interface...

Vielleicht meinst du aber auch, das man dann gleich Methodenaufrufe mit Parameterübergabe verwenden könnte und auf die Ereignisse ganz verzichten?
Zitat
Du vermischst in deiner Implementation Manager und abhängige Klassen.

Das verstehe ich nicht. Abhängige Klassen? Abhängig von was?

-------------------------
Vielleicht noch mal was zum Hintergrund. Der Manager ist zur Ansteuerung von (virtuellen) Ausgabegeräten vorgesehen. Ich habe eine Konfigurationsdatei vorliegen in der steht, welche Ausgabegeräte benutzt werden sollen und welche Sorte von Informationen (Dialog-, System- und/oder Dateninformationen) sie ausgeben sollen. Da steht zum Beispiel, benutze ein Gerät "Screen" zur Ausgabe von Dialog- und Systeminformationen, aber nicht für Daten. Benutze ein Gerät "Printer" zur Ausgabe von System- und Dateninfos, aber nicht für Dialoge usw...

Momentan weis ich nicht, welche Geräte und wie diese später einmal implementiert werden. Was ich aber weis ist, dass alle Geräte von einer abstrakten Klasse "OutputDevice" erben. Daher benutze ich den in der Konfiguration angegebenen Namen, um in der Assembly nach einer gleichnamigen Klasse zu suchen und diese zu initialisieren. Das Interface IManagable ist damit meines Erachtens nach unnötig, da explizit angegeben wird welche Klassen zu benutzen sind. Diese sind also auf jeden Fall "Managable", da sie von OutputDevice erben.

Dann prüfe ich (nach dem Ansatz im ersten Post), welche Interfaces diese implementieren. Ein "Screen" ist vielleicht in der Lage, alle Informationen auszugeben, besitzt also alle 3 Interfaces. Die Konfiguration sagt mir, dass er aber nur Dialog- und Systeminformationen ausgeben soll. Daher registriert der Manager den Empfänger Screen entsprechend. Da (nach meinem Ansatz) die Methoden zur Ereignisbehandlung durch das Interface vorgegeben sind, kann der Manager die Geräte nun selbst als Empfänger für bestimmte Ereignisse registrieren, in Abhängigkeit von den implementierten Interfaces und der gegebenen Konfiguration.

Damit ist der wesentliche Unterschied, dass die Geräte sich nicht selbst für bestimmte Ereignisse registrieren, sondern der Manager dies für sie tut. Wenn ich dich richtig verstanden habe meinst du, dass sich damit die Ereignisse auch erübrigt haben weil man ja gleich entsprechende Methoden aufrufen könnte. Stimmt das so in etwa?

Thema: [erledigt] Design? Eventempfänger registrieren über Interfaces
Am im Forum: Rund um die Programmierung

Hallo zusammen!

Nachdem die Foren- und Google-Suche wenig zu Tage gefördert hat, versuche ich es mal wieder hier und hoffe, das Forum ist das richtige für diese Art von Fragen....

Prinzipiell geht es mir darum, das eine Reihe von Klassen für den Empfang von Ereignissen registriert werden soll. Dies soll in Abhängigkeit von den implementierten Interfaces geschehen. Ich habe eine Idee wie das umzusetzen wäre, allerdings keine Ahnung ob an der Sache ein Haken ist oder ob es überhaupt funktioniert. Daher wäre es nett, wenn mich jemand aufklärt ob das so funktionieren kann...oder ob es bessere Lösungen gibt, bevor ich das alles implementiere und hinterher nichts geht.

Hintergrund ist der folgende:

Ich habe eine Klasse "Manager", die eine ganze Reihe von Ereignissen anbietet. Diese lassen sich in 3 Gruppen unterteilen, Dialog-, System- und Datenändungsereignisse. Der "Manager" initialisiert und verwaltet die Klassen und löst die Ereignisse aus. Einige der Klassen sollen nun nur auf Dialog- bzw. Systemereignisse reagieren, andere auf Datenänderungs- und Dialogereignisse und wiederum andere auf alle Ereignisse. Angedacht war, dies über Interfaces zu regeln. Eine Klasse die das Interface "IDialogConsumer" implementiert, wird für den Empfang von Dialogereignissen, mit dem Interface "ISystemStateConsumer" für Systemereignisse registriert. Und so weiter ...

Ein Beispiel:
Der Manager bietet das Dialog-Ereignis ValueChange an das mit den entsprechenden Eventargs ausgelöst wird.


public event ValueChangeHandler ValueChange;
public delegate void ValueChangeHandler(ValueChangeEventArgs e);

public void OnValueChange(ValueChangeEventArgs e)
        {
            ValueChange(e);
        }

Eine Klasse "Receiver" implementiert das Interface "IDialogConsumer", welches eine Methode handleValueChange spezifiziert.


void handleValueChange(ValueChangeEventArgs e);

Beim Programmstart wird vom Manager überprüft, welche Klassen das Interface "IDialogConsumer" implementieren und die Klassen für die Ereignisbehandlung registriert.


public void registerListeners()
{
    foreach(Receiver r in receiverList)
    {
         if(r is IDialogConsumer)
         {
             ValueChange+=new ValueChangeHandler(r.handleValueChange);
         }
         
         if(r is ISystemStateConsumer)
         {
            ......
         }
         
         .....
    }
}



Nachdem jetzt wahrscheinlich genug Verwirrung gestifetet wurde, noch einmal die Frage:
Kann das so funktionieren oder lässt sich das schlauer machen?






Ich danke schonmal für etwaige Antworten,
der Lutz

Thema: Panel nicht angezeigt
Am im Forum: GUI: Windows-Forms

Hallo nochmal,

ich habe mal nach den Locations geschaut. Du hattest natürlich recht mit deinem Tip. Wenn ich die alle "von Hand" setze, geht es. Wahrscheinlich hätte ich noch ewig danach gesucht. Ich bin davon ausgegangen, dass ein Control welches als letztes zu Form.Controls hinzugefügt wird, die anderen überzeichnet, so das wenigstens irgendwas zu sehen sein müsste. Scheinbar wird es aber in einigen Fällen einfach nicht gezeichnet, wenn sich die Location mit anderen mit Controls überschneidet. Danke nochmal für den Tip.


---------------------------------------------------

Und meine letzte Erklärung war vielleicht etwas undeutlich. Ich weis vor Programmstart nicht, was für Ein- und Ausgabegeräte angesprochen werden. Das ist in einer Config-Datei festgelegt die bestimmt wie meine Dialogbeschreibung interpretiert werden soll. Eine visuelle Ausgabe mit Forms kann dabei sein, ist es in vielen Fällen aber nicht. Für Tastatureingaben oder Mauseingaben greife ich daher ebenfalls nicht auf die Form-Events zurück, sondern nutze einen globalen Hook der mir alle WindowsMessages abfängt.

Daher bringt es mir wenig die Anwendung als WindowsForms-Anwendung zu programmieren, wenn die Forms recht häufig weder für Eingabe- noch für Darstellung verwendet werden. Deswegen habe ich mich für eine Konsolenanwendung mit optionaler Ausgabe über WindowsForms als Ausgangspunkt entschieden. Mit den daraus enstehenden Zugriffs-Beschränkungen habe ich beschlossen zu leben. ;-)




Edit 2:

Kann ich hier irgendwo ein Häkchen setzen um den Thread als beantwortet/erledigt zu deklarieren? Ich finde da gerade nix. ^^

Thema: Panel nicht angezeigt
Am im Forum: GUI: Windows-Forms

Hallo Herbivore und erstmal danke für die Antwort.

Meine Anwendung ist als Konsolenanwendung für einen PDA konzipiert die ein Benutzerinterface für Blinde oder Sehebhinderte anbieten soll. Die Beschreibung der Dialoge erfolgt in einer einfachen Sprache in einer XML-Datei, welche zum Beispiel das im Quellcode erwähnte Container-Element beschreibt.

Die Beschreibung wird dann von einem Renderer, z. Bsp. Text to Speech-Engine, Braillezeile oder graphisch auf einem Touchscreen dargestellt. Ähnlich divers sind die Eingabegeräte. Es bietet sich daher nicht an das ganze als WinForm-Anwendung hochzuziehen und vom Standard-GUI-Thread aus zu arbeiten. Daher die Auslagerung in einen extra Thread, der quasi als GUI Thread agieren soll.


Nun zurück zum eigentlichen Problem:

Die neuen Elemente werden ja in der updateContainer Methode erzeugt. Diese wurde durch ein BeginInvoke auf den entsprechenden Delegate aufgerufen. Das ganze sollte sich also im Thread-Context des Forms abspielen. Oder etwa nicht? Der Zugriff auf das Infolabel und Form.Text erfolgt in der gleichen Methode und dort funktioniert die Änderung der entsprechenden Werte. Ich bekomme keinerlei Ausnahmen das ich auf den GUI-Thread nicht von außen zugreifen darf, aber halt auch die neuen Elemente nicht angezeigt. Warum ist das so? Deine Erklärung habe ich nicht leider verstanden.

Und wenn es so nicht geht, gibt es einen Weg bei Invoke der updateContainer-Methode Controls zu entfernen oder hinzuzufügen?

Thema: Panel nicht angezeigt
Am im Forum: GUI: Windows-Forms

Hallo zusammen,

ich möchte aus einem anderen Thread auf ein Form zugreifen.
Das funktioniert über BeginInvoke() auf einen Delegaten auch ganz gut, die Methode wird aufgerufen und Form.Text und ein Label nehmen die neuen Werte an.

Warum wird nun aber mein Panel bzw. der Button darauf nicht angezeigt? Ich nehme an ich habe etwas wesentliches übersehen und finde es nur nicht da ich mich seit einer ganzen Weile nicht mehr mit Forms beschäftigt habe.

Noch eine Frage hinterher:
Wenn ich einen neuen Thread starte und das Form mit ShowDialog() anzeige, wird der Thread beendet wenn das Form geschlossen wird?


Danke schonmal für eure Antworten.

Formerstellung, Updatemethode und Threadstart


protected override void startProcessing()
        {
            //Create the form that is showed on screen
            thread = new Thread(new ThreadStart(startFormThread));
            thread.Start();
        }

void outputMgr_ContainerChange(ContainerChangeEventArgs e)
        {
            form.BeginInvoke(form.delegateUpdateForm, e.NewContainer);
        }


private void startFormThread()
        {
            form = new TSForm();
            form.ShowDialog();
            
        }

protected override void shutDownDevice()
        {
            form.BeginInvoke(delegateCloseForm);
        }



Das Formelement:


class TSForm:Form
{
        private Label infoline;
        private Panel p;

        public delegate void DelegateUpdateForm(Container c);
        public DelegateUpdateForm delegateUpdateForm;
        
        public delegate void DelegateCloseForm();
        public DelegateCloseForm delegateCloseForm;


        public TSForm()
        {
            SystemSettings.ScreenOrientation = ScreenOrientation.Angle90;
            this.ControlBox = false;
            this.Width = 320;
            this.Height = 200;
            this.Text = "Testform";
            
            infoline = new Label();
            infoline.Text = "Info:";
            infoline.Width=200;
            infoline.Height=30;
            infoline.Anchor = AnchorStyles.Top;
            Controls.Add(infoline);

            delegateUpdateForm = new DelegateUpdateForm(updateContainer);
            delegateCloseForm = new DelegateCloseForm(closeForm);
        }

        private void updateContainer(Container c)
        {
            ListSelector ls = (ListSelector)c.Elements[0];
            this.Text = c.Name + "<->" + ls.Name + "<->" + ls.Options[ls.SelectedOption].Name;
            this.infoline.Text = ls.Options[ls.SelectedOption].Name;

            p = new Panel();
            p.Size = new Size(200, 200);
            p.Visible = true;
            p.Anchor = AnchorStyles.Bottom;

            Button b1 = new Button();
            b1.Text = ls.Options[ls.SelectedOption].Name;
            b1.Size = new Size(100, 20);

            p.Controls.Add(b1);

            Controls.Add(p);
            this.Refresh();
            this.Update();
            this.Show();
            UICore.Logger.Info("Anzahl Controls: " + this.Controls.Count);
        }
}

Thema: [CF2.0] Design u. Architekturfrage - Multimodale Benutzerschnittstelle
Am im Forum: Rund um die Programmierung

Erstmal danke für die Kommentare, auch wenn es nur zwei gab.
Vielleicht kommt ja später noch jemand angelaufen.

@Herbivore:
Ne da bin ich nicht böse, immerhin gab's ne Antwort und den Hinweis mit dem GUI-Thread. Das hatte ich die Tage schon ein paar Mal gelesen, aber es war trotzdem schon fast wieder im hintersten Gehirnwinkel verschwunden.

@svenson:
Deine Bedenken bezüglich Linearität von Sprache und Parallelität von visuellen Informationen sind durchaus berechtigt. Mit dem Gedanken schlag ich mich auch seit einer Weile herum.

Abgemildert wird das Problem etwas, da für sehbehinderte Menschen, auf Grund des kleinen Monitors auf dem PDA, auch nur sehr wenig Informationen zugleich dargestellt werden können.

Und den Rest werde ich versuchen mit einer automatischen Zusammenfassung von Teildialogen für Sehende und/oder eine Aufspaltung in mehrere Teildialoge für Blinde in den Griff zu bekommen. Ich halt mich da in etwa an die Kognitionsforscher-Typen die da behaupten, man könne im Arbeitsgedächtnis etwa 7 Information"chunks" zugleich behalten. Mit der Grundregel kann man bei der Gestaltung von Dialogen so ungefähr arbeiten, auch wenn die anstehende Strukturierung der Informationen nicht sonderlich einfach ist.^^

Die Beschreibungssprachen habe ich mir mal angeschaut, vorerst aber nur überflogen. DAISY scheint mir wenig geeignet, da zu sehr auf interaktive Hörbücher ausgelegt. (wenn ich das richtige DAISY gefunden habe) Zu SMIL habe ich bisher nur vage Informationen gefunden und MPML scheint mir ein wenig zu komplex für meine Zwecke zu sein. Für solche Sprachen wird ein Interpreter zu umfangreich glaube ich. Ich werde morgen nochmal mit mehr Ruhe nachlesen.

Und zum Schluss:
"Freie" Spracherkennung ist nur eine Zukunftsoption. Soweit ich weis ist das erkennen eines kleinen und auf einen Sprecher trainierten Vokabulars von einigen dutzend Kommandos wesentlich einfacher und wird zum Beispiel von der Sphinx-Engine ganz gut gemacht. Geht mich aber eigentlich auch nix an, muss mein potentieller Nachfolger sich anschauen.



Weitere Kommentare natürlich erwünscht!

Thema: Dijkstra-Algorithmus
Am im Forum: Rund um die Programmierung

Wie das denn? Wenn schon Gerüchte, dann bitte etwas ausführlicher.^^ Soweit ich weis muss man auch bei der dortigen Architektur das Problem ausformulieren. Die im ausformulierten Problem inhärente (≤== schreibt man das wirklich so?) Lösung kann dann nur schneller gefunden werden.

Thema: Dijkstra-Algorithmus
Am im Forum: Rund um die Programmierung

Keine Ahnung wie man das nennt.

Das klingt aber eher nach eine bildlichen Veranschaulichung einer Gruppe von Algorithmen die nach einem ähnlichen Prinzip arbeiten. Doof ist nur, das sich beim "Wasser einlassen", das Wasser selbstständig und gleichzeitig verteilt. Das ist beim virtuellen Wasser nunmal nicht so und alle Wege (oder Kanäle) müssen einzeln durchgerechnet werden. Mir scheint nach deiner Beschreibung so als müsste man erst "alle Wege vollschütten" um hinterher zu wissen welcher der kürzeste ist. Damit müsste man wieder alle Möglichkeiten durchtesten und ist nicht schneller als sonst irgendwie.

Wenn das so ist, dann ist das Bild mit dem Wasser mehr oder weniger irreführend. Hat man aber öfters. Ich habe auch schon Leute getroffen die sich bei dem Algorithmus ====> Hier, den meine ich! ≤=== wirklich vorgestellt haben, das man da kleine virtuelle Ameisen-Abbilder programmiert die schon wissen wo es Futter gibt. 8o


Edit:

@Frisch:

Wenn du zu einem Optimierungs-Problem mit vielen Unbekannten eine gute Lösung brauchst, musst du meist auf solche Algorithmen zurückgreifen. Standard-Beispiele sind meist sowas wie:

Du hast 100 Rohstofflager und 100 Produzenten mit soundsoviel LKW. Wie schaffst du nun zum geringsten Preis allen Produzenten die benötigten Rohstoffe hin.

Oder auch: Such mir den kürzesten Weg von A nach B, wenn es unheimlich viele Möglichkeiten für die Wege gibt, bei einem Routenplaner zum Beispiel.

Solange du nicht solche Optimierungsdinger programmieren musst, kannst du eigentlich einen großen Bogen um diese Algorithmen machen.

Thema: [CF2.0] Design u. Architekturfrage - Multimodale Benutzerschnittstelle
Am im Forum: Rund um die Programmierung

Tag zusammen,

ich bin mal wieder auf eure Hilfe angewiesen weil ich mir gerade gar nicht so sicher bin, dass ich nicht drauf und drann bin, einen großen Haufen Mist zusammenschreibe. ^^

Ich habe mir zusammengereimt was ich für mein Projekt alles brauche und mit welcher Software-Architektur man das vielleicht umsetzen könnte. Genau da hakt es aber. Ob diese Umsetzung sinnvoll, realisierbar, halbwegs performant oder blödsinnig ist, kann ich schlecht einschätzen da sich meine Erfahrung mit .Net in Grenzen hält.

Ich würde demzufolge mal beschreiben, was mir bisher vorschwebt und wäre für jegliche Kommentare und Anregungen dankbar.




//Aufgabe:

Ich steh vor der Aufgabe für einen PDA den Prototyp eines multimodalen Benutzerinterfaces für ein etwas umfangreicheres Programm zu basteln. Da das Program unter anderem für blinde und sehbehinderte Nutzer geeignet sein soll, kann ich mit Windows.Forms alleine erstmal relativ wenig anfangen. Man stelle sich zum Beispiel sowas vor:

Eingabe:
- ein Tastaturaufsatz auf dem Touchscreen
- vielleicht kommandobasierte Spracherkennung
- Exotischere Dinge wie USB-Joysticks, Gamepads, Brailletastaturen

Ausgabe:
- Sprachsynthese
- Taktile Ausgabe über Braillezeilen oder eigens entwickelte Spezialgeräte
- angepasste visuelle Ausgabe für sehbehinderte




// weitere Forderungen:

- Prinzipielle erweiterbar bezüglich Dialoginhalten und Sprachanpassungen
- Möglichkeit zur Einbindung zukünftiger Ein- und Ausgabegeräten
- Weitreichende Konfigurierbarkeit über Benutzerprofile




// Damit verbundene Probleme:

- Kopplung der Ein- und Ausgabegeräte
===> Bei Windows.Forms enge Kopplung. Ein ListView "weis" was und wie es darstellen soll, feuert Events wenn etwas selektiert wird und unterstützt Stifteingaben etc....
===> Im Fall eines Tastaturaufsatz und Sprachausgabe muss erst implementiert werden wie Selektionen oder Auswahlen in einer Liste funktionieren und woher die Ausgabe "weis" das etwas selektiert wurde

- Multithreading
(Sprachverarbeitung braucht viel Zeit, im Hintergrund laufen komplexe Algorithmen ...)

- Präsentation der Inhalte muss vielfältig anpassbar sein. Farbschemata, Soundschemata und bestimmte Dialoge sollen nur für bestimmte Nutzergruppen zugänglich sein.



-----------------------------------------
Bisherige Ideen zur Umsetzung:
-----------------------------------------

1. Einführung einer (sehr begrenzten) Dialogbeschreibungssprache:

- Ausgelagerte Dialogbeschreibung in einer XML. Der Ansatz dürfte von XAML oder UIML bekannt vorkommen. Diese sind aber viel zu groß/komplex und meines Wissens nach gibt es keine Umsetzung für PDA's und CompactFramework. Neben Dialogablauf und Dialogelementen stehen auch die Inhalte in der XML-Datei. Damit schlag ich hoffentlich auch die Umsetzung verschiedener Sprachen tot. Will jemand das alles auf usbekisch haben, muss er halt die Xml-Inhalte ins Usbekische übersetzen.

- Definition einer Handvoll abstrakter "Widgets". Beispielsweise wird für Auswahl aus einer Liste ein "ListChooser" eingeführt. Der enthält alle wählbaren Optionen, die damit verknüpften Aktionen und eine textuelle Selbstbeschreibung als Hilfestellung. Die Präsentation bleibt den angeschlossenen Geräten überlassen. Ich weis vorher halt nicht, ob da nun vorgelesen wird oder das ganze doch grafisch dargestellt wird.
Für sehende Nutzer kann das ganze dann in einem Windows ListView dargestellt werden, für sehbehinderte kann eine Anordnung extragroßer Windows-Buttons genutzt werden und für Blinde eine textuelle Beschreibung der Optionen über Sprachausgabe. Ähnlich für Labels, InputFields für einfache Typen und Buttons.




2. modulares Gerätekonzept:

- Ein- und Ausgabegeräte werden getrennt und in einem eigenen Thread ausgeführt.
Mit Hilfe zweier Basisklassen, von denen jeweils eine von jedem Geräte implementiert werden muss, wird die grundlegende Funktionalität für Ein- und Ausgabegeräte festgelegt. Als Geräte werden in diesem Sinn zum Beispiel auch zwei unterschiedliche Sprachsynthese-Engines verstanden.

- Klassifizierung von Informationsklassen
Über die jeweils implementierten Interfaces wird festgelegt welche Ein- oder Ausgabeoperationen das Gerät unterstützt.

Doofes Beispiel:
Eine Handytastatur kann zur Navigation durch Dialoge und zur Texteingabe genutzt werden, muss also die entsprechenden Interfaces implementieren. Ein ..hmmm...vielleicht .... Joypad zur Texteingabe ist eher unschön, zur Dialognavigation würde es sich aber eignen, daher nur IDialogNavigation implementieren.

Dööferes Beispiel:
Über Sprachsynthese lassen sich die definierten Widgets prinzipiell beschreiben und darstellen. Das Interface IDialogOutput würde also implementiert werden. Ein ...pfff....tja... Armband mit Vibratoren zur taktilen Ausgabe würde damit nichts anfangen können, wäre aber wohl zur Ausgabe von Richtungsanweisungen geeignet. Dieses würde daher nur IDirectionOutput implementieren.

Wie die entsprechenden Informationen präsentiert werden ist der Umsetzung der entsprechenden Geräteklassen überlassen. Welche Arten von Informationen das Gerät erhält wird über die Interfaces geregelt und was für Informationen präsentiert oder eingegeben werden sollen, steht in der XML-Dialogbeschreibung.



3. Kommunikation der Geräte untereinander:

Ein zentrales Verwaltungssystem hält aktuellen Dialog- und Systemzustand, und informiert die angeschlossenen Geräte über Änderungen.

Jedes Ausgabegerät verfügt über eine queue in die vom Verwaltungssystem Nachrichten gepackt werden. Anstatt die queue immerzu abzufragen sollen Events eingesetzt werden um den Gerätethread zu informieren, das etwas Neues in der Queue liegt.

Das Verwaltungssystem selbst unterhält eine Queue in der alle Eingabegeräte Nachrichten hinterlassen können, wenn irgendeine Nutzeraktion vorliegt.

Für die verschiedenen Informationsklassen werden verschiedene Events definiert damit nur die Geräte reagieren, die die entsprechende Informationen abonniert haben.

Synchronisierung ist angedacht über Locking (mittels lock(queue.syncroot)) für alle Schreib- und Lesezugriffe. Reicht das schon aus oder steckt da wesentlich mehr dahinter? Synchronized queues stehen im CompactFramework nicht zur Verfügung soweit ich das überblicke.


------------------------------------------------------------------------------------------


Soweit erstmal wie ich mir das bisher gedacht habe. Falls noch irgendjemand bis hierher gelesen hat, würde ich gerne wissen ob sowas prinzipiell funktioniert und sinnvoll ist. ^^ Ich bin für jede Anregung offen und stelle diesen Ansatz hiermit zur Diskussion. Ideen, Verbesserungen, Schimpfe, Deklarationen meiner Person als komplett verrückt, Hinweise auf ähnliche Arbeiten oder Tools zur Realisierung sind ausdrücklich erwünscht.

Das ganze soll übrigens keine fertige Anwendung werden sondern ein Prototyp der in der Lage ist, die prinzipielle Funktionsfähigkeit des gewählten Konzepts zu demonstrieren.


Ich danke für die Aufmerksamkeit,
der Lutz

Thema: BindingSource.filter() Problem
Am im Forum: Datentechnologien

Versuch mal sowas:


String Filter1 =  "SpalteX LIKE '*54*'";
String Filter2 = "SpalteX LIKE '*57*'";
source1.Filter = Filter1 + " AND "+Filter2;

Der * dient als Wildcard und mit " AND " oder "OR" kannst du Ausdrücke verbinden.
Dann kannst du auch mal hier nachschauen: DataColumn.Expression

Ich hoffe mal, das hilft ein Stück weiter. So habe ich das zumindest bei meinen DataViews gemacht.

Thema: Filterergebniss zwischen DataViews übernehmen?
Am im Forum: Datentechnologien

Guten Tag zusammen,

ich bastel gerade wieder an meinem Tabellenkram rum und steh irgendwie auf dem Schlauch. Benutzt wird VS2003 und .net Framework 1.1 Situation ist folgende:

Ich habe 1 DataSet mit 3 Tabellen. Tabelle "Person" mit Spalte PersonID und eine Tabelle "Region". Jede Region hat nun einen Eintrag "PersonID" der besagt wem sie nun zugeordnet ist. PersonID ist Primärschlüssel der Personen und Fremdschlüssel in Regionen. Das Ganze lasse ich mir in einem DataGrid anzeigen und wechsel per Button zwischen den Tabellen, indem ich mir je einen DataView für jede Tabelle definiert habe. Im unteren Beispiel vPerson.

dGrid.DataSource = data.filter.vPerson;

Was ich nun erreichen möchte ist, das ich z. Bsp. einen RowFilter auf die Person anwende und nur noch die Einträge in meinem RegionenView habe, deren PersonID's nach dem Filtern noch in Personen verblieben sind. Wie macht man sowas? Bringt es mich weiter wenn ich einfach nur mit DataRelations arbeite oder muss ich auf sowas wie DataViewManager zurückgreifen oder mir neue Tabellen basteln und anzeigen?

Bisher habe ich es versucht, indem ich die nach dem Filtern vorhandenen PersonID's in ein Array gepackt habe. Danach habe ich versucht einen RowFilter für die Regionen zusammenzubasteln mit "PersonID = id1 OR PersonID = id2....."
Das ist bei etwa 100.000 Datensätzen aber reichlich langsam...

Wenn es eine bessere Vorgehensweise gibt, würde ich mich über einen Tip sehr freuen.