public void run() { // start a new thread // this thread has one task. It repeatedly reads from the input pipe // and writes modified data to the output pipe. This is the heart // of the command station simulation. if (log.isDebugEnabled()) { log.debug("Simulator Thread Started"); } Random rgen = new Random(); ConnectionStatus.instance() .setConnectionState(this.getCurrentPortName(), ConnectionStatus.CONNECTION_UP); for (; ; ) { DCCppMessage m = readMessage(); if (log.isDebugEnabled()) { log.debug("Simulator Thread received message " + m.toString()); } DCCppReply r = generateReply(m); // If generateReply() returns null, do nothing. No reply to send. if (r != null) { writeReply(r); if (log.isDebugEnabled()) { log.debug("Simulator Thread sent Reply" + r.toString()); } } // Once every SENSOR_MSG_RATE loops, generate a random Sensor message. int rand = rgen.nextInt(SENSOR_MSG_RATE); if (rand == 1) { generateRandomSensorReply(); } } }
/** * Get characters from the input source, and file a message. * * <p>Returns only when the message is complete. * * <p>Only used in the Receive thread. * * @returns filled message * @throws IOException when presented by the input source. */ private DCCppMessage loadChars() throws java.io.IOException { // Spin waiting for start-of-frame '<' character (and toss it) String s = new String(); byte char1; boolean found_start = false; while (!found_start) { char1 = readByteProtected(inpipe); if ((char1 & 0xFF) == '<') { found_start = true; log.debug("Found starting < "); break; // A bit redundant with setting the loop condition true (false) } else { char1 = readByteProtected(inpipe); } } // Now, suck in the rest of the message... for (int i = 0; i < DCCppConstants.MAX_MESSAGE_SIZE; i++) { char1 = readByteProtected(inpipe); if (char1 == '>') { log.debug("msg found > "); // Don't store the > break; } else { log.debug("msg read byte {}", char1); char c = (char) (char1 & 0x00FF); s += Character.toString(c); } } // TODO: Still need to strip leading and trailing whitespace. log.debug("Complete message = {}", s.toString()); return (DCCppMessage.parseDCCppMessage(s)); }
// generateReply is the heart of the simulation. It translates an // incoming DCCppMessage into an outgoing DCCppReply. @SuppressWarnings("fallthrough") private DCCppReply generateReply(DCCppMessage msg) { String s, r; Pattern p; Matcher m; DCCppReply reply = null; log.debug("Generate Reply to message type {} string = {}", msg.getElement(0), msg.toString()); switch (msg.getElement(0)) { case DCCppConstants.THROTTLE_CMD: log.debug("THROTTLE_CMD detected"); s = msg.toString(); try { p = Pattern.compile(DCCppConstants.THROTTLE_CMD_REGEX); m = p.matcher(s); if (!m.matches()) { log.error("Malformed Throttle Command: {}", s); return (null); } r = "T " + m.group(1) + " " + m.group(3) + " " + m.group(4); reply = DCCppReply.parseDCCppReply(r); log.debug("Reply generated = {}", reply.toString()); } catch (PatternSyntaxException e) { log.error("Malformed pattern syntax! "); return (null); } catch (IllegalStateException e) { log.error("Group called before match operation executed string= " + s); return (null); } catch (IndexOutOfBoundsException e) { log.error("Index out of bounds string= " + s); return (null); } break; case DCCppConstants.TURNOUT_CMD: if (msg.isTurnoutAddMessage()) { log.debug("Add Turnout Message"); r = "O"; } else if (msg.isTurnoutDeleteMessage()) { log.debug("Delete Turnout Message"); r = "O"; } else if (msg.isListTurnoutsMessage()) { log.debug("List Turnouts Message"); r = "H 1 27 3 1"; } else { log.debug("TURNOUT_CMD detected"); r = "H" + msg.getTOIDString() + " " + msg.getTOStateString(); } reply = DCCppReply.parseDCCppReply(r); log.debug("Reply generated = {}", reply.toString()); break; case DCCppConstants.SENSOR_CMD: if (msg.isSensorAddMessage()) { log.debug("SENSOR_CMD Add detected"); s = msg.toString(); r = "O"; // TODO: Randomize? } else if (msg.isSensorDeleteMessage()) { log.debug("SENSOR_CMD Delete detected"); s = msg.toString(); r = "O"; // TODO: Randomize? } else if (msg.isListSensorsMessage()) { r = "Q 1 4 1"; // TODO: DO this for real. } else { log.debug("Invalid SENSOR_CMD detected"); r = "X"; } reply = DCCppReply.parseDCCppReply(r); log.debug("Reply generated = {}", reply.toString()); break; case DCCppConstants.PROG_WRITE_CV_BYTE: log.debug("PROG_WRITE_CV_BYTE detected"); s = msg.toString(); try { p = Pattern.compile(DCCppConstants.PROG_WRITE_BYTE_REGEX); m = p.matcher(s); if (!m.matches()) { log.error("Malformed ProgWriteCVByte Command: {}", s); return (null); } r = "r " + m.group(3) + " " + m.group(4) + " " + m.group(2); CVs[Integer.parseInt(m.group(1))] = Integer.parseInt(m.group(2)); reply = DCCppReply.parseDCCppReply(r); log.debug("Reply generated = {}", reply.toString()); } catch (PatternSyntaxException e) { log.error("Malformed pattern syntax! "); return (null); } catch (IllegalStateException e) { log.error("Group called before match operation executed string= " + s); return (null); } catch (IndexOutOfBoundsException e) { log.error("Index out of bounds string= " + s); return (null); } break; case DCCppConstants.PROG_WRITE_CV_BIT: log.debug("PROG_WRITE_CV_BIT detected"); s = msg.toString(); try { p = Pattern.compile(DCCppConstants.PROG_WRITE_BIT_REGEX); m = p.matcher(s); if (!m.matches()) { log.error("Malformed ProgWriteCVBit Command: {}", s); return (null); } r = "r " + m.group(4) + " " + m.group(5) + " " + m.group(3); int idx = Integer.parseInt(m.group(1)); int bit = Integer.parseInt(m.group(2)); int v = Integer.parseInt(m.group(3)); if (v == 1) CVs[idx] = CVs[idx] | (0x0001 << bit); else CVs[idx] = CVs[idx] & ~(0x0001 << bit); reply = DCCppReply.parseDCCppReply(r); log.debug("Reply generated = {}", reply.toString()); } catch (PatternSyntaxException e) { log.error("Malformed pattern syntax! "); return (null); } catch (IllegalStateException e) { log.error("Group called before match operation executed string= " + s); return (null); } catch (IndexOutOfBoundsException e) { log.error("Index out of bounds string= " + s); return (null); } break; case DCCppConstants.PROG_READ_CV: log.debug("PROG_READ_CV detected"); s = msg.toString(); try { p = Pattern.compile(DCCppConstants.PROG_READ_REGEX); m = p.matcher(s); if (!m.matches()) { log.error("Malformed PROG_READ_CV Command: {}", s); return (null); } // TODO: Work Magic Here to retrieve stored value. int cv = CVs[Integer.parseInt(m.group(1))]; r = "r " + m.group(2) + " " + m.group(3) + " " + Integer.toString(cv); reply = DCCppReply.parseDCCppReply(r); log.debug("Reply generated = {}", reply.toString()); } catch (PatternSyntaxException e) { log.error("Malformed pattern syntax! "); return (null); } catch (IllegalStateException e) { log.error("Group called before match operation executed string= " + s); return (null); } catch (IndexOutOfBoundsException e) { log.error("Index out of bounds string= " + s); return (null); } break; case DCCppConstants.TRACK_POWER_ON: log.debug("TRACK_POWER_ON detected"); TrackPowerState = true; reply = DCCppReply.parseDCCppReply("p1"); log.debug("Reply generated = {}", reply.toString()); break; case DCCppConstants.TRACK_POWER_OFF: log.debug("TRACK_POWER_OFF detected"); TrackPowerState = false; reply = DCCppReply.parseDCCppReply("p0"); log.debug("Reply generated = {}", reply.toString()); break; case DCCppConstants.READ_TRACK_CURRENT: log.debug("READ_TRACK_CURRENT detected"); int randint = 480 + rgen.nextInt(64); reply = DCCppReply.parseDCCppReply("a " + (TrackPowerState ? Integer.toString(randint) : "0")); log.debug("Reply generated = {}", reply.toString()); break; case DCCppConstants.READ_CS_STATUS: log.debug("READ_CS_STATUS detected"); generateReadCSStatusReply(); // Handle this special. break; /* case DCCppConstants.QUERY_SENSOR_STATE: // Obsolete ?? log.debug("QUERY_SENSOR_STATUS detected"); s = msg.toString(); try { p = Pattern.compile(DCCppConstants.QUERY_SENSOR_REGEX); m = p.matcher(s); if (!m.matches()) { log.error("Malformed Sensor Query Command: {}", s); return(null); } r = "Q " + m.group(1) + " "; // Fake reply: Odd sensors always active, even always inactive. r += ((Integer.parseInt(m.group(1)) % 2) == 1 ? "1" : "0"); reply = new DCCppReply(r); log.debug("Reply generated = {}", reply.toString()); } catch (PatternSyntaxException e) { log.error("Malformed pattern syntax! "); return(null); } catch (IllegalStateException e) { log.error("Group called before match operation executed string= " + s); return(null); } catch (IndexOutOfBoundsException e) { log.error("Index out of bounds string= " + s); return(null); } break; */ case DCCppConstants.FUNCTION_CMD: case DCCppConstants.ACCESSORY_CMD: case DCCppConstants.OPS_WRITE_CV_BYTE: case DCCppConstants.OPS_WRITE_CV_BIT: case DCCppConstants.WRITE_DCC_PACKET_MAIN: case DCCppConstants.WRITE_DCC_PACKET_PROG: log.debug("non-reply message detected"); // Send no reply. return (null); default: return (null); } return (reply); }