001package jmri.server.json.audioicon; 002 003import com.fasterxml.jackson.databind.JsonNode; 004import com.fasterxml.jackson.databind.node.ObjectNode; 005 006import java.beans.PropertyChangeEvent; 007import java.beans.PropertyChangeListener; 008import java.io.IOException; 009import java.util.HashMap; 010import java.util.concurrent.ThreadLocalRandom; 011 012import javax.servlet.http.HttpServletResponse; 013 014import jmri.Audio; 015import jmri.JmriException; 016import jmri.jmrit.audio.AudioSource; 017import jmri.jmrit.display.AudioIcon; 018import jmri.server.json.JSON; 019import jmri.server.json.JsonConnection; 020import jmri.server.json.JsonException; 021import jmri.server.json.JsonRequest; 022import jmri.server.json.JsonSocketService; 023import static jmri.server.json.audioicon.JsonAudioIconServiceFactory.AUDIO_ICON; 024 025/** 026 * JSON socket service provider for managing {@link jmri.jmrit.display.AudioIcon}s. 027 * 028 * @author Randall Wood 029 * @author Daniel Bergqvist (C) 2023 030 */ 031public class JsonAudioIconSocketService extends JsonSocketService<JsonAudioIconHttpService> { 032 033 private final HashMap<AudioIcon, AudioIconListener> beanListeners = new HashMap<>(); 034 035 public JsonAudioIconSocketService(JsonConnection connection) { 036 super(connection, new JsonAudioIconHttpService(connection.getObjectMapper())); 037 } 038 039 @Override 040 public void onList(String type, JsonNode data, JsonRequest request) throws IOException, JmriException, JsonException { 041 throw new JsonException(HttpServletResponse.SC_METHOD_NOT_ALLOWED, Bundle.getMessage(request.locale, "GetListNotAllowed", type), request.id); 042 } 043 044 @Override 045 public void onMessage(String type, JsonNode data, JsonRequest request) throws IOException, JmriException, JsonException { 046 if (request.method.equals(JSON.GET)) { 047 connection.sendMessage(service.doGet(type, "audioicon", data, request), request.id); 048 } 049 AudioIcon audioIcon = AudioIcon.IDENTITY_MANAGER.getAudioIcon(data.get("identity").asInt()); 050 if (!beanListeners.containsKey(audioIcon)) { 051 addListenerToBean(audioIcon); 052 } 053 } 054 055 @Override 056 public void onClose() { 057 beanListeners.values().stream().forEach(listener -> listener._audioIcon.removePropertyChangeListener(listener)); 058 beanListeners.clear(); 059 } 060 061 protected void addListenerToBean(AudioIcon audioIcon) { 062 if (audioIcon != null) { 063 AudioIconListener listener = new AudioIconListener(audioIcon); 064 audioIcon.addPropertyChangeListener(listener); 065 this.beanListeners.put(audioIcon, listener); 066 } 067 } 068 069 private class AudioIconListener implements PropertyChangeListener { 070 071 public final AudioIcon _audioIcon; 072 073 public AudioIconListener(AudioIcon audioIcon) { 074 this._audioIcon = audioIcon; 075 } 076 077 @Override 078 public void propertyChange(PropertyChangeEvent evt) { 079 // Do nothing if unknown property 080 if (!evt.getPropertyName().equals(AudioIcon.PROPERTY_COMMAND)) return; 081 082 try { 083 String command; 084 int numLoops = 0; 085 switch (evt.getNewValue().toString()) { 086 case AudioIcon.PROPERTY_COMMAND_PLAY: 087 command = JSON.AUDIO_COMMAND_PLAY; 088 Audio audio = _audioIcon.getAudio(); 089 if (audio instanceof AudioSource) { 090 AudioSource source = (AudioSource)audio; 091 if (source.isLooped()) { 092 if (source.getMinLoops() != source.getMaxLoops()) { 093 numLoops = source.getMinLoops() 094 + ThreadLocalRandom.current().nextInt( 095 source.getMaxLoops() - source.getMinLoops()); 096 } else { 097 numLoops = source.getMinLoops(); 098 } 099 } else { 100 numLoops = 1; 101 } 102 } 103 break; 104 case AudioIcon.PROPERTY_COMMAND_STOP: 105 command = JSON.AUDIO_COMMAND_STOP; 106 break; 107 default: 108 // Do nothing if unknown property command 109 log.debug("Unknown command: {}", evt.getNewValue()); 110 return; 111 } 112 113 JsonRequest request = new JsonRequest(getLocale(), getVersion(), JSON.GET, 0); 114 115 ObjectNode root = connection.getObjectMapper().createObjectNode(); 116 root.put(JSON.TYPE, AUDIO_ICON); 117 root.put(JSON.METHOD, JSON.GET); 118 root.put(JSON.ID, request.id); 119 120 ObjectNode data = root.with(JSON.DATA); 121 data.put(JSON.AUDIO_ICON_IDENTITY, _audioIcon.getIdentity()); 122 data.put(JSON.AUDIO_COMMAND, command); 123 data.put(JSON.AUDIO_COMMAND_PLAY_NUM_LOOPS, numLoops); 124 connection.sendMessage(root, 0); 125 } catch (IOException ex) { 126 // if we get an error, unregister as listener 127 this._audioIcon.removePropertyChangeListener(this); 128 beanListeners.remove(this._audioIcon); 129 } 130 } 131 } 132 133 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(JsonAudioIconSocketService.class); 134}