BOBmoraine Geschrieben April 7, 2015 at 07:52 Geschrieben April 7, 2015 at 07:52 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? Zitieren
photron Geschrieben April 7, 2015 at 09:42 Geschrieben April 7, 2015 at 09:42 Ist eine HashMap ist nicht thread-safe. Versuch mal eine ConcurrentHashMap zu verwenden. Zitieren
BOBmoraine Geschrieben April 7, 2015 at 11:45 Autor Geschrieben April 7, 2015 at 11:45 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. Zitieren
photron Geschrieben April 7, 2015 at 12:11 Geschrieben April 7, 2015 at 12:11 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. Zitieren
BOBmoraine Geschrieben April 7, 2015 at 13:26 Autor Geschrieben April 7, 2015 at 13:26 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. Zitieren
BOBmoraine Geschrieben April 12, 2015 at 07:38 Autor Geschrieben April 12, 2015 at 07:38 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 Zitieren
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.