Montag, 15. August 2011

August 2011 / C# AccessDB auf DataGridView

Zugriff auf Access-DB mit C#

Auftrag:
In der Firma bekam ich den Auftrag, ein Testprogramm zu schreiben, welches eine Möglichkeit bietet, Daten aus einer Access-Datenbank zu lesen, zu manipulieren und zu schreiben.
Als erstes erstellte ich mir eine ganz simple Access Datenbank mit 1. Tabelle.
Anschliessend versuchte ich einmal eine Datenbankverbindung herzustellen und das ganze einer Listview zuzuweisen, da ich in einem Anderen Projekt Erfahrungen mit diesem Objekt gesammelt habe.
Die Verbindung konnte ich zwar nach kleineren Problemen Herstellen, doch schlussendlich hat es nicht so funktioniert, wie ich wollte. Das Handling der Listview ist für diese Situation nicht sehr angenehm.
Ich stiess auf das Objekt DataGridView, welches genau dazu da ist, Daten anzuzeigen.

Code:
Zuerst muss ein Connecting-String definiert werden. Im Internet findet man Oftmals folgendes dazu:

String strConnect = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Datenbank.mdb";

Mit diesem String wird meines Wissens auf eine Datenbank von Access 2003 oder älter zugegriffen.
Zumindest hat er bei mir nicht funktioniert.
Für Access 2007 habe ich folgenden Connecting-String erfolgreich verwendet:

String strConnect = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source = C:\\Datenbank.accdb;"

Anschliessend wird ein Select-Statement in einem String gespeichert. In meinem Fall sah das so aus:

strAbfrage = "Select * FROM TestTabelle";

Nun muss ein OleDbDataAdapter erstellt werden. Er dient als Brücke zwischen dem DataTable und der Datenbank. Als Parameter werden das Select-Statement und der ConnectingString mitgegeben:

OleDbDataAdapter dAdapter = new OleDbDataAdapter(strAbfrage, StrConnect);

Das nächste Objekt heisst OleDbCommandBuilder. Es generiert automatisch SQL-Code um die Änderungen, welche wir an unserem DataTable machen, der Datenbank mitzuteilen:

OleDbCommandBuilder cBuilder = new OleDbCommandBuilder(dAdapter);

Natürlich brauchen wir noch ein DataTable. Dieses Objekt speichert unsere Daten Temporär, damit wir sie später wieder in die Datenbank schreiben können:

DataTable dTable = new DataTable();

Nun füllen wir das DataTable noch mit den Daten:

dAdapter.Fill(dTable);

Nun haben wir die Daten ins DataTable gefüllt.
Jetzt müssen wir sie noch mit dem DataGridView synchronisieren.
Dazu erstellen wir ein BindingSource-Objekt:

BindingSource bSource = new BindingSource();

Natürlich muss der BindingSource noch das DataTable und dem DataGridView die BindingSource bekannt gemacht, also zugewiesen werden:

bSource.DataSource = dTable;
dgvTest.DataSource = bSource;

Nun funktioniert alles bis auf das synchronisieren mit der Datenbank, denn der OleDbDataAdapter synchronisiert die Daten ja nicht immer mit der Datenbank, sondern nur mit folgendem Befehl:

dAdapter.Update(dTable);

Diesen Befehl kann man nun z.B. per Knopfdruck, beim Verlassen einer Zelle oder beim Validating Event des DataGridViews aufrufen.

Wichtig wäre nun noch ein korrektes Error-Handling.
Was ich bis jetzt eingebaut habe ist das Error-Handling bei falschen Daten. Wenn man also Text anstelle von Zahlen eingibt, wo zahlen gefordert werden.
Wird das gemacht, wird dar DataError-Event ausgelöst. Dort kann dann reagiert werden.

Montag, 4. Juli 2011

Juli 2011 / Routing

Routing

Unter Routing versteht man das Festlegen der Wege in bzw. zwischen Netzwerken, um Daten möglichst schnell zu übertragen.
Das ganze kann man am besten mit einem Zugnetz vergleichen. Die Bahnhöfe sind dabei die Router und die Züge die einzelnen Daten-Pakete.
Angenommen ein Zug kommt auf Bern und will Richtung Thun weiter. Wenn der Bahnhof alle Informationen hat die er benötigt, findet er schnell heraus, dass er den Zug am besten über Münsingen leiten würde, da dies der schnellste Weg ist.
Was aber macht der Bahnhof wenn diese Verbindung unterbrochen oder ihm gar nicht bekannt ist?
Er sucht, die nächst beste Variante heraus. In diesem Fall würde er den Zug nun über Belp fahren lassen.
Dieses Vorgehen würde er wiederholen, bis er eine funktionierende Verbindung findet.
Wie bereits erwähnt muss der Bahnhof jedoch genau wissen, wo er die Züge überall durch leiten kann.

Beim Router wird das in der Sogenannten Routing-Tabelle gespeichert.
Diese besteht aus folgenden Spalten:
- Zieladresse: Die 1. IP des Zielnetzes.
- Netzmaske: Die Subnetzmaske des Zielnetzes
- Gateway: Adresse des nächsten ihm bekannten Ziels (Kann eigene IP sein)
- Schnittstelle: IP-Adresse der Schnittstelle zum jeweiligen Netz.

- Metrik: Dient zur Priorisierung der möglichen Wege


Das Ganze funktioniert ziemlich einfach.

Wir gehen von folgender Situation aus:

Wir haben 2 Router und 3 Netze.

Router Nr. 1 verbindet das Netz 192.168.0.0/24 mit dem Netz 192.168.1.0/24.

Router Nr. 2 verbindet das Netz 192.168.1.0/24 mit dem Netz 192.168.2.0/24.

Die Netze 192.168.0.0/24 und 192.168.2.0/24 haben also keine direkte Verbindung, müssen also über das Netz 192.168.1.0/24 verbinden.

Der Router Nr. 2 hat nebst den Standard-Routen, loopback-Routen usw. noch folgende Einträge:



Daraus sieht man also im 1. Eintrag, dass der Router, wenn er etwas ins 192.168.0.0-Netz schicken muss, über den Gateway 192.168.1.1 und die Schnittstelle 192.168.1.2 gehen muss.

Die IP-Adresse das Gateways entspricht dabei Router Nr. 1 da dieser das Paket ins nächste bzw. Ziel-Netz schicken muss.

Die Schnittstelle entspricht dabei der eigenen IP-Adresse im Netz 192.168.1.0 durch welches das Paket übertragen werden muss.


Solche Routing-Tabellen könnten natürlich fast unendlich lang werden.

Standard-Router wie wir sie von zuhause kennen, verbinden jedoch nur das eigene LAN-Netzwerk mit dem "Internet".

Solche Router schicken alles, was Richtung Internet geht einfach Richtung Provider und dieser kümmert sich dann um die Weiterleitung.

Mittwoch, 22. Juni 2011

Juni 2011 / Sortier-Programm




Sortier-Programm

Im Modul 318 - Analysieren und objektbasiert programmieren mit Komponenten bekamen wir den Auftrag, ein Programm mit Visual Basic .NET zu programmieren, welches Texte nach Ziffern oder Wörtern alphabetisch sortieren kann.

Dazu mussten wir mindestens 2 Sortieralgorithmen programmieren und das GUI des Programms möglichst Benutzerfreundlich gestalten.
Der 1. Sortieralgorithmus war einer der folgenden:


Bubble-Sort:
Der Bubble-Sort vergleicht der Reihe nach zwei benachbarte Elemente und vertauscht diese, falls sie in der falschen Reihenfolge stehen. Dieser Vorgang wird so lange wiederholt bis alle Elemente in der richtigen Reihenfolge sind und somit nichts mehr vertauscht werden kann.
Nachteil: Er ist der Langsamste Sortieralgorithmus.


Select-Sort:

Beim Select-Sort wird ein Feld einmal komplett durchlaufen. Dabei wird durch einfache Vergleiche das Grösste (oder kleinste) Element herausgesucht und ans Ende (bzw. zum Anfang) des Feldes gesetzt.
Dieser Schritt wird nun mit dem kleineren Teilfeld(Feld ohne das letzte bzw. erste Element) wiederholt, bis alle Elemente richtig eingeordnet sind.


Insert-Sort:

Beim Insert-Sort wird zuerst 1 Element eingefügt. Anschliessend wird dieses Feld um ein weiteres Element erweitert. Dieses neue Element wird durch Vergleiche und Verschiebungs- oder Vertauschungsoperationen an die richtige Stelle im Feld platziert.
Dieser Schritt wird wiederholt, bis alle Elemente im Feld eingereiht sind.


Ich entschied mich vorerst für den Insert-Sort, da dieser etwas komplizierter war und mich daher auch etwas mehr forderte.
Zu unserem ausgewählten Algorithmus sollten wir nun ein sogenanntes Struktogramm erstellen:


Nun musste jede Person noch einen 2. Algorithmus erstellen. Und das war der

Quick-Sort:
Der Quicksort teilt das zu sortierende Feld in 2 Teilfelder. Dazu wählt er ein beliebiges Element (bei mir das in der Mitte) aus dem Feld und positioniert alle kleineren Elemente links und alle grösseren Elemente rechts davon.
Anschliessend wiederholt er diesen Vorgang mit den erstellten Teilfeldern, bis alle Elemente richtig positioniert sind.
Auch hier mussten wir wieder ein bzw. zwei Struktogramme erstellen:


Das Programmieren der Algorithmen an sich war nicht wirklich schwierig, man konnte den gesamten Code eigentlich aus den Struktogrammen lesen.
Die Funktionen die ich gebraucht habe wie Mid(), Len(), Trim() und Split() kannte ich alle bereits von früheren Programmierarbeiten.
Was ich spannender fand, war das Programmieren des GUIs, da ich neue Sachen kennen gelernt habe.


Die Abfrage des Active-Conrols:
Diese Abfrage habe ich gebraucht, um zu überprüfen ob meine Icons auf enabled gesetzt werden können oder nicht.
Würde ich das nämlich nicht überprüfen und versuchen den "markierten" Text zu kopieren oder auszuschneiden würde das Programm abstürzen.
Da ich nur mit Textboxen gearbeitet habe, überprüfe ich mit Hilfe der ActiveControl-Eigenschaft der Form, ob eine Textbox ausgewählt ist.
Aber bevor nun etwas kopieren kann muss ich auch noch überprüfen, ob überhaupt ein Text markiert ist. Im Ganzen sieht das dann so aus


If TypeOf Me.ActiveControl Is Textbox Then
If CType(Me.ActiveControl, Textbox).SelectedText <> "" Then
Kopieren
End If
End If


Das arbeiten mit dem Clipboard:
Wie bereits vorhin angesprochen hab ich die Funktionen um Text zu Kopieren, Auszuschneiden, und einzufügen programmiert.


Kopieren/Auschneiden:
Um etwas ins Clipboard zu speichern wird die Clipboard-Methode SetDataObject verwendet:


Clipboard.SetDataObject(CType(Me.ActiveControl,Textbox).SelectedText)


Beim Ausschneiden wird anschliessend über das ActiveControl noch der ausgewählte Text gelöscht.


Einfügen:
Beim Einfügen habe ich immer noch etwas mehr überprüfen müssen. Den ohne gespeicherten Text kann man auch nichts einfügen.
Dazu wird mithilfe der Clipboard-Methode GetDataObejct der gespeicherte Text abgefragt.
Die Abfrage, ob nun Text gespeichert ist oder nicht sieht so aus:


If Clipboard.GetDataObject.GetData(DataFormats.Text) <> "" Then


Das Einfügen Selbst ist sehr ähnlich wie das überprüfen:


CType(Me.ActiveControl,TextBox).SelectedText = _
Clipboard.GetDataObject.GetData(DataFormats.Text)


Die Open-/Save-FileDialogs:
Mithilfe der Dialoge kann man sehr schwierige Funktionen sehr vereinfachen.
Der OpenFileDialog wird dazu verwendet Dateien zu öffnen und der SaveFileDialog logischerweise um Dateien zu Speichern.


Bei beiden Dialogen muss man zuerst ein Objekt als solchen Dialog Initialisieren:


Dim dlgSaveFile As New SaveFileDialog()
Dim dlgOpenFile As New OpenFileDialog()


Anschliessend kann man einen Filter setzten, damit nicht alle Dateien angezeigt werden. Man kann auch über die Eigenschaft RestoreDirectory festlegen, ob sich das Programm den ausgewählten Pfad merken soll, damit es beim nächsten Aufruf denselben Pfad öffnet.
Öffnen kann man diesen Dialog mit:


If dlgSaveFile.ShowDialog() P= DialogResult.OK Then
End If


So kann direkt abgefragt werden ob auf OK geklickt wurde.

Dienstag, 24. Mai 2011

Mai 2011 / Subnetting

Subnetting

Subnetting bedeutet das unterteilen eines Grösseren Adressraums von IP-Adressen in mehrere kleinere Adressräume.
Subnetting betreibt man z.B. um ein Firmennetzwerk in örtlich- oder abteilungs-bedingte Netzwerke zu unterteilen.

Wie ist eine IP-Adresse überhaupt aufgebaut?
Eine IP-Adresse der IPv4 besteht aus 4 Dezimalzahlen. Jede dieser Dezimalzahlen besteht aus 8 Bits was bedeutet, dass jede dieser Dezimalzahlen eine Zahl von 0 bis 255 darstellen kann.
Beispiel:
196.168.1.255
11000100.10101000.00000001.11111111
Insgesamt ermöglicht das 4294967296 verschiedene Kombinationen, also fast 4,3 Milliarden.
Diese Adressen sind jedoch nun bereits alle vergeben und daher sollte bald die IPv6 mit 128 anstatt 32 Bit herauskommen.

Wie funktioniert Subnetting?
Da das die IPv6 aber noch nicht eingeführt wurde, beschreibe ich hier das Subnetting mit IPv4, wobei das unter IPv6 eigentlich genau gleich funktioniert.
Der IPv4 Adressraum wurde in 5 verschiedene Netzklassen unterteil.
Sie wurden jedoch später überarbeitet, da diese Netzklassen keine genügende Flexibilität boten. Es wurde durch das CIDR(Classless Inter-Domain Routing) möglich gemacht X-Beliebig grosse Netze zu vergeben. Daher kann heutzutage nichtmehr nur durch die IP-Adresse festgestellt werden, wie Gross ein Netzwerk ist.
Dazu braucht man nun die Netzmaske, welche festlegt, wie gross das Netzwerk ist.
Wie genau das CIDR funktioniert erkläre ich nach den Netzklassen.

Netzklasse A:
Diese Netzklasse hat als Binären Präfix nur ein 0.
Das bedeutet das jede IP dessen Adresse Binär mit 0 beginnt zu einem Klass A-Netz gehören sollte.
Das entspricht allen IPs von 0.0.0.0 bis 127.255.255.255
Ein solches Netz hat die Netzwerkmaske 255.0.0.0 was bedeutet dass die erste Zahl festgelegt ist und der Besitzer alle Adressen dieses Bereiches frei verwenden kann. Hätte ich also dass Klass A-Netz 12.0.0.0 hätte ich alle Adressen von 12.0.0.0 bis 12.255.255.255 was einer Anzahl von 16'777'216 Adressen entspricht.
Da man jedoch die erste und die letzte Adresse nicht verwenden kann, besitzt man genau 2 weniger.
Von diesen netzen gibt es logischerweise allerdings nur 128.

Netzklasse B:
Diese Netzklasse hat als Binären Präfix 10.
Das bedeutet das jede IP dessen Adresse Binär mit 10 beginnt zu einem Klass B-Netz gehören sollte.
Das entspricht allen IPs von 128.0.0.0 bis 191.255.255.255
Die Netzwerkmaske von diesem Netz ist 255.255.0.0 was bedeutet, dass die ersten beiden Zahlen festgelegt sind und der Besitzer alle Adressen dieses Bereichs frei verwenden kann. Hätte ich das Klass B-Netz 135.34.0.0 hätte ich alle Adressen von 135.34.0.0 bis 135.34.255.255 was einer Anzahl von 65'536 Adressen entspricht. Auch hier hätte man wieder 2 weniger.
Anzahl Klass B-Netze: 16'384

Netzklasse C:
Diese Netzklasse hat als Binären Präfix 110
Das bedeutet das jede IP dessen Adresse Binär mit 110 beginnt zu einem Klass C-Netz gehören sollte.
Das entspricht allen IPs von 192.0.0.0 bis 223.255.255.255
Die Netzwerkmaske von diesem Netz ist 255.255.255.0 was bedeutet dass die ersten 3 Zahlen festgelegt sind und der Besitzer alle Adressen dieses Bereichs frei verwenden kann. Hätte ich das Klass A-Netz 197.188.20.0 hätte ich alle Adressen von 197.188.20.0 bis 197.188.20.255 was einer Anzahl von 256 Adressen entspricht. Auch hier hätte man wieder 2 weniger.
Anzahl C-Netze: 2'097'152

Netzklasse D:
Diese Netzklasse hat als Binären Präfix 1110
Das bedeutet das jede IP dessen Adresse Binär mit 1110 beginnt zu einem Klass D-Netz gehören sollte.
Das entspricht allen IPs von 224.0.0.0 bis 239.255.255.255
Diese Klass D-Netze werden für Multicast-Anwendungen verwendet.

Netzklasse E:
Diese Netzklasse hat als Binären Präfix 1111
Das bedeutet das jede IP dessen Adresse Binär mit 1111 beginnt zu einem Klass E-Netz gehören sollte.
Das entspricht allen IPs von 240.0.0.0 bis 255.255.255.255
Diese Klass E-Netze sind für Zukünftige Zwecke reserviert.

Classless Inter-Domain Routing
Um die Funktion von CIDR zu beschreiben wechseln wir nun in die Binäre Darstellung der Zahlen.
Der CIDR-Präfix ist eine Zahl zwischen 0 und 32 der die Netzwerkmaske beschreibt. Entsprechend diesem Präfix bzw. der Netzwerkmaske sind die Netze festgelegt.
Die Präfixe der Netzklassen wären folgendermassen:
Klass A: 8 Binäre Netzwerkmaske: 11111111.00000000.00000000.00000000
Klass B: 16 Binäre Netzwerkmaske: 11111111.11111111.00000000.00000000
Klass C: 24 Binäre Netzwerkmaske: 11111111.11111111.11111111.00000000
Der Präfix bestimmt also die Anzahl festgelegter 1 innerhalb der Netzwerkmaske.
Nun könnte man auch innerhalb der eigentlichen Klass A-Netze Netze mit dem CIDM-Präfix 24 vergeben werden.
Der eigentliche Vorteil daran ist, dass man auch andere Netz-Grössen hat. Hätte man früher für ein Firmennetzwerk mit 100'000 Adressen ein Klass A Netz gebraucht - und damit über 16 millionen IPs verschwendet - kann man nun ein Netz mit dem CIDR-Präfix 15 (131'072 IPs)nehmen. Es werden zwar immer noch zu viele IPs zugewiesen, doch der Verlust ist viel geringer.

Möchte diese Firma nun jedoch nicht nur 1 grosses Netz mit 131'072 IPs sondern vieleicht 8 verschiedene Netze in dem je 12'500 Hostadressen verfügbar sind, kann man dieses Netz durch Subnetting entsprechend aufteilen:
Ich nehme an ich habe das Netz 123.34.0.0.
Dann würde mein IP-Adressraum von 123.34.0.0 bis 123.35.255.255 gehen.
Nun könnte ich diesen Adressraum ohne Probleme halbieren, indem ich ein Netz von 123.34.0.0 bis 12.34.255.255 und eins von 123.35.0.0 bis 123.35.255.255 mache.
Jedes dieser Netze hätte nun noch 65'534 Hostadressen. Jedes dieser Netze hätte nun also wieder den CIDR-Präfix 16.
Teile ich die Netze immer weiter auf komme ich schlussendlich auf 8 Netze mit jeweils 16'382 Hostadressen.
Der CIDR-Präfix dieser Netzwerke wäre nun 18.
Das 1. dieser 8 Netze hätte also eine Reichweite von 123.34.0.0 bis 123.34.63.255

Freitag, 29. April 2011

April 2011 / Leitungscodierung

Leitungscodierung

Im Modul 129 - Lankomponenten in Betrieb nehmen, teilten wir unsere Klasse in 5 Gruppen. Jede Gruppe sollte zu einem vom Lehrer definierten Thema eine Dokumentation erstellen und eine Präsentation halten.
Unsere Gruppe bekam das Thema Leitungscodierung. Dieses Thema ist ziemlich komplex und es zu verstehen ist nicht ganz einfach.

Was ist Leitungscode?
Der Leitungscode dient dazu ein digitales Signal in eine Form abzubilden, damit dieses für eine Übertragung geeignet ist. Er nimmt also die Rohdaten, welche er übertragen sollte, und codiert sie so um, dass sie (möglichst) optimal übertragen werden können.
Dabei gibt es verschiedene Codierungen wie die Binäre Codierung, die Manchester-Codierung oder die MLT 3-Codierung welche alle einzelne Vor- und Nachteile haben.

Die Taktrückgewinnung
Die Taktrückgewinnung ermöglicht es dem Empfänger genau zu wissen, wann er das Signal abtasten muss, also in welcher Geschwindigkeit die Bits übertragen werden.
Die Taktrückgewinnung wird dabei aus den Signalflanken gewonnen. Das bedeutet es braucht genügend Spannungswechsel. Das bedeutet, dass lange folgen von Nullen oder Einsen zu vermeiden sind.
Dies kann durch eine entsprechende Leitungscodierung gemacht werden.
Kann ein Code keine Taktrückgewinnung erzeugen, braucht es zusätzlich eine Taktleitung, welche dem Empfänger den Takt mitteilt.

Binärer Leitungscode





Der binäre Leitungscode ist sehr einfach. Bei diesem Code bedeutet jeder Zustand eine Information.
Ein Beispiel ist die RS232-Schnittstelle, bei der eine positive Spannung eine logische 0 und eine negative Spannung eine logische 1 bedeutet.
Im Manchester-Code ist das z.B. nicht der Fall. Bei ihm gibt es viele Zustände, welche keine direkte Information enthalten, denn es wird bei jeder Information mind. 1-mal die Spannung gewechselt.
Bei diesem Code ist also keine Taktrückgewinnung möglich, da es bei dieser Codierung lauter Nullen oder Einsen geben kann.
Ein weiteres Problem bei diesem Code ist, dass er nicht gleichspannungsfrei ist, da Nullen oder Einsen stark überwiegen können. Das macht eine Übertragung über längere Distanz unmöglich.





Manchester-Code Der Manchester-Code verhält sich im Vergleich zu den anderen Codes etwas Speziell. Er benötigt für jedes übertragene Bit einen Spannungswechsel, also eine 1 und eine 0.
Bei einer logischen 1 wird zuerst eine 1 und dann eine 0 gesendet.
Bei einer logischen 0 zuerst eine 0 und dann eine 1.
Oder genau umgekehrt, wie beim IEEE 802.3 Standard, welcher beim 10M/Bits-Ethernet verwendet wird.
Diese Codierung bedeutet, dass die Übertragungsrate der effektiven Daten halbiert wird.
Dafür hat der Manchester-Code 2 grosse Vorteile.
Da bei jedem Signal mind. einen Spannungswechel vorkommt, ist die Übertragung komplett gleichspannungsfrei. Dadurch können die Daten über grösse Distanzen übertragen werden.
Der 2. Vorteil ist, dass aus der ständig ändernden Spannung ohne Probleme eine Taktrückgewinnung möglich ist.


4B5B-Code
Der B4B5-Code wird z.B. in Kombination mit dem MLT 3-Code beim 100-Base-TX Ethernet verwendet.
Er codiert Blöcke von 4Bits in 5-Bit-Blöcke um.
Dadurch werden nie mehr als 3 logische 0 oder 1 aufeinander folgen, was die Taktrückgewinnung erleichtert.
Der Nachteil jedoch ist, dass nur 4 von 5 Bits Nutzdaten sind, also 20% des Übertragenen Signals unbrauchbar ist.

MLT 3-Code

Der MTL 3-Code wird wie bereits erwähnt in Kombination mit dem B4B5-Code verwendet, da dieser alleine keine Taktrückgewinnung ermöglicht.
Er benutzt 3 Spannungspegel welche mit +, 0 und - bezeichnet werden.
Bei einer logischen 0 wird die aktuelle Spannung gleichgehalten und bei einer logischen 1 wird die Spannung geändert. Dies geschieht in folgender Reihenfolge:
0+0-0+0- usw.
Daher bedeutet bei dieser Codierung auch jeder Zustand eine Information, wie bei der Binären Codierung.
Ein Vorteil des MLT 3-Codes ist, dass er fast gleichspannungsfrei ist. Das bedeutet dass er ebenfalls über längere Distanzen übertragen werden kann.
Da er aber nicht komplett gleichspannungsfrei ist, kann das zu einer erhöhten Bitfehlerrate oder sogar zu einem Verbindungsabbruch beim Empfänger führen.
Dieses Problem wurde jedoch behoben, wie genau, habe ich jedoch nicht herausgefunden bzw. verstanden.

Mittwoch, 30. März 2011

März 2011 / VBA im Bezug auf Datenbanken

VBA

Ich bekam den Auftrag in Access mit VBA eine Funktion zu Programmieren, welche auf unsere Zeiterfassungsdatenbank (Dynabase2) zugreift, die Daten im ausgewählten Zeitrahmen ausliest und diese anschliessend auf die Korrektheit überprüft.

Da ich noch keine Ahnung hatte begann ich sehr einfach.
Zuerst suchte ich mir in den Bereits vorhandenen Funktionen den Datenbank-Zugriff und das Select-Statement.
Dabei fand ich folgende Code Zeilen:

Dim Db as Datebase
Dim Rs as Recordset
Set Db = CurrentDb()
Set Rs = Db.OpenRecordset("Select-Statement")

Die ersten beiden Zeilen deklarieren die Variablen Db als Datenbank und Rs als Recordset(Enthält Kopien von Datensätzen).
Mit CurrentDb() wird der Variable Db die aktuell im Access geöffnete DB zugewiesen. Mit OpenRecordset wird der Variable Rs eine Kopie der Daten, welche durch das Select-Statement ausgewählt werden zugewiesen.

Ich musste nun als erstes mein Select-Statement anpassen.
Ich brauchte die MitarbeiterID, das Datum und die beiden Uhrzeiten, welche Start und Beginn einer Arbeitsphase sind, aus der Tabelle Aktivitaet von allen Datensätzen, welche innerhalb des per GUI ausgewählten Datums lagen.
Also musste ich herausfinden, wie ich auf ein Objekt der benutzeroberfläche Zugriff.
Da ich die ganze Funktion per Button in der dazugehörigen Form aufrief, Programmierte ich bereits in der Richtigen Form. Aus diesem Grund konnte ich direkt über Me auf die Objekte zugreifen.
Das Startdatum wurde im Objekt ctlStart gespeichert und das Enddatum in ctlEnde. Da ich nun auf die Werte zugreifen wollte musste ich also die Values abrufen.
Das funktioniert so:

StartDatum = Me.ctlStart.Value
EndDatum = Me.ctlEnde.Value

Wobei StartDatum und EndDatum Date-Variablen darstellen.

Da ich später bemerkte dass das Select-Statement das Datum "Amerikanisch" interpretiert, also die 1 Zahl als Monat nahm, musste ich mir etwas anderes Einfallen lassen.
Dazu brauchte ich nun die Funktionen Year(), Month() und Day(). Als Parameter übergab man diesen Funktionen ein Datum und sie lasen automatisch jeweils Jahr, Monat und Tag heraus. Diese Werte konnte ich anschliessend an Variablen übergeben und das Datum der Select-Statement-Syntax anpassen.
Dabei musste ich es folgendermassen Formatieren: 03/24/2011 also Monat/Tag/Jahr

In der SQL Abfrage musste ich vor und hinter die Daten je noch ein # stellen und die Daten wurden erkannt.

Schlussendlich lautete mein Select-Statement folgendermassen:

"SELECT [MitarbeiterID], [Datum], [Zeit1], [Zeit2] FROM [Aktivitaet] WHERE [Datum] >= #" & Zeit1 & "# ABD [Datum] <= #" & Zeit2 & "# ORDER BY [MitarbeiterID], [Datum], [Zeit1]"

Die Wörter in [] sind dabei Spalten oder Tabellen(Aktivitaet) innerhalb der Datenbank. Zeit1 und Zeit2 beinhalten die ausgewählten Start- und End-Daten. Geordnet wurde die Ausgabe nach Mitarbeiter, dann nach Datum und dann nach Zeit.

Nun musste ich auf die einzelnen ausgegebenen Datensätze zugreifen.
Das funktioniert über folgende While-Schlaufe:

Do While Not Rs.EOF
Rs.MoveNext
Loop

Mit Not Rs.EOF wird die Schlaufe so lange durchlaufen, bis der Zeiger im Recordset ans Ende gelangt ist.
Mit Rs.MoveNext springe ich bei jedem Durchlauf auf den Nächsten Datensatz.
Nun brauchte ich noch den Zugriff auf die einzelnen Zellen des Datensatzes und das funktioniert so:

Variabel = Rs![Datum]

So wird der Variabel den Wert des Feldes Datum zugeordnet.
Zum Schluss muss man den Recordset und die Datenbank schliessen, bzw. die Instanzierung auflösen.
Das funktioniert mit Close.

Rs.Close
Db.Close

Alle Anderen Funktionen welche ich verwendete, sind hier irrelevant, weil sie entweder zu grundlegend waren oder ich sie bereits einmal in einem Anderen Post erwähnt habe. Ebenfalls haben sie nicht direkt etwas mit dem Datenbank-Programmieren zutun, sondern sind bloss normale VB bzw. VBA Funktionen.

Montag, 28. Februar 2011

Februar 2011 / WSH

WSH

Wir begannen mit dem 2. Semester des 2. Schuljahres und daher begannen wir auch mit den neuen Modulen.
Im Modul 122 - Abläufe mit Skripts/Makros automatisieren lernten wir den WSH kennen.
WSH bedeutet Windows Script(auch Scripting) Host und dient dazu Funktionen unter Windows zu Automatisieren. Der Windows Script Host stellt JScript und VBScript als Programmiersprachen zur Verfügung, deckt die Funktionen der älteren Batch-Scripts ab und ermöglicht die Steuerung von einigen Programmen wie dem Internet Explorer, Datenbanken oder den Office-Programmen.
In der Schule begannen wir vorerst mit VBScript zu arbeiten. Daher sind die Dateiendungen unserer Scripts auch ".vbs".
Ich habe bereits 1 mal über VBScript geschrieben. Jedoch hatte ich damals keine Ahnung was genau wie funktionierte, welche Zeile genau welche Funktion hatte usw.
Ausserdem programmierte ich damals für Windows CE, was nur eine sehr abgespeckte VBScript Version zur Verfügung stellte.
Im Unterricht begannen wir jedoch komplett bei 0 und wir lernten das Grundlegende im Selbststudium.

Das WScript-Objekt
Das WScript-Objekt ist das einzige Objekt, welches man direkt verwenden kann, ohne dieses zuerst instanziieren zu müssen.
Dieses Objekt hat mehrere Methoden und Eigenschaften, so wie eigentlich fast jedes andere Objekt auch.

Die Methode .echo() hat eine sehr einfache Funktion: Sie gibt eine Meldung aus.
Dabei muss ein Parameter übergeben werden. Also entweder ein Text, eine Variable oder eine Eigenschaft. Das sieht dann so aus:
Wscript.echo Wscript.Name oder Wscipt.echo "Test"
Dabei kann der Parameter auch in Klammern Stehen.

Nun zu der Eigenschaft die ich bereits verwendet habe, Name
Verwende ich diese Eigenschaft als Parameter bei der Methode .echo gibt die Meldung "Windows Script Host" aus.
Nun schön damit können wir jedoch noch nicht besonders viel anfangen.

Das FileSystemObject
Das nächste Objekt dass wir kennen lernten war das FileSystemObject und es war mir nicht ganz unbekannt. Im Gegensatz zum Wscript-Objekt muss dieses Objekt, wie alle anderen auch, instanziiert werden, dafür hat es aber keine "direkten" Eigenschaften sondern nur Methoden, die Eigenschaften von File-Objekten liest.
Instanziieren bedeutet dass das Objekt "Kopiert" wird, dafür Speicherplatz reserviert wird und einem Namen übergeben wird, damit man es leichter ansprechen kann.
Das FileSystemObject instanziiert man folgendermassen:
Dim Fso
Set Fso = CreateObjext("Scripting.FileSystemObject")
Mit dem Befehl Dim deklariere ich Fso als Variable. Der Name wird standardmässig für das FileSystemObject verwendet und dient zur Übersichtlichkeit.
Danach instanziiere ich das Objekt mit dem Set-Befehl.

Nun kann ich über Fso alle Eigenschaften und Methoden des FileSystemObjects aufrufen.
Dazu ein kleines beispiel mit der Methode .CopyFile()
Mit folgender Zeile kopiere ich eine Textdatei und benenne Sie um.
Fso.CopyFile ("C:\Textdatei.txt", "C:\Neue Textdatei.txt")
Der erste Parameter übergibt das zu kopierende File und der 2. den Zielpfad + Namen.

Die GetFile() Methode ist eine Weitere Methode, mit dem man eine Datei einer Variabel zuordnen kann. So sichert man den Speicherplatz und den Zugriff auf die Datei.
Das Objekt das daraus entsteht heisst File Objekt.
Daher muss auch hier wieder ein Objekt instanziiert werden.
Dim f
Set f = Fso.GetFile("C:\Textdatei.txt")
Nun kann man viel einfacher auf die Datei zugreifen und muss nicht über das FileSystemObject gehen.
Möchte man die Datei löschen kann man das nun so:
f.Delete
Über das FileSystemObject müsste man das so machen:
Fso.DeleteFile("C:\Textdatei.txt")

Wie bereits erwähnt, kann man über das FileSystemObject auch Eigenschaften von File-Objekten auslesen. Natürlich geht das ganze wieder einfacher, wenn man direkt über das File-Objekt gehen kann.