/**********************************************************************
* $Source: /cvsroot/hibiscus/hbci4java/src/org/kapott/hbci/manager/FlickerRenderer.java,v $
* $Revision: 1.6 $
* $Date: 2011/06/07 13:55:08 $
* $Author: willuhn $
*
* Copyright (c) by willuhn - software & services
* All rights reserved
*
**********************************************************************/
package org.kapott.hbci.manager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Uebernimmt das Umwandeln eines Flicker-Codes in die blinkende Darstellung.
* Da wir hier in HBCI4Java ja keinen GUI-spezifischen Code (Swing, SWT, etc.)
* haben, uebernimmt die Klasse lediglich das Erzeugen der schwarzen und weissen
* Balken sowie das Timing. Sie ruft dann im Wiedergabe-Takt die Funktion paint()
* auf, die ueberschrieben werden muss, um dort dann das eigentliche Zeichnen
* der 5 Balken durchzufuehren.
* Die paint-Funktion wird so ca. 10-20 mal pro Sekunde aufgerufen, sollte die
* Ausgabe auf dem Bildschirm daher flott machen ;)
*/
public class FlickerRenderer
{
/**
* Default-Taktfequenz in Hz.
* Soll laut tan_hhd_uc_v14.pdf, Kapitel C.1 zwischen 2 und 20 Hz liegen.
*/
public final static int FREQUENCY_DEFAULT = 10;
/**
* Minimale Taktfrequenz.
*/
public final static int FREQUENCY_MIN = 2;
/**
* Maximale Taktfrequenz.
* Laut Spec. sind die Geraete bis 20 Hz zugelassen, viele koennen aber schneller.
*/
public final static int FREQUENCY_MAX = 40;
private int halfbyteid = 0;
private int clock = 0;
private List<int[]> bitarray = null;
private Thread thread = null;
private int iterations = 0;
private int freq = FREQUENCY_DEFAULT;
/**
* ct.
* @param code der zu rendernde Flicker-Code.
* Er wird von HBCI4Java ueber den Callback NEED_PT_TA uebergeben.
*
* Etwa so:
*
* case HBCICallback.NEED_PT_TAN:
* String flicker = retData.toString();
* if (flicker != null && flicker.length() > 0)
* {
* MyFlickerRenderer = new FlickerRenderer(flicker) {
* // hier paint() ueberschreiben
* };
* }
*/
public FlickerRenderer(String code)
{
// Sync-Identifier vorn dran haengen.
code = "0FFF" + code;
// Das Bitfeld mit der BCD-Codierung.
// Koennte man auch in einer For-Schleife und etwas Bit-Schieberei machen.
// Aber das ist ist besser lesbar ;)
Map<String,int[]> bcdmap = new HashMap<String,int[]>();
bcdmap.put("0",new int[]{0, 0, 0, 0, 0});
bcdmap.put("1",new int[]{0, 1, 0, 0, 0});
bcdmap.put("2",new int[]{0, 0, 1, 0, 0});
bcdmap.put("3",new int[]{0, 1, 1, 0, 0});
bcdmap.put("4",new int[]{0, 0, 0, 1, 0});
bcdmap.put("5",new int[]{0, 1, 0, 1, 0});
bcdmap.put("6",new int[]{0, 0, 1, 1, 0});
bcdmap.put("7",new int[]{0, 1, 1, 1, 0});
bcdmap.put("8",new int[]{0, 0, 0, 0, 1});
bcdmap.put("9",new int[]{0, 1, 0, 0, 1});
bcdmap.put("A",new int[]{0, 0, 1, 0, 1});
bcdmap.put("B",new int[]{0, 1, 1, 0, 1});
bcdmap.put("C",new int[]{0, 0, 0, 1, 1});
bcdmap.put("D",new int[]{0, 1, 0, 1, 1});
bcdmap.put("E",new int[]{0, 0, 1, 1, 1});
bcdmap.put("F",new int[]{0, 1, 1, 1, 1});
// Wir mappen den Code basierend auf dem Bit-Array.
// Linkes und rechtes Zeichen jedes Bytes wird getauscht.
this.bitarray = new ArrayList<int[]>();
for (int i = 0; i < code.length(); i += 2) {
bitarray.add(bcdmap.get(Character.toString(code.charAt(i+1))));
bitarray.add(bcdmap.get(Character.toString(code.charAt(i))));
}
}
/**
* Legt die Taktfrequenz in Hz fest.
* @param hz die zu verwendende Taktfrequenz.
* Es werden nur Werte zwischen {@link FlickerRenderer#FREQUENCY_MIN} und
* {@link FlickerRenderer#FREQUENCY_MAX} akzeptiert.
*/
public void setFrequency(int hz)
{
if (hz < FREQUENCY_MIN || hz > FREQUENCY_MAX)
return;
this.freq = hz;
}
/**
* Startet das Rendering des Flicker-Codes.
* Die Funktion startet einen neuen Thread, kehrt also sofort zurueck.
*
* Da diese Funktion einen neuen Thread startet und somit sofort
* zurueckkehrt, kann es ggf. noetig sein, eine Warteschleife zu
* implementieren. Hierzu kann einfach die Funktion "waitFor()" aufgerufen
* werden. Sie pausiert solange, bis "stop()" augerufen wurde.
*
* Beispiel:
*
* FlickerRenderer renderer = new FlickerRenderer(meinCode) {
* public void paint(boolean b1,boolean b2,boolean b3,boolean b4,boolean b5)
* {
* // hier eigenen Code zum Rendern einbauen
* }
*
* public void done(int iterations)
* {
* // Nach 20 Uebertragungen hoeren wir auf.
* if (iterations > 20)
* stop();
* }
* };
*
* renderer.start();
* renderer.waitFor();
* System.out.println("Code uebertragen");
*
*/
public final synchronized void start()
{
// ggf. laufenden Thread beenden
stop();
this.thread = new Thread("Flicker Update-Thread")
{
public void run()
{
// Wir fangen beim ersten Halbbyte an.
halfbyteid = 0;
// Die Clock, die immer hin und her kippt. Wir beginnen bei 1.
// Sonst wuerde das allererste Zeichen nur einmal uebertragen
// werden, was bewirkt, dass der Code erst einmal komplett
// durchlaufen muesste, bevor wir einen kompletten gesendet haetten
clock = 1;
try
{
// Die Endlos-Schleife mit der Uebertragung
while (true)
{
int[] bits = bitarray.get(halfbyteid);
bits[0] = clock;
paint(bits[0] == 1,bits[1] == 1,bits[2] == 1,bits[3] == 1,bits[4] == 1);
clock--;
if (clock < 0)
{
clock = 1;
// Jedes Zeichen muss doppelt uebertragen werden. Einmal mit clock 0
// und einmal mit clock 1.
halfbyteid++;
if (halfbyteid >= bitarray.size())
{
halfbyteid = 0;
// Wir sind einmal mit dem Code komplett durch
iterations++;
done(iterations);
}
}
// Warten
// Wir errechnen die Wartezeit in jedem Durchlauf.
// Dann kann die Frequenz auch waehrend des Blinkens geaendert werden.
long sleep = 1000L / freq;
sleep(sleep);
}
}
catch (InterruptedException e)
{
// Ende der Anzeige
}
}
};
thread.start();
}
/**
* Stoppt das Rendern.
*/
public final void stop()
{
if (this.thread != null)
{
try
{
if (this.thread != null)
{
this.thread.interrupt();
synchronized (this.thread)
{
this.thread.notifyAll();
}
}
}
finally
{
this.thread = null;
}
}
}
/**
* Wird immer dann aufgerufen, wenn die 5 Balken der Flicker-Grafik aktualisiert
* werden sollen. Die 5 Boolean-Parameter legen die aktuell anzuzeigende Codierung
* fest.
*
* Die Default-Implementierung ist leer.
*
* Diese Funktion muss auf jeden Fall ueberschrieben werden. Sonst kriegt man
* keinen Flicker-Code.
*
* @param b1 Balken 1. true=weiss, false=schwarz
* @param b2 Balken 2. true=weiss, false=schwarz
* @param b3 Balken 3. true=weiss, false=schwarz
* @param b4 Balken 4. true=weiss, false=schwarz
* @param b5 Balken 5. true=weiss, false=schwarz
*/
public void paint(boolean b1,boolean b2,boolean b3,boolean b4,boolean b5)
{
}
/**
* Wird immer dann aufgerufen, nachdem der Flicker-Code einmal komplett
* an den TAN-Generator uebertragen wurde.
* Die Funktion wird zum ersten Mal NACH der ersten Uebertragung aufgerufen
* und anschliessend nach jeder weiteren.
*
* Die Default-Implementierung ist leer.
*
* Fuer gewoehnlich wird die Funktion zur Darstellung nicht benoetigt.
* Sie kann aber zu Debugging-Zwecken verwendet werden oder zum automatischen
* Abbruch nach einer definierten Anzahl von Uebertragungen.
* Sie muss also nicht ueberschrieben werden.
*
* Die Funktion wird direkt im Flicker-Thread aufgerufen. Sie sollte daher
* auf keinen Fall irgendwas aufwaendiges machen, da das zum Ausbremsen
* der Flicker-Uebertragung fuehren wuerde.
*
* @param iterations Anzahl der bisherigen Uebertragungen (beginnend bei 1 -
* da die Funktion ja erst nach der ersten Uebertragung aufgerufen wird)
*/
public void done(int iterations)
{
}
/**
* Kann verwendet werden, um den Aufrufer-Thread solange zu pausieren,
* bis "stop()" aufgerufen wurde. Damit kann warten, bis die Uebertragung
* abgeschlossen ist.
*/
public final synchronized void waitFor()
{
if (this.thread == null)
return;
synchronized (this.thread)
{
if (this.thread == null)
return;
try
{
this.thread.wait();
}
catch (InterruptedException e)
{
// Wir sind raus.
}
}
}
}
/**********************************************************************
* $Log: FlickerRenderer.java,v $
* Revision 1.6 2011/06/07 13:55:08 willuhn
* @N 28-hbci4java-flicker-speed2.patch
*
* Revision 1.5 2011-06-06 15:32:51 willuhn
* @N 26-hbci4java-flicker-speed.patch
*
* Revision 1.3 2011-06-06 15:25:12 willuhn
* @N 26-hbci4java-flicker-speed.patch
*
* Revision 1.2 2011-05-27 15:46:13 willuhn
* @N 23-hbci4java-chiptan-opt2.patch - Kleinere Nacharbeiten
*
* Revision 1.1 2011-05-27 10:28:38 willuhn
* @N 22-hbci4java-chiptan-opt.patch
*
**********************************************************************/