📄 controller.java
字号:
package net.sf.dz.daemon.tcp;import java.io.BufferedReader;import java.io.PrintWriter;import java.net.Socket;import java.util.Iterator;import java.util.Map;import java.util.Set;import java.util.StringTokenizer;import java.util.TreeMap;import java.util.TreeSet;import net.sf.dz.daemon.onewire.OneWireServer;import net.sf.dz.daemon.onewire.DeviceContainer;import net.sf.dz.daemon.onewire.SwitchContainer;import org.freehold.jukebox.logger.LogChannel;import org.freehold.jukebox.sem.RWLock;/** * The TCP controller. * * Accepts the control messages, interprets and tries to execute. * * @author Copyright © <a href="mailto:vt@freehold.crocodile.org">Vadim Tkachenko</a> 2001-2002 * @version $Id: Controller.java,v 1.16 2004/06/28 20:35:47 vtt Exp $ */public class Controller extends Connector { public static final LogChannel CH_CTL = new LogChannel("TCP/Controller"); public static final LogChannel CH_LOCK = new LogChannel(CH_CTL, "lock"); /** * Set of known switch addresses. */ private Set deviceSet = new TreeSet(); /** * Map of known device channel numbers by address. * * It is assumed that the device with the same 1-Wire address will * never change its channel count, therefore, the probe for channel * count will be attempted only once. */ private Map channelMap = new TreeMap(); /** * Device map. * * The key is the device address, the value is the device container. */ private Map switchMap = new TreeMap(); /** * Server map. * * The key is the device address, the value is the server the device is * controlled by. This is necessary to obtain a write lock from the server. */ private Map serverMap = new TreeMap(); /** * Clean flag. */ private boolean clean = true; protected LogChannel getLogChannel() { return CH_CTL; } protected String getAnnounce() { StringBuffer sb = new StringBuffer(); sb.append("DZ DAC Switches").append(super.getAnnounce()).append("/");; // Need to get the data from the switch map, because otherwise we're // likely to get devices other than switches synchronized ( deviceSet ) { for ( Iterator i = deviceSet.iterator(); i.hasNext(); ) { sb.append(" ").append(getDeviceSignature(i.next().toString())); } } return sb.toString(); } protected String getDeviceSignature(String address) { String signature = ""; Integer iCount = (Integer)channelMap.get(address); if ( iCount == null ) { complain(LOG_WARNING, CH_CTL, address + ": how come we don't have a channel map for it? Assuming 2 until able to resolve"); iCount = new Integer(2); } int channelCount = iCount.intValue(); for ( int idx = 0; idx < channelCount; idx++ ) { signature += address + ":" + idx; if ( idx < (channelCount - 1) ) { signature += " "; } } return signature; } protected int getChannelCount(String address) { String signature = ""; Integer iCount = (Integer)channelMap.get(address); if ( iCount == null ) { complain(LOG_WARNING, CH_CTL, address + ": how come we don't have a channel map for it? Assuming 2 until able to resolve"); iCount = new Integer(2); } return iCount.intValue(); } protected String getServiceSignature() { return "DZ DAC Switches"; } protected int getDefaultListenPort() { return 5002; } protected int getDefaultBroadcastPort() { return 5003; } protected ConnectionHandler createHandler(Socket socket, BufferedReader br, PrintWriter pw) { clean = false; return new ControlHandler(socket, br, pw); } protected synchronized void cleanup() throws Throwable { if ( clean || switchMap.isEmpty() ) { complain(LOG_DEBUG, CH_CTL, "Already clean"); return; } for ( Iterator i = switchMap.values().iterator(); i.hasNext(); ) { SwitchContainer sc = (SwitchContainer)i.next(); complain(LOG_INFO, CH_CTL, "Cleanup: shutting down " + sc); sc.reset(); } complain(LOG_INFO, CH_CTL, "Cleaned up"); clean = true; } protected void shutdown2(Throwable cause) throws Throwable { } protected boolean isUnique() { return true; } protected synchronized void broadcastArrival(String address, String type) { for ( Iterator si = serverSet.iterator(); si.hasNext(); ) { OneWireServer s = (OneWireServer)si.next(); DeviceContainer dc = s.getDeviceContainer(address); // The only containers we care about are DS2406 and DS2408. // VT: NOTE: This may change in the future. But at this point // adding DS2409 to the switch map messes things up because the // controller will try to shut them off on shutdown - this // doesn't really make any sense because they're supposed to be // controlled by lower level logic anyway. // VT: NOTE: Actually, now DS2409 is cut off a level above this // call - in OneWireServer. if ( (dc != null) && (dc instanceof SwitchContainer) ) { deviceSet.add(address); switchMap.put(address, dc); serverMap.put(address, s); updateChannelMap(address, (SwitchContainer)dc); announce(getAnnounce()); for ( Iterator i = iterator(); i.hasNext(); ) { ConnectionHandler ch = (ConnectionHandler)i.next(); ch.iHave(); ch.send("+ " + address); } return; } } } private synchronized void updateChannelMap(String address, SwitchContainer sc) { if ( channelMap.get(address) != null ) { // The number of channels for a given 1-Wire address will never // change, they're burned into the hardware return; } channelMap.put(address, new Integer(sc.getChannelCount())); } protected synchronized void broadcastDeparture(String address) { if ( !deviceSet.contains(address) ) { // This is not ours return; } deviceSet.remove(address); switchMap.remove(address); serverMap.remove(address); announce(getAnnounce()); for ( Iterator i = iterator(); i.hasNext(); ) { ConnectionHandler ch = (ConnectionHandler)i.next(); ch.iHave(); for ( StringTokenizer st = new StringTokenizer(getDeviceSignature(address), " "); st.hasMoreTokens(); ) { ch.send("- " + st.nextToken()); } } } protected class ControlHandler extends ConnectionHandler { public ControlHandler(Socket socket, BufferedReader br, PrintWriter pw) { super(socket, br, pw); } protected void sayHello() { // VT: FIXME: Isn't this a duplication of the arrival/departure // notification handler? Set switchSet = new TreeSet(); for ( Iterator si = serverSet.iterator(); si.hasNext(); ) { OneWireServer s = (OneWireServer)si.next(); for ( Iterator ai = s.iterator(); ai.hasNext(); ) { String address = ai.next().toString(); DeviceContainer dc = s.getDeviceContainer(address); if ( dc instanceof SwitchContainer ) { switchSet.add(address); switchMap.put(address, dc); serverMap.put(address, s); } } } iHave(); } public synchronized void iHave() { String message = ""; int deviceCount = 0; for ( Iterator i = deviceSet.iterator(); i.hasNext(); ) { String address = i.next().toString(); message += getDeviceSignature(address); deviceCount += getChannelCount(address); if ( i.hasNext() ) { message += " "; } } send("IHAVE " + deviceCount + ": " + message); } protected CommandParser createParser() { return new ControlParser(); } protected class ControlParser extends CommandParser { protected void parse2(String command) throws Throwable { complain(LOG_INFO, CH_CTL, "Command received: '" + command + "'"); StringTokenizer st = new StringTokenizer(command, " "); // Have a problem here. Old format is: // // <addr> read <channel> // <addr> write <channel> value // // New format is: // // <addrc> read // <addrc> write value // // where <addrc> is <addr>:<channel>. // // At this point, old style direct TCP connector uses // old format, PnP aware code uses new format (which is // better because it treats a channel as an atomic // entity), so will have to parse both. An indication of // a new format is a colon in the first word. String address = st.nextToken(); if ( address.indexOf(":") != -1 ) { parseNew(address, st); } else { parseOld(address, st); } } private void parseOld(String address, StringTokenizer st) throws Throwable { String verb = st.nextToken(); String channel = st.nextToken(); boolean value = false; if ( "write".equalsIgnoreCase(verb) ) { String sValue = st.nextToken(); if ( "1".equals(sValue) ) { value = true; } else if ( "0".equals(sValue) ) { value = false; } else { throw new IllegalArgumentException("Illegal value: '" + sValue + "'"); } } execute(address, Integer.parseInt(channel), verb, value); } private void parseNew(String extendedAddress, StringTokenizer st) throws Throwable { int offset = extendedAddress.indexOf(":"); String address = extendedAddress.substring(0, offset); String channel = extendedAddress.substring(offset + 1); String verb = st.nextToken(); boolean value = false; complain(LOG_DEBUG, CH_CTL, "Extended: " + extendedAddress); complain(LOG_DEBUG, CH_CTL, "Address: " + address); complain(LOG_DEBUG, CH_CTL, "Channel: " + channel); complain(LOG_DEBUG, CH_CTL, "Verb: " + verb); if ( "write".equalsIgnoreCase(verb) ) { String sValue = st.nextToken(); if ( "1".equals(sValue) ) { value = true; } else if ( "0".equals(sValue) ) { value = false; } else { throw new IllegalArgumentException("Illegal value: '" + sValue + "'"); } complain(LOG_DEBUG, CH_CTL, "Value: " + value); } execute(address, Integer.parseInt(channel), verb, value); } private void execute(String address, int channel, String verb, boolean value) throws Throwable { SwitchContainer sc = (SwitchContainer)switchMap.get(address); if ( sc == null ) { send("E " + address + ":" + channel + " Device Not Present"); return; } if ( "read".equalsIgnoreCase(verb) ) { send(sc.read(channel) ? "1" : "0"); } else if ( "write".equalsIgnoreCase(verb) ) { // VT: FIXME: Watch for exceptions sc.write(channel, value); send("OK"); } } } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -