kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




DAY BY DAY zu Entwicklung fehlertoleranter Software für eingebettete Echtzeitsysteme WS 2023/24

(EN google-translate)

(PL google-translate)

Übersicht

  • Die vorliegende Seite stellt den Einstiegspunkt für diese Lehrveranstaltung dar und verzeichnet chronologisch die behandelten Inhalte.
  • Aber ein großer Teil der Inhalte findet sich nicht direkt hier, sondern die Seite hier verlinkt auf andere Bereiche von kramann.info.

Allgemeines

Die Lehrveranstaltung "Entwicklung fehlertoleranter Software für eingebettete Echtzeitsysteme" enthält sowohl theoretische, praktische, seminaristische und Projekt-bezogene Anteile. Sie folgt zudem einem bestimmten Arbeitszyklus, in dem diese Anteile miteinander verzahnt sind:

  1. Zu Beginn werden die Pattern für fehlertoleranten Software-Entwurf theoretisch vorgestellt.
  2. Praktisch angewendet werden sollen diese Pattern dieses Jahr auf ein autonomes Vehikel (esp32AV), das im Kurs in Zweiergruppen eingeführt, aufgebaut und getestet wird.
  3. Dann sind Sie in Zweiergruppen gefordert, sich eine mögliche praktische Umsetzung eines von Ihnen ausgewählten Pattern für eine konkrete Anwendung vorzustellen. Dies ist der seminaristische Teil.
  4. Schließlich sind die vorgestellten Pattern von den jeweiligen Zweiergruppen als Projektarbeit mit Java/Processing umzusetzen und als Prüfung in der letzten LV zu präsentieren. Auch ein Bericht ist abzugeben.

Chronologisches Verzeichnis der im Verlauf des Semesters behandelten Themen


#1 Mi 27.09.2023

1 Überblick zum Kurs

Inhalte:

  1. Fehlertoleranz-Pattern
  2. Java-Programmierung
  3. Bearbeitung von Projektthemen

2 Einführung der Pattern für fehlertoleranten Software-Entwurf

81_FTSD/01_Fehlertoleranz

3 Überblick zu Java / Processing

30_Informatik3/09_Java
78_Processing
92_Soundinstallation/03_material/11_Snippets
int x=0;
int y=0;
int z=0;

public void setup()
{
    size(600,300);
    frameRate(30);
}

public void draw()
{
    background(x,y,z);
    fill(255,0,0);
    rect(0,0,100,100);
    fill(0,255,0);
    rect(100,0,100,100);
}

public void mousePressed()
{
    if(mouseX<100 && mouseY<100)
    {
        x=255;
        y=0;
        z=0;
    }
    if(mouseX>100 && mouseX<200 && mouseY<100)
    {
        x=0;
        y=255;
        z=0;
    }
}

Code 0-1: Sketch, der auf Klicks die Hintergrundfarbe ändert. Im Unterricht entstanden.

4 Vorbesprechung der Projektthemen


#2 Mi 04.10.2023

Organisatorisches: Android-Tablets
  • Zunächst werden die Tablets im Unterricht ausgegeben und am Ende wieder eingesammelt.
  • Die Softwareentwicklung für die individuellen Projekte soll zunächst vom Dozenten begleitet komplett innerhalb der Unterrichtszeit laufen.
  • Gegen Ende des Semesters können sie zeitweise ausgeliehen werden, um die Projektarbeiten fertig zu bekommen.

Themen

  1. Wiederholung fault-error-failure
  2. Übersicht über alle Fehlertoleranz-Pattern
  3. Gemeinsame Textarbeit: Mitigation und Recovery-Block
  4. Programmieraufgabe zur Konsolidierung des bisher Erlernten
  5. Unified Modelling Language (UML) und Objekt Orientierte Programmierung (OOP)
  6. Objekt Orientierte Programmierung (OOP) am Beispiel eines selbst programmierten beschrifteten Funktionsknopfes

zu 1. Wiederholung fault-error-failure

81_FTSD/05_Fehlertoleranz

zu 2. Übersicht über alle Fehlertoleranz-Pattern


Anforderung an Ihre Projektarbeit: Bezugnahme auf mehrere Fehlertoleranz-Pattern und Vorführung simulierter Fehlersituationen, bei denen die Pattern zum Einsatz kommen


81_FTSD/01_Fehlertoleranz

zu 4. Programmieraufgabe zur Konsolidierung des bisher Erlernten

  • Beim Klicken in die linke Hälfte des Fensters soll der Text "AN" im Fenster erscheinen.
  • Beim Klicken in die rechte Hälfte des Fensters soll der Text "AUS" im Fenster erscheinen.

Recherchieren Sie:

  • Wie kann die Textgröße beeinflusst werden?
  • Wie können Breite und Position des Textes an die Fenstergröße angepasst werden?
  • Wie kann dafür gesorgt werden, dass "AN" grün und "AUS" rot erscheint?
public void setup()
{
  size(600,300);
  frameRate(30);
  background(42,60,80); 
}

String x="AN";
String y="AUS";

public void draw()
{

}

public void mousePressed()
{
  if(mouseX<300)
  {
    background(42,60,80); 
    textSize(50);   
    text(x,270,150);
  }
  if(mouseX>300)
  {
    background(42,60,80); 
    textSize(50);   
    text(y,270,150);
  }
}

Code 0-2: Studentische Lösung

boolean an = false;

public void setup()
{
  size(600,300);
  frameRate(30);
}

public void draw()
{
   background(255);
   fill(0);
   textSize(30);
   textAlign(CENTER,CENTER);
   //text(""+an,width/2,height/2);
   if(an==true)
      text("AN",width/2,height/2);
   else   
      text("AUS",width/2,height/2);
}

public void mousePressed()
{
  if(mouseX<width/2)
  {
      an=true;
  }
  else
  {
      an=false;
  }
}

Code 0-3: Zweite Lösung

zu 5. Unified Modelling Language (UML)


Anforderung an Ihre Projektarbeit: Dartstellung und Präsentation der gesamten Softwarestruktur anhand von UML-Diagrammen


30_Informatik3/02_UML/02_UML_Klassendiagramm
30_Informatik3/06_Vererbung/01_MesswerteUML
30_Informatik3/03_Strings/02_stringUML
30_Informatik3/19_UML_ALT/02_UML_Flaechenberechnung
30_Informatik3/02_UML/03_Konstruktor
30_Informatik3/15_Rasterlayout/02_interfaces
30_Informatik3/19_UML_ALT/04_ListeUML
73_COACH3/09_Algorithmen/02_Loesungsansatz
05_esp32AV/10_Fehlertoleranz/20_Umsetzungsbeispiel

zu 6. Objekt Orientierte Programmierung (OOP) am Beispiel eines selbst programmierten beschrifteten Funktionsknopfes

Knopf-Programm mit selbst geschriebener Klasse Knopf:
Knopf knopf1;
Knopf knopf2;
int x;
public void setup()
{
  size(600,300);
  frameRate(30);
  knopf1 = new Knopf(10,10,200,50);
  knopf2 = new Knopf(10,70,200,50);
}

public void draw()
{
   background(255);
   knopf1.draw();
   knopf2.draw();
}

public void mousePressed()
{
   knopf1.clicked(mouseX,mouseY);
   knopf2.clicked(mouseX,mouseY);
}

Code 0-4: Hauptprogramm.

public class Knopf
{
    private int x,y,breite,hoehe;
    private boolean an;
    public Knopf(int x,int y,int breite,int hoehe)
    {
        this.an = false;
        this.x = x;
        this.y = y;
        this.breite = breite;
        this.hoehe = hoehe;
    }
    
    public void draw()
    {
        if(this.an)
            fill(0,255,0);
        else    
            fill(255,0,0);
        rect(x,y,breite,hoehe);
    }
    
    public void clicked(int x, int y)
    {
        if(x>this.x && x<this.x+this.breite && y>this.y && y<this.y+this.hoehe)
        {
             this.an=!this.an;
        }
    }       
}

Code 0-5: Klasse Knopf.

Verwendung von Interfaces (Schnittstellen)
Knopf knopf1;
Knopf knopf2;
Toaster toaster;
Foehn foehn;
int x;
public void setup()
{
  size(600,300);
  frameRate(30);
  toaster = new Toaster();
  foehn = new Foehn();
  knopf1 = new Knopf(10,10,200,50,foehn);
  knopf2 = new Knopf(10,70,200,50,toaster);
}

public void draw()
{
   background(255);
   knopf1.draw();
   knopf2.draw();
}

public void mousePressed()
{
   knopf1.clicked(mouseX,mouseY);
   knopf2.clicked(mouseX,mouseY);
}

Code 0-6: Hauptprogramm.

public class Knopf
{
    private int x,y,breite,hoehe;
    private boolean an;
    
    private iGeraet geraet;
    
    public Knopf(int x,int y,int breite,int hoehe,iGeraet geraet)
    {
        this.an = false;
        this.x = x;
        this.y = y;
        this.breite = breite;
        this.hoehe = hoehe;
        this.geraet = geraet;
        this.geraet.stop();
    }
    
    public void draw()
    {
        if(this.an)
            fill(0,255,0);
        else    
            fill(255,0,0);
        rect(x,y,breite,hoehe);
    }
    
    public void clicked(int x, int y)
    {
        if(x>this.x && x<this.x+this.breite && y>this.y && y<this.y+this.hoehe)
        {
             this.an=!this.an;
             if(this.an)
                geraet.start();
             else
                geraet.stop();
        }
    }       
}

Code 0-7: Klasse Knopf.

public interface iGeraet
{
     public abstract void start();
     public abstract void stop();
}

public class Foehn implements iGeraet
{
    public void start()
    {
         println("Föhn gestartet");
    }
    public void stop()
    {
         println("Föhn gestoppt");
    }
}

public class Toaster implements iGeraet
{
    public void start()
    {
         println("Toaster gestartet");
    }
    public void stop()
    {
         println("Toaster gestoppt");
    }
}

Code 0-8: Interface iGeraet und spezielle Geräte.

Testsketch011AnAus.zip -- Verwendung von Interfaces.
Testsketch010AnAus.zip -- Ganzes Projekt.
Testsketch011AnAus.zip -- Ganzes Projekt, optimiert.

Mausmethoden bei Processing:

https://processing.org/reference/mouseX.html
https://processing.org/reference/mouseClicked_.html

ACHTUNG: mouseClicked() funktioniert nicht beim Touchscreen mit dem Tablet, es muss statt dessen mousePressed() benutzt werden!


https://processing.org/reference/mousePressed_.html
https://processing.org/reference/mouseReleased_.html
https://processing.org/reference/mouseMoved_.html
https://processing.org/reference/mouseDragged_.html
Siehe die bei den Beispielen in Processing:
  • Basics/Input/MousePress
  • Basics/Input/Mouse2D
  • Basics/Input/MouseFunctions
  • Basics/Typography/Letters
  • Basics/Typography/Words
  • Basics/Interaction/Follow
  • Basics/File IO/LoadFile1
  • Basics/File IO/SaveFile1
  • Contributed Libraries/Ketai/... (Android-Sensorik nutzen)

#3 Mi 11.10.2023

Themen

  1. Wiederholungen: Mitigation, OOP (Klasse, Objekt, Schnittstelle)
  2. Das Fehlertoleranz-Pattern Recovery-Block
  3. Programmierübung
  4. Beispielhafte Umsetzung des Recovery-Block-Patterns

1. Wiederholungen: Mitigation, OOP (Klasse, Objekt, Schnittstelle)

81_FTSD/05_Fehlertoleranz
30_Informatik3/01_Vom_struct_zur_Klasse/06_Objektabstraktion
30_Informatik3/02_UML/02_UML_Klassendiagramm
30_Informatik3/06_Vererbung/01_MesswerteUML
30_Informatik3/02_UML/03_Konstruktor
30_Informatik3/15_Rasterlayout/02_interfaces

2. Das Fehlertoleranz-Pattern Recovery-Block

3. Programmierübung

  • Es geht um die modellhafte Umsetzung einer Besucher-App mit zwei Wahlmöglichkeiten
  • Beim Start der App soll der Benutzer die Möglichkeit haben, zwischen "THB Rallye" und "THB Führung" zu wählen.
  • Die Auswahl soll über einen Mausklick erfolgen.
  • Findet länger als zehn Sekunden nach Aufruf der App keine Reaktion statt, wird automatisch "THB Führung" gestartet.
  • Die Umsetzung soll zunächst ohne besondere OOP-Techniken erfolgen.
  • Implementieren Sie das Programm innerhalb der Methoden setup(), draw() und mousePressed().
  • "THB Rallye" und "THB Führung" können in einem ersten Schritt durch Anzeigen entsprechender Schriftzüge repräsentiert werden.
  • Im weiteren Verlauf sollen "THB Rallye" und "THB Führung" jeweils durchklickbare Diashows sein mit einem Knopf, um auf den Startbildschirm zurückzukehren.

Hilfestellung:

  • Führen Sie eine globale Variable "int MODE = 0;" ein.
  • In dieser wird gemerkt, in welchem Zustand sich das System gerade befindet: 0: Startbildschirm, 1: Rallye, 2: Führung.
  • In draw() wird mit Hilfe von MODE entschieden, was dargestellt wird.
  • Solche State-Machines sind eine gängige Technik, innere Zustände eines Systems zu ermöglichen.
Besucher_App2.zip -- studentische Beispiellösung.
int MODE = 0;
long time = 0;
long time_start = 0;

public void setup()
{
  size(600,300);
  frameRate(30);
  time_start = System.currentTimeMillis();
}

public void draw()
{  
  background(255);
  
  if (MODE == 0) // Wahlbildschirm
  {
     fill(255);
     rect(0,0,300,300);
     rect(300,0,300,300);
     fill(0);
     textSize(30);
     text("Zur THB Rallye", width/4, height/2); 
     text("Zur THB Führung", 3*width/4, height/2); 
     textAlign(CENTER);
     
     time = System.currentTimeMillis();     
     
     if (time - time_start >= 10000)
     {
         MODE = 2;
     }
  }
  else if (MODE == 1) // THB Rallye
  {
     fill(0);
     textSize(30);
     text("THB Rallye", 300, 120); 
     textAlign(CENTER);
     
     fill(255);
     rect(0,0,100,50);
     fill(0);
     textSize(20);
     text("zurück", 50, 25); 
    
  }
  else // THB Führung (MODE == 2)
  {
     fill(0);
     textSize(30);
     text("THB Führung", 300, 120); 
     textAlign(CENTER);
     
     fill(255);
     rect(0,0,100,50);
     fill(0);
     textSize(20);
     text("zurück", 50, 25); 
  }

}

public void mousePressed()
{
  
  if (MODE == 0) // Wahlbildschirm
  {
    if(mouseX<width/2)  // width = Fensterbreite
    {
      MODE = 1;
    }
    else //if(mouseX>width/2)
    {
      MODE = 2;
    }
 
  }
  else if (MODE == 1) // THB Rallye
  {
    
    if(mouseX<100 && mouseY<50)  // width = Fensterbreite
    {
      time_start = System.currentTimeMillis();
      MODE = 0;
    }
 
    
  }
  else // THB Führung (MODE == 2)
  {
    
    if(mouseX<100 && mouseY<50)  // width = Fensterbreite
    {
      time_start = System.currentTimeMillis();
      MODE = 0;
    }
 
  }

}

Code 0-9: Besucher_App.zip -- studentische Beispiellösung.

Modularisierung:

  • Entwerfen Sie eine Schnittstelle "iModul" für die drei Modi oder Zustände Startbildschirm, Rallye und Führung.
  • Alle drei Module sollen die Schnittstelle iModul implementieren.
  • Also müssen Sie sich überlegen, welche Methoden allen drei Modulen gemeinsam sein sollten.

weiter in Richtung OOP:

Besucher_App3_iMode.zip -- Drei Module jeweils mit eigener draw-Methode.
Besucher_App4.zip -- Zusammenfassen mehrerer Objekte gleicher Schnittstelle in einem Array.
Besucher_App5.zip -- Einbeziehen der Mausreaktion in die Schnittstelle.
Besucher_App6.zip -- Recoveryblock modularisiert.

4. Beispielhafte Umsetzung des Recovery-Block-Patterns

Die vorangehende Programmierübung soll nun die Grundlage bilden, das Recovery-Block-Pattern als gut erkennbares Modul zu implementieren.

#4 Mi 18.10.2023 #block Wegen Überschneidung mit einer Sitzung findet ab spätestens 14:20Uhr eine umfangreiche Übung statt, deren Ergebnisse dann erst kommende Woche besprochen werden.

Themen

  1. Neue Pattern: Maximize Human Particitaption und Minimize Human Intervention
  2. Nachbesprechung zur beispielhaften Umsetzung des Recovery-Block-Patterns
  3. Exportieren von Projekten mit Processing / Import nach BlueJ
  4. UML-Darstellung der Software-Struktur zur Umsetzung des Recovery-Block-Patterns
  5. Darstellung von Bildern mit Processing
  6. Umfangreiche Übung

1. Neue Pattern: Maximize Human Particitaption und Minimize Human Intervention

2. Nachbesprechung zur beispielhaften Umsetzung des Recovery-Block-Patterns

Besucher_App7.zip -- optimierte Fassung in Hinblick auf Objekt Orientierte Programmierung.

3. Exportieren von Projekten mit Processing / Import nach BlueJ

Optionen beim Exportieren von Projektren (Sketchen) in Processing.

Bild 0-1: Optionen beim Exportieren von Projektren (Sketchen) in Processing.

Besucher_App7_BLUEJ_EINZEL.zip -- Aus Besucher_App7 heraus gebildetes BlueJ-Projekt.

4. UML-Darstellung der Software-Struktur zur Umsetzung des Recovery-Block-Patterns

UML-Diagramm zu Besucher_App7 mit BlueJ.

Bild 0-2: UML-Diagramm zu Besucher_App7 mit BlueJ.

UML-Klassendiagramm zur Klasse Rallyse mit BlueJ.

Bild 0-3: UML-Klassendiagramm zur Klasse Rallyse mit BlueJ.

Java-Quelltext zur Klasse Rallye, so, wie sie in BlueJ importiert wurde.

Bild 0-4: Java-Quelltext zur Klasse Rallye, so, wie sie in BlueJ importiert wurde.

Teilansicht des Quelltexts zum nach BlueJ exportierten Hauptprogramm Besucher_App7.

Bild 0-5: Teilansicht des Quelltexts zum nach BlueJ exportierten Hauptprogramm Besucher_App7.

5. Darstellung von Bildern mit Processing

Siehe Beispiele innerhalb der Processing IDE unter: Datei/Beispiele/Basics/Image

6. Umfangreiche Übung

Aufgabe 1

Erinnern Sie sich an Ihnen bekannte, konkrete technische Beispiele, bei denen "Maximize Human Participation" und "Minimize Human Intervention" realisiert wurde. Notieren und diskutieren Sie diese Beispiele.

Aufgabe 2

Ergänzen Sie in nachfolgendem Sketch in sinnvoller Weise das fehlende Interface und testen Sie das dann lauffähige Programm:

import java.util.Random;
Random zufall = new Random(System.currentTimeMillis());
iFigur[] figur;
public void setup()
{
    size(600,400);
    frameRate(30);
    figur = new iFigur[100];
    int ii=0;
    for(int i=0;i<50;i++)
       figur[ii++] = new BlauesQuadrat(zufall.nextInt(width),zufall.nextInt(height));
    for(int i=0;i<50;i++)
       figur[ii++] = new RoterKreis(zufall.nextInt(width),zufall.nextInt(height));
       
}

public void draw()
{
    background(255);
    for(int i=0;i<figur.length;i++)
        figur[i].draw();
}

//### Fügen Sie hier das fehlende Interface ein. ###

public class BlauesQuadrat implements iFigur
{
    int x,y;
    public BlauesQuadrat(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    public void draw()
    {
         noStroke();
         fill(0,0,255,100);
         rect(x,y,30,30);
    }
}

public class RoterKreis implements iFigur
{
    int x,y;
    public RoterKreis(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    public void draw()
    {
         noStroke();
         fill(255,0,0,100);
         ellipse(x,y,20,20);
    }
}

Code 0-10: Sketch zu Aufgabe 2

Beispiel-Ergebnis nach Start des Sketches.

Bild 0-6: Beispiel-Ergebnis nach Start des Sketches.

Beantworten Sie sich folgende Fragen zum Sketch:

  1. Welche Bedeutung hat der vierte Parameter in fill(255,0,0,100); ?
  2. Warum wird der Generator für Pseudozufallszahlen "Random" mit der aktuellen Systemzeit "System.currentTimeMillis()" initialisiert?
  3. Was bewirkt die Anweisung ii++?
  4. Warum sind bei den verwendeten for-Schleifen keine geschweiften Klammern nötig?

Ergänzen Sie eine Funktionalität, durch die sich alle Figuren zufällig bewegen. Dies soll dadurch geschehen, indem bei jedem Durchlauf von draw() x oder y jeder Figur zufällig inkrementiert oder dekrementiert wird.

Aufgabe 3

Nehmen Sie den Quelltext aus Aufgabe 2 als Grundlage.

Um in BlueJ die untergeordneten Klassen zu befähigen, die Mal-Befehle zu verwenden, musste das Objekt PApplet bei den Klassen registriert werden und beispielsweise fill(255); durch pap.fill(255); ersetzt werden.

Dieses Vorgehen sorgt grundsätzlich für mehr Transparenz der Software, da so klar ist, woher die Befehle fill(), rect() ellipse() und so weiter stammen.

  • Speichern Sie den lauffähigen Sketch von Aufgabe 2 unter einem neuen Namen ab.
  • Übertragen Sie dieses Konzept auf den Quelltext von Aufgabe 2 und testen Sie das Programm erneut.
Aufgabe 4

Analysieren Sie das Beispielprogramm Datei/Beispiele/Basics/Image/LoadDisplayImage.

Suchen Sie im Internet nach einem Bild der Mensa der THB, einem des Technikgebäudes und einem des Haptgebäudes.

Machen Sie mit dem Werkzeug Zubehör/Bildschirmfoto von jedem dieser drei Bilder einen Screenshot und speichern es jeweils unter mensa.png, technik.png, haupt.png ab.

Erzeugen Sie einen Neuen Sketch "Diashow001" und legen in den Sketchordner die drei Bilder.

Programmieren Sie Ihren Sketch zunächst so, dass das Bild mensa.png angezeigt wird.

Überlegen Sie sich nun ein Konzept, mit dem es möglich ist, die drei Bilder durch wiederholtes Klicken mit der Maus zyklisch anzuschauen. Setzen Sie dieses minimale Konzept einer Diashow in Diashow002 um.

Aufgabe 5

Grundlage bildet Diashow002 aus Aufgabe 4.

Versuchen Sie Besucher_App7 so umzuschreiben, dass die Klasse Besucher die Funktionalität der Diashow aus Diashow002 erhält. Speichern Sie dazu Besucher_App7 zunächst als Besucher_App8 ab.

Aufgabe 6

Grundlage bildet Besucher_App8 aus Aufgabe 5.

Versuchen Sie das vor zwei Wochen entwickelte Konzept zur Umsetzung von Buttons (Funktionsknöpfen, Testsketch011AnAus.zip, siehe oben) zur Anwendung zu bringen, um den Knopf "zurück" damit umzusetzen. Speichern Sie dazu zunächst Besucher_App8 als Besucher_App9 ab und arbeiten Sie darin weiter.

Testsketch011AnAus.zip -- Ganzes Projekt, optimiert.

Die Knöpfe in Testsketch011AnAus haben keine Beschriftung. Beheben Sie diesen Mangel durch Ergänzung und Verwendung eines geeigneten Konstruktors.

#5 Mi 25.10.2023

Themen

  1. Neues Pattern: Escalation
  2. Beispiellösungen zu der Übung von letzter Woche
  3. Erste Schritte mit den Android-Tablets

1. Neues Pattern: Escalation

2. Beispiellösungen zu der Übung von letzter Woche

Lösung zu Aufgabe 1
  • Error-Code bei Spülmaschinen verhindert, das der Endbenutzer versucht selber die Reparatur durchzuführen.
  • Anzeigen einer Sanduhr beim PC, um anzuzeigen, dass das System beschäftigt, aber nicht tot ist. Soll verhindern, dass der Benutzer irgendeine Aktion ausführt, z.B. Neustart.
  • ...
Lösung zu Aufgabe 2
public interface iFigur
{
    public abstract void draw();
}

Code 0-11: Fehlender Code.

Erzeugen von Bewegung:

    public void draw()
    {
         ...
         x+=zufall.nextInt(3)-1;
         y+=zufall.nextInt(3)-1;
    }

Code 0-12: Zusatzcode in den beiden draw()-Methoden von BlauesQuadrat und RoterKreis

Lösung zu Aufgabe 3
import java.util.Random;
Random zufall = new Random(System.currentTimeMillis());
iFigur[] figur;
public void setup()
{
    figur = new iFigur[100];
    int ii=0;
    for(int i=0;i<50;i++)
       figur[ii++] = new BlauesQuadrat(zufall.nextInt(width),zufall.nextInt(height),this);
    for(int i=0;i<50;i++)
       figur[ii++] = new RoterKreis(zufall.nextInt(width),zufall.nextInt(height),this);
    size(600,600);   
}

public void draw()
{
    background(255);
    for(int i=0;i<figur.length;i++)
        figur[i].draw();
}

public interface iFigur
{
    public abstract void draw();
}

public class BlauesQuadrat implements iFigur
{
    int x,y;
    PApplet pap;
    public BlauesQuadrat(int x, int y, PApplet pap)
    {
        this.x = x;
        this.y = y;
        this.pap = pap;
    }
    public void draw()
    {
         pap.noStroke();
         pap.fill(0,0,255,100);
         pap.rect(x,y,30,30);
         x+=zufall.nextInt(3)-1;
         y+=zufall.nextInt(3)-1;
    }
}

public class RoterKreis implements iFigur
{
    int x,y;
    PApplet pap;
    public RoterKreis(int x, int y, PApplet pap)
    {
        this.x = x;
        this.y = y;
        this.pap = pap;
    }
    public void draw()
    {
         pap.noStroke();
         pap.fill(255,0,0,100);
         pap.ellipse(x,y,20,20);
         x+=zufall.nextInt(3)-1;
         y+=zufall.nextInt(3)-1;
    }    
}

Code 0-13: Musterlösung zu Aufgabe 3 (mit Bewegung).

Lösung zu Aufgabe 4
PImage[] img;
int index=0;
public void setup()
{
    img = new PImage[3];
    img[0]=loadImage("mensa.png");
    img[1]=loadImage("technik.png");
    img[2]=loadImage("haupt.png");
    //size(600,400);
    fullScreen();
}

public void draw()
{
    image(img[index],0,0,width,height);
}

public void mousePressed()
{
     index++;
     index%=img.length;
}

Code 0-14: Diashow002

Lösung zu Aufgabe 5
public class Besucher implements iMode
{
    PImage[] img;
    int index=0;
  
    public Besucher()
    {
        img = new PImage[3];
        img[0]=loadImage("mensa.png");
        img[1]=loadImage("technik.png");
        img[2]=loadImage("haupt.png");      
    }
  
    public void draw()
    {
     fill(0);
     textSize(30);
     //text("THB Führung", 300, 120);
     image(img[index],0,0,width,height);
     
     textAlign(CENTER);
     
     fill(255);
     rect(0,0,100,50);
     fill(0);
     textSize(20);
     text("zurück", 50, 25); 
    }
    
    public int setMode(int mode, int mx, int my)
    {
        if(mx>=0 && mx<100 && my>=0 && my<50)
        {
           mode = 0;
        }    
        else if(mx>=0 && my>=0)
        {
           index++;
           index%=img.length;
        }
        return mode;   
    }            
}

Code 0-15: Veränderte Klasse Besucher. Nur diese muss geändert werden (und die Bilder in den Sketchordner kopiert werden).

Lösung zu Aufgabe 6

... wird im Unterricht gemeinsam besprochen und umgesetzt.

Besucher_App9.zip -- Lösung, hier holen Sie sich die Klasse Knopf her
Diashow002.zip -- Grundlage für Übung mit drei Knöpfen
Diashow003.zip -- studentische Lösung
Diashow004.zip -- Variante

3. Erste Schritte mit den Android-Tablets

Um auf das Tablet Android-Apps von Processing aus übertragen zu können, müssen auf dem Tablet die Entwickleroptionen aktivert und konfiguriert werden.

1. Sieben mal auf die Build-Nummer tippen, um die Entwickleroptionen zu aktivieren

Dazu:

1.1 Entwickleroptionen öffnen.

Bild 0-7: 1.1 Entwickleroptionen öffnen.

Infos zu Tablet öffnen.

Bild 0-8: Infos zu Tablet öffnen.

Softwareinformationen öffnen.

Bild 0-9: Softwareinformationen öffnen.

Dort sieben mal auf die Buildnummer tippen, um die Entwickleroptionen zu aktivieren.

Bild 0-10: Dort sieben mal auf die Buildnummer tippen, um die Entwickleroptionen zu aktivieren.

2. Entwickleroptionen konfigurieren

Dazu:

In Einstellungen zu Entwickleroptionen gehen.

Bild 0-11: In Einstellungen zu Entwickleroptionen gehen.

Bild 0-12: "Aktiv lassen" aktivieren.

Bild 0-13: "USB-Debugging" und "adb-autorisierungsteimeout deaktiviert" aktivieren

weitere aktivieren.

Bild 0-14: weitere aktivieren.

  1. Lösung zu Aufgabe 6 auf Tablet übertragen.
  2. Sensoren testen unter Verwendung der Beispiele aus der KETAI-Library.

#6 Mi 01.11.2023

Themen

  1. Neue Fehlertoleranzpattern: Fault Observer und Software Update
  2. QR-Codes nutzen
  3. GPS nutzen
  4. OOP-Übung mit GPS

2. QR-Codes nutzen

Hier wurde eine Library zu QR-Codes mit Processing aus dem jahr 2016 mit zwei Testbeispielen zur Verfügung gestellt:

Quelle: https://forum.processing.org/two/discussion/15572/qr-code-library-for-processing-3-0-2-windows-linux-macosx-android-writeqr-and-readqr.html

Kombination mit der Kamera:

Sketchpermissions:

  • CAMERA
  • READ_MEDIA_IMAGES
  • WRITE_EXTERNAL_STORAGE
  • READ_EXTERNAL_STORAGE

QR-Codes testweise mit Linux erzeugen, Beispiel, im Terminal einzugeben:

  • qrencode "Das ist ein Test." -o hello.png
QRreaderCam002.zip -- Zusammenführung von Ketai/CameraGettingStarted und QRreader.
  • Installieren Sie QRreaderCam002 auf dem Tablet.
  • Testen Sie QRreaderCam002, indem Sie einen QR-Code sehr groß am PC-Bildschirm anzeigen und ihn mit dem Tablet scannen.

Man muss den QR-Code sehr groß machen und sehr genau ein umscheibendes Quadrat aufnehmen.


3. GPS nutzen

  • Öffnen Sie LocationDistance aus Beispiele/Contributed Libraries/Ketai
  • Speichern Sie LocationDistance unter LocationDistance2 im Sketchbook.
  • Ersetzen Sie die Koordinaten in Zeile 32 und 33 bei LocationDistance2 ( uic.setLatitude(41.874698); uic.setLongitude(-87.658777); ) durch die Position des Mensaeingangs (Herausfinden mit Google-Maps).
  • Werfen Sie einen Blick in Android/Sketchpermissions
  • Übertragen Sie LocationDistance2 auf das Tablet.
  • Testen Sie die Funktionalität draußen (Position und Distanz zum Mensa-Eingang).
Sketchpermissions für Geolocation.

Bild 0-15: Sketchpermissions für Geolocation.


Sketchpermissions: Jede App muss ausweisen, welche Sensoren sie benutzt und ob sie auf Benutzerdaten zugreift.


4. OOP-Übung mit GPS

Ausgangspunkt sei LocationDistance2.

  • Speichern Sie LocationDistance2 unter LocationDistance3.
  • Versuchen Sie die GPS-Funktionalität weitestgehend in einer Klasse mit dem Namen MeinGPS zu kapseln.
  • Ergänzen Sie MeinGPS um eine FaultObserver-Methode boolean isOkay(), indem Sie eine entsprechende Schnittstelle definieren und MeinGPS diese Schnittstelle implementieren lassen, s.u.
public interface iFaultObserver
{
   public abstract boolean isOkay();
}

Code 0-16: Quelltext der einzubauenden Schnittstelle.

Entwerfen Sie in Form eines UML-ähnlichen Diagramms eine Software-Architektur für ein Projekt, das in folgender Weise arbeiten soll:

  • Wenn das GPS nicht verfügbar ist, soll dem Benutzer angezeigt werden, dass er sich ins Freie begeben soll.
  • Ob das GPS verfügbar ist, soll über die Objektmethode isOkay() der Klasse MeinGPS abgeprüft werden.
  • Die Funktionalität (Textanzeige, wenn GPS nicht verfügbar), soll über eine einem RecoveryBlock entsprechende Architektur realisiert werden.

Tipp: Bevor Sie die geforderte Software-Architektur umsetzen, sollten Sie grob die geforderte Funktionalität auf die einfachste Weise umsetzen.


Ausbaustufen der Lösung, Ausgangspunkt: Geolocation
Geolocation4.zip -- Anzeige der Distanz zur Mensa in Grad (2*10^-4==Mensaeingang, 7*10^-4==Umgebung)
Geolocation5.zip -- Kapselung der GPS-Funktion in eine eigene Klasse. (Leide muss Methode onLocationEvent() außerhalb bleiben!)
Geolocation6.zip -- ...mit minimalistischer Umsetzung eines Fault Observers.

#7 Mi 08.11.2023

Themen

  1. Neue Fehlertoleranzpattern: Correcting Audits und Someone in Charge
  2. KRcode ;-)
  3. Ideensammlung für die Abschlussprojekte
  4. Planung eines Referenzprojektes als Ausgangspunkt

1. Neue Fehlertoleranzpattern: Correcting Audits und Someone in Charge

2. KRcode ;-)

Es wurde bisher ein Verfahren getestet, bei dem festgestellt werden kann, ob eine Person ein bestimmtes Ziel erreicht hat und dann Infos über das Ziel bereit gestellt wurden: Das Verfahren sollte mit QR-Codes arbeiten. Diese sind an markanten Punkten bei den Sehenswürdigkeiten angebracht und sollten durch die App-Benutzer gescannt werden.

Hier traten nun gravierende Schwierigkeiten auf: Da die verfügbare Library nur unverzerrte Codes scannen kann, ist es sehr schwierig den Code so in den Kamerafokus zu bekommen, dass er dann erkannt wird. Das macht die ganze Sache unpraktikabel.

Eine Lösung könnte es sein, einen eigenen Scancode zu entwickeln, der robuster ist, sich also auch dann scannen lässt, wenn man den Code nicht so gut im Fokus hat. Was die Sache erleichtern könnte, wäre, dass nicht unbedingt sehr viel Information übertragen werden muss. Im Grunde würde es schon reichen, wenn eine bloße Nummer übermittelt wird, die jedem Ort, der angesteuert wird, vergeben wird. Die eigentlichen Infos können ohne weiteres in der App gespeichert sein.

Grundidee

Nimmt man kreisförmige Ringe als Grundlage für den Code, hat man schon einmal den Vorteil, dass der Winkel egal ist, in dem der Code mit der Kamera erfasst wird.

Zwei immer gleiche äußere Ringe könnten dazu dienen, zu prüfen, ob ein gültiger Code im Fokus ist.

Die inneren Ringe sind dann in der Farbe veränderlich und kodieren dann eine Zahl.

Passend zur Corporate Identity der THB könnten die Ringe rot sein.

Ob der Scan richtig erfasst wird, könnte daran festgemacht werden, dass immer vier Punkte um 90 Grad verdreht die gleiche Information liefern:

In den vier schwarzen Punkten muss die gleiche Farbe zu sehen sein, damit der Code als lesbar erkannt wird.

Bild 0-16: In den vier schwarzen Punkten muss die gleiche Farbe zu sehen sein, damit der Code als lesbar erkannt wird.

Die geringere Informationsdichte des Codes sollte also zu einer höheren Fehlertoleranz beim Scannnen führen.

Es wäre noch denkbar, den Code als Zahl ins Zentrum zu schreiben und dem Benutzer die Möglichkeit zu geben, den Code auch einzutippen, je nach Anwendungsfall.

Umsetzung

Mögliches Beispiel eines solchen Codes nebst einem Codegenerator in Processing:

Kodierung der Zahl 11 mit KRcode.

Bild 0-17: Kodierung der Zahl 11 mit KRcode.

int ZAHL=0;
String name="code";
boolean SAVE=false;
public void setup()
{
    size(500,500);
    frameRate(2);
}

public void draw()
{
     float w = width;
     
     background(255);
     noStroke();
     
     fill(255,0,0);
     ellipse(w/2,w/2,w*0.9,w*0.9);
     fill(255,255,255);
     ellipse(w/2,w/2,w*0.8,w*0.8);
     fill(255,0,0);
     ellipse(w/2,w/2,w*0.7,w*0.7);
     fill(255,255,255);
     ellipse(w/2,w/2,w*0.6,w*0.6);
     
     int Z=ZAHL;
     String text1 = "";
     for(int i=0;i<4;i++)
     {
          float d = 0.5-(float)i*0.1;
          text1=Z%2+" | "+text1;
          if(Z%2==1)
              fill(255,0,0);
          else
              fill(255);
          ellipse(w/2,w/2,w*d,w*d);
          Z/=2;
     }
     text1 = " | "+text1+" = "+ZAHL;
     fill(255);
     ellipse(w/2,w/2,w*0.1,w*0.1);
     fill(0);
     textSize(12);
     textAlign(LEFT,TOP);
     text(text1,10,20);
     textAlign(CENTER);
     textSize(20);
     text(""+ZAHL,w/2,w/2+w/70);
     
     if(ZAHL<16)
     {
         save(name+ZAHL+".png");
         ZAHL++;
     }
}

Code 0-17: Processing-Sketch zur Generierung der Codes für die Zahlen 0 bis 15.

Processing-Sketch KRcode001 mit generierten Codes.
Entwicklung eines minimalen Beispielprogramms zum Scannen von KRcodes

Unter Verwendung der Library Ketai, ist nachfolgend eine minimalistische eines Scan-Programms zu sehen.

Sobald ein gültiger Code im Fokus ist, wird der Hintergrund von rot auf grün gesetzt und der erfasste Code angezeigt.

Screenshot bei Verwendung von KRscan002, wenn ein KRcode korrekt erfasst wurde, hier die Zahl 3.

Bild 0-18: Screenshot bei Verwendung von KRscan002, wenn ein KRcode korrekt erfasst wurde, hier die Zahl 3.

import ketai.camera.*;

KetaiCamera cam;
int BREITE = 640;
int HOEHE = 480;
int[][] bild;

// 4*8 Datenpunkte, immer von innen nach Außen gescannt, siehe auswerten()
int[][] data = new int[4][8]; //erste 4: Zahl, zweite 4: äußere Ringe

int ZAHL = -1; //erkannte Zahl
int LETZTEZAHL = -1; //zuletzt erfolgreich erkannte Zahl

void setup() 
{
  bild = new int[HOEHE][BREITE]; 
  fullScreen();
  orientation(LANDSCAPE);
  cam = new KetaiCamera(this, BREITE, HOEHE, 24);
}

void draw() 
{
  if(ZAHL<0)
  {
       background(255,0,0);
  }
  else
  {
       background(0,255,0);
  }
  
  if(!cam.isStarted())
  {
      cam.start();
  }
  
  if (cam != null && cam.isStarted())
  {
      image(cam, 0, 0, BREITE, HOEHE);
  }
  fill(255);
  rect(BREITE,0,BREITE,HOEHE);
  noStroke();
  for(int i=0;i<bild.length;i++)
  {
      for(int k=0;k<bild[i].length;k++)
      {
          if(bild[i][k]==0)
          {
              fill(0);
              rect(BREITE+k,0+i,1,1);
          }     
          else if(bild[i][k]==1)
          {
              fill(0,255,0); //erkannt, logisch 1
              ellipse(BREITE+k,0+i,5,5);
          }    
          else if(bild[i][k]==2)
          {
              fill(255,0,0); //erkannt, logisch 0
              ellipse(BREITE+k,0+i,5,5);
          }    
      }        
  }           
    stroke(0);
    noFill();
    int durchmesser = (int)round(0.9f*(float)HOEHE);
    ellipse(BREITE/2,HOEHE/2,durchmesser,durchmesser);
    
    //data ausgeben (gescannte Binärcodes):
    fill(0);
    textSize(20);
    for(int i=0;i<data.length;i++)
    {
         for(int k=0;k<data[i].length;k++)
         {
               text(""+data[i][k],60+k*20,height-200+i*30);
         }
    }
    
    //erkannte Zahle ausgeben:
    textSize(60);
    fill(0);
    text(""+ZAHL,width/2,height-200);
    fill(127);
    text("("+LETZTEZAHL+")",width/2+width/4,height-200);
}

/**
Liefert die gescannte Zahl, oder -1
*/
public int auswerten(int[][] bild)
{
    int dx = (int)round(0.05f*(float)HOEHE);//Schrittweite von einem Ring zum nächsten
    int im = HOEHE/2; //Zentrum y
    int km = BREITE/2; //Zentrum x
    
    for(int k=0;k<data[0].length;k++)
    {
         int p1 = km+dx+dx/2+dx*k;
         int p2 = km-dx-dx/2-dx*k;
         int p3 = im+dx+dx/2+dx*k;
         int p4 = im-dx-dx/2-dx*k;
         if(bild[im][p1]==0) //horizontal nach rechts
         {
               bild[im][p1]=1; //logisch 1 erkannt
               data[0][k]=1;
         }
         else
         {
               bild[im][p1]=2; // logiscgh 0 erkannt
               data[0][k]=0;
         }
         if(bild[im][p2]==0) //horizontal nach links
         {
               bild[im][p2]=1; //logisch 1 erkannt
               data[1][k]=1;
         }
         else
         {
               bild[im][p2]=2; // logiscgh 0 erkannt
               data[1][k]=0;
         }
         if(bild[p3][km]==0) //vertikal nach unten
         {
               bild[p3][km]=1; //logisch 1 erkannt
               data[2][k]=1;
         }
         else
         {
               bild[p3][km]=2; // logiscgh 0 erkannt
               data[2][k]=0;
         }
         if(bild[p4][km]==0) //vertikal nach oben
         {
               bild[p4][km]=1; //logisch 1 erkannt
               data[3][k]=1;
         }
         else
         {
               bild[p4][km]=2; // logiscgh 0 erkannt
               data[3][k]=0;
         }
    }
    
    //1. prüfen, ob alle Winkel die gleichen Daten enthalten:
    boolean OKAY = true;
    for(int i=0;i<data.length-1;i++)
        for(int ii=i+1;ii<data.length;ii++)
            for(int k=0;k<data[i].length;k++)
                if(data[i][k]!=data[ii][k])
                    OKAY=false;

    //2. prüfen, ob der Code der äußeren Ringe 0101 ist:
    if(OKAY)
    {
         if(data[0][7]==1 && data[0][6]==0 && data[0][5]==1 && data[0][4]==0)
               OKAY=true;
         else
               OKAY=false;
    }
                    
    if(OKAY)
    {
         //Zahl berechnen (eine Richtung reicht)
         int Z = 0;
         for(int k=0;k<4;k++)
         {
              Z*=2;
              Z+=data[0][k];
         }
         return Z;
    }
    else
    {
         return -1;
    }
}

void onCameraPreviewEvent()
{
  cam.read();
  cam.updatePixels();
  
  if(cam.pixels!=null)
  {
      int ii=0;
      for(int i=0;i<bild.length;i++)
      {
          for(int k=0;k<bild[i].length;k++)
          {
              int pix = cam.pixels[ii];
              int bb = (pix & 0xFF);
              int gg = (pix & 0xFF00)>>8;
              int rr = (pix&0xFF0000)>>16;
              int p = rr-((gg*2)/3)-((bb*2)/3); //***NEU***
              if(p>0)
                  bild[i][k] = 0; //schwarz, wenn rot
              else
                  bild[i][k] = 255; // weiss wenn anderes
              ii++;    
          }
      }
      
      ZAHL = auswerten(bild);
      if(ZAHL>=0)
         LETZTEZAHL = ZAHL;
  }
}

Code 0-18: KRscan002 (Entstanden, indem zunächst CameraGettingStarted aus der Ketai-Library unter neuem Namen gespeichert und dann modifiziert wurde.)

KRscan002.zip -- Android-Processing-Sketch KRscan002.
ÜBUNG
  1. Analysieren Sie die Sketche KRcode001 und KRscan002
  2. Testen Sie KRscan002 und KRcode001
  3. Versuchen Sie KRscan002 so umzuorganisieren, dass es möglichst einfach wird, die Fähigkeit des Scannens von KRcodes in andere Projekte zu importieren.
  4. Entwickeln Sie auf möglichst einfache Weise eine Besucher-Rallye auf Basis von KRcode mit folgenden Eigenschaften:
  • Es soll die Zeit gestoppt werden, die es dauert, alle Stationen in der richtigen Abfolge zu nehmen.
  • Bei Scan von KRcode mit dem Wert 1 soll die Zeit starten.
  • Dann müssen KRcodes mit den Werten 2,3,4 folgen, in dieser Reihenfolge.
  • Schliesslich wird die Zeit angehalten, wenn der KRcode mit dem Wert 5 gescannt wird.
  • Überlegen Sie sich:
  1. Wie tolerant soll die App sein (Reihenfolge der gescannten Codes)?
  2. Wie wird die Zeit angezeigt?
  3. Wie könnten die Ergebnisse mehrerer Teilnehmer*Innen am Ende der Rallye zusammengeführt werden?
  4. Welche Aufgaben könnte man an den Stationen zusätzlich geben?

3. Ideensammlung für die Abschlussprojekte

Interessant wäre Konzepte, die deutliche Alternativen zu der voran umgesetzten App bilden.

4. Planung eines Referenzprojektes als Ausgangspunkt

  • Die vorangehend entwickelte Besucher-Rallye mit Stationen soll als
  • Referenz-Projekt ausgebaut werden.
  • Insbesondere sollen in sauberer Art und Weise Fehlertoleranz-Pattern implementiert werden.
  • Der Gesamtaufbau soll objektorientiert sein und von Schnittstellen als architektonischem Prinzip Gebrauch machen.

#8 Mi 15.11.2023

Themen

  1. Neue Fehlertoleranzpattern: Redundancy und Maintanance Interface
  2. Beispiellösung für Besucherrallye mit KRcode
  3. Entwicklung eines Referenzprojektes
  4. Konkretisierung der Ideen für die Abschlussprojekte

1. Neue Fehlertoleranzpattern: Redundancy und Maintanance Interface

2. Beispiellösung für Besucherrallye mit KRcode

KRscan003Rallye.zip -- Zwischenstand: Kamera und KRscan in Klassen gekapselt.
KRscan004Rallye.zip -- Endversion mit Stationen und Zeitmessung.
import ketai.camera.*;

int MODE = 0;
long T = 0;
long Tstart = 0;
String[] texte = 
{
     "Scanne den KRcode am Startpunkt", //MODE0
     "Scanne den KRcode bei Station 1", //MODE1
     "Scanne den KRcode bei Station 2", //MODE2
     "Scanne den KRcode bei Station 3", //MODE3
     "Scanne den KRcode bei Station 4", //MODE4 (Ziel)
     "Ziel erreicht in "+(T/1000)+" Sekunden! RESET: scanne 5" //MODE 5
};

Kamera kamera;
KRscan krscan;
int BREITE = 640;
int HOEHE = 480;
void setup() 
{
  fullScreen();
  orientation(LANDSCAPE);
  kamera = new Kamera(this);
  krscan = new KRscan();
}

void draw() 
{
  
  int ZAHL = krscan.getZahl();
  if(ZAHL<0)
  {
       background(255,0,0);
  }
  else
  {        
       if(krscan.getLetzteZahl()==MODE)
       {
            if(MODE==0) Tstart = System.currentTimeMillis();
            if(MODE==4)
            {
                T = System.currentTimeMillis() - Tstart;
                texte[5]="Ziel erreicht in "+(T/1000)+" Sekunden! RESET: scanne 5"; //MODE 5
            }
            MODE++;
            MODE%=texte.length;
       }
       background(0,255,0);
  }  
  kamera.draw(0,0);
  krscan.drawZielkreis(0,0);
  int[][] bild = kamera.getBild();
  krscan.auswerten(bild);
  textSize(40);
  fill(255,255,0);
  text(texte[MODE],10,height/2+height/4);
  if(MODE>=1 && MODE<=4)
  {
       text("Die Zeit läuft: "+((System.currentTimeMillis()-Tstart)/1000)+"s",10,height/2+height/4+height/8);
  }
}

Code 0-19: Hauptprogramm bei KRscan004Rallye.

3. Entwicklung eines Referenzprojektes

Dazu zunächst: Analyse der Programmiertechniken, die die Lösungen KRscan003Rallye und KRscan004Rallye ermöglicht haben.

  1. Verwendung von Vererbung
  2. Verwendung von Threads
  3. Verwendung der Schnittstelle Runnable
import ketai.camera.*;

public class Kamera extends KetaiCamera implements Runnable
{
    PApplet pap;
    int BREITE = 640;
    int HOEHE = 480;
    int[][] bild;
    public Kamera(PApplet pap)
    {
          super(pap, 640, 480, 24);
          this.pap = pap;
          bild = new int[HOEHE][BREITE]; 
          (new Thread(this)).start();
    }
    
    public void run()
    {
        while(true)
        {
             if(!this.isStarted())
             {
                 this.start();
             }
             try
             {
                 Thread.sleep(500);
             }
             catch(Exception e)
             {
             }
        }
    }

    public int[][] getBild()
    {
        if(!this.isStarted())
            return bild;
        this.read();
        this.updatePixels();
  
        if(this.pixels!=null)
        {
             int ii=0;
             for(int i=0;i<bild.length;i++)
             {
                 for(int k=0;k<bild[i].length;k++)
                 {
                     int pix = this.pixels[ii];
                     int bb = (pix & 0xFF);
                     int gg = (pix & 0xFF00)>>8;
                     int rr = (pix&0xFF0000)>>16;
                     int p = rr-((gg*2)/3)-((bb*2)/3);
                     if(p>0)
                          bild[i][k] = 0; //schwarz, wenn rot
                     else
                          bild[i][k] = 255; // weiss wenn anderes
                     ii++;    
                 }
              }      
         }
         return bild;
    }
    public void draw(int x, int y)
    {
         if(this.isStarted()) pap.image(this, x, y, BREITE, HOEHE);
    }
    
    public void drawBild(int x, int y)
    {
        fill(255);
        rect(x,y,BREITE,HOEHE);
        noStroke();
        for(int i=0;i<bild.length;i++)
        {
            for(int k=0;k<bild[i].length;k++)
            {
                if(bild[i][k]==0)
                {
                    fill(0);
                    rect(x+k,y+i,1,1);
                }     
                else if(bild[i][k]==1)
                {
                      fill(0,255,0); //erkannt, logisch 1
                      ellipse(x+k,y+i,5,5);
                }    
                else if(bild[i][k]==2)
                {
                      fill(255,0,0); //erkannt, logisch 0
                      ellipse(x+k,y+i,5,5);
                }    
          }        
        }                 
    }
}

Code 0-20: Klasse Kamera zur Analyse.

UML Objekt-Diagramm zur Klasse Kamera.

Bild 0-19: UML Objekt-Diagramm zur Klasse Kamera.

  • Ein Objekt vom Typ Kamera erbt alle Attribute und Methoden der Klasse KetaiKamera.
  • Außerdem wird die Schnittstelle Runnable implementiert, um einen Thread (nebenläufiger Prozess in Form der Methode run()) zu ermöglichen.
UML Objekt-Diagramm zum Projekt KRscan004Rallye

Bild 0-20: UML Objekt-Diagramm zum Projekt KRscan004Rallye

  • Ein Objekt der Hauptklasse KRscan004Rallye benutzt sowohl ein Objekt vom Typ Kamera und eines vom Typ KRscan.
  • Die hardwarenahen Aspekte der Kaerabenutzung sind in Kamera gekaspelt. Nichts davon befindet sich in KRscan.
  • KRscan benötigt das in Kamera vorverarbeitete Bild.
36_Java/04_Threads -- Erläuterungen zu Threads
77_Android/04_Threads -- Beispiel zu Threads
Praktisches Schema für den Arbeitsablauf bei der Entwicklung von Software: Agiler Softwareentwurf
67_Echtzeitsysteme/13_Fuzzy/05_Softwareentwicklung/01_AgileSoftwareentwicklung
User Stories zur Leitung des weiteren Entwicklungsprozesses des Referenzprojektes ausgehend von KRscan004Rallye
  1. Das Design folgt der Corporate ID der TH-Brandenburg.
  2. Eine Vielzahl an Sensorabfragen wird anderen Entwicklern durch eine entsprechende Anzahl an Sensorklassen mit ihren Methoden auf einfache Art und Weise zur Verfügung gestellt.
  3. Konzept eines SOUNDWALKS wurde erarbeitet.
  4. Es gibt sinnhaften Content, statt einfach nur durch Beschriftungen ausgewiesene Statusanzeigen.
  5. Der Content wird modularisiert in der Software abgebildet.
  6. Neben einfachen Zielen, die es zu erreichen gilt, wird die App mit zu erledigenden Aufgaben angereichert.
  7. Der Content kann über Extra-Dateien konfiguriert werden, um so schneller angezeigte Texte und Bilder anpassen zu können.
  8. Die Software wird in Hinblick auf Ansätze zu Fehlertoleranz analysiert und eine modifizierte Software-Architektur entworfen.
  9. Einige Fehlertoleranzkonzepte sind umgesetzt worden.
  10. Fehlerkorrekturen nach ausführlichen User-Tests überführen die App vom Teststatus in den Auslieferungsstatus.
ÜBUNG zu KRscan003Rallye und KRscan004Rallye
  1. Analysieren Sie die beiden Projekte.
  2. Speichern Sie KRscan004Rallye unter KRscan005Rallye und modifizieren Sie das Projekt so, dass den angezeigten Texten auch Bilder beigefügt werden.
  3. Helfen Sie dem Benutzer, falls die Scans nicht gelingen sollten. Gehen Sie dabei wie folgt vor:
  • Entwickeln Sie eine Methode als Objektmethode von KRscan, die in der Lage ist, zu entscheiden, ob der Benutzer gerade probiert Scans durchzuführen und die einen Verzweiflungsgrad liefert.
  • Ergänzen Sie die Software um ein Element im Sinne eines Recovery-Blocks, das dem ggf. verzweifelten Benutzer anbietet, den Scan durch Eingabe eines Codes zu ersetzen.
  • Überlegen Sie sich ein Konzept, wie vermieden werden kann, dass der Benutzer das System überlistet, wenn etwas anderes angeboten wird, als den Scan durchzuführen und setzen dieses Konzept um.
KRscan005Rallye.zip -- Studentische Lösung für den Bildteil.

4. Konkretisierung der Ideen für die Abschlussprojekte

ÜBUNG

Formulieren Sie Userstories zu Ihrem geplanten Abschlussprojekt und starten einen ersten "daily scrum".

ArrayList

ArrayList<int[]> arr;

public void setup()
{
    arr = new ArrayList<int[]>();
    arr.add(new int[] {300,400});
    arr.add(new int[] {400,400});
    arr.add(new int[] {400,300});
    arr.add(new int[] {300,400});
    size(600,600);
}

public void draw()
{
    background(255);
    int[] b = arr.get(0);
    stroke(0);
    for(int i=1;i<arr.size();i++)
    {
         int[] a = arr.get(i);
         line(a[0],a[1],b[0],b[1]);
         b=a;
    }
    
   
}

Code 0-21: ArrayList-Beispiel

#8 Mi 22.11.2023

Beginn mit den Projektarbeiten

  1. Formulieren Sie eine User-Story passend zu Ihrem Projektvorhaben und zu der verfügbaren Zeit.
  2. Versuchen Sie das gesteckte Ziel zu erreichen.
  3. Diskutieren Sie am Ende der Unterrichtseinheit, ob das Ziel erreicht wurde und warum, bzw. warum nicht.
  • Der Dozent wird sich alle User-Stories anhören und die Arbeit daran individuell betreuen.
  • Im Bedarfsfall werden allgemein notwendige Techniken in das Referenzprojekt übernommen und für alle Präsentiert.

#9 Mi 29.11.2023

Themen

  1. Präsentation einiger Snippets
  2. Fortsetzung der Projektarbeiten

1. Präsentation einiger Snippets

In der letzten Besprechung der Projektthemen wurden einige Anforderungen identifiziert, zu denen nachfolgend kleine Beispielprogramme bereitgestellt werden, die aufzeigen, wie bestimmte Dinge bewerkstelligt werden können.

Alle diese kleinen Beispielprogramme (Snippets) haben gemeinsam, dass sie genau eine Sache zeigen, jedoch keine weitere Funktionalität. Das macht die Programme kurz. Zum anderen obliegt es den Projektteams die Transferleistung zu erbringen, die Beispiele nötigenfalls für ihre jeweilige Softwarearchitektur anzupassen und zu integrieren.

Stets ist bei den Snippets die zu demonstrierende Funktionalität in einer Klasse eingebettet, die dann einfache Objektmethoden bereitstellt, um die Funktionalität im Hauptprogramm (draw()) zu nutzen.


Die einzufügenden Libraries können im Klassentab angegeben werden. Das spart Ärger beim Übertragen auf ein anderes Projekt.


Screenshot zu Snippet1

Bild 0-21: Screenshot zu Snippet1

Snippet1_Beschleunigung.zip -- Download von Snippet 1.
Snippet #1 -- Beschleunigung -- Anzeige des Richtungsvektors in der Ebene, wohin die Erdbeschleunigung weist

Ausgangspunkt der Entwicklung ist das Beispiel Accelerometer aus der Ketai-Library.


Beschleunigung beschleunigung;
PImage thblogo;
public void setup()
{
    beschleunigung = new Beschleunigung(this);
    thblogo = loadImage("thb2.png");
    fullScreen();    
    orientation(LANDSCAPE);
    frameRate(30);
}

/** Callback-Methode ist leider nicht in die Klasse Beschleunigung kapselbar: */
public void onAccelerometerEvent(float x, float y, float z)
{
    beschleunigung.onAccelerometerEvent(x,y,z);
} 


public void draw()
{
     background(255);
     fill(127);
     rect(0,0,height,height);
     stroke(255);     
     strokeWeight(height/100.0);
     float[] vek = beschleunigung.getRichtung();
     float radius = height/2.0;
     float laenge = sqrt(vek[0]*vek[0]+vek[1]*vek[1]);
     if(laenge>0.0)
        line(radius,radius,radius+vek[1]*radius/laenge,radius+vek[0]*radius/laenge);
     strokeWeight(1.0);     
     image(thblogo,width-height/8,0.0,height/8,height/8);
     fill(0);
     noStroke();
     textSize(36);
     text("Snippet #1",height+10,50);
     text("Beschleunigungsrichtung",height+10,150);
     
}

Code 0-22: Hauptprogramm von Snippet1_Beschleunigung

import ketai.sensors.*;

/**
Analyse von
api ketai.sensors.KetaiSensor

http://ketai.org/reference/sensors/ketaisensor/
*/
public class Beschleunigung extends PApplet
{
    KetaiSensor sensor;
    float accX, accY, accZ;
    public Beschleunigung(PApplet pap)
    {
        sensor = new KetaiSensor(pap);
        sensor.start();        
    }
    
    public void onAccelerometerEvent(float x, float y, float z)
    {
        accX = x;
        accY = y;
        accZ = z;
    } 
    
    
    public float[] getRichtung()
    {
         if(accX!=0.0 || accY!=0.0 || accZ!=0.0)
         {
             float laenge = sqrt(accX*accX+accY*accY+accZ*accZ);
             return new float[] {accX/laenge,accY/laenge,accZ/laenge};
         }
         else
         {
             return new float[] {0.0,0.0,0.0};
         }
    }
}

Code 0-23: Klasse Beschleunigung in Snippet1_Beschleunigung

Snippet #2 -- UDP-Nachrichten (Kommunikation zwischen den Tablets)

Hierzu muss die UDP-Library installiert werden!


Ausgangspunkt ist das Beispiel udp aus der UDP-Library.


  • Testbeispiel, bei dem ein Smartphone als Hotspot dient und ein W-LAN-fähiger Laptop und das Tablet sich dort anmelden.
  • WICHTIG: Wechselseitig müssen die IP-Adressen von Laptop und PC auf dem Hotspot bekannt sein, siehe ZIEL_IP im jeweiligen Sketch.
  • Es gibt eine Tablet und eine PC-Version des Sketches.
  • Im Anwendungsfall könnte ein Sender einfach eine Liste von IPs durchgehen und an alle senden.
  • Der Empfänger zeigt immer an, von wem er eine Nachricht empfangen hat.
  • Mit ifconfig (Linux), bzw. ipconfig kann die IP-Adresse des PCs über das Terminal angezeigt werden, unter der er sich beim Hotspot angemeldet hat.
  • Bei/Mit mousePressed() wird eine Nachricht an den Empfänger geschickt.

HINWEIS: In der Android-Version des Sketches wird versucht

Snippet2_UDP.zip -- Android-Version
Snippet2_UDP_PC.zip -- PC-Version
Beim PC empfangene Nachricht vom Tablet. Hier spätestens kann die IP des Tablets erkannt werden.

Bild 0-22: Beim PC empfangene Nachricht vom Tablet. Hier spätestens kann die IP des Tablets erkannt werden.

Screenshot zu Snippet2 (Tablet nach Empfang der PC-Nachricht).

Bild 0-23: Screenshot zu Snippet2 (Tablet nach Empfang der PC-Nachricht).

Ergänzung: Verwendung der Broadcast-Adresse

Um zu vermeiden, die Ziel-IP hart zu kodieren, kann einfach an alle im Subnetz angemeldeten Geräte gresendet werden, indem die Broadcast-Adresse verwendet wird:

Ausgabe nach

Bild 0-24: Ausgabe nach "ifconfig" im Terminal. Die Broadcastadresse wurde markiert, hier: 192.168.147.255


Schließen Sie alle anderen Apps, bevor Sie Snippet2 testen, da sonst der Datenaustausch eventuell nicht funktioniert.


2. Fortsetzung der Projektarbeiten