Table of Contents Introductie
1.1
Dankwoord
1.1.1
Stagebedrijf
1.1.2
Wat
1.1.2.1
Waar
1.1.2.2
Toekomst
1.1.2.3
Probleemstelling
1.1.3
Technisch
1.2
HLE13
1.2.1
Logisch Schema
1.2.1.1
Controllers
1.2.1.2
FIP
1.2.1.2.1
MPU
1.2.1.2.2
AAUX
1.2.1.2.3
ACTRL
1.2.1.2.4
Thyristor: GTO
1.2.1.2.4.1
Transistor: IGTB
1.2.1.2.4.2
Problemen
1.2.1.3
Database
1.2.2
Acces
1.2.2.1
SQL
1.2.2.2
Relationele SQL
1.2.2.3
Contraints
1.2.2.3.1
Primairy Key
1.2.2.3.2
Indexes
1.2.2.3.3
Foreign Key
1.2.2.3.4
Software
1.2.3
Server programma
1.2.3.1
Installatie
1.2.3.1.1
FileSystemWatcher
1.2.3.1.2
Queue
1.2.3.1.3
Opbouw
1.2.3.1.4
Inlezen
1.2.3.1.5
Analyse
1.2.3.1.6
Implementatie
1.2.3.1.7
DataTableCreate
1.2.3.1.8
StagingTablesCreate
1.2.3.1.9
2
SqlBulkCopy
1.2.3.1.10
MergeTables
1.2.3.1.11
Client programma
1.2.3.2
Herstelmanager
1.2.3.2.1
EPPLUS
1.2.3.2.1.1
ADO.NET
1.2.3.2.1.2
NET 4.0
1.2.3.2.1.3
Locanalyser
1.2.3.2.2
Probleemstellingen
1.2.4
Client programma
1.2.4.1
Schrijftijd Excel
1.2.4.1.1
Datagrid naar Excel
1.2.4.1.2
SQL database
1.2.4.2
Transactie log te groot
1.2.4.2.1
Dode tijd bij schrijven naar database
1.2.4.2.2
Server programma
1.2.4.3
Overflow trigger event
1.2.4.3.1
OnCreate() filteren
1.2.4.3.2
Conclusie
1.3
Future Work
1.4
Bronnen
1.5
3
Abstract Als er een locomotief binnenkomt wordt deze voorzien van een preventieve herstelling of correctief onderhoud. Bij het onderhoud/herstelling hoort ook het uitlezen van rapporten uit de locomotief. Deze rapporten worden uitgelezen doormiddel van de software MMAP geleverd door de fabrikant zelf. De rapporten worden uitgelezen onder de vorm van twee verschillende type bestanden. Namelijk als tekst bestanden en als een speciaal formaat van bestand voor de fabrikant van de locomotief zelf. De software kan tot 8 van deze bestanden genereren. Deze 8 komen overeen met de 8 componenten van de locomotief. 2x MPU (Main Processing Units) 2x AAUX (Agate Auxiliary) 4x ACTRL (Agate Control) Het uitlezen van deze rapporten gebeurt lokaal en is manueel te verrichten op de locomotief. Omdat MMAP gelimiteerde functionaliteit aanbiedt voor het genereren en weergeven van deze rapporten is het als technieker zeer omslachtig om deze te creëren en te onderzoeken. Verder worden deze rapporten opgeslagen op de server door de technieker voor verwerking door middel van een script. De uitgelezen rapporten bevatten informatie over de staat van de locomotief. Wanneer er een fout of actie gebeurt (Bv. een essentieel onderdeel van de locomotief stopt met werken) wordt deze geregistreerd. Daarom is deze informatie dan ook zeer interessant voor de technieker omdat deze hieruit mogelijk kan afleiden welke elektronische componenten/verbindingen gerepareerd moeten indien hij het probleem kan identificeren aan de hand van opeenvolgende fouten. Vroeger werd hiervoor de tekst documenten van de locomotief één voor één naast elkaar geopend en met elkaar vergeleken om zo de oorzaak van problemen te achterhalen. Alleen was dit een zeer tijdrovend proces en wat veel concentratie vereiste. Daarom werden er in het verleden hier al oplossingen voor gezocht. Zoals de Acces toepassing, wat tot nu toe nog steeds gebruikt wordt maar enkel om data lokaal te verwerken, en de eerste versie van de Herstelmananger. Die informatie real time van een SQL server kon halen, bewerken en weergeven. Enkel werkte de Herstelmanager heel traag wat ervoor zorgde dat deze niet zo vaak gebruikt werd. Dit moet nu opgelost worden, het programma moet vlotter en gemakkelijker werken met de ingelezen fouten op een SQL database op de server om zo het oplossen van problemen op locomotieven te verbeteren. Dit zou zich dan moeten vertalen in een hogere productiviteit van techniekers omdat er zo tijd uitgespaard wordt. Terwijl zou het programma ook nog andere functionaliteit implementeren, zoals het inlezen van lokale gegevens en het live uitlezen van gegevens van een locomotief. Verder zou ook nog het server-side programma en de SQL database zelf bijgewerkt moeten worden. Het serverside programma zodanig dat het efficiënter en sneller werkt en de SQL database zodanig dat deze minder plaats in neemt en geen foutieve data bevat/kan bevatten. Door dit alles zou het programma dan ook gebruikt kunnen worden voor onderzoeksdoeleinden. Zo zou er dan bv. afgeleid kunnen worden wanneer er een specifieke vervanging/controle plaats vindt deze zich ook resulteert in minder fouten van een specifiek type. Waardoor deze locomotieven dan mogelijk langer kunnen rijden en minder geld kosten aan het bedrijf wegens buiten werking.
4
Dankwoord Een stage voltooi je nooit alleen, daarom dan ook dit dankwoord. Vooreerst zou ik zeker mijn begeleider, Craens Jeroen, willen bedanken voor het begeleiden van mij tijdens deze stage. Niet alleen zou ik hem willen bedanken voor zijn steun en kennis die al meermaals van pas gekomen is maar ook voor het vertrouwen dat hij mij geschonken heeft. Dit liet mij toe om met meer geavanceerdere technologie te werken ondanks dat de uitkomst soms onzeker was. Wie ik ook sterk wil bedanken is mijn stage promotor Dams Tims en zeker ook alle andere docenten die mij les hebben gegeven aan de AP Hogeschool, dit voor de kennis en steun die deze mij gegeven hebben. Met mijn vragen kon ik altijd bij hun terecht en daarvoor grote dank. Zonder deze personen had het verloop van de stage er ongetwijfeld anders uitgezien. En last but not least, wil ik graag mijn ouders bedanken, die mij de mogelijkheid gaven om te verder te studeren!
5
NMBS NMBS is een van de belangrijkste vervoersbedrijven van het land. Van Antwerpen tot Aarlen, van Oostende tot Eupen: de trein rijdt tot in de verste uithoeken van het land. Veilig, steeds strevend naar stiptheid, ecologisch verantwoord en altijd met het oog op de tevredenheid van de klant. Nationaal of internationaal: de NMBS vervoert en brengt in vervoering. 750.000 reizigers per dag 3.800 treinen per dag Over een spoornet van 3.578 kilometer Dit wordt allemaal verwezenlijkt door meer dan 16.000 medewerkers die dag en nacht in de weer zijn om alles in staat te stellen om de klant zo tevreden mogelijk van punt A naar punt B te brengen. Met meer als 300 verschillende jobs en ruim 1.500 aanwerving per jaar horen ze dan ook bij de top werkgevers van België.
6
Wat Na de ingrijpende hervorming van de spoorwegen in 2005, moet een nieuwe structuur NMBS op weg zetten naar een efficiënter beheer en betere operationele prestaties. De klant moet, meer dan vroeger, centraal staan in de ondernemingsvisie. En NMBS zelf zal nog meer inzetten op verandering. De spoorwegen hebben zich steeds weten aan te passen aan de wijzigende omstandigheden; veranderen is een blijvende opdracht om de toekomst van de onderneming te verzekeren. We veranderen bovendien de samenleving: door nieuwe mobiliteit te creëren, door de stedelijke omgeving te hertekenen, door impulsen te geven aan de economie en duurzame oplossingen te bevorderen. Als overheidsbedrijf met een sterk maatschappelijk karakter en als hoofdrolspeler in de mobiliteit moeten we meer zijn dan een puike dienstverlener: we willen mee richting geven aan de samenleving van morgen, nieuwe oplossingen aanreiken voor onze mobiliteit en bouwen aan duurzame vooruitgang. We willen verandering inspireren.
De NMBS in cijfers (jaarverslag 2012):
7
Technics (onder deze afdeling vindt de stage plaats) De directie Technics staat in voor de aankoop, de modernisering en het onderhoud van het rollend materieel. De opdracht van de directie bestaat erin de klanten voldoende, veilig en betrouwbaar materieel ter beschikking te stellen dat aangepast is aan de operationele en commerciële behoeften om de reizigers een kwaliteitsvolle dienstverlening te kunnen aanbieden. Wat zijn de doelstellingen en de belangrijkste prioriteiten voor de toekomst: het ETCS-systeem installeren op het rollend materieel om het veiligheidsniveau te verhogen de beschikbaarheid en de betrouwbaarheid van het materieel optimaliseren de technische capaciteit van de werkplaatsen vergroten om het hoofd te kunnen bieden aan de voortdurende stijging van het aantal rijtuigen de organisatie en de werking van de werkplaatsen voortdurend verbeteren. De stage vindt specifiek plaats op de Tractie Werkplaats te Antwerpen.
8
9
Bestemming beter Het motto “bestemming beter” vormt de kern van onze strategie om te verbeteren. We werken aan een solide onderneming die duurzame verbeteringen beoogt op drie niveaus: economisch sociaal/maatschappelijk ecologisch Om die verbeteringen te realiseren, heeft NMBS strategische projecten gelanceerd in alle geledingen van de onderneming
10
Probleemstelling Als er een locomotief binnenkomt wordt deze voorzien van een herstelling preventief of correctief onderhoud, bij deze herstelling/onderhoud hoort het uitlezen van rapporten die bestanden met foutmeldingen bevatten. Deze bestanden komen uit 8 elektronica racks die zich binnenin de locomotief bevinden. Deze 8 racks leveren door middel van een software programma genaamd MMAP, dat de gebruiker in staat stelt om rapporten te kunnen exporteren, in totaal 16 bestanden op. Voor elke elektronica rack is dat één tekst bestand en één bestand in een speciaal formaat voor de fabrikant van de locomotief zelf. Het MMAP software programma werd dan ook geleverd door de fabrikant zelf. Het uitlezen van deze rapporten gebeurt lokaal en is manueel te verrichten door middel van een laptop aan te sluiten op de locomotief. Het is enkel op de huidige moment mogelijk om elke elektronica rack apart uit te lezen waarbij dit telkens jammer genoeg twee maal moet gebeuren, elks één keer voor de twee verschillende bestand formaten. In deze geëxporteerde tekst bestanden staan tijdstippen van foutmeldingen die door verloop van tijd gegenereerd zijn en extra rand informatie rond deze meldingen. Als technicus is het zeer omslachtig om deze 8 bestanden naast elkaar te openen om de foutcodes van de 8 units rond hetzelfde tijdstip te kunnen zien om de oorzaak te achterhalen. Daarom worden deze tekst bestanden doormiddel van een script naar de server van de NMBS op een specifieke plaats opgeslagen om zo deze te kunnen verwerken. Het gaat in de rapporten over foutmeldingen die gebeuren in deze elektronica racks: AAUX (Agate Auxiliary) ACTRL (Agate Control) MPU (Main Processing Units) Bij een uitlezing van zo’n locomotief worden deze bestanden worden door de manueel benoemd naar de naam van de bijhorende elektronica rack, voor deze tekst bestanden wordt dan een folder gemaakt met de bestanden in doormiddel van een script genaamd “CopyMMAP”. De naam is door het script gegeneerd en bevat een datum, deze datum wordt uit de de property datum uit de tekst bestanden gehaald. Nadien kopieert deze de bestanden naar de server op de juiste locatie aan de hand van die naam. Alleen is dit proces zo omslachtig dat het al wel eens is gebeurt dat er hier fouten op gemaakt worden. Hierdoor kan het zijn dat de benoeming van de bestanden niet even consistent is. Een gevolg hiervan is, omdat het huidige gebruikte script dat de bestanden op de server plaats gebruikt door de technieker ontoereikende controle uitvoert op deze naam, dat er daardoor data op een specifieke locatie op de server kan komen te staan dat soms verkeerd geïnterpreteerd kan worden omdat de naam niet klopt. . Vervolgens moeten de bestanden ingelezen worden in een relationele database. Door dat deze database gebruikt wordt in combinatie met een software programma is het veel gemakkelijker, vlotter en overzichtelijker om bepaalde foutmeldingen aan elkaar te linken. Om zo de tijd die gespendeerd moest worden aan alle bestanden te openen en op zoek te gaan naar foutmeldingen te verkleinen. Er wordt ook nog extra tijd uitgespaard doordat ook het extern Excel bestand ingeladen zal worden dat oplossingen rond deze foutmeldingen bevat.
11
Ook zal dit programma niet enkel een groot voordeel hebben voor de technicus maar ook voor de ondersteunende mensen, het software programma zal hun in staat de informatie te filteren en te exporteren naar Excel. Zo kunnen ze met deze data grafieken maken wat bv. de verhouding van bepaalde fouten tussen alle treinen kan weergeven. Door dit te doen kunnen patronen opduiken van bepaalde fouten, waarom heeft locomotief 1301 4 maal zoveel kritische fouten dan locomotief 1302? Om dan het verschil tussen deze twee treinen te bepalen wat een blijk zou kunnen geven van de oorzaak. Wat dan op beurt een oplossing zou kunnen worden voor alle treinen.
12
HLE13 De locomotief reeks 13 is een type elektrische locomotief dat sinds 1997 wordt ingezet door de NMBS. Deze reeks is gebouwd door Alstom en draagt de nummers 1301 - 1360.
Kenmerken Reeks 13 heeft een maximumsnelheid van 200 km/h en kan zowel rijden onder de traditionele Belgische bovenleiding met een gelijkspanning van 3000 V, als onder de recentere van 25 kV wisselspanning, die onder andere op de hogesnelheidslijnen gebruikt wordt en op Belgische lijnen met een bovenleiding van 25 kV zoals de Athus-Maaslijn. Bijkomend kan er onder de Nederlandse 1500 V gelijkspanning gereden worden, maar dit met beperkt vermogen.
13
Logisch Schema Dit schema geeft je een idee van waar alle componenten van de trein bevinden, het is belangrijk om te weten dat sommige componenten dubbel uitgevoerd zijn. Dit is omdat moest er nu één component kapot gaan er steeds een back-up is.
Dubbel uitgevoerd MPU's (2x) AAUX (2x)
14
Controllers Type 13 locomotieven bevat meer meerdere controllers maar acht hier van kan via de MMAP software uitgelezen worden. Namelijk de twee AAUX’s, vier ACTRL’s en de twee MPU’s. Deze elektronica racks zullen de hele locomotief controleren en sturen. Deze controllers zijn verbonden via een netwerk dat met het industriële FIP protocol werkt (1 Mbps). Het netwerk bestaat uit twee gescheiden kanalen voor communicatie en zorgt zo voor redundancy, zodat in geval van een storing op één van de kanalen het andere kanaal kan gebruikt worden. In onderstaande figuur is een afbeelding van de opbouw van het netwerk weergegeven.
15
FIP WorldFIP: is de standaard versie van de FIP (Factory Instrumentatie Protocol). Deze bestaat uit drie lagen, namelijk: de Applicatie laag, de Data Link laag en de Fysieke laag. De Applicatie laag wordt gebruikt om de berichten die over het netwerk worden verzonden configureren. De Data Link laag stuurt de berichten waar ze moeten gaan, en de Fysieke laag verbindt de knooppunten van het netwerk samen. Beperkte berichten kunnen worden verstuurd via WorldFIP. Het opzettelijk beperkt omwille van de snelheid, maar de aard van digitale berichten laten het protocol enige flexibiliteit. FIP heeft toegang tot maar liefst 65.536 variabelen en 16.777.216 boodschap adressen.
16
MPU De MPU (Main Processing Unit) is in feite het elektronisch hart van de locomotief: hier komen de signalen toe en aan de hand van deze signalen zullen de MPU’s commando’s geven. De signalen worden door middel van handelingen door de trein bestuurder in de machinisten kamer verstuurd (bv. vraag om een bepaalde trekkracht, inschakelen van de locomotief). Deze gaat dan deze signalen interpreteren en vertalen naar de andere elektronica racks. Van de MPU zijn er telkens twee aanwezig in de locomotief. Dit is om er voor te zorgen dat als er één faalt, er altijd een tweede is om over te nemen. Deze twee elektronica racks zullen de locomotief sturen. De MPU’s werken steeds in Master/Slave. Voor moest er nu een MPU defect geraken zal de tweede MPU overnemen. Zo blijft er steeds 100% trekkracht behouden. Verder beheren deze elektronica racks ook de automatische opstart tests en kijkt of elk uitgestuurd signaal ook effectief uitgevoerd wordt. Zo niet zal hij hiervoor een foutmelding genereren waarin onder andere staat waar, wanneer en wat er fout gelopen is. Hieruit haalt de technieker dan ook, via het MMAP programma, het rapport van de foutgeneraties. Ook zorgt de MPU nog voor het netwerk tussen andere locomotieven en diverse. Zo kan de locomotief aangestuurd worden van uit een andere locomotief bijvoorbeeld.
17
AAUX De AAUX (Agate Auxiliary) is een elektronica rack in de locomotief. Deze bestaat uit twee units, weeral twee omdat bij een defect het falen van de trein kan worden voorkomen. Die twee units sturen elks apart een CVS kring aan. De CVS is een statische omvormer. Deze maakt de verschillende hulpbedrijfsspanningen die nodig zijn op de locomotief, om componenten aan te sturen (compressoren, ventilatoren, elektronicaracks, batterijlading...): 380V~ 3 fase, 220V~, 110V=, 24V=.
18
ACTRL De ACTRL (Agate Control) is een elektronicarack die instaat voor het omvormen van de bovenleiding spanning, via gelijkrichting, naar een wisselspanning met een bepaalde frequentie en spanning/stroom, die geschikt is voor de aansturing van een asynchrone motor. Omdat er vier motoren zijn die de locomotief aandrijven zijn er ook vier ACTRL’s. Deze sturen, via een interface, de halfgeleiders GTO & IGBT aan en zorgt er zo voor dat de driefase asynchrone motor de juiste spanning en fase aangelegd krijgt om in zijn werkingsgebied te kunnen functioneren. Deze elektronica rack wordt aangestuurd door de MPU, die op beurt aangestuurd wordt door de machinist. Zo kan de trekkracht van de motoren bepaald worden. Deze ACTRL’s & MPU staan in verbinding met elkaar via het voertuignetwerk. Dit netwerk maakt gebruik van het door het boven vermelde FIP protocol. Indien er een motor wegvalt of iets mis is met een ACTRL zal deze afgezonderd worden. Zo zal nog maar 75% van het tractie vermogen beschikbaar zijn (3motoren) in plaats van het gebruikelijke 100% (4 motoren) Onder het 25kV~ net, zal de ACTRL elektronicarack d.m.v. de techniek "Pont Monofase à Commutation Forcée" ervoor zorgen dat de spanning wordt gelijkgericht waarbij de spanning en stroom zoveel mogelijk in fase met elkaar is. Met andere woorden: zo hoog mogelijke arbeidsfactor (= cosinus van hoek tussen stroom en spanning) die dus 1 benadert, waardoor we zo weinig mogelijk reactief of blind vermogen hebben en we dus zoveel mogelijk actief / bruikbaar vermogen hebben.
19
Thyristor: GTO Een gate turn-off thyristor (GTO) is een thyristor die men vanuit de gate in geleiding kan brengen door een positieve puls te genereren op deze gate. Men kan de GTO uit geleiding sturen (sperren) door een negatieve impuls naar de gate te sturen. Buiten dit is de werking van de GTO gelijk aan die van de thyristor. Het stuurcircuit van de GTO zal meer complex zijn dan dit van de thyristor daar wij hier een manier moeten bedenken om zowel positieve als negatieve pulsen te genereren. De GTO wordt als een oplossing gezien voor het probleem dat vermogensregeling van een belasting op DC gebied erg moeilijk te realiseren valt. Bij AC vermogensregeling is dit veel eenvoudiger omdat we hier met behulp van de nul doorgangen de faseaansnijding perfect kunnen regelen. Goede DC-spanningen hebben geen perioden en hebben meestal geen nulpunt. Voor DC-spanningen die wel een nulpunt hebben (dubbelzijdige gelijkrichting zonder afvlakking) rijzen er geen problemen. Dankzij de GTO kunnen we nu dus de component uitschakelen zonder dat er een nul doorgang vereist is..
20
Transistor: IGTB IGTB (Insulated Gate Bipolar Transistor) is een transistor die veel vermogen kan schakelen en die met een kleine stuurspanning uit bijvoorbeeld een microprocessor aangestuurd kan worden. Dit is een verdere doorontwikkeling van de GTO. Dit type transistor combineert de gatekarakteristiek van een MOSFET-transistor met de mogelijkheid tot de grote stroom en lage verzadigingsspanning van een bipolaire transistor door een MOSFET en een bipolaire vermogenstransistor in een enkele behuizing te plaatsen. Omdat het blokkerende vermogen van een IGBT klein is wordt er een diode in serie geplaatst. Een IGBT heeft een relatief hoge doorlaatspanning ca. 3,5 Volt.
21
Problemen Van bij de eerste levering zijn er problemen geweest met diverse systemen van deze locomotieven. Alle locomotieven zijn meerdere keren voor aanpassingen terug naar de fabriek geroepen. Dit waren wel alleen maar kinderziektes. Ondertussen rijdt deze vele stabieler en is qua betrouwbaarheid beter dan de vorige generatie locomotieven. Wel minder betrouwbaar dan de nieuwere generatie “Traxx” die ook door TW Antwerpen onderhouden worden. Wat op zich logisch is.
22
Design proces Voor er gesproken kan worden over de huidige database wordt er best eerst woordje uitleg gegeven over hoe het proces van het databeheer tot stand gekomen is door heen de jaren. Waarbij de Acces database de eerste fase was, hieruit is de SQL database voortgevloeid
De data voor in de database komt uit een locatie op de server. Hierop bevindt zich een folder genaamd MMAP. Deze bevat 60 folders, 1301 -> 1360, en elk van deze folders bevat sub folders die de rapporten voorstelling. Hierin zitten de tot 8 tekst documenten. De rapporten worden hier geplaats doormiddel van een script gebruikt door de technieker. Dat automatisch de rapporten op de juiste locatie zet.
23
Acces Voordat er een SQL database ontworpen is door een vorige stage student werd er gebruik gemaakt van een Microsoft Acces database Het gebruik ervan heeft voordelen en nadelen. De Acces database werd toen alleen maar lokaal gebruikt waardoor sommige voordelen zelfs beperkt bleven.
Voordelen Gemakkelijk te installeren & gebruiken Gemakkelijk te integreren in je project Relatief grote opslag capaciteit voor kleine projecten (2GB) .NET vriendelijk Bespaart je geld Meerdere gebruikers ondersteuning Makkelijk data importeren Jammer genoeg zijn er ook duidelijke nadelen aan verbonden, wat het voor sommige projecten onmogelijk maakt om deze technologie te gebruiken. In de huidige situatie zeker, dit zal later verder toegelicht worden.
Nadelen Eindige grootte (2GB) SQL (Structure Query Language) niet even robuust als voor MS SQL Server Alles wordt opgeslagen in één bestand Moeilijk om bestanden te publiceren die een deel zijn van statische bestanden Meerdere gebruikers ondersteuning gelimiteerd Technisch gezien is de limiet 255 gebruikers. Realiteit: tussen de 10 & 80 gebruikers (afhankelijk van applicatie)
Besluit Er waren te sterke nadelen, wat het gewoon onmogelijk maakte om een Acces database te gebruiken in de huidige situatie, namelijk: Eindig grootte (2GB) Meerdere gebruikers integreren is te moeilijk De huidige situatie zoals ze nu is moet er voor 8GB~ aan data verwerkt worden. Dit is natuurlijk niet de hoeveelheid die effectief in de database beland, maar geeft toch een blijk van de schaal. In totaal spreken we over meer dan 30 miljoen rijen met gemiddeld 8 kolommen die in de database geplaatst moeten worden. Ook zal naarmate de jaren vorderen de situatie alleen maar extremer worden, er zullen uitlezingen blijven gebeuren met als gevolg dat de database zal blijven verder groeien. Sinds 2012 worden er tekst bestanden opgeslagen op de server. Ongeveer 4 jaar verder (ruimschoots gerekend) wat zorgt voor een groei van 2GB per jaar aan tekst files. Dit is niet de effectieve groei van de database uiteraard maar geeft je toch een idee van de groei per jaar. Daarom is er zeker een juiste beslissing gemaakt om over te stappen naar een SQL database. Aangezien de data die opgeslagen moet worden zeker over de 2GB is. Maar de vraag blijft nog of dit effectief de beste oplossing is, MySQL is bijvoorbeeld nog nooit onderzocht geweest. Meer hierover in de toekomst.
24
SQL De SQL database is ontworpen met behulp van het programma Microsoft SQL Server. Dit is een relationeel databasebeheersysteem ontwikkeld door Microsoft. Het ondersteunt een dialect van SQL, de meest gebruikte databasetaal. Het wordt algemeen gebruikt door organisaties voor kleine tot middelgrote databases. De database hiermee is tussen 2012-2013 ontworpen door een stage student, jammer genoeg ontbraken veel optimaliseringen en database principes in de SQL database. Hierdoor werkte deze wel maar niet optimaal. Dit zullen even kort toegelicht worden.
Problemen Voor de tabellen in de database waren er geen relaties onderling gecreëerd, dit zorgde dat er veel lege velden aanwezig waren. Er was ook geen sprake van Indexes, Constraints of zelfs Primairy Keys. Dit zorgde ervoor dat de database moeilijk te beheren was. Indexes zorgden er voor dat als je een SQL query schrijft waarin je naar elementen zoekt die een bepaalde kolom bevat deze vele malen sneller resultaten gaat vinden wat essentieel is voor een snel werkend programma. Er waren ook geen Constraints in de tabellen zelf. Dit zorgde ervoor dat de software eerst via een SQL commando de tabel moest doorzoeken naar een match, als er dan geen match was, werd deze record toegevoegd. Terwijl met een constraint zou de insert gewoon falen zonder een extra query te moeten uitvoeren. Zo blijft data integriteit verzekerd, moest dan een ander programma indentieke data op de database proberen plaatsen zou dit niet mogelijk zijn.
Besluit Al dit zorgde ervoor dat er in de eerste plaats sprake was van optimalisatie van het software programma “Herstelmanager”. De laad tijden waren ongelofelijk lang en soms starten het programma gewoon niet. In mijn ogen kwam dit grotendeels door het ontwerp van de SQL database. Deze was ontworpen op de meest basic manier die er bestaat, zo bleef het potentieel van MS SQL volledig onbenut met de gevolgen van dien. Ook omdat alles in 1 tabel opgeslagen stond, werd het ook al veel moeilijker om data geleidelijk binnen te halen. Deze tabellen werden aangemaakt per jaar van de uitlezing, daaruit kon je dan ook direct zien dat er iets mis was met de data integriteit. Zo waren er tabellen voor het jaar 2028 bijvoorbeeld. De oorzaak is hiervan dat soms de systeem datum van de uitgelezen elektronica racks in de locomotief niet goed stonden. Hierdoor is de data onbruikbaar geworden en zou dus als gevolg daardoor moeten verwijdert worden van de database. Maar dit was het geval niet. Ook werden er heel veel lege velden opgeslagen in deze tabellen, er waren 40 velden gereserveerd voor informatie rond een fout generatie op te slaan, alleen was dit gemiddeld maar voor 10 velden nodig. Zo ontstond er vaak een gap van 30 lege velden. Door al deze redenen is er in het begin van de stage voorgesteld om over te gaan naar een relationele database inclusief Primairy Keys, Constraints & Indexes. Zo zou de data makkelijker, sneller te beheren zijn terwijl de integriteit van de data behouden wordt. Er zou voorkomen worden dat er onnodig lege velden gereserveerd worden wat ook ten goede komt van de database grootte. Ook moest er dan in de toekomst ervoor gekozen worden om de database uit te breiden zou dit vele gemakkelijker kunnen en zonder performance hits (tabellen die ongeloofelijk veel kolommen bevatten).
25
Relationele SQL Het ontwerpen van de relationele database zoals ze nu werkt heeft meerdere fasen doorlopen. Er is begonnen met een ER diagram van de originele database maken om dan een ER diagram van de vorige versies en dan uiteindelijk de huidige versie te maken. Zo konden de veranderingen duidelijk in kaart gebracht worden en tegelijkertijd een duidelijk doel voor ogen te houden, namelijk het gecreëerde diagram succesvol proberen implementeren.
Vroeger: bevatten alle velden in één tabel waarbij de "Value" kolom 40x herhaald werd
Versie 1:
26
Versie 2: Bij het begin van het creëren van de database leek het toevoegen van een rapporten tabel logisch, uiteindelijk is deze geknipt geweest. Om de simpele redenen dat deze geen meerwaarde kon bieden, met de opgeslagen naam/datum van het rapport kon niets gedaan worden. Het compliceerde alleen maar de database en is daarom dus verwijdert.
Versie 3: Hierbij werden enkel de onnodige velden uit de tabel "Fouten" geknipt. Zo kon een kleinere database bewaard blijven.
Technisch De database maakt gebruik van een 1 op meerdere relatie (1/*). Dit betekent dat één trein meerdere fouten kan bevatten maar één fout kan geen meerdere treinen bevatten. De navigatie properties zorgen voor de links tussen de tabellen door middel van Foreign Keys. Alleen de tabel Treinen bezit geen Foreign Key, dit komt doordat het de eerste tabel is in de ketting en daarom dus niet nodig is.
27
Op deze tabellen zit telkens één constraint geprogrammeerd. Deze bevat de kolommen die uniek moeten zijn. Treinen tabel Name Dit zorgt er voor dat er dat elke Trein nummer uniek is, er kunnen geen twee 1301 in staan. Fouten tabel TreinId (foreign key) Datum Time FoutCode Omschrijving Omdat er vele rapporten dubbele informatie bevat moeten gereduceerd worden. Dit wordt verwezenlijkt door deze kolommen. Het resultaat hiervan is dat er gemiddeld van de 100.000 fouten 50.000 effectief wordt toegevoegd per trein. Het toevoegen van de TreinId zorgt er voor dat elke fout enkel uniek is voor elke trein, dit resulteert in dat twee verschillende treinen elks dezelfde identieke fout kan bevatten. ExtraInfo tabel FoutId (foreign key) Value Dit zorgt er voor dat als een identieke fout twee keer ingelezen wordt er steeds unieke waarden blijft instaan, anders zou deze telkens per keer van toevoegen het aantal values verdubbelen met duplicates in. FoutId zorgt voor dat het enkel uniek is per Fout & dus niet globaal. Verder wordt elke Foreign Key nog geïndexeerd, dit om de perfomantie van de zoek resultaten te versnellen. Enkel had dit het nadeel dat het inlezen van de data ook aanzienlijk langer duurt.
28
Contraints SQL Constraints worden gebruikt om de regels te specifiëren voor de data in de tabellen. Als er een van deze regels gebroken wordt tussen de Constraint en de data actie, dan zal de Constraint de data actie annuleren.
29
Primairy Key De Primairy Key is ook een type Constraint, alleen bevat deze maar 1 kolom in tegenstelling tot een gewone Constraint die er meerdere kan bevatten. De Primairy Key Constraint identificeert elke record in een database tabel als uniek. Primairy Key
Name
Value
1
x
y
2
x
y
Ook al zouden er dan twee keer dezelfde rij toegevoegd worden, deze hebben dan steeds een uniek element zodat er onderscheid kan gemaakt worden tussen de twee. Een gewone Constraint zou dan bijvoorbeeld kunnen zijn: de kolom Name & Value bevatten, als dan de tweede rij toegevoegd zou willen worden zou deze falen en zo data integriteit te bewaren.
30
Indexes Indexen zijn speciale tabellen waarin opgezocht wordt door de database zoek machine, deze wordt gebruikt om de data sneller te verkrijgen.
Voordelen Een index helpt resultaten verkregen via SELECT & WHERE clausules aanzienlijk te versnellen.
Nadelen Jammer genoeg vertraagt dit ook de data input, gebruik makend van UPDATE & INSERT commando’s, dit nadeel zorgde voor een belangrijke verandering in de database, het overschakelen van single query naar batch copy.
31
Foreign Key Een Foreign Key in één table wijst naar een Primairy Key in een andere table. De foreign Key wordt gebruikt om acties te voorkomen die die de links tussen tabellen zou breken. Deze voorkomt ook dat als er geen foute data in de Foreign Key tabel kan geplaats worden door alleen maar inserts toe te laten waarbij de de primairy key bestaat. Kort voorbeeld: PersonId
LastName
FirstName
1
Hansen
Ola
2
Svendson
Tove
3
Pettersen
Kari
PersonId is hier de primairy key. PersonId
OrderNo
OrderId
3
77895
1
3
44678
2
2
22456
3
1
24562
4
Als er hier een OrderId zou toegevoegd worden met bijvoorbeeld PersonId gelijk aan 4, dan zou deze falen. Dit komt doordat PersonId een Foreign Key is, enkel als de Foreign Key bestaat is het mogelijk om deze toe te voegen. Ook is het mogelijk om cascade delete in te schakelen, dit betekent dat als er een Persoon verwijdert wordt, automatisch alle bijhorende OrderId’s ook verwijdert worden.
32
Software Wat er ondertussen besproken is: Van waar komt de data Waar worden deze tekst bestanden bewaart Waar deze in opgeslagen moet worden Wat er ontbreekt: De tekst bestanden moeten nu nog verwerkt worden Inlezen tekst bestanden Verwerken van deze data (filtering) Verwerkte data in de Relationele SQL database plaatsen Het software gedeelte van het project bestaat uit de delen: Client programma Server programma
33
Server programma Het originele server programma, gemaakt door de vorige stage student, was niet meer van toepassing doordat de database die daarbij gebruikt werd compleet verschillend was van de huidige database. Wel kon er bepaalde code snippets uit gehaald worden & aangepast worden. Om zo het productie proces te versnellen. Het ontwerpen van het programma is in bepaalde stappen gebeurt. Dit programma werd eerst als executable en vervolgens als service beschikbaar gemaakt. Want het is mogelijk dat in de toekomst er van een bepaald platform gebruik gemaakt zou worden waardoor er geen server meer is om deze service op te draaien. Het zou daarom nog mogelijk moeten zijn om toch alsnog de SQL database up te daten met een executable ipv. een service. De service heeft als voordeel dat deze altijd runt zodra de server opgestart wordt, indien correct ingesteld. Het nadeel van een executable is dat deze enkel kan werken als de user ingelogd is. In deze code wordt ook een configureer file gebruikt. Dit heeft als grote voordeel dat alle variabelen veranderbaar zijn. Gaande van de locatie van de "Watched" folder tot de locatie van de database server. Ook kan je de locatie van waar het log bestand veranderen.
34
Installatie Voor de service is het belangrijk dat je de juiste instellingen mee geeft. Deze heeft toegang nodig tot meerdere locaties waardoor een correcte user account nodig is. De service werkt default met een ander soort rechten. Normaal gezien wordt de NETWORK SERVICE account gebruikt om veranderingen op remote locaties toe te staan, dit werkte jammer genoeg niet van op de server. Dit probleem werd ook grotendeels veroorzaakt dat de service op een andere server geplaatst moest worden dan de server waar de MMAP folder en database zich bevinden. Dit kwam doordat de server waar de MMAP folder zich bevindt zo goed als vol zit waardoor er geen .NET 4.0 geinstalleerd kan worden. Anders zou dit zelfs niet nodig geweest zijn. Het is daarom belangrijk om de Log On property van de service te veranderen naar een admin account die rechten heeft op beide servers en op de locatie waar alle gegevens gelogd moeten worden. Anders zal deze service niet werken. Verder werden er ook twee batch files gecreëerd, deze dienen om de service te installeren en te verwijderen. Dit is essentieel omdat je niet kunt verwachten van een niet-IT persoon om via cmd de service te installeren.
35
FileSystemWatcher Deze start de FileSystemWatcher klasse. In de vroegere server werd er gebruikt gemaakt van een time based service, de code van deze service werd na een bepaalde tijd telkens uitgevoerd. Dit had als nadeel dat je soms tot maximaal 5:00 min moest wachten voor deze begon met het importeren van nieuwe rapporten in de MMAP folder. Daarom is er hier gekozen voor de FileSystemWatcher, deze triggerd indien er een verandering optreedt in de MMAP folder op de server. Hij gebruikt dan de volledige folder extensie in de OnCreate methode om de ProcessController te starten. Dit doet hij wel via een omweg. Het gebeurt namelijk dat er wel eens dat er veel folders tegelijk gekopieerd worden naar de MMAP folder. Waardoor deze de triggered events niet kan bijhouden. Daarom moeten deze eerst in een queue geplaatst worden. private void Watcher() { // Create a new FileSystemWatcher with the path //and text file filter watcher = new FileSystemWatcher(DP.PathToMMAP()); //Whether subdirectories within the specified path should be monitored. watcher.IncludeSubdirectories = true; //Watch for changes in LastAccess and LastWrite times, and //the renaming of files or directories. watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; // Add event handlers. watcher.Deleted += new FileSystemEventHandler(OnDelete); watcher.Created += new FileSystemEventHandler(OnCreate); watcher.Error += new ErrorEventHandler(OnError); // Begin watching. watcher.EnableRaisingEvents = true; }
private void OnCreate(object sender, FileSystemEventArgs e) { string MMAPnaam = new DirectoryInfo(e.FullPath).Parent.Parent.Name; // If file is created... if (e.ChangeType == WatcherChangeTypes.Created && e.FullPath.IndexOf("MMAPserviceLog") < 0 && MMAPnaam == "MMAP") { // ...enqueue it's file name so it can be processed... FP.EnqueueFileName(e.FullPath); } // ...and immediately finish event handler }
36
Queue De FileSystemWatcher wacht voor veranderingingen in een folder. Wanneer een folder is verandert (bv. file wordt aangemaakt), zal deze het juiste event triggeren. De eventhandler zal de file openen en de content ervan lezen om te beslissen hoe deze verder verwerkt zal worden. Dit kan een beetje tijd in beslag nemen. Gedurende deze tijd kan een andere file aangemaakt worden in de "Watched" folder. Sinds de FileSystemWatcher event handler nog steeds de eerste file aan het verwerken is zal deze de tweede niet registeren.
Door onderstaande code wordt deze tweede file eerst in een queue geplaatst, deze queue wordt dan één voor één afgewerkt. Zo zal elke file verwerkt worden, ongeacht het aantal dat in één keer gekopieerd wordt in de "Watched" folder genaamd MMAP.
class FileProcessor : IDisposable { // The FileProcesser class is used to queue files for processing. // If there are a lot of files copied at the same time to // the watched directory, the event trigger won't keep up. // From all the events triggered he will drop only the events // where it's still doing work in the trigger method when the next one arrives. // Create an AutoResetEvent EventWaitHandle. private EventWaitHandle eventWaitHandle = new AutoResetEvent(false); private Thread worker; private readonly object locker = new object(); private Queue<string> fileNamesQueue = new Queue<string>(); public string PathToLog() { return @ConfigurationManager.AppSettings["PathToLog"]; } // Starts new thread (allows to process multiple things at the same time) public FileProcessor() { // Create worker thread worker = new Thread(Work); // Start worker thread
37
worker.Start(); } // Places File in a queue public void EnqueueFileName(string FileName) { // Enqueue the file name // This statement is secured by lock to prevent other thread to mess with queue while enqueuing file name lock (locker) fileNamesQueue.Enqueue(FileName); // Signal worker that file name is enqueued and that it can be processed eventWaitHandle.Set(); } // The actual queue private void Work() { while (true) { string fileName = null; // Dequeue the file name lock (locker) if (fileNamesQueue.Count > 0) { fileName = fileNamesQueue.Dequeue(); // If file name is null then stop worker thread if (fileName == null) return; } if (fileName != null) { // Process file ProcessFile(fileName); } else { // No more file names - wait for a signal eventWaitHandle.WaitOne(); } } } // The processing of the data from the files + logging private void ProcessFile(string FileName) { LogEvent(); string msg = string.Format("File " + FileName + " | " + "Created"); LogEvent(msg); DataProcessor DP = new DataProcessor(); DP.ProcessController(FileName);
} // Logging public void LogEvent(string message) { DateTime dt = new DateTime(); dt = System.DateTime.Now; message = dt.ToString("dd/MM/yy HH:mm:ss",CultureInfo.CurrentCulture) + ": " + message; string PathLogFile = PathToLog(); if (!File.Exists(PathLogFile)) { // Create a file to write to. using (StreamWriter sw = File.CreateText(PathLogFile))
38
{ sw.WriteLine(message); } } else { using (StreamWriter sw = File.AppendText(PathLogFile)) { sw.WriteLine(message); } } } public void LogEvent() { string PathLogFile = PathToLog(); if (!File.Exists(PathLogFile)) { // Create a file to write to. using (StreamWriter sw = File.CreateText(PathLogFile)) { sw.WriteLine(); } } else { using (StreamWriter sw = File.AppendText(PathLogFile)) { sw.WriteLine(); } } } #region IDisposable Members public void Dispose() { // Signal the FileProcessor to exit EnqueueFileName(null); // Wait for the FileProcessor's thread to finish worker.Join(); // Release any OS resources eventWaitHandle.Close(); } #endregion }
39
Opbouw Het programma volgt een duidelijke structuur en bevat twee overloaded methoden, zo blijft het programma overzichtelijk en is tegelijk makkelijk te begrijpen. De methode die een enkel rapport inleest wordt gebruikt door de queue, de andere overloaded methode door de OnStart() van de service zelf. Deze bevat een methode genaamd "ProcessController", het enigste wat deze doet is deze methoden aanroepen. Hierin zit nog wel een verschil, omdat de ProcessController overloaded is heeft deze een dubbele functionaliteit. De methode zonder parameter leest alle mogelijke folders in de targeted folder in. Deze methode wordt gecalled wanneer de service/executable opstart. Indien deze dan crasht en opnieuw moet opstarten hij altijd alle data nog eens gaat controleren voor moest er tussen de tijd van de crash nog een rapport bij gekomen zijn. De tweede methode met een string parameter dient voor het individueel inlezen van een rapport. Dit rapport is een volledige folder naam binnen een nummer van een trein. Deze wordt gecalled wanneer er een verandering gedetecteerd wordt door de FileSystemWatcher. private void ProcessController() private void ProcessController(string rapport) private void GetInfoAboutDatabase() private void TextRead() private void DataTableCreate() private void DataTableFill() private void TableTempCreate() private void DatabaseBulkCopyInsert() private void DatabaseMerge() private void TableTempDelete() private void DatabaseFinalCount()
40
Inlezen Er is begonnen met een console applicatie in .NET te starten om dit te testen. Het belangrijkste was om de data uit de tekst bestanden correct in te lezen. Als er hier foutieve data ingelezen zou worden zouden deze fouten zich vertalen tot data in de database. Dit moet uiteraard kost wat kost voorkomen worden. Er werd tegelijk gebruikt gemaakt van een "BufferedStream" om snelheid van het inlezen te versnellen. Deze code wordt uitgevoerd voor elke rapport folder in elke trein folder, in totaal gaat het over 80.000 tekst bestanden die gescand moeten worden. De reden van de FileStream in combinatie met de BufferedStream komt uit dit onderzoek. http://cc.davelozinski.com/c-sharp/the-fastest-way-to-read-and-process-text-files using (FileStream fs = File.Open(fileName, ..... )) using (BufferedStream bs = new BufferedStream(fs)) using (StreamReader sr = new StreamReader(bs)) { string s; while ((s = sr.ReadLine()) != null) { //Verwerken van de output } }
Voor we kunnen begrijpen hoe deze verwerkt wordt moeten we weten hoe de tekst bestanden opgemaakt worden.
41
Analyse Voorbeeld van een rapport: MPU0_A5091911.txt Date :19/09/2015 Name : Description : No Vehicle : No Series : No Train : Location : Case : Comments : Date
Code
30/06/2015 11:30:05:900 07-00-07
Mnemo
Status
Acc Label
Absent 2 Fout SANA MESD
RTCCONVOI_OK_ctx = 0x00 ' ctx_mesd_type_defaut = 0x00 ' ctx_mesd_num_es = 0x00 ' ctx_mesd_num_bloc = 0x01 '' ctx_code_eqt = 0x01 '' ctx_num_eqt = 0x04 ''
01/07/2015 06:46:52:830 07-00-07
Present 1 Fout SANA MESD
RTCCONVOI_OK_ctx = 0x00 ' ctx_mesd_type_defaut = 0x00 ' ctx_mesd_num_es = 0x00 ' ctx_mesd_num_bloc = 0x01 '' ctx_code_eqt = 0x01 '' ctx_num_eqt = 0x02 ''
Wat merken we hier op: De “Date:” veld niet nuttig is De volgende 9 lijnen leeg zijn De eerste 10 lijnen kunnen dus overgeslagen worden. De lijnen met fouten worden altijd voorafgegaan met een lege lijn. De fouten beginnen altijd met een getal De extra informatie rond de fout begint altijd met tekst
42
Implementatie De eerste 10 lijnen overslaan. while ((s = sr.ReadLine()).Skip(10) != null) { //Verwerken van de output }
Omdat de fouten altijd met een getal beginnen zijn deze makkelijk te vinden De fout lijn vinden: If (s != string.Empty && char.isDigit(s[0]) { // Verwerken van de lijn // Splitsen van de input string op basis van de spaties Array[0] bevat dan bv. de datum Fouten.Add(“Verwerkte data” + referentieGetal) Int referentieGetal++; }
Zolang de lijn niet leeg is en het eerste letter is een getal, voor dan deze code uit. De lijn wordt dan gesplitst volgens spaties en in overeenkomende variabelen geplaats. Ondertussen is er ook een integer aan het tellen om in deze statement. Al dit wordt dan in een List Fouten toegevoegd inclusief het “Id” van de fout. Vervolgens wordt er de extra informatie rond de fout ingelezen. If (s != string.Empty && char.isLetter(s[0]) { // Verwerken van de lijn ListExtraInfo.Add(“Verwerkte data” + referentieGetal)} }
Hier wordt de link gelegd tussen de fout en de meerdere extra informatie lijnen. Dit gebeurt doormiddel van het zelfde referentie getal te gebruiken. Heeft de fout een “Id” van 1 dan zullen de bijhorende extra informatie lijnen ook een “Id” van 1 toegewezen krijgen.
43
DataTableCreate Er worden eerst twee overeen komende DataTables gecreëerd. Nadat deze gemaakt zijn wordt de data doormiddel van een foreach ingelezen in een DataRow, vervolgens worden deze toegevoegd aan de DataTable. Elke rij wordt gecontroleerd op lengte. Zodanig zal, later bij het weg schrijven naar de database, deze niet falen. Ook wordt de datum van plaats gewisseld. Dit komt omdat deze volgens jaar, maand & dag geschreven is. Dit moet dag, maand & jaar worden. Dit zal het gebruik van de data gebruiksvriendelijker maken, ook al is het maar een beetje. Deze actie gebeurt voor beiden Listen. De reden waarom we dit in een DataTable moeten plaatsen is omdat SQL Bulk Copy hier gebruik van maakt. Deze dient om in een grote hoeveelheden rijen van de DataTable in één maal weg te schrijven in plaats van het traditionele lijn per lijn.
44
StagingTablesCreate In tegenstelling tot SQL queries één voor één uit te voeren gaat dit niet bij SQL Batch Copy, als een SQL Query faalt kan je de error opvangen bij bijvoorbeeld een duplicate record door middel van een try/catch. Alleen is dit bij batch copy niet zo, omdat de hele tabel in één maal weg geschreven moet worden is het niet mogelijk dat de query zou falen. Zo ja, draait de database de resultaten terug naar hun originele staat van voor de error. Dit maakt het inserten van duplicate records erg moeilijk indien er Constraints zijn op deze tabel. Net om deze reden wordt er gebruik gemaakt van staging tabellen. Deze zijn identieke tabellen als de tabellen waar je wilt dat je data uiteindelijk terecht komt maar dan zonder duplicates. Het verschil zit hierop dat er geen Primairy Keys, Constraints, Foreign Keys of Indexes aanwezig zijn in deze staging tabel. Zo kan de query zonder error voltooien. Om die reden wordt er vervolgens eerst twee staging tabellen aangemaakt, na gebruik worden deze terug gedelete.
45
SqlBulkCopy De SQL Bulk heeft een aanzienlijk kleinere schrijftijd voor veel resultaten dan een enkele insert per record. Dit heeft de reden dat per insert er elke keer netwerk informatie mee rond gaat. Met SQL bulk kan je gemakkelijk de DestinationTable instellen om dan vervolgens te specifiëren hoeveel rijen je per keer uit je DataTable wilt wegschrijven naar de overeenkomende tabel in de database. Je kunt ook instellen hoe lang de WriteToServer mag duren doormiddel van een BulkCopyTimeout, in het huidige geval is dit 0 wat oneindig betekent. De reden hierachter is dan het de tijd dat nodig is om deze weg te schrijven onvoorspelbaar is. Soms moeten er ongelooflijk veel resultaten weg geschreven worden, andere keren niet. Dit gebeurt twee keer, telkens één keer voor elke DataTable die we gemaakt hebben. Met als resultaat dat de staging table nu de data bevat van één type trein. Door middel van een loop wordt elke trein map afgegaan met als gevolg dat deze code 60x loopt, zo wordt type 1301 -> 1360 ingelezen in de Staging Tables. Maar voor dit gebeurt moeten de staging tables nog samen gevoegd worden met de tabellen met regels. Maar voor hij gaat beginnen kopieren naar de staging tables gaat hij de laatst gebruikte FoutId zoeken in de echte "Fouten" table. Zo zal er steeds een unieke Id aanwezig zijn. Het kan wel voorvallen dat deze Id's elkaar niet opvolgen aangezien in een volgende stap de duplicates er tussen uit gefilterd worden. Als we dan naar de log file gaan kijken zien we dat deze inderdaad aanzienlijk sneller is. Gemiddeld duurde het 1:30 min om een hele folder voor een bepaalde locomotief in te lezen. Als je dit dan vergelijkt met de 1:30 voor een enkel rapport is dit een significante verbetering. 18/05/16 17:04:24: Trein nummer: 1356 18/05/16 17:04:59: Aantal ingelezen fouten: 71307 18/05/16 17:04:59: Aantal ingelezen details: 640841 18/05/16 17:05:57: Aantal ingelezen Fouten toegevoegd: 780 18/05/16 17:05:57: Aantal ingelezen Details toegevoegd: 6237 18/05/16 17:05:57: Ingelezen in: 1:32.54 18/05/16 17:05:57: Trein nummer: 1357 18/05/16 17:06:34: Aantal ingelezen fouten: 77104 18/05/16 17:06:34: Aantal ingelezen details: 699298 18/05/16 17:07:29: Aantal ingelezen Fouten toegevoegd: 0 18/05/16 17:07:29: Aantal ingelezen Details toegevoegd: 0 18/05/16 17:07:29: Ingelezen in: 1:32.84 18/05/16 17:12:21: 01:39:34.37
Nadat alles ingelezen is wordt er overgeschakeld op de FileSystemWatcher die rapporten één voor één gaat inlezen eens deze toegevoegd worden. 19/05/16 08:08:34: File \\mat06antrafp01\bmat.06A_shares\HLE13\Resultaten\MMAP\1311\20160519 | Created 19/05/16 08:09:14: Aantal ingelezen fouten: 934 19/05/16 08:09:14: Aantal ingelezen details: 8434 19/05/16 08:09:47: Aantal ingelezen Fouten toegevoegd: 281 19/05/16 08:09:47: Aantal ingelezen Details toegevoegd: 2109 19/05/16 08:09:47: Ingelezen in: 0:50.58
19/05/16 08:24:41: File \\mat06antrafp01\bmat.06A_shares\HLE13\Resultaten\MMAP\1338\20160519 | Created 19/05/16 08:25:07: Aantal ingelezen fouten: 1236 19/05/16 08:25:07: Aantal ingelezen details: 11877 19/05/16 08:25:36: Aantal ingelezen Fouten toegevoegd: 420 19/05/16 08:25:36: Aantal ingelezen Details toegevoegd: 3465 19/05/16 08:25:36: Ingelezen in: 0:54.15
46
Het uiteindelijke resultaat is nog sneller. Dit komt doordat SQL.BATCH nog niet correct ingesteld was. Dit bepaald het aantal rijen dat in één keer gekopieerd moet worden naar de database. Hoe hoger, hoe sneller. Hierdoor werd de snelheid nog eens 30s sneller. Zo duurde het ongeveer een uur voor alle 60 folders & tussen de 10-20s voor een enkel rapport in te lezen. Hierdoor zouden de resultaten zo goed als realtime gelezen kunnen worden met het client programma. Bij het instellen van de SQL batch is het wel belangerijk deze niet te hoog in te stellen. Anders valt er een OutOfMemoryException voor. Een manier om de juiste hoeveelheid te vinden voor in te stellen is om de waarde te blijven verhogen tot de error valt en om dan deze gedeeld door twee te doen.
47
MergeTables
48
using (SqlConnection con = new SqlConnection(GetConnectionString())) { con.Open(); //Now use the merge command to upsert from the temp table to the Fouten table string mergeFouten = "INSERT INTO [TestDatabase].[dbo].Fouten (FoutId, Datum, Time, FoutCode,Omschrijvin g,TreinId, Module) " + "SELECT st.FoutId, st.Datum, st.Time, st.FoutCode, st.Omschrijving, st.TreinId, st.Mod ule " + "FROM ( " + "SELECT FoutId, " + "Datum, " + "Time, " + "FoutCode, " + "Omschrijving, " + "TreinId, " + "Module, " + "row_number() over (partition by TreinId, Datum, Time, FoutCode order by TreinId, Datum, Time, FoutCode) as rn " + "FROM [TestDatabase].[dbo].foutenTemp ) as st " + "WHERE st.rn = 1 " + "AND NOT EXISTS (SELECT 1 " + "FROM [TestDatabase].[dbo].Fouten t2 " + "WHERE t2.TreinId = st.TreinId " + "AND t2.Datum = st.Datum " + "AND t2.Time = st.Time " + "AND t2.FoutCode = st.FoutCode " + "AND t2.Omschrijving = st.Omschrijving)"; cmd.CommandText = mergeFouten; cmd.CommandTimeout = 0; cmd.ExecuteNonQuery(); //Now use the merge command to upsert from the temp table to the ExtraInfo table string mergeExtraInfo = "INSERT INTO [TestDatabase].[dbo].ExtraInfo (FoutId, Value) " + "SELECT a.FoutId, a.Value " + "FROM ( " + "SELECT FoutId, " + "Value, " + "row_number() over (partition by FoutId, Value order by FoutId, Valu e) as rn " + "FROM [TestDatabase].[dbo].extrainfoTemp ) as a " + "WHERE a.rn = 1 and a.FoutId in (select b.FoutId from [TestDatabase].[dbo].Foute n as b where a.FoutId = b.FoutId) " + "AND NOT EXISTS (SELECT 1
" +
"FROM [TestDatabase].[dbo].ExtraInfo t2 " + "WHERE t2.FoutId = a.FoutId " + "AND t2.Value = a.Value)";
cmd.CommandText = mergeExtraInfo; cmd.CommandTimeout = 0; cmd.ExecuteNonQuery(); //Clean up the temp table cmd.CommandText = "drop table TestDatabase.dbo.foutenTemp"; cmd.CommandTimeout = 0; cmd.ExecuteNonQuery(); cmd.CommandText = "drop table TestDatabase.dbo.extrainfoTemp"; cmd.CommandTimeout = 0; cmd.ExecuteNonQuery(); cmd.Dispose(); con.Close(); }
49
De reden dat deze code vermeld wordt is omdat dit het essentiële gedeelte is van het programma waardoor het goed werkt of niet goed werkt. Voor er tot deze code gekomen is zijn er vele verschillende mogelijkheden geprobeerd om de staging tables te kopiëren naar de bijhorende tabellen, geen van deze succesvol. De ene al heel wat onduidelijker waarom als de andere. Het eerste merge commando, namelijk, “mergeFouten” dient alleen om de unieke rijen uit de staging tabel te halen en deze in de bijhorende tabel met regels te steken. De volgende merge was al heel wat moeilijker om te creëren, dit komt omdat niet alleen unieke rijen moeten toegevoegd worden. Maar ook omdat er tegelijkertijd degene die geen overeenkomende foreign id hebben ook weg te werken. Zo wordt er data integriteit behouden. Al dit is samen gegoten in een Windows Service die gebruik maakt van een FileSystemWatcher. Vroeger werd er gewerkt met een timer, als deze verlopen was werd de code uitgevoerd. Het leek logischer als er een verandering gebeurt in een folder waart de rapporten opgeslagen worden, deze ingelezen wordt en opgeslagen in de SQL database. Dit vermijdt dode tijd en zorgt er voor in combinatie met SQL batch dat de resultaten onmiddellijk weergegeven worden. Verder zal ook bij opstarten van de service alles ingelezen worden, voor moest de service of server waar deze op draait crashen, de data terug opnieuw volledig gecontroleerd wordt en zodanig data integriteit bewaart.
50
Herstelmanager Dit programma zal zowel door de techniekers als door de mensen die technische ondersteuning bieden gebruikt worden. Beiden voor verschillende doelen. Dit programma bezit bepaalde kenmerkende functionaliteiten: Onderzoeker Data uit de SQL database halen Deze kunnen filteren op bepaalde kolommen Deze gefilterde data naar Excel kunnen exporteren Met dat Excel bestand grafieken tekenen Uit deze grafieken kan dan uit onder andere op gemaakt worden hoe kost efficiënt een bepaalde trein is bijvoorbeeld (valt hij veel stil). Technieker Kan gemakkelijk de fouten sorteren op dag/uur Daardoor makkelijker samenhang van fouten die elkaar opvolgen zien Elke fout haalt data uit een Excel file voor extra informatie hierin staat oplossingen, graad & probleem Bespaart zo opzoek werk & verbetert productiviteit Het is ook mogelijk te kiezen van welke datum tot welke datum je informatie wilt zien en voor welke treinen. Dit laat toe om specifiek te filteren naar data.
51
EPPLUS Is een zeer sterke tool om excel gebaseerde bestanden te generen. In de herstel manager wordt deze gebruikt om snel een datagrid naar Excel te exporteren en te bewerken. Vervolgens wordt deze dan gebruikt om grafieken mee te tekenen zodat er data makkelijk met elkaar vergeleken kan worden. Zo kon er bijvoorbeeld afgeleid worden dat er bepaalde fouten zeer sterk seizoen's gebonden waren.
Korte demo: Worksheet creeëren: private static ExcelWorksheet CreateSheet(ExcelPackage p, string sheetName) { p.Workbook.Worksheets.Add(sheetName); ExcelWorksheet ws = p.Workbook.Worksheets[1]; ws.Name = sheetName; //Setting Sheet's name ws.Cells.Style.Font.Size = 11; //Default font size for whole sheet ws.Cells.Style.Font.Name = "Calibri"; //Default Font name for whole sheet return ws; }
Met de hulp van deze functie kan je bijvoorbeeld gemakkelijk een afbeelding toevoegen private static void AddImage(ExcelWorksheet ws, int columnIndex, int rowIndex, string filePath) { //How to Add a Image using EP Plus Bitmap image = new Bitmap(filePath); ExcelPicture picture = null; if (image != null) { picture = ws.Drawings.AddPicture("pic" + rowIndex.ToString() + columnIndex.ToString(), image); picture.From.Column = columnIndex; picture.From.Row = rowIndex; picture.From.ColumnOff = Pixel2MTU(2); //Two pixel space for better alignment picture.From.RowOff = Pixel2MTU(2);//Two pixel space for better alignment picture.SetSize(100, 100); } }
52
ADO.NET WAT ADO.NET is bedoeld om een standaard interface voor data access te definiëren. De ADO.NET namespace System.Data binnen het .NET-raamwerk bevat onder andere interfaces als Connection en Command. Iedere technologie (SQL Server, Oracle, XML) kan zijn eigen implementatie geven aan deze interfaces. Met de komst van ADO.NET kan de keuze voor een data access technologie configureerbaar gemaakt worden. Het .NET-raamwerk voorziet in de communicatie naar vier verschillende ADO.NET connectors: Microsoft SQL Server 7.0 en hoger, OLE DB, ODBC en Oracle. De laatste twee zijn pas ingebouwd vanaf .NET framework v1.1. Het .NETraamwerk implementeert de programmeerinterface door verschillende klassen aan te bieden, die een applicatieprogrammeur kan gebruiken voor communicatie met databases. .NET maakt nu onderscheid tussen provider-specifieke klassen en provider-onafhankelijke klassen. De eerste groep klassen is geoptimaliseerd voor een bepaalde database server, zoals Microsoft SQL-server.
Werking ADO.Net databevattende componenten kunnen binnen een applicatie gebruikt worden zonder een voortdurtend actieve connectie met één of meer databases open te hoeven houden, in het Engels "disconnected". Continu actieve connecties zorgen voor een zeer zware belasting van database servers. Het nadeel is dat de applicatie die Ado.Net gebruikt genoeg geheugen moet hebben om vlot te blijven werken.
53
Om deze 'offline' manier van werken te bewerkstelligen zijn er objecten die een actieve connectie naar de database kunnen aanmaken (van de 'classes' connection, command, datareader) en objecten die voor de opslag van de gegevens zorgen in het geheugen (van de 'classes' Dataset, dataTable). De TableAdapter is een snelle manier om een DataTable te vullen met gegevens uit een database. De DataView slaat geen gegevens op, maar is enkel een manier om gegevens in een DataTable te raadplegen.
54
.NET 4.0 Omdat een van de vereisten was dat het programma op Windows XP zou moeten kunnen werken heeft dit er waarschijnlijk voor gezorgd dat de vorige stage student voor .NET 2.0 gekozen heeft. Maar na onderzoek bleek dit dus onnodig te zijn. Wat het werk alleen maar moeilijker zou maken. NET 4.0 requires XP SP3, Win2k3 SP2, Vista, 7, or 2008(R2) NET 3.5 requires XP SP2 or newer. NET 2.0 requires Win2K SP(3?) or newer Zoals je hierboven kunt zien is .NET 4.0 mogelijk voor XP SP3. Daarbij is er ook gekozen voor een WPF programma, zo is het bouwen van interfaces aanzienlijk gemakkelijker en minder tijd rovend. Omdat de keywords async/await .NET 4.5 zijn, zullen deze niet bruikbaar zijn. Daarom wordt er in de plaats background workers gebruikt. Deze zullen werk uit voeren op een ander thread om zo UI freezing te voorkomen.
55
Locanalyser Er werd ook nog een tweede sub programma geïmplementeerd. Dit gedeelte dient om in real time waardes uit bepaalde componenten van de locomotief te lezen. Zo kan de technieker direct zien waar er geen spanning is. Dit programma is een verbetering van het programma PcRisc, dit is een programma geschreven in VB, wat moeilijk verder uitgebreid/verandert kon worden. Daarom is er hier gekozen voor dit mee toe te voegen aan WPF. Omdat dit gedeelte nog niet volledig af was, kan het daarom gemakkelijker verder gezet worden in de toekomst. Om zo een uitgebreide weergave te maken van alle componenten van een locomotief. Met als doel de herstellingen sneller te doen verlopen. De Locanalyser maakt gebruik van de dll's van Alstom. Deze zijn jammer genoeg niet volledig gedocumenteerd en bieden geen toegang tot source code. Daarom is er hier meer onderzoek naar nodig.
56
Client programma
57
Schrijftijd Excel Origineel werd de library “Microsoft.Interop.Excel” gebruikt om de Datagrid te exporteren naar Excel. Deze liet toe op de rijen en kolommen makkelijk te bewerken en weg te schrijven. Enkel hingen er aan deze voordelen ook een sterk nadeel: Heel erg traag voor veel rijen Net omdat er rij per rij weg geschreven wat meer controle gaf was het heel resource intensief. Het wegschrijven van +10.000 rijen gaf als resultaat dat dit zeer lang kon duren. Ettelijke minuten. Daarom is er gekozen voor EPPLUS na het bezien van meerdere soort gelijke libaries. Dit is een library dat het toelaat om in plaats van rij per rij ineens een hele Datatable weg te schrijven. Wat een serieuze boost aan schrijf tijd geeft. Het voordeel van EPPLUS is dat het tegelijk toe laat om de kolommen te formatteren.
OutOfMemory Exception Als je 320.000+ rijen wegschrijft kom je de OutOfMemory exception tegen. De hoeveelheid rijen die je kan weg schrijven hangt af van je RAM. Een gemakkelijke oplossing zou zijn om het wegschrijven in een paar keer te doen. Dit is wel nog niet geïmplementeerd of gewoon minder als 320.000 lijnen per keer weg te schrijven.
58
Datagrid naar Excel Het was een vereiste voor het project om de data die uit de SQL database werden gelezen gefilterd konden worden op bepaalde kolommen. Na filtering kon deze alsnog heel veel rijen bevatten (+300.000). Eerste werd er geëxporteerd naar een .XLS bestand (Excel type). Dit is uiteindelijk verandert naar een .XLSX bestand omdat XLS beperkingen te grote beperkingen heeft.
Wat is XLS Het formaat .XLS voor Excel bestanden is een van de standaard formaten voor Excel bestanden sinds de creatie hiervan in 2003. Enkel heeft dit een kenmerkend nadeel. Het .XLS is gebaseerd op BIFF (Binary Interchange File Format). Hierdoor wordt de informatie direct opgeslagen in een binair formaat.
Kenmerken Maximum aantal rijen: 65.536 Binair opgeslagen Elke Excel versie kan dit openen
Wat is XLSX Dit is ook een formaat voor Excel bestanden. Enkel is het verschil hier dat deze recenter is en meer toelaat. Het verschil met XLS is dat XLSX gebaseerd is op de Office Open XML formaat, een bestand formaat dat uit voortgevloeid is XML. De informatie in een XLSX bestand wordt opgeslagen in een tekst bestand dat XML gebruikt om al zijn parameters te bepalen. Kenmerken Maximum aantal rijen: 1.048.576 Als tekst opgeslagen In combinatie met XML Enkel leesbaar door Excel versies van 2007 & later
Besluit Het probleem waarbij je +65.536 rijen (meer als de maximum aantal rijen in één sheet van XLS) wilt wegschrijven in een Excel bestand maakt het aanzienlijk moeilijker in XLS, je zou de informatie al over verschillende sheets moeten beginnen wegschrijven. Omdat XLSX een vele hogere maximum biedt is dit vele aantrekkelijker om te gebruiken.
59
Transactie log te groot Elke SQL Server database heeft een transactie log dat alle transacties bij houdt en de database veranderingen gemaakt door elke transactie. De transactie log vult zich altijd vanzelf en kan niet voorkomen worden. Bij het instellen van een vaste grootte van de transactie log zal de database crashen eens deze vol zit. Daarom moet de transactie log regelmatig gekrompen worden om te voorkomen dat deze vol loopt. Maar sommige factoren kan kunnen dit vertragen, daarom is het belangrijk om de grootte van de database in het oog te houden. Sommige operaties kunnen mimimaal gelogd worden (is een instelling) om hun impact op de database log bestand te verminderen. Door deze eigenschap kwam het dat op een gegeven moment de database 4GB groot was en de log bestand voor deze database nog groter. Daarom moet deze regelmatig gekrompen worden. Dit gebeurt op de moment manueel en niet automatisch.
60
Dode tijd bij schrijven naar database Origineel werd de database ingelezen door middel van rij per rij. Dit zorgde was een zeer kostelijke operatie op niet alleen de database zelf maar ook op het log bestand. Elke keer werd er netwerk informatie mee rond gestuurd en als de transactie faalde, wat regelmatig gebeurde door dubbele rijen, werd deze gelogd in het log bestand.
Voorbeeld De gemiddelde leeftijd van alle tekst bestanden binnen één rapport duurde ongeveer 1:30 min. Alleen was dit niet voor één rapport maar voor gemiddeld 100 rapporten. Dit is dan maar voor één trein. Het gaat in totaal over 60 treinen. Dus meer dan 60.000 rapporten in totaal wat er voor zorgt dat heel deze operatie langer dan 150 uur zou duren om in te lezen. Dit kwam doordat elke rij die toegevoegd werd door SQLCommand indien ze niet faalde ook nog eens geïndexeerd moest worden. Wat uiteraard heel erg lang is voor alle resultaten in de database te krijgen. Ook heeft dit het nadeel dat bij een nieuwe inlezing de technieker 1:30 min moet wachter vooraleer hij deze resultaten met het bijhorende software programma “Herstelmanager” kan zien. Het is natuurlijk mogelijk om deze rapporten lokaal in te lezen met de Herstelmanager maar dit zou in feiten enkel moeten indien er geen netwerk beschikbaar is om de rapporten niet naar de server te kunnen verplaatsen. Om dit probleem te voorkomen is er na onderzoek uitgekomen op SqlBulkCopy
Wat is SqlBulkCopy Microsoft SQL Server heeft een populaire Command-line utiliteit geïntegreerd genaamd bcp voor het snel bulk kopiëren van grote bestanden in tabellen of views in SQL server databasen. Er zijn uiteraard andere manieren om data in de tabellen te laden (Insert commando bijvoorbeeld). Maar SqlBulkCopy biedt een serieuze boost in performantie over deze. Gebruik maken van SqlBulkCopy klasse laat je toe: Een enkele bulk copy operatie Meerdere bulk copy operaties Een bulk copy operatie binnen een transactie
Voordelen Snelheid
Nadelen Er kan geen controle gedaan worden op data integriteit tegelijk
61
Overflow trigger event Wanneer de OnCreate() op korte tijd heel veel getriggerd wordt zal deze niet kunnen volgen. Hierdoor worden er file veranderingen niet opgemerkt. Daarom was het belangerijk om de OnCreate() zo snel mogelijk te laten uitvoeren en de data in een queue te plaatsten voor latere verwerking. Zo zouden alle veranderingen steeds geregistreerd moeten worden.
62
OnCreate() filteren De OnCreate event triggerde ook voor sub-folders, daarom is het belangerijk deze correct in te stellen. Het is niet belangerijk om te weten of alle bestanden toegevoegd zijn, wel of het rapport met de bestanden toegevoegd is. Ook is het belangerijk om te zien of er wel data ingelezen in, wat mogelijk via de ServiceLog.txt file. Zo wordt steeds een overzichtelijk log bestand bij gehouden dat steeds raadpleegbaar is. Dit voorkomt ook dat de queue onnodig gevult wordt wat alleen maar de performatie van het server programma ten goede komt.
63
Conclusie Het server programma: Voordelen: Werkt snel Werkt efficiënt Werkt op de achtergrond Start automatisch indien server herstart Controleert steeds alle data naar foutieve rapporten Stabiel
Nadelen: Kan soms niet functionaliseren (netwerk dat weg gevallen is) Log file moet daarvoor regelmatig gecheckt worden Fix: herstarten Als er een rapport verkeerd geplaatst wordt Geen tijd om ongedaan te maken Database terug opnieuw populaten
Het client programma: Voordelen: Werkt snel Simpele interface Multifunctioneel (Herstelmanager + Locanalyser) Gebruiksvriendelijk Kan veel informatie binnenhalen (2.000.000 resultaten in 5-10s) Meerdere type exe (Executable & Service) Filteren gaat snel voor gelimiteerd aantal resultaten Makkelijk uitbreidbaar
Nadelen: Filteren van veel resultaten duurt lang Kan sneller
SQL database: Voordelen: Relationeel Zo light weight mogelijk Makkelijk uitbreidbaar
64
Nadelen: CategorieMMAP is niet gekoppeld Apart bestaande tabel Geen recovery
65
Future Work Algemeen zit het project goed in mekaar. Alleen zouden er bepaalde fundamentele design principes nog moeten veranderen in de toekomst en een groot deel hiervan zit in de structuur.
Hoe het nu is
Op zich is dit niet erg praktisch. Het zou vele logischer zijn om lokaal de rapporten te generen, deze met de Herstelmanager in te laden en zo ineens deze in de SQL database te pushen. Zo voorkom je dat de data op zowel de database en de server opgeslagen worden en dat je moet wachten voor je de resultaten kunt zien (ook al duurt het 10s + tijd van het script, het blijft wachten). Als je dan de datum van de actie meegeeft in de database kan je indien de data onder de verkeerde locomotief opgeslagen wordt deze ook gemakkelijk terug verwijderen ipv. de database terug opnieuw te moeten populaten.
Ook zou het Excel bestand "foutcodelijst" mee geïntegreerd kunnen worden in de SQL database. Dit zou dan aanpasbaar moeten kunnen zijn vanuit de herstelmanager, zo zal steeds iedereen de nieuwste foutcodelijst hebben en zal deze vele zorgvuldiger bijgehouden kunnen worden. Dit zou ook de performantie van de Herstelmanager verbeteren. En zou dit een schaalbaar project kunnen maken naar andere typen locomotieven. Zodat er misschien één Herstelmanager bestaat voor allé typen locomotieven binnen de Tractie Werkplaats Antwerpen. 66
67
Bronnen http://stackoverflow.com/ http://stackoverflow.com/questions/36056771/c-sharp-pushing-to-database-really-slow https://social.msdn.microsoft.com/Forums/vstudio/en-US/home?forum=csharpgeneral https://nl.wikipedia.org/wiki/Hoofdpagina http://zeeshanumardotnet.blogspot.be/2011/06/creating-reports-in-excel-2007-using.html https://nl.wikipedia.org/wiki/ADO.NET https://msdn.microsoft.com/en-us/library http://epplus.codeplex.com/ http://wpftoolkit.codeplex.com/ http://www.codeproject.com/Articles/8477/Using-ADO-NET-for-beginners http://cc.davelozinski.com/c-sharp/the-fastest-way-to-read-and-process-text-files
68