Examensarbete för magisterexamen 160p i datateknik MULTITASKING II UTÖKNING AV ROBOTSPRÅKET RAPID Utfört av: Handledare: Robert Kostelac, rkc99001@student.mdh.se Christer Norström, Mälardalens högskola, Västerås Ingemar Reyer, ABB Robotics AB, Västerås
Datum och ort: Västerås, 00-09-28 1 Sammanfattning ABB Robotics AB tillverkar industrirobotar och är ett av de världsledande företagen inom området. Robotarna programmeras i robotspråket rapid som utvecklats av ABB Robotics. Denna rapport beskriver en undersökning av multitasking i rapid. I rapid finns det möjlighet att skapa task, tilldela dessa task olika prioriteter och en Round-Robin schemaläggare används. Dock saknas det funktionalitet för synkronisering- och kommunikation mellan olika task. Huvuduppgiften med detta examensarbete har varit att implementera instruktioner i rapid för just synkronisering och kommunikation. Denna rapport beskriver ABB Robotics mjukvara och robotspråket rapid utförligt och ett relaterat område; multitasking. Detta teoretiska område ligger som grund för de idéer och tankar som ledde fram till de nya instruktionernas syntax och funktionalitet. De avslutande kapitlen som handlar om implementation och förslag på förbättringar av multitasking-systemet. Robert Kostelac 2
2 Innehållsförteckning 1 Sammanfattning...2 2 Innehållsförteckning...3 3 Inledning...5 4 Relaterat område...6 4.1 Multitasking... 6 4.1.1 Schemaläggning... 6 4.1.2 Synkronisering... 7 4.1.3 Kommunikation... 10 4.1.4 Felsituationer... 11 5 Problembeskrivning...15 6 Robot Systemet...16 6.1 Översikt av systemet... 16 6.1.1 Interaktiva Enheter... 17 6.1.2 Teach pendant... 17 6.1.3 Cabinet... 17 6.1.4 Configuration... 17 6.1.5 Program server... 17 6.1.6 Process... 17 6.1.7 Interpolator... 17 6.1.8 Servo... 17 6.1.10 Segment... 17 6.1.11 Target... 18 6.1.12 EIO... 18 6.1.13 Kommunikation mellan klasserna... 18 6.2 Rapid... 19 6.2.1 Syntax... 20 6.2.2 Strukturen på Rapid... 21 6.2.3 Några vanliga instruktioner... 22 6.2.4 Multitasking i Rapid... 22 6.3 Program Server (PGM)... 24 6.3.1 Interpretator... 25 6.3.2 Kompilator... 26 6.3.3 Länkare... 26 6.3.4 Editor... 26 6.3.5 Viewport... 26 6.4 ReaL... 26 6.4.1 ReaL - Rapid... 27 6.4.2 ReaL-rutin C-funktion parametrar... 27 6.4.3 ReaL rutin C funktion returvärden... 28 6.4.4 Ny Rapid instruktion... 28 6.5 Shared database... 30 6.6 Inter Process Communication... 30 6.6.1 Asynkrona meddelanden... 31 6.6.2 Synkrona meddelanden... 32 6.6.3 Prenumerationer... 32 Robert Kostelac 3
6.7 Klassen PGMRUN... 32 7 Lösning...33 7.1 Nya Rapid Instruktioner... 33 7.1.1 Semaforer... 33 7.1.2 Meddelandeköer... 37 7.2 Implementation... 39 7.3 Komplett multitasking design... 45 7.3.1 Task spawn... 45 7.3.2 Schemaläggare och prioriteter... 48 7.3.3 Meddelandeköer... 49 8 Slutsats...49 9 Litteraturförteckning...50 BILAGOR A. Producent/konsument problemet i Rapid Robert Kostelac 4
3 Inledning Som redan nämnts så tillverkar ABB Robotics AB industrirobotar. Dessa robotar används främst inom bilindustrin. Några av ABB Robotics största kunder är BMW, GM och Opel. I dagsläget är konkurrensen mycket hård och det japanska företaget Fanuk har gått om ABB Robotics och är numera världsledande. Mjukvaran i ABB Robotics system är väldigt kraftfull med vilken det är möjligt att programmera snabba och precisa robotrörelser. Programmeringsspråket rapid är enkelt, flexibelt och samtidigt kraftfullt. Multitasking i rapid är ganska nytt och utvecklades omkring 1992. Troligtvis är ABB Robotics det enda företaget i världen som har implementerat multitasking i sitt robotspråk (mjukvaruavdelningen kunde ej bekräfta detta vid tidpunkten då examensarbetet utfördes). Det är förvånansvärt att inte fler företag implementerat multitasking eftersom det underlättar enormt vid programmeringen av industrirobotar. Det är vanligt att en robot placeras enligt löpande band principen i t.ex. bilfabriker. I ett sådant scenario är det passande att låta ett task (se nästa kapitel för noggrann förklaring av multitasking) övervaka t.ex. sensorer samtidigt som ett annat task utför robotens rörelser. ABB Robotics multitaskingsystem är inte helt färdigutvecklat. Det som återstår att lösa är synkronisering och kommunikation mellan task på ett bra sätt. Självklart finns det redan möjlighet till synkronisering och kommunikation mellan task, men detta kan endast göras genom utbyte av information i globala variabler (så kallade persistents i rapid) och pollning av dessa för att garantera ömsesidig uteslutning. Detta medför att synkronisering och kommunikation i praktiken blir begränsad eftersom programmen ökar enormt i komplexitet. Trots denna begränsning används multitasking av många av ABB Robotics stora kunder, bl.a. GM. GM har ofta flera robotar som samarbetar vid t.ex. montering av bilar. Robotarna måste synkroniseras och rapidprogrammen blir oerhört komplicerade och ibland inträffar till synes obegripliga fel. Dessa rapid program hamnar på ABB Robotics mjukvaruavdelning som får försöka lösa problemen. Detta är tidskrävande och kostsamt för båda parter. Det är därför av yttersta vikt att funktionaliteten i rapid utökas så att synkronisering och kommunikation mellan task klaras av på ett effektivt sätt. Huvuduppgiften med examensarbetet har varit att lösa biten med synkronisering och kommunikation mellan task genom att implementera semaforer och meddelandeköer. Dessutom skulle en helhetssyn för multitaskingsystemet presenteras. Robert Kostelac 5
4 Relaterat område I detta kapitel förklaras grunderna i multitasking. Detta område ligger som grund till kapitel 6 Systemet och kapitel 7 Lösning. Om läsaren är bekant med begrepp som schemaläggning, semaforer, meddelandesändning etc. kan detta kapitel hoppas över. Detta kapitel är tänkt som kompletterande material till multitaskingkursen som ges av ABB Robotics. 4.1 Multitasking I datavärlden kallas förmågan att kunna exekvera mer än ett task (program) samtidigt multitasking. I multitasking är en CPU involverad, men den hoppar mellan program så fort att det uppfattas som att alla program exekverar samtidigt (parallellt). Exempel 4.1 Antag fyra program A, B, C och D. CPU:n exekverar program A under ett kort tidsintervall, därefter exekveras program B ett kort tidsintervall osv. När program D har exekverat klart börjar CPU:n om igen med program A, osv. Se figuren nedan: Program D Program C Program B Program A Fig. 4.1 CPU:n exekverar programmen A, B, C och D under korta tidsintervall CPU:n hoppar mellan dessa program så fort att det uppfattas som om de exekveras parallellt, se figuren nedan: Program D Program C Program B Program A I praktiken innebär multitasking att flera program existerar i minnet samtidigt och exekveras parallellt. Flera nya begrepp introduceras med avseende på parallell programmering jämfört med sekventiell programmering; task (program, process), schemaläggning, synkronisering och kommunikation. Dessutom kan felsituationer som deadlock och svält uppstå. Begreppet task har redan förklarats och nedan följer en förklaring på de övriga. 4.1.1 Schemaläggning Fig. 4.2 Programmen A, B, C och D ser ut att exekvera parallellt. Robert Kostelac 6
På något sätt måste arbetet mellan olika task fördelas, detta görs av schemaläggaren. Målet är att alltid ha ett task (program) som exekverar för att på så sätt utnyttja CPU:n maximalt. Det finns många olika schemaläggningsalgoritmer, den som visades i exempel 4.1 är en Round-Robin (RR) schemaläggare. En RR schemaläggare hoppar mellan task och låter varje task exekvera under ett bestämt tidsintervall. Tidsintervallet (time quantum) är en tidsenhet som tilldelas varje task och ligger normalt mellan 10 och 100 ms. Prestandan på en RR schemaläggare beror huvudsakligen på tidsintervallet. Generellt sett skall tidsintervallet vara stort med avseende på tiden det tar att byta task (context switch). 4.1.2 Synkronisering Exempel 4.2 Antag att en context switch tar 5 ms och att tidsintervallet definieras till 20ms. Det skulle innebära att 20% av all CPU-tid skulle gå förlorad. Exempel 4.3 Antag ett tidsintervall på 500 ms och samma context switch som i exemplet ovan. Den förlorade CPU-tiden skulle minskas till 1%. Nackdelen är att svarstiden skulle bli stor. T.ex. vid 10 task skulle svarstiden bli hela 5 sekunder. Eftersom olika task exekverar helt oberoende av varandra måste det finnas funktionalitet för att koordinera exekveringsordningen mellan dessa task. Ett lämpligt sätt att synkronisera task är att använda sig av semaforer. En semafor tillåter att olika task synkroniserar accesser till gemensamma resurser (en resurs kan vara en global variabel, skrivare etc.), oftast delat minne. En semafor är i stort sett bara en räknare på vilken de två atomära operationerna wait och signal kan göras. wait(sem) tar semaforen sem signal(sem) släpper semaforen sem Begreppet semafor kommer ifrån järnvägar [4], där system utvecklades för att förhindra att flera tåg använde samma järnvägsspår. Ett tåg som vill använda ett järnvägsspår, väntar på att semaforen som indikerar om ett tåg finns på spåret blir ledig. När semaforen blir ledig, kör tåget ut på spåret samtidigt som semaforen sätts till att indikera att spåret är upptaget. Ett annat tåg som närmar sig detta spår blir tvunget att vänta tills semaforen blir ledig. När det första tåget lämnar spåret, återställs semaforen och det andra tåget tar semaforen och kör in på spåret. Det finns två typer av semaforer, binära semaforer (kallas även mutex semaforer) och generella semaforer. binära semaforer kan anta värdet 0 eller 1 generella semaforer kan anta värdet 0 eller ett heltal större än 0. Exempel 4.4 Detta exempel är typiskt för hur binära semaforer används för att synkronisera två task runt en synkroneringspunkt i ett program. I det här fallet kan inte task Robert Kostelac 7
B passera punkt 2 i sitt program innan task A har släppt semaforen s. Först initieras en semafor s som tagen. TASK A TASK B Task B gör Task A gör 2. wait(s) 1. signal(s) och släpper sema- foren. När semaforen blir ledig kan Task B fortsätta exekvera förbi punkt 2. Fig. 4.3 Två task synkroniseras m.h.a. en binär semafor Exempel 4.5 Antag ett diskotek som kan innehålla upp till 200 människor samtidigt. Vid ingången står en vakt och räknar antalet människor som går in och antalet människor som går ut för att på så sätt se till att det aldrig finns mer än 200 personer inne i byggnaden samtidigt. När diskoteket är fullt blir de människor som vill in på diskoteket tvungna att köa för att bli insläppta. En generell semafor kan användas för att simulera ovanstående scenario. Semaforen s initieras till initialvärdet 200. En person som vill gå in på diskoteket gör wait(s) och om det finns plats blir personen insläppt. En person som lämnar diskoteket (t.ex. går ut och tar lite frisk luft) gör signal(s). På så vis garanteras att det aldrig finns mer än 200 personer inne på diskoteket samtidigt. Exempel 4.6 Producent/Konsument problemet är ett klassiskt problem när det gäller synkronisering [14]. Problemet löses naturligtvis enklast med meddelandesändning. I detta exempel visas dock en lösning där semaforer används. Problemet definieras enligt följande: En producent producerar meddelanden som läggs i en buffert En konsument hämtar meddelanden från samma buffert producent konsument Robert Kostelac 8
Fig. 4.4 producent/konsument En korrekt lösning följer dessa kriterier: Konsumenten måste vänta på meddelande när bufferten är tom. Producenten måste vänta på att skicka meddelande om bufferten är full. Producenten och konsumenten kan ej samtidigt manipulera bufferten, dvs. de måste vara ömsesidigt uteslutande. Följande semaforer behövs för att lösa problemet: full en generell semafor som håller reda på antalet meddelanden i bufferten, initieras till 0 empty en generell semafor som håller reda på hur många nya meddelanden får plats i bufferten, t.ex. 100 mutex en binär semafor som kontrollerar att bufferten ej används av både producenten och konsumenten samtidigt, initieras till 1 Pseudokod för lösningen: semafor full(0); semafor empty(100); semafor mutex(1); void producent() { wait(empty); wait(mutex); skicka_meddelande(); signal(mutex); signal(full); } void konsument() { wait(full); wait(mutex); hämta_meddelande(); signal(mutex); signal(empty); } // antal meddelanden i bufferten // antal platser i bufferten // bufferten är ledig // vänta på att det finns plats i bufferten // vänta på att ingen använder bufferten // skicka ett meddelande till bufferten // låt andra använda bufferten // signalera att ett nytt meddelande finns i bufferten // vänta på ett meddelande // vänta på att ingen använder bufferten // hämta ett meddelande från bufferten // låt andra använda bufferten // det finns plats för ännu ett meddelande i bufferten Robert Kostelac 9
Att skydda resurser med semaforer fungerar bara om alla involverade task samarbetar genom att vänta på semaforen när resursen är upptagen och släppa semaforen när resursen ej längre används. Ovanstående exempel kan tyckas komplicerat och det är det också eftersom semaforer ej är en lämplig konstruktion för att lösa producent/konsument problemet. 4.1.3 Kommunikation Normalt används kommunikation mellan task för att övervaka exekveringsordningen eller för att utbyta information mellan task. När en utvecklare designar algoritmer identifieras de beroenden som existerar mellan de olika tasken. Under exekveringen hanteras dessa beroenden genom att tasken kommunicerar med varandra. Det finns flera olika sätt att kommunicera; semaforer förklarades i kapitlet ovan och härnäst följer en beskrivning på meddelandeköer. Meddelandeköer (message queues) fungerar genom utbyte av data i buffertar. Alla task kan använda sig av dessa köer, oavsett om dessa task är relaterade eller ej. TASK 1 meddelanden TASK 2 meddelandekö Fig. 4.5 Task 1 skickar meddelanden till task 2 via en meddelandekö Meddelandeköer är det naturligaste sättet att skicka meddelanden mellan olika task. Två task med en tvåvägskommunikation kräver normalt två köer. För ett task som samlar in information från andra task och sedan bearbetar denna information räcker en kö. Notera att efter att ett task har mottagit ett meddelande från en kö, tas meddelandet bort från kön. Exempel 4.7 Meddelandeköer kan användas till att synkronisera task. I figuren nedan visas hur task 2 väntar på ett meddelande från task 1. När meddelandet mottagits kan exekveringen i task 2 fortsätta. TASK 1 TASK 2 Task 1 skickar ett Task 2 väntar på ett meddelande som 2. meddelande från Task 1, 1. talar om att Task 2 exekveringen fortsätter kan fortsätta exe- när meddelandet kvera mottagits. Robert Kostelac 10
Fig. 4.6 task 2 väntar på meddelande från task 1 innan exekveringen fortsätter 4.1.4 Felsituationer När två eller flera task kan använda en gemensam resurs, som t.ex. en variabel i ett delat minne kan det ibland inträffa att samma resurs används av flera task samtidigt. Detta innebär att felaktiga och oförutsebara resultat kan uppstå. Det enda sättet att lösa sådana fel är att garantera ömsesidig uteslutning till en sådan resurs, dvs. endast tillåta ett task i taget access till resursen. Andra fel som kan uppstå är svält och deadlock. Svält betyder helt enkelt att ett eller flera task inte får tillräckligt med CPU-tid på grund av att andra task ockuperar CPU:n. Deadlock innebär att ett eller flera task väntar på varandra och backlås har uppstått. Exempel 4.8 Exemplet visar ett fall där Task A och Task B båda vill skriva ut på samma skrivare. Task A börjar skriva ut på en skrivare Task B avbryter Task A och skriver ut på skrivaren Task A fortsätter att skriva ut där den slutade, vilket medför blandade utskrifter Det behövs mekanismer för att skydda ett task som är i sitt kritiska avsnitt (avsnitt där globala resurser modifieras) och ett ömsesidigt uteslutande (mutual exclusion) mellan task måste garanteras. Lyckligtvis är semaforen en utmärkt konstruktion som kan användas för att garantera ömsesidig uteslutning. Notera att vid användning av semaforer så måste programmeraren framförallt förhindra att deadlock inträffar. Exempel 4.9 För att undvika blandade utskrifter används en semafor för att garantera ömsesidigt uteslutande: En semafor s för resursen skrivare deklareras. Task A tar semaforen s och börjar skriva ut på skrivaren Robert Kostelac 11
Task B vill skriva ut på samma skrivare men semaforen s är upptagen, tasket väntar Task A skriver klart och släpper se semaforen s Task B får semaforen s och skriver ut I en situation där ett task väntar på resurser som ägs av andra task, som i sin tur väntar på resurser som ägs av det ursprungliga tasket, inträffar deadlock. Kort sagt är deadlock en cirkulär väntan på resurser. Nedanstående figur visar deadlock i trafiken; ett mycket vanligt scenario, speciellt när vi har bråttom. Fig. 4.7 deadlock i trafiken När inträffar deadlock? I normala fall kommer ett task som vill allokera en resurs att göra på följande sätt: 1. begära resursen 2. använda resursen 3. släppa resursen Om resursen är allokerad av ett annat task, kommer tasket att vänta. Om tasket som håller resursen vill ha en resurs som det väntande tasket håller i, kommer det tasket också att vänta. Båda tasken kommer att vänta för evigt. Alltså, deadlock kan inträffa när ett task måste vänta på en resurs. Det går att visa att följande fyra kriterier måste vara uppfyllda för att deadlock skall kunna inträffa: Robert Kostelac 12
1. Varje resurs är antigen allokerad av endast ett task, eller är ledig. 2. Task som håller resurser kan begära nya resurser. 3. Task som håller resurser kan ej kan ej fråntas resursen. 4. Cirkulär väntan. Det måste finnas en kedja av två eller flera task, där alla task väntar på en resurs som är allokerad av ett annat task i kedjan. Robert Kostelac 13
På grund av punkt nummer 4, cirkulär väntan går det att rita en graf och kontrollera om deadlock existerar. väntar på Resurs X Task A ägs av Resurs Y ägs av Task B väntar på Fig. 4.8 deadlock existerar mellan task A och task B Hur kan deadlock hanteras? Ignorera problemet Denna lösning är inte så dum som den först låter. T.ex. om en maskin förväntas stanna en gång i månaden p.g.a. av deadlock och blir stående en timme, så kan problemet vara försumbart. Fast om denna maskin är en del av ett större system i ett kärnkraftverk bör inte denna metod väljas. Ibland kan andra typer av fel vara mycket vanligare, t.ex. hårdvarufel, och då kan deadlock problemet ignoreras. Detektera deadlock och åtgärda I detta fall håller systemet kontroll på de resurser som fördelas och använder informationen till att bygga en deadlock graf. Om deadlock påträffas finns två möjliga åtgärder: 1. Ta en resurs från ett task och ge den till ett annat task 2. Döda ett task som håller en resurs som ett annat task behöver Förhindra deadlock genom att bryta en av de fyra nödvändiga punkterna för att deadlock skall kunna uppstå. En möjlighet att förhindra deadlock är låta task ta alla resurser som behövs på en gång. Det är dock vanligt att task i förväg inte vet vilka resurser som behövs. Dessutom hålls resurserna ofta längre än nödvändigt, så den här lösningen är inte bra. En lösning som fungerar i praktiken är följande: ordna resurserna i en viss ordning, dvs. alla resurser tilldelas ett nummer. När sedan resurserna allokeras tas dom alltid i samma ordning. t.ex. 1. plotter 2. bläckstråleskrivare 3. laser printer Förhindra deadlock genom att noggrant fördela resurser. Robert Kostelac 14
5 Problembeskrivning De problem som skulle analyseras var att presentera en helhetssyn för multitasking passande ABB Robotics robotsystem. En komplett instruktionsuppsättning och dess funktionalitet för multitasking skulle presenteras. Semaforer och meddelandeköer skulle implementeras. Det som finns i systemet idag är möjligheten att skapa upp till 10 task. Prioriteter kan tilldelas dessa task. Dessutom finns en RR schemaläggare. Notera dock att task måste definieras statiskt, dvs. alla task läses ifrån en konfigurationsfil. En diskussion kring dessa områden finns i kapitel 7.3 komplett multitasking design. Det finns möjligheter till kommunikation och synkronisering mellan task. Detta görs genom att polla globala variabler, genom att använda interrupt eller med en dispatcher. Med andra ord, det finns ingen möjlighet för en rapidprogrammerare att på ett naturligt sätt kommunicera mellan task och synkronisera dessa. Därför var det av yttersta vikt att implementera semaforer och meddelandeköer för att förenkla för rapidprogrammeraren, dvs. kunden. Resten av denna rapport beskriver systemet, implementation av semaforer och meddelande-sändning i systemet och slutligen en sammanfattning på multitasking i rapid. Robert Kostelac 15
6 Robot Systemet I detta kapitel förklaras de delar av systemet som är viktigast för att uppgiften skulle kunna lösas. Först beskrivs systemets huvuddelar och hur dessa delar kommunicerar vid t.ex. exekvering. Därefter förklaras robotspråket rapid med en betoning på multitasking delen. För att kunna lösa uppgiften och införa ny funktionalitet som t.ex. semaforer är vissa delar av systemet speciellt intressanta. Dessa är Program Servern (PGM), ReaL, Shared database (sdb), Inter Process Communicaton (IPC) och klassen PGMRUN. Dessa delar beskrivs utförligt i kapitlen 6.3-6.7. Det visade sig att det var lämpligast att implementera så mycket så möjligt i ReaL där den största delen av robotens funktionalitet redan är implementerad. Därför ges ett ganska utförligt exempel på hur en ReaL instruktion skapas i kapitel 6.4 ReaL. 6.1 Översikt av systemet Figuren nedan visar en översiktlig bild av systemet och därefter följer en förklaring av delarna. 1 Servo Interactive Units 1 1,m Cabinet 1,m 1 Ipol 1,m 0,m 0,m Comp Tp Program Segm. Target 1,m 1 Config EIO Process Sensor Euq. Fig. 6.1 Översiktlig bild av klasserna i systemet. Robert Kostelac 16
6.1.1 Interaktiva Enheter Det går att kommunicera med systemet på två olika sätt, antigen med en dator (Comp) eller med en kommunikationsenhet (Teach Pendant, Tp). Utifrån användarens synvinkel består systemet av ett interface (Cabinet) och en konfigurationsfil (Config). 6.1.2 Teach pendant Kommunikationsenheten (Tp) är en fysisk enhet som ger en operatör möjlighet att programmera och kontrollera roboten. T.ex. finns en joystick med vilken roboten kan flyttas manuellt, nödstopp, möjlighet till att skapa nya rapid program etc. 6.1.3 Cabinet Cabinet (CAB) är ett interface mot alla interaktiva enheter kopplade till systemet. Den kontrollerar tillstånd i systemet och fördelar resurser. 6.1.4 Configuration Systemet har en klass som hanterar all konfigurationsinformation. Konfigurationen som specificeras är konsistent och ändras ej under exekvering. 6.1.5 Program server Programservern (PGM) innehåller tjänster och information som behövs för att skapa en interaktiv programmeringsmiljö och exekvera roboten. PGM förklaras i kap 6.3. 6.1.6 Process Denna klass består av ett process skelett som möjliggör installation av nya delar i systemet som t.ex. inkapsling av sensorenheter, processutrustning mm. 6.1.7 Interpolator Interpolatorn beräknar positioner varje nytt tidsintervall och använder metoder i servo för att flytta roboten i små steg mot den slutgiltiga destinationen. 6.1.8 Servo Servo ser till att robotens axlar följer positionen, farten etc. av de dynamiska steg som genererades av interpolatorn. Dessutom kontrollerar servo bromsar på roboten. 6.1.10 Segment Robert Kostelac 17
Segment innehåller all information som behövs för att flytta roboten till nästa position. PGM skapar ett nytt segment, använder 'set-' metoder i segment för att sätta nya värden och skickar informationen till interpolatorn. 6.1.11 Target Denna klass kapslar in information om positioner på en manipulator. Metoder finns för omvandling mellan kartesisk- och axel representation, och vice versa. 6.1.12 EIO Denna klass är ansvarig för kontrollen av analoga och digitala IO signaler. 6.1.13 Kommunikation mellan klasserna Figuren nedan visar den huvudsakliga kommunikationen under exekvering: Interactive Units CAB Ipol PGM Process Fig. 6.2 huvudsaklig kommunikation under exekvering Cabinet (CAB) exekverar program med hjälp av exekveringsmetoder i PGM. Programservern (PGM) använder metoder i Ipol för att utföra robotrörelser. PGM samarbetar också med IO och Process för att skapa och kontrollera processdelar. Vissa processer kommunicerar med Ipol för att t.ex. korrigera robotens rörelser. Huvudsaklig kommunikation under programmering: Robert Kostelac 18
Interactive Units CAB Ipol Config PGM Process. Fig. 6.3 huvudsaklig kommunikation under programmering 6.2 Rapid Interaktiva enheter, t.ex. Tp (kommunikationsenheter) manipulerar innehållet i ett program med hjälp av metoder i Cabinet och via Config. Cabinet använder metoder i Program (PGM) för att utföra den begärda uppgiften. Cabinet kan beordra Ipol att flytta roboten. Rapid är programmeringsspråket som används för att programmera en robot. Ett rapidprogram består av instruktioner och data som kontrollerar en robot och dess utrustning på ett önskvärt sätt. Nedan förklaras robotspråket rapid kortfattat, speciellt betonas multitaskingdelen. Ett normalt rapidprogram är oftast uppbyggt av tre delar: en huvudrutin där exekveringen av huvudprogrammet startar flera subrutiner, som används för att dela upp programmet i mindre beståndsdelar programdata, som används för att definiera positioner, numeriska värden etc. Dessutom innehåller programminnet systemmoduler, se figuren nedan: Robert Kostelac 19
Program minne Program Program data Huvud rutin Sub rutiner System moduler Fig. 6.4 uppbyggnaden av ett Rapid program Systemmoduler är program som alltid finns i minnet. Rutiner och data som har med installationen att göra, som t.ex. verktyg och servicerutiner är installerade som systemmoduler. 6.2.1 Syntax En instruktion definierar en specifik händelse som skall inträffa när instruktionen exekveras; t.ex. flytta roboten. Robert Kostelac 20
En instruktion beskrivs enligt: TPWrite String [\Num] [\Bool] [\Pos] [\Orient] Instruktion Argument Ömsesidigt uteslutande argument Ett valfritt argument omges av [ och ]. Dessa argument behövs ej specificeras. Argument separerade med är ömsesidigt uteslutande. Med detta menas att de ej kan existera i en instruktion samtidigt. Argument som kan repeteras ett godtyckligt antal gånger omges med { och }. Exempel: TPWrite test ; 6.2.2 Strukturen på Rapid Programmet består av ett antal instruktioner som beskriver på vilket sätt roboten skall arbeta. Instruktionerna anropas oftast med ett antal argument som definierar vad som skall hända i en specifik instruktion. Dessa argument specificeras på ett av dessa sätt: som ett numeriskt värde, t.ex. 6 eller 6.6 som en referens till data, t.ex. reg1 som ett uttryck, t.ex. 5 + reg1 * 5 som en funktion, t.ex. Abs(reg1) som en sträng, t.ex. "Produce result" Det finns tre typer av rutiner: En procedur som används som ett subprogram. En funktion som returnerar ett värde av en specifik typ och som används som ett argument till en instruktion. Traprutiner som ger möjlighet att svara på interrupt. En traprutin kan associeras till ett specifikt interrupt. Det finns tre typer av data - konstanter, variabler och persistents: En konstant representerar ett statiskt värde och kan endast tilldelas ett nytt värde manuellt. En variabel kan tilldelas ett nytt värde under exekvering. En persistent kan beskrivas som en variabel som behåller sitt värde. När ett program sparas lagras även det nuvarande värdet på variabeln. När sedan programmet laddas på nytt, får variabeln det värde den hade när den programmet sparades. En persistent är global över task och används vid bl.a. synkronisering av task. Några andra egenskaper i rapid är: Aritmetiska och logiska uttryck Automatisk felhantering Modulära program Robert Kostelac 21
Multitasking Av dessa kommer endast multitasking att förklaras närmare. 6.2.3 Några vanliga instruktioner Eftersom det är av föga vikt att förklara alla detaljer av rapid kommer endast några enkla och vanliga instruktioner förklaras. Exempel på rapid program finns bl.a. i kapitel 6.2.4.1 som handlar om hur synkronisering av task löses i rapid. Rapid har många likheter med procedurella språk som t.ex. C. IF, FOR, WHILE, GOTO etc. är instruktioner som kontrollerar programflödet, och finns i både C och andra programmeringsspråk. Det som skiljer rapid från t.ex. C är att det finns bl.a. instruktioner för att flytta en robot. Nedan följer en lista på några rapid instruktioner: IF WHILE FOR GOTO Stop := WaitTime WaitUntil! MoveC MoveL etc. Exekvera en sekvens av olika instruktioner beroende på om ett test är sant eller inte. Repetera en sekvens av instruktioner så länge ett test är sant. Repetera en sekvens av instruktioner ett visst antal gånger. Hoppa till en 'label'. Stoppa exekveringen. Tilldela ett värde till data. Vänta en tid, eller tills roboten har stannat. Vänta tills ett uttryck blir sant. Kommentar Flytta roboten längs en cirkulär väg. Flytta roboten längs en linjär väg. 6.2.4 Multitasking i Rapid Händelserna i en robotcell är ofta parallella, därför har man infört multitasking. Exekveringen startar vid 'power on' och fortsätter därefter för evigt, om inte ett fel uppstår i programmet. Ett program kan placeras i bakgrunden eller förgrunden av ett annat program. Det kan också placeras på samma nivå som ett annat program (standard). På så vis kan programmen tilldelas prioriteter. Upp till 10 olika tasks kan exekveras samtidigt. Begränsningen beror på att det inte finns resurser i systemet för att hantera fler än 10 task, framför allt är det dåligt med minne. Alla task består av ett antal moduler, på samma sätt som ett normalt program. Variabler och konstanter är lokala i varje task men persistents är globala och kan nås från alla task. Om två persistents har samma namn men dess typ är olika så kommer ett run-time fel att inträffa. En restriktion är att endast huvudtasket kan utföra rörelseinstruktioner. Robert Kostelac 22
6.2.4.1 Synkronisering av task I många applikationer behövs synkronisering av olika task. Eftersom funktionalitet som semaforer inte (ännu) finns görs det antigen genom pollning eller via interrupt. Det enklaste sättet att synkronisera två task är med pollning, med det är också det långsammaste. Persistents används då tillsammans med instruktionerna WaitUntil, IF, WHILE eller GOTO. Exempel 6.1 I detta exempel kommer WaitUntil instruktionen i task 1 att polla varje 100:de ms. När variabeln startsync sätts till sann i task 0 kommer exekveringen i task 1 att fortsätta. TASK 0 TASK 1 MODULE t0 MODULE t1 PERS bool startsync := FALSE; PERS bool startsync := FALSE; PROC main() PROC main() startsync := TRUE; WaitUntil startsync; ENDPROC ENDPROC ENDMODULE ENDMODULE Ett bättre sätt att synkronisera två task är med hjälp av interrupt eftersom det är händelsestyrt. Exempel 6.2 I task 1 deklareras ett interruptnummer isiint1. Det kopplas till traprutinen isi_trap. När task 0 sätter signalen do1 till 1 anropas traprutinen isi_trap i task 1. TASK 0 TASK 1 MODULE t0 MODULE t1 PROC main() VAR intnum isiint1; PROC main() SetDO do1,1; CONNECT isiint1 WITH isi_trap; ISignalDO do1, 1, isint1; ENDPROC WHILE TRUE DO ENDMODULE WaitTime 200; ENDWHILE IDelete isiint1; ENDPROC TRAP isi_trap ENDTRAP Robert Kostelac 23
ENDMODULE 6.2.4.2 Kommunikation mellan task När två task behöver kommunicera görs det med hjälp av persistents variabler. 6.2.4.3 Prioriteter Exempel 6.3 I exemplet visas hur strängen robot skickas från task 0 till task 1. TASK 0 TASK 1 MODULE t0 MODULE t0 PERS bool startsync := FALSE; PERS bool startsync := FALSE; PERS string stringtosend := ""; PERS string stringtosend := ""; PROC main() PROC main() stringtosend := "robot"; WaitUntil startsync; startsync := TRUE; IF stringtosend = "robot" THEN ENDPROC... ENDMODULE ELSE... ENDIF ENDPROC ENDMODULE Normalt exekveras alla task på samma nivå i en round robin. Det är möjligt att ändra prioriteringen på ett task så att det exekveras i bakgrunden av ett annat task. Då kommer bakgrundstasket endast att exekvera när förgrundstasket väntar på någon händelse, eller om exekveringen för det tasket har stannat. Ett program med rörelseinstruktioner är inaktivt nästan hela tiden vilket innebär att eventuella bakgrundstask får gott om CPU-tid. 6.3 Program Server (PGM) PGM utför tjänster åt cabinet (CAB). CAB utför i sin tur tjänster åt en operator eller/och en extern dator. De tjänster PGM utför är av tre typer: kontrollera exekveringen av rapidprogram interaktiv utveckling av rapidprogram utbyte av data, t.ex. läsa/skriva till globala variabler Ett rapidprogram kommunicerar med alla typer av resurser i systemet. Det mesta av rapids funktionalitet är uppbyggt av ett fördefinierat C-bibliotek kallat ReaL. Detta gör det möjligt att Robert Kostelac 24
expandera och förändra funktionaliteten på rapid utan några som helst ändringar i kärnan. PGMs uppgift är att manipulera och exekvera rapid task. En instans av PGM kapslar in exakt ett task. PGM klarar av att ladda, spara, visa, editera och exekvera rapidprogram. Det finns också rutiner för att installera ReaL arkiv och modifiera symboltabellen. PGM tillåter att rapid program skapas interaktivt eller att rapid program laddas från en fil. Ett syntax träd (internal form) används för den interna representationen av ett rapid program. PGMs huvudkomponenter är: kompilator, länkare, interpretator, editor, viewport och en printer. Det finns naturligtvis andra komponenter i PGM, men de tas inte upp i denna rapport. CAB Interpretator Länkare Editor Viewport 1, m Kompilator PGM ReaL Fig. 6.5 visar PGMs huvudkomponenter 6.3.1 Interpretator Interpretatorns uppgift är att exekvera rapid program. Det centrala i interpretatorn är två stackar, en exekverings stack och en run-time stack. Exekveringsstacken har en kontroll på programräknaren, så att det från innehållet på exekveringsstacken går att ta reda på var i koden den nuvarande exekveringen är, och hur (via rutinanrop) den kom dit. Runtimestacken har motsvarande kännedom av dynamiska värden associerade med exekveringsstacken. När interpretatorn anropas kommer den först att kontrollera om det finns något interrupt som måste köras innan nästa instruktion kan börja exekveras. Efter detta kommer interpretatorn att hämta det översta elementet på exekveringsstacken och bearbeta detta element. Vad som händer därefter beror naturligtvis på elementet som hämtades, men kortfattat kan sägas att interpretatorn kommer att hämta och lägga till värden på exekverings-stacken och runtimestacken för att exekvera rapidprogram. Meningen med dessa aktioner är att ta interpretatorn igenom ett basic step. Ett basic step kan ses som den minsta exekveringen från ett väldefinierat tillstånd till nästa väldefinierade tillstånd. Normalt motsvarar ett basic step en rapid instruktion. I vissa fall är ett basic step otydlig t.ex. när en ReaL instruktion (ReaL instruktioner förklaras i kapitel 6.4) väntar på en händelse. När interpretatorn befinner sig i ett väldefinierat tillstånd kan den släppa kontrollen på exekveringen. Robert Kostelac 25
6.3.2 Kompilator Kompilatorn läser rapid källkod och genererar den interna formen (syntax trädet) av rapid. Dessutom genereras symboltabeller och referenser. När kompilatorn läser koden analyserar den rapid moduler, tasks, alla filhuvuden, kodrader och den lexikala korrektheten på identifierare och kommentarer kontrolleras. Scannern och parsern är genererade av ToolMaker-verktygen ScannerMaker och ParserMaker. Kompilatorn bara anropar scannern och parsern för att få jobbet utfört. Grammatiken är på formen EBNF (Extended Backus-Naur Form). 6.3.3 Länkare Länkaren utför alla semantiska kontroller på rapid task. Den fyller i data i symboltabellen och i den interna formen. Den allokerar minne för variabler, skapar persistents i sdb och initierar konstanter och modulvariabler. Kortfattat kan sägas att länkaren utför alla kontroller som ej görs av kompilatorn eller editorn. Länkningen utförs i ett antal link passes. Först genomförs alla kontroller på tasknivå. Sedan följer länkning av alla modul datadeklarationer, alla rutin deklarationer tillsammans med alla parameter- och rutin datadeklarationer, och sist alla instruktioner i alla rutiner. 6.3.4 Editor Editorn hanterar current object, vilket är det objektet som operatören arbetar med. Här finns alla editeringsfunktioner som är relaterade till rapidkod. Editorn stödjer 'klipp ut', 'kopiera' och 'klistra in' operationer av rapidkod. T.ex. när ett kodavsnitt av ett rapid program klipps ut uppdateras symboler i symboltabellen och syntaxträdet struktureras om. Dessutom informerar editorn viewport-klassen med positionen av current object. 6.3.5 Viewport Viewport hanterar den del av koden som är synlig för användaren. Koden visas på formen pretty print (detta innebär bl.a. automatisk indentering av rapid kod). Klassen används också för att spara rapid moduler till en fil. 6.4 ReaL Den största delen av rapids funktionalitet är definierad i ReaL. ReaL i rapid motsvarar ett C- bibliotek i programspråket C. Ett gränssnitt kallat REAL (stora bokstäver) innehåller ett antal rutiner som tillåter att ReaL-instruktioner kommunicerar med rapid interpretatorn och andra delar av PGM. När det blev uppenbart att det inte skulle gå att utöka PGM med nya instruktioner (varför det inte går förklaras i kapitel 7) undersöktes möjligheterna till att utnyttja ReaL. Robert Kostelac 26
Notera att detta kapitel handlar om hur helt nya ReaL instruktioner skapas, dvs. sett ifrån realprogrammerarens synvinkel. ReaL rutiner kan göra följande: prenumerera på händelser från manipulatorn, IO eller andra objekt. vänta på en viss händelse (event) avallokera händelser, och returnera allokerade resurser hämta rapid argument och hämta/spara funktioners returvärden spara data mellan en wait och nästa aktivering rapportera runtimefel till interpretatorn använda normala C primitiver 6.4.1 ReaL - Rapid Alla objekt, datatyper och rutiner i rapid har en egen definition i ReaL. En tabell innehållande pekare till sådana definitioner skickas till PGM för att installeras vid uppstarten av systemet. I och med att en ReaL instruktion blir installerad blir den också känd för alla delar av PGM och därmed resten av systemet. En komplett definition av en ReaL-rutin ser ut på följande sätt: 1. en lista där all parametrar till funktionen definieras 2. definition av rutinen 6.4.2 ReaL-rutin C-funktion parametrar Det finns fem parametrar till C-funktionen som användas i en ReaL rutin. Dessa parametrar ger ReaL programmeraren möjlighet att t.ex. skilja på olika task. void *origin Detta är en identifierare på program servern (PGM). Tidigare förklarades att upp till tio instanser av PGM kan skapas. Detta argument ger ReaL programmeraren en möjlighet att skilja dessa instanser åt. REAL_MODE mode Detta är exekveringsriktningen på detta anrop för ReaL rutinen. T.ex. kan rapid program exekveras baklänges vilket är lämpligt i ett robotspråk. Realprogrammeraren måste ta hänsyn till alla olika exekverings-riktningar. Nedan följer de exekveringsriktningar som finns: REAL_FORWARD Normal exekvering (med riktig robot). REAL_SIMULATE REAL_FORWARD_INSTR REAL_BACK REAL_UNDO Simulera instruktionen (används vid utveckling). Exekvera ett steg i taget. Normalt samma som REAL_FORWARD utom för instruktioner som har med rörelser att göra. Exekvera baklänges. Avsluta rutinen. Detta kan endast inträffa när ReaLrutinen tidigare returnerat REAL_WAIT och väntar på någon händelse. Robert Kostelac 27
void *save Denna parameter pekar på en reserverad minnesarea som inte rörs av PGM mellan två anrop till ReaL rutinen, där det första anropet returnerade REAL_WAIT. IPC_MSG *event Pekare till ett inkommande IPC beordrat av ett tidigare anrop till ReaL rutinen. short *iptnum När en ReaL instruktion tidigare returnerat REAL_WAIT returneras det interrupt nummer instruktionen väntar på i denna parameter. 6.4.3 ReaL rutin C funktion returvärden En ReaL funktion returnerar till rapid interpretatorn med C-funktionen return. Den kan returnera en av följande: REAL_READY ReaL rutinen är klar. REAL_RECALC Omevaluera rapid argument och anropa igen. REAL_RECALL Avsluta basic step. Sedan omevaluera argumenten och anropa igen. REAL_WAIT Vänta på en händelse. REAL_STOP Stoppa exekveringen. REAL_EXIT Avsluta rapid programmet. REAL_BREAK Stoppa exekveringen. REAL_REFUSE ReaL rutinen vägrar exekvera. T.ex. kan ej exekvera baklänges. 6.4.4 Ny Rapid instruktion I detta kapitel visas ett exempel på hur en helt ny rapid instruktion example definieras. ReaL instruktionerna programmeras i ANSI-C. /* Rapid instruktionen definieras */ PRIVATE REAL_CFUN example_cfun; /* Parametrar till instruktionen definieras. Denna instruktion kan ta fyra olika parametrar, * varav de två sista ej kan existera samtidigt. Varje parameter definieras genom att fylla i sju * fält i strukturen REAL_PARDEF. De olika fälten är: * 1. valfri/nödvändig parameter * 2. typ av parameteröverföring (in, variabel, persistent...) * 3. parameter typ * 4. storleken på arrayen (eller REAL_SYMPARTYP_NOTCARR om ej array) * 5. parameter namn * 6. alternativ parameter (TRUE om föregående och denna parameter är ömsesidigt * uteslutande) * 7. namnet på typen * * En fil med förlängning.eng som innehåller den exakta syntaxen på typer, instruktioner * osv. såsom det skrivs i rapid måste också definieras. * Denna fil är en trivial textfil och skulle kunna se ut så här: Robert Kostelac 28
* * 1: * num * 2: * bool * 3: *. * osv. */ PRIVATE REAL_PARDEF example_par[] = { {REAL_SYMPARTYP_REQ, REAL_SYMPARMOD_IN, 1 /* num */, REAL_SYMDATDIM_NOTCARR, 14 /* n */, FALSE, "n"}, {REAL_SYMPARTYP_OPT, REAL_SYMPARMOD_IN, 2 /* "bool" */, REAL_SYMDATDIM_NOTCARR, 15 /* b */, FALSE, "b"}, {REAL_SYMPARTYP_OPT, REAL_SYMPARMOD_IN, 3 /* switch */, REAL_SYMDATDIM_NOTCARR, 17 /* s1 */, FALSE, "s1"}, {REAL_SYMPARTYP_OPT, REAL_SYMPARMOD_IN, 3 /* switch */, REAL_SYMDATDIM_NOTCARR, 18 /* s2 */, TRUE, "s2"} }; /* Rutinen example definieras. * I.eng filen återfinns "Example" som nummer 19. */ PRIVATE REAL_RTNDEF example_rtndef = { -1, /* ej funktion */ 19, /* Example */ example_cfun, example_par, sizeof(example_par)/sizeof(example_par[0]), REAL_SYMKEY_IO, /* Klassificering på Tp */ "example" /* syntax i.eng filen */ }; /* Det publika handtaget till alla objekt som skall installeras. */ PUBLIC REAL_SYMINSDEF example_syminsdef[] = { {REAL_SYMDEF_VERSION, REAL_VERSION}, /* Version */ {REAL_SYMDEF_PRC, &example_rtndef}, /* Rutinen */ {REAL_SYMDEF_END, NULL} /* Slutet på listan */ }; /* example_cfun - Example ReaL rutin * Rapid syntax: * PROC Example( Robert Kostelac 29
* num n * \bool b, * \switch s1 * switch s2) * * I rapid kan ett anrop till rutinen example se ut enligt: * Example 3 \s2; */ PRIVATE REAL_CFUNDEF(example_cfun) { REAL_ARLNUM *np; REAL_ARLBOOL *bp; REAL_ARLSWITCH *s1p, *s2p; /* Hämta argumenten */ np = (REAL_ARLNUM *) real_getarg(origin,1); bp = (REAL_ARLBOOL *) real_getarg(origin,2); s1p = (REAL_ARLSWITCH *) real_getarg(origin,4); s2p = (REAL_ARLSWITCH *) real_getarg(origin,5); } /* Gör något när s2p är specificerad */ if (s2p) {... return REAL_READY; 6.5 Shared database Shared database (sdb) hanterar variabler skapade i rapid som är synliga för alla andra objekt i systemet. Dessa variabler är globala, dvs. synliga för alla task. T.ex. persistents lagras i sdb. En variabel läggs in i sdb med metoden variable_create. Då allokeras minne för variabeln, och dess data initieras. Variabler lagrade i databasen kan accessas av all som känner till databasens id och namnet på variabeln. Följande sekvens används för att accessa en variabel: Hämta databasens id. Hämta en variabel descriptor. Accessa data med hjälp av variable read, write eller erase metoder. Det är också möjligt att prenumerera på ändringar av variabler lagrade i databasen. När det inte längre finns några användare av en variabel i databasen, tas den bort. 6.6 Inter Process Communication Inter process communication (IPC) används för att kommunicera mellan- och synkronisera asynkrona processer i systemet. Det finns tre olika typer av kommunikation; asynkron, synkron eller Robert Kostelac 30
broadcast. En process kan också begära att prenumerera på ett specifikt meddelande i en annan process. 6.6.1 Asynkrona meddelanden Den vanligaste kommunikationen är när en process önskar lämna ett meddelande till en annan process och inte behöver vänta på svar. För att åstadkomma detta gör den sändande processen ett ipc_send, medan mottagaren gör ett ipc_receive. Robert Kostelac 31
6.6.2 Synkrona meddelanden Om en process måste vänta på ett svar kan man använda ipc_sendwait. Den andra processen tar emot meddelandet med ipc_receive och skickar ett nytt meddelande tillbaka med ipc_answer. 6.6.3 Prenumerationer Detta är ett sätt att distribuera ett meddelande till de processer som prenumererar på ett meddelande. Processen som vill distribuera ett meddelande gör ett ipc_distribute, och sedan tar IPC hand om meddelandet och distribuerar det till de processer som vill ha det. För att meddela IPC att en process vill ha en distribution från en annan process använder den sig av ipc_subscribe, sedan ipc_receive för att hämta meddelandet. 6.7 Klassen PGMRUN PGMRUN är den del av PGM som inte fanns med från början utan den utvecklades i samband med att rapid utökades med multitasking. PGMRUN ansvarar för att PGM exekverar rapid program. Denna klass startar flera instanser av PGM, en instans för varje task. PGMRUN PGM Fig. 6.6 PGMRUN skapar flera instanser av PGM. PGMRUN utför tre uppgifter när det gäller multitasking: Skapar flera instanser av PGM, där en instans av PGM arbetar med ett rapid program. Det medför att en instans av PGM kapslar in ett task. Tillåter att prioriteter tilldelas dessa task. En Round-Robin schemaläggare används och varje task exekverar ett basic step per varv. Robert Kostelac 32
7 Lösning Möjlighet till multitasking i rapid finns redan. T.ex. finns möjlighet att skapa flera task och tilldela prioriteter till dessa. Det finns också en enkel schemaläggare. Det som saknas är funktionalitet för att hantera synkronisering och kommunikation mellan olika rapid task. Grundidén har varit att de rapid instruktioner som implementeras skall vara enkla för en rapid- programmerare att använda. Samtidigt skall instruktionerna vara kraftfulla, så att en erfaren rapidprogrammerare skall kunna utföra de uppgifter som ställs på ett multitaskingsystem. 7.1 Nya Rapid Instruktioner I detta kapitel visas hur synkronisering och kommunikation mellan rapid task ser ut sett ifrån rapidprogrammerarens synvinkel. Nedan följer de instruktioner som implementerades och dess syntax i rapid. SemInit Sem [\Take] SemTake (Sem [\Timeout]) SemRelease Sem MsgInit MsgQ [\MaxMsgs] MsgQSend (MsgQ Buff [\Timeout]) MsgQReceive (MsgQ Buff [\Timeout]) Initierar en semafor Tar en semafor Släpper en semafor Initierar en meddelandekö Skickar ett meddelande till en kö Tar emot ett meddelande från en kö Dessutom implementerades två nya typer; sem och msgq. 7.1.1 Semaforer Semaforerna implementerades som binära semaforer. Det finns ingen anledning att ha generella semaforer i rapid eftersom det sällan (eller aldrig) uppstår problem som kan lösas genom att tillåta flera task åtkomst till samma resurs samtidigt. Oftast används semaforerna till att skydda persistents, eller till att synkronisera olika task, och då räcker det med binära semaforer. Rapidprogrammeraren måste deklarera och initiera en semafor i alla task som skall använda semaforen. Dessutom bör semaforen intieras till samma värde i alla task (detta är dock inte nödvändigt). Vid den första påträffade initieringen kommer semaforen initieras till ett värde och alla därefter påföljande initieringar kommer att ignoreras. Om programmeraren av någon anledning har olika initieringsvärden på en semafor, kan denne ej med säkerhet veta vilket initieringsvärde semaforen får. Robert Kostelac 33
Exempel 7.1 Exempel på hur en semafor mysem deklareras och initieras som ledig (standardvärdet) i rapid. VAR sem mysem; SemInit mysem; Exempel 7.2 I detta exempel initieras semaforen mysem som tagen. VAR sem mysem; SemInit mysem \Take; Efter att en semafor deklarerats och initierats används den av samarbetande task med hjälp av funktionen SemTake och instruktionen SemRelease. Exempel 7.3 Ett task som vill använda en global resurs gör på följande sätt:... status := SemTake (mysem);! Använd global resurs...... SemRelease mysem;! Ta semaforen som skyddar global resurs.! Släpp semaforen. SemTake är en funktion som returnerar TRUE om det gick bra att ta en semafor, FALSE annars. Orsaken till att SemTake är en funktion är att det går att skicka med en timeout, som är maximala tid ett task väntar på en semafor. Om timeout ej är specificerad motsvarar detta WAIT_FOREVER, dvs. vänta för evigt. Exempel 7.4 Detta exempel visar hur ett task försöker ta semaforen mysem med en timeout på fem sekunder. IF SemTake (mysem \Timeout := 5) THEN! Fick semaforen inom 5 sekunder. Använd global resurs.... ELSE! Fick ej semaforen inom 5 sekunder, timeout! Gör någonting annat.... ENDIF Robert Kostelac 34
I kapitel 6.2.4.1 Synkronisering av task, exempel 6.1, finns exempel på hur två task synkroniseras med hjälp av pollning och i samma kapitel visas i exempel 6.2 synkronisering med interrupt. Följande exempel visar hur samma synkronisering kan se ut med semaforer. Exempel 7.5 Task 0 och task 1 synkroniseras m.h.a. semaforen mysem. Task 1 försöker med instruktionen SemTake att ta semaforen mysem men blir tvungen att vänta eftersom semaforen är upptagen. När task 0 släpper semaforen med SemRelease tar task 1 semaforen och exekveringen i det tasket fortsätter. TASK 0 TASK 1 MODULE t0 MODULE t1 PROC main() PROC main() VAR sem mysem; VAR sem mysem; SemInit mysem \Take; VAR bool status; SemInit mysem \Take; SemRelease mysem; status := SemTake (mysem);... ENDPROC ENDPROC ENDMODULE ENDMODULE Exempel 7.6 I kapitel 6.2.4.2 Kommunikation mellan task, exempel 6.3 visades hur två task kommunicerar m.h.a. persistents och pollning. Här visas hur det ser ut när semaforer används istället för pollning. TASK 0 TASK 1 MODULE t0 MODULE t1 PERS string stringtosend := ""; PERS string stringtosend := ""; PROC main() PROC main() VAR sem mysem; VAR sem mysem; VAR bool status; SemInit mysem \Take; SemInit mysem \Take; stringtosend := "really"; SemRelease mysem; ENDPROC ENDMODULE status := SemTake (mysem); IF stringtosend = "really" THEN ELSE ENDIF ENDPROC ENDMODULE Robert Kostelac 35