package edu.colostate.vchill.socket; import edu.colostate.vchill.ChillDefines; import edu.colostate.vchill.ChillDefines.Channel; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.UnsupportedEncodingException; /** * CHILL format data structure for storing archive control packets * (as expected by the archive server) * * @author Jochen Deyke * @author Alexander Deyke * @version 2007-09-10 */ public final class SocketArchCtl { private static final int IN_FILE_LENGTH = 100; public static final int BYTE_SIZE = //116 ChillDefines.INT_BYTE_SIZE + ChillDefines.SHORT_BYTE_SIZE * 4 + ChillDefines.INT_BYTE_SIZE + IN_FILE_LENGTH; /** * What do we want from the server? */ public enum Command { /** * not used */ unknown, /** * not used by Java VCHILL */ RAY_MODE, /** * request data for a sweep from a file */ SWEEP_MODE, /** * not used by Java VCHILL */ VOLUME_MODE, /** * not used by Java VCHILL */ NOSTOP_MODE, /** * get details on a file */ STATUS_REQ, /** * ask server to stop sending current sweep */ HALT_COMMAND, /** * not used by Java VCHILL */ SWEEP_SEEK, /** * list directories, files, both, or bookmarks available */ DIRECTORY_REQ, /** * initial connection to the server */ CONNECT_REQ, /** * close session with server */ DISCONNECT_REQ, } /** * Subtypes for Command.DIRECTORY_REQ requests */ public enum DirType { /** * not used */ unknown, /** * no longer used - now using CONTENTS instead */ DIRECTORIES, /** * no longer used - now using CONTENTS instead */ FILES, /** * no longer used - now using xml based bookmarks from webserver instead */ BOOKMARKS, /** * generic for files and subdirs */ CONTENTS, } /** * type of request */ private final Command archMode; /** * starting sweep number: 0=start from current */ private final short startSweep; /** * num rays to read */ private final short rayStep; /** * sweep filter (not used?) */ private final short sweepLow, sweepHigh; /** * added delay between reads (always 0) */ private final int extraDelay; /** * ascii filename, length = 100 */ private final byte inFile[]; /** * Sole constructor * * @param archMode the type of request * @param startSweep the starting sweep number, 0 = start from current * @param rayStep the number of rays to read * @param sweepLow sweep filter (not currently used) * @param sweepHigh sweep filter (not currently used) * @param extraDelay added delay between reads (always 0) * @param inFile filename, maximum 99 characters (plus terminating 0) */ public SocketArchCtl( final Command archMode, final short startSweep, final short rayStep, final short sweepLow, final short sweepHigh, final int extraDelay, final String inFile) { this.archMode = archMode; assert archMode != Command.unknown; this.startSweep = startSweep; this.rayStep = rayStep; this.sweepLow = sweepLow; this.sweepHigh = sweepHigh; this.extraDelay = extraDelay; this.inFile = new byte[IN_FILE_LENGTH]; try { //translate String to byte[] byte[] tmp = inFile.getBytes("US-ASCII"); if (tmp.length >= IN_FILE_LENGTH) { throw new Error("inFile too long"); } for (int i = 0; i < IN_FILE_LENGTH; ++i) { this.inFile[i] = ((i < tmp.length) ? tmp[i] : 0); } } catch (UnsupportedEncodingException uee) { throw new Error(uee); } } /** * @param in the DataInput to load values from */ public SocketArchCtl(final DataInput in) { try { this.archMode = Command.values()[in.readInt()]; this.startSweep = in.readShort(); this.rayStep = in.readShort(); this.sweepLow = in.readShort(); this.sweepHigh = in.readShort(); this.extraDelay = in.readInt(); in.readFully(this.inFile = new byte[IN_FILE_LENGTH]); } catch (IOException ioe) { throw new Error(ioe); } } /** * @param data the byte[] to load values from * @param offset the index of the first byte to read */ public SocketArchCtl(final byte[] data, int offset) { if (data.length < offset + SocketArchCtl.BYTE_SIZE - 1) { throw new IllegalArgumentException("not enough data in input array"); } this.archMode = Command.values()[SocketUtil.readInt(data, offset)]; offset += ChillDefines.INT_BYTE_SIZE; this.startSweep = SocketUtil.readShort(data, offset); offset += ChillDefines.SHORT_BYTE_SIZE; this.rayStep = SocketUtil.readShort(data, offset); offset += ChillDefines.SHORT_BYTE_SIZE; this.sweepLow = SocketUtil.readShort(data, offset); offset += ChillDefines.SHORT_BYTE_SIZE; this.sweepHigh = SocketUtil.readShort(data, offset); offset += ChillDefines.SHORT_BYTE_SIZE; this.extraDelay = SocketUtil.readInt(data, offset); offset += ChillDefines.INT_BYTE_SIZE; this.inFile = new byte[IN_FILE_LENGTH]; for (int i = 0; i < IN_FILE_LENGTH; ++i) this.inFile[i] = data[offset++]; assert offset == SocketArchCtl.BYTE_SIZE : "number of bytes read != size"; } public SocketArchCtl(final byte[] data) { this(data, 0); } /** * @param out the DataOutput to write values to */ public void write(final DataOutput out) { try { out.writeInt(this.archMode.ordinal()); out.writeShort(this.startSweep); out.writeShort(this.rayStep); out.writeShort(this.sweepLow); out.writeShort(this.sweepHigh); out.writeInt(this.extraDelay); out.write(this.inFile); } catch (IOException ioe) { throw new Error(ioe); } } /** * Translates this SocketArchCtl object to a byte[] for transmission over the network * * @return byte[] representation of this SocketArchCtl object */ public byte[] getBytes() { final byte[] output = new byte[SocketArchCtl.BYTE_SIZE]; int offset = 0; offset += SocketUtil.writeInt(this.archMode.ordinal(), output, offset); offset += SocketUtil.writeShort(this.startSweep, output, offset); offset += SocketUtil.writeShort(this.rayStep, output, offset); offset += SocketUtil.writeShort(this.sweepLow, output, offset); offset += SocketUtil.writeShort(this.sweepHigh, output, offset); offset += SocketUtil.writeInt(this.extraDelay, output, offset); offset += SocketUtil.copyBytes(this.inFile, 0, output, offset, IN_FILE_LENGTH); assert offset == SocketArchCtl.BYTE_SIZE : "Num bytes returned != size"; return output; } /** * Translates this SocketArchCtl object, prefaced by a command code identifing this as the control channel, to a byte[] for transmission over the network * * @return byte[] representation of this SocketArchCtl object prefaced by ARCH_CTL_CHANNEL */ public byte[] prepareFirstPacket() { final byte[] output = new byte[SocketArchCtl.BYTE_SIZE + ChillDefines.INT_BYTE_SIZE]; int offset = 0; offset += SocketUtil.writeInt(Channel.ARCH_CTL.ordinal(), output, offset); offset += SocketUtil.writeInt(this.archMode.ordinal(), output, offset); offset += SocketUtil.writeShort(this.startSweep, output, offset); offset += SocketUtil.writeShort(this.rayStep, output, offset); offset += SocketUtil.writeShort(this.sweepLow, output, offset); offset += SocketUtil.writeShort(this.sweepHigh, output, offset); offset += SocketUtil.writeInt(this.extraDelay, output, offset); offset += SocketUtil.copyBytes(this.inFile, 0, output, offset, IN_FILE_LENGTH); assert offset == SocketArchCtl.BYTE_SIZE + ChillDefines.INT_BYTE_SIZE : "Num bytes returned != size + ChillDefines.INT_BYTE_SIZE"; return output; } public Command getArchMode() { return archMode; } public short getStartSweep() { return startSweep; } public short getRayStep() { return rayStep; } public short getSweepLow() { return sweepLow; } public short getSweepHigh() { return sweepHigh; } public int getExtraDelay() { return extraDelay; } public String getInFile() { return new String(inFile).trim(); } }