Jump to content
Sign in to follow this  
BOBmoraine

[Java] Problem: Listener updatet HashMap

Recommended Posts

Moin, ich habe ein Problem mit HashMaps und Listenern:

 

public class EListener_Impl_EnumerateListener implements IPConnection.EnumerateListener {
private String hostname;

/**
 * Konstruktor des EnumerateListeners mit Angabe des auslösenden Hostnamens
 * @param hostname Hostname
 */
public EListener_Impl_EnumerateListener(String hostname) {
	this.hostname = hostname;
}

@Override
public void enumerate(String uid, String connectedUid, char position, short[] hardwareVersion, short[] firmwareVersion, int deviceIdentifier, short enumerationType) {
        switch(enumerationType) {
        case IPConnection.ENUMERATION_TYPE_AVAILABLE:
        	//(0): Gerät ist verfügbar (Enumerierung vom Benutzer ausgelöst).
        	System.out.println("ENUMERATION_TYPE_AVAILABLE " + this.hostname + ":" + uid + ":" +enumerationType);
        	TF_StackManager.addBricklet(uid, new Bricklet(hostname, uid, connectedUid, position, firmwareVersion, firmwareVersion, deviceIdentifier));
        	break;
        case IPConnection.ENUMERATION_TYPE_CONNECTED:
        	// (1): Gerät wurde neu verbunden (Automatisch vom Brick gesendet nachdem die Kommunikation aufgebaut wurde). Dies kann bedeuten, dass das Gerät die vorher eingestellte Konfiguration verloren hat und neu konfiguriert werden muss.
        	System.out.println("ENUMERATION_TYPE_CONNECTED " + this.hostname + ":" + uid + ":" +enumerationType);
        	TF_StackManager.addBricklet(uid, new Bricklet(hostname, uid, connectedUid, position, firmwareVersion, firmwareVersion, deviceIdentifier));
        	break;
        case IPConnection.ENUMERATION_TYPE_DISCONNECTED:
        	//(2): Gerät wurde getrennt (Nur bei USB-Verbindungen möglich). In diesem Fall haben nur uid und enumerationType einen gültigen Wert.
        	System.out.println("ENUMERATION_TYPE_DISCONNECTED " + this.hostname + ":" + uid + ":" +enumerationType);
        	TF_StackManager.removeBricklet(uid);
        	break;
        }
    }

}

Mein Enumerate-Listener

 

	/**
 * Ein Bricklet hinzufügen
 * @param uid Bricklet UID als Key
 * @param bricklet Bricklet als Objekt
 */
public static void addBricklet(String uid, Bricklet bricklet) {
    	if ((uid == null) || uid.equals("")) 
    		{ throw new IllegalArgumentException(); }
	if (!list_bricklets.containsKey(uid)) {
		//216 Temperature Bricklet
		if(bricklet.getDeviceIdentifier() == 216) {
			try {
				String te_hostname = bricklet.getHostname();
				IPConnection te_ipcon = list_stacks.get(te_hostname);
				BrickletTemperature temp = new BrickletTemperature(uid, te_ipcon); // Create device object
				temp.setTemperatureCallbackPeriod(1000);
				temp.addTemperatureListener(new TempListener_Impl_TemperatureListener(uid) );
				} 
			catch (TimeoutException e) { e.printStackTrace(); } 
			catch (NotConnectedException e) { e.printStackTrace(); }
	        
			}
		list_bricklets.put(uid, bricklet);
		}    	
	}

TF_StackManager.addBricklet(String uid, Bricklet bricklet)

 

private static class TempListener_Impl_TemperatureListener implements BrickletTemperature.TemperatureListener {
	private final String uid;
	public TempListener_Impl_TemperatureListener(String uid) {
		this.uid = uid;
	}

	@Override
	public void temperature(short temperature) {
		list_brickletWerte.put(this.uid, new BrickletWert(this.uid, "Temperatur", "" + temperature/100.0, "°C"));
        	System.out.println(this.uid + " Temperature: " + temperature/100.0 + " °C");
	}

}

Inner Class von TF_BrickManager.

Inner Class damit ich auf die HashMap zugreifen kann mit dem Listener.

 

private static HashMap<String, BrickletWert> list_brickletWerte = new HashMap<String, BrickletWert>();

Meine HashMap der BrickletWerte.

 

for(Entry<String, BrickletWert> entry: stackManager.getBrickletWertlist().entrySet()) {
			String key = entry.getKey();
			BrickletWert brickletWert = entry.getValue();
			System.out.println(
					entry.getKey() + ": " +
					brickletWert.getBrickletUID() + " -> " +
					brickletWert.getBrickletDescription() + " -> " +
					brickletWert.getBrickletWert() + " " +
					brickletWert.getBrickletWertMasseinheit()
					);
			}

Diesen Code lasse ich nun alle 5 Sekunden Testweise ausführen um die aktuellen Temperaturen der Bricklets auszulesen.

Leider bekomme ich immer sowas:

rmk: 6EN -> Temperatur -> 33.56 °C

dDY: 6EN -> Temperatur -> 33.56 °C

6EN: 6EN -> Temperatur -> 33.56 °C

 

Komischerweise überschreibt der zuletzt getriggerte Listener alle Werte in der HashMap, und nicht wie vorgesehen nur ein Key,Value-Paar.

Hatte jemand schonmal ein ähnliches Problem oder kann mir evtl. helfen und mir sagen wo mein Fehler liegt?

 

Share this post


Link to post
Share on other sites

Ist eine HashMap ist nicht thread-safe. Versuch mal eine ConcurrentHashMap zu verwenden.

Share this post


Link to post
Share on other sites

Moin,

ConncurrentHashMap hab ich versucht, leider ohne Änderung.

Ich hab meinen Listener nochmal verändert und ein PreparedStatement hinzugefügt:

		@Override
	public void temperature(short temperature) {
		list_brickletWerte.put(this.uid, new BrickletWert(this.uid, "Temperatur", "" + temperature/100.0, "°C"));
        	System.out.println(this.uid + " Temperature: " + temperature/100.0 + " °C");
		//test
			try {
				java.sql.Connection connect = MysqlConnection.getConnection();
				PreparedStatement preparedStatement;
				preparedStatement = (PreparedStatement) connect.prepareStatement("insert into BrickletWert (brickletUID, brickletDescription, brickletWert, brickletWertMasseinheit) values (?, ?, ?, ?) ON DUPLICATE KEY UPDATE BrickletWert=?");
				preparedStatement.setString(1, this.uid);
			    preparedStatement.setString(2, "Temperatur");
			    preparedStatement.setString(3, "" + temperature/100.0);
			    preparedStatement.setString(4, "°C");
			    preparedStatement.setString(5, "" + temperature/100.0);
			    preparedStatement.executeUpdate();
				} 
			catch (Exception e) { e.printStackTrace(); }  
		//test
	}

Komischerweise stehen in der Mysql-Tabelle die Werte entsprechend richtig.

Nur das mit der HashMap funktioniert nicht so wie ich das möchte.

 

 

Wie würdet ihr das denn lösen? Ihr habt nen Host/Stack mit unbekannter Anzahl (in diesem Falle TempBricklets). Wenn der Stack verbunden ist sollen die tempListener den Temperaturwert in einer Variablen aktuell halten so das man anhand der BrickletUID die Temperatur aus der Variablen auslesen kann.

Share this post


Link to post
Share on other sites

Ich sehe nicht warum das mit der HashMap nicht funktionieren sollte.

 

Hast du mal eine Hashtable statt einer HashMap getestet? Du solltest hier definitiv einen thread-safen Container nehmen, da der Temperature Callback von einem internen Thread der IPConnection aufgerufen wird.

 

Wann gibst du den die HashMap über die entrySet() Methode aus? Laut Dokumentation ist der Iterator des Sets das du von entrySet() bekommst ungültig sobald die HashMap geändert wird:

 

http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html#entrySet()

 

Sprich, wenn während der Ausgabe der HashMap ein Temperature Callback die Map ändert kommt da irgendwas beim Iterator heraus.

 

Versuch es mal mit clone(), bin mir aber nicht sicher ob das hilft:

 

for (Entry<String, BrickletWert> entry: stackManager.getBrickletWertlist().clone().entrySet()) { ... }

 

Ansonsten muss du den Zugriff auf die HashMap manuell synchronizen, damit entweder der Temperature Callback Daten einfügen kann, oder du die Werte ausgibst, aber nicht beides gleichzeitig, oder verschachtelt passieren kann.

Share this post


Link to post
Share on other sites

Moin,

 

Die HashMap hole ich mit getHashMap über eine Methode in eine Andere Klasse. Da wird dann auch entrySet() angewendet.

return hashMap.clone() hat leider auch nix gebracht, und ich habs auch nicht hinbekommen die HashMap manuell zu synchronisieren.

 

Hashtable klappt, werde wohl diesen Weg weiter verfolgen.

Danke für die Denkanstöße.

Share this post


Link to post
Share on other sites

Moin,

ich noch mal, habe die Lösung falls es jemanden interessiert :)

 

Das Problem lag wohl nicht an der HashMap oder der Iteration, sondern an meiner eigenen Werte Objekt Klasse "BrickletWert.java".

 

Ich musste daraus ein "immutable" Objekt machen.

Ich habe mich dabei an diese Seite gehalten:

http://www.torsten-horn.de/techdocs/java-concurrency.htm#Fehlschlagende-Synchronisationsversuche-Collections

 

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...