• No results found

iPad app

In document De iPad als energiemonitor (pagina 44-49)

6 CONSTRUCTION FASE

6.7 iPad app

Toen de relatief eenvoudige applicaties af waren, de server en simulaties, kon ik beginnen aan de iPad app.

De app moest gegevens ontvangen van de simulaties, via de server. Ik heb de iPad echter zich eerst als simulatie laten gedragen. Dit betekent dus dat de iPad geen gegevens ontving van de andere simulaties, maar gegevens verzond, zoals de andere simulaties. De server gaf in dit geval de gegevens weer, iets wat de iPad uiteindelijk zelf moest gaan doen.

Zoals gemeld in paragraaf 6.5 heb ik dit gedaan omdat het makkelijker was om de server de gegevens te laten zien die werden verzonden door de iPad. In Visual Studio wist ik namelijk nog steeds makkelijker alle functies te vinden dan in XCode, bijvoorbeeld het debuggen.

Ook de iPad app maakt gebruik van sockets om verbinding te maken met de server. Het

aanmaken van een socket in Objective-C ging weer net iets anders dan in C#. Op momenten als deze viel het op hoe lastig de syntax van Objective-C was. Waar een socket in C# wordt

aangemaakt als in het voorbeeld in paragraaf 6.1, moest internet er aan te pas komen om uiteindelijk daar te vinden dat het in Objective-C, in mijn specifieke app, op de volgende manier moest:

Voorbeeld aanmaken socket Objective-C

Zoals duidelijk te zien is, is dit een heel andere syntax dan in C#. De debugger van XCode werkte ook niet mee. Ik was gewend aan de debug mogelijkheden van Visual Studio, maar dit is iets waar XCode nog in moet groeien. Om te beginnen zijn de foutmeldingen die XCode geeft (de zogenaamde “errors”) minder uitgebreid dan die van Visual Studio. Als er een error optrad gaf XCode hoogstens één kleine zin waar verder niet meer op te klikken viel voor verdere uitleg. Het opzoeken van de foutmelding op internet was dan het enige dat ik kon doen voor meer informatie, in de hoop dat andere gebruikers van XCode deze foutmelding ook tegen waren gekomen en dit op internet gedeeld hadden. Dit is iets dat in Visual Studio veel uitgebreider was. Iedere foutmelding in Visual Studio werd duidelijk omschreven en kon aangeklikt worden voor verdere uitleg.

Om te debuggen als de applicatie “gebuild” was en opgestart was (dus er waren geen errors) was het alleen mogelijk om informatie op te vragen via een NSLog functie. Deze informatie werd weergegeven in de console (de output) van XCode. Het was ook mogelijk om breakpoints te gebruiken en zo in te zien welke gegevens de app verzond en ontving, echter gaf de

debugger bij ieder breakpoint de hexadecimale geheugenadressen van de data. Dit kwam omdat de data via WiFi van een andere applicatie kwam. Dit maakte het onmogelijk om te debuggen met behulp van breakpoints.

Wat het ook niet eenvoudig maakte was het feit dat via de simulatie die XCode biedt (een simulatie van een iPad) het niet mogelijk was om gebruik te maken van WiFi. Dit wist ik, hier is

socket = [Socket socket]; @try {

[socket connectToHostName:host port:port]; }

@catch (NSException * exception) { socket = nil;

NSLog(@"uitzondering: %@", exception); }

immers de fysieke iPad voor aangeschaft, maar tijdens het debuggen werd dit een vervelend gebrek. Iedere keer als de app opgestart moest worden, moest deze geïnstalleerd worden op de iPad.

Een voordeel hiervan was dat ik wel gelijk een goed beeld kreeg van hoe de app werkte op het platform waarvoor hij was bedoeld, maar voor het debuggen was het vervelend dat de app iedere keer op de iPad geïnstalleerd moest worden. Dit duurde eenvoudigweg te lang. Doordat de syntax van Objective-C nog steeds relatief nieuw voor mij was, sloop er op sommige

momenten een fout in de code, waar ik pas achter kwam tijdens het debuggen. Gevolg: De app moest afgesloten worden, aangepast worden, opnieuw gebuild worden, op de iPad geïnstalleerd worden en opnieuw uitgevoerd worden. Een vervelende manier van debuggen, zeker na een tiental pogingen, wat via een iPad simulatie mijns inziens veel sneller had kunnen gaan, als daar WiFi ondersteuning in had gezeten.

Wat XCode wel biedt is een Interface Builder, waarmee relatief eenvoudig een GUI opgezet kan worden. Maar ook dit ging weer anders dan in Visual Studio. Het maken van een GUI opzet was wel makkelijk. De Interface Builder liet een leeg iPad scherm zien waar buttons, menu’s en labels in konden worden gesleept. Maar om bijvoorbeeld de achterliggende code van een button te maken moest de button eerst verbonden worden met de code door hem daar letterlijk naar toe te slepen. Iets wat mijns inziens makkelijker gaat met Visual Studio, waar een dubbelklik op een button genoeg is om de achterliggende code op te zetten.

Het grootste probleem moest echter nog komen. Ik wist dat Objective-C altijd event based was. Dat wil zeggen dat op het moment dat data ontvangen wordt, er een event plaats moet vinden om deze te verwerken (zoals het indrukken van een button). Events worden afgehandeld door de event handler van het iOS framework.

De GUI van mijn app moest regelmatig geupdate worden, omdat de simulaties hun

energieverbruik om de vijf seconden doorstuurden naar de iPad. Dit moest de iPad app laten zien in zijn GUI. Ook als de iPad een simulatie zijn status stuurde (d.w.z. de iPad geeft de simulatie opdracht zijn status te wijzigen van aan naar uit of vice versa), dan moest de iPad de status van deze simulatie ook updaten in de GUI. De gebruiker moest immers zien dat de simulatie aan of uit ging.

Deze gegevens werden alleen geüpdate als er een event plaats vond. Ik heb de event handler van het iOS framework echter nooit kunnen vinden. Ook niet met behulp van de documentatie die Apple aan zijn developers biedt en ook niet na het zoeken op internet naar ervaringen van XCode programmeurs. Hier heb ik dus niet op kunnen reageren. Ik moest dus een manier verzinnen om de gegevens te updaten.

Omdat het klikken op een button ook gezien wordt als een event besloot ik twee knoppen (buttons) toe te voegen aan mijn GUI, een connect- en een receive button. Via de Interface Builder, die standaard wordt geleverd bij XCode en waar ook de GUI voor de app in wordt gemaakt, kon ik per button een aantal events kiezen. In de code kon ik dan weer aangeven wat er moet gebeuren op het moment dat deze event werd aangeroepen. Op deze events kon ik dus wel reageren.

De events die echter gebeuren bij bijvoorbeeld het starten van de app heb ik nooit kunnen vinden. Deze events waren nodig om ervoor te zorgen dat de app verbinding ging maken met de server. Ik wilde dat dit automatisch gebeurde zodra de app opstartte, maar moest dit dus koppelen aan een connect button, waar ik zelf kon aangeven wat er moest gebeuren zodra deze knop werd ingedrukt.

Ook de events die ervoor zorgen dat de app continue geüpdate werd, wat nodig was om het meest recente verbruik van een simulatie te laten zien, heb ik nooit kunnen vinden. Dit heeft er toe geleidt dat ik een receive knop moest aanbrengen. Deze knop zorgde ervoor dat zodra hij werd ingedrukt, het meest recente verbruik van de simulaties werd opgehaald en weergegeven. De verbinding met de server wordt dus pas gelegd zodra er op de connect button wordt geklikt in de app. Nadat die verbinding was gelegd heb ik het message systeem opgezet, waarna het probleem naar voren kwam met betrekking tot het ASCII encoderen, zoals beschreven in paragraaf 6.5.

Al deze problemen hebben ervoor gezorgd dat de app veel meer tijd in beslag heeft genomen dan dat ik in de planning had opgenomen. Mijn gehele week die ik had opgenomen als “onvoorzien” in de planning werd opgeslokt door de moeilijkheden die Objective-C en XCode met zich meebrachten. In overleg met mijn begeleider werd besloten om niet alle requirements in de app te verwerken, alleen de “Must have requirements” werden toegevoegd voor oplevering richting school. De andere functionaliteiten mochten ontwikkeld worden na het afronden van de afstudeeropdracht.

Dit leidde ertoe dat ik me kon concentreren op de verbinding van de app met de simulaties en de server, het message systeem waarmee dit moest worden gerealiseerd, het weergeven van de data die de simulaties doorgaven aan de app en het commando versturen om een cliënt aan of uit te zetten.

Op het moment dat dus de connect knop wordt ingedrukt, wordt de socket verbinding gemaakt. Nadat het encoding probleem was opgelost zoals beschreven in paragraaf 6.5 heb ik de iPad niet langer meer laten fungeren als simulatie. Vanaf dit punt wilde ik het mogelijk maken om de iPad gegevens te laten ontvangen die afkomstig waren vanaf de simulaties. Als dit bereikt was dan waren alle “Must have requirements” aanwezig.

Dit was gelukkig redelijk eenvoudig te realiseren, mede omdat ik er al voor had gezorgd dat de iPad messages kon versturen. Er moest een decoding functie komen om de data die binnen kwam om te zetten in leesbare data. Hierbij heb ik rekening gehouden met het feit dat de data die binnen kwam in UTF8 geencodeerd was:

Het voorbeeld van de decoding functie uit paragraaf 6.3 is een decoding functie gemaakt in C#. Hierboven is het equivalent van de decoding functie in Objective-C.

Om alle gegevens weer te geven heb ik met de interface builder labels toegevoegd waarin de iPad het verbruik van een simulatie kan weergeven.

- (void) Decode : (NSMutableData*) data { NSLog(@"id BEFORE is: %d", m_ID); char* chars = [data mutableBytes];

NSString* receivedDataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

NSLog(@"string INSIDE is: %@", receivedDataString); switch (chars[3] - 48) { case 0: m_type = 0; break; case 1: m_type = 1; break; case 2: m_type = 2; break; default:

NSLog(@"unknown messagetype: %d", chars[3]); break;

Omdat de event handler van het iOS framework nooit is gevonden moest er een ander event aan te pas komen om ervoor te zorgen dat het verbruik werd weergegeven en geupdate. Dit werd de receive knop omdat ik aan deze knop een event kon toewijzen waar de app op zou reageren. Deze button zorgde ervoor dat de meest recente message werd opgehaald en gedecodeerd. Nadat de message was gedecodeerd werd de data uit de message gehaald en weergegeven in de Usage labels.

Om de simulaties vanaf de iPad aan en uit te kunnen zetten heb ik per device een switch button toegevoegd. Deze switch button zorgde ervoor dat er een status message werd verzonden naar de simulatie:

Voorbeeld status message verzenden naar simulatie

Nu er eenmaal messages binnen kwamen en deze werden getoond op de iPad ontdekte ik nog een probleem. Als er op de receive knop werd gedrukt dan werd de data opgehaald van de server. Omdat de simulatie echter om de vijf seconden een usage message verzond werd deze opgeslagen in een buffer. Zodra er op de receive knop werd gedrukt werd de gehele buffer opgehaald en weergegeven in de usage labels op de iPad. Met andere woorden, de iPad wist niet waar de ene message ophield en de andere begon en gaf dus de gehele buffer weer in de usage labels. Om ervoor te zorgen dat de iPad wel ging weten waar een message ophield en de nieuwe message begon heb ik een pipe teken aan het einde van de messages toegevoegd. De iPad ziet dit als het einde van de message en decodeerd alleen het stuk voor dit teken en leest daardoor dus de goede data uit en geeft deze weer in de usage labels.

Voorbeeld uitlezen message tot laatste pipeteken

Nadat ook dit probleem was opgelost had ik een werkende app. Deze app bevatte alle

functionaliteiten die zijn opgesteld aan de hand van de requirements die gelabeld waren als een “Must have” requirements. In overleg met mijn begeleider is besloten om na dit afstudeertraject verder te werken aan de app om ook de andere functionaliteiten hierin te verwerken.

Het maken van al de aanwezige functionaliteiten koste namelijk veel tijd. Dit was volledig te wijten aan het feit dat de syntax van Objective-C erg tegenviel en ook XCode nog steeds een nieuwe IDE voor mij was. Ik heb er echter voor gekozen om niet te stoppen met het

ontwikkelen van de functionaliteiten en de tijd die ik normaal gesproken in de transition fase zou gebruiken heb ik gebruikt om de app af te maken. Dit leidde ertoe dat er geen mogelijkheid meer was om een Mastertest plan volgens TMap op te stellen, maar wel een acceptatietest en een test aan de hand van de requirements. Op deze manier zou ik toch een functionele app hebben en deze ook kunnen testen.

NSMutableString* string = [NSMutableString stringWithString:@"!"]; [string appendString:[device1 getStatusText]];

[message Create:1 :2 :string];

[socket writeString:[message Encode]];

NSRange stop;

stop = [[receivedDataString substringFromIndex:5] rangeOfString:@"|"];

NSLog(@"Range: %@", NSStringFromRange(stop));

m_data = [receivedDataString substringWithRange:NSMakeRange(5, stop.location)];

In document De iPad als energiemonitor (pagina 44-49)