001package jmri.jmrix.lenz.messageformatters; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import jmri.jmrix.Message; 005import jmri.jmrix.lenz.FeedbackItem; 006import jmri.jmrix.lenz.XNetConstants; 007import jmri.jmrix.lenz.XNetReply; 008import jmri.jmrix.lenz.XPressNetMessageFormatter; 009 010import java.util.Optional; 011 012/** 013 * Format XPressNet feedback reply messages for display. 014 * 015 * @author Paul Bender Copyright (C) 2024 016 */ 017public class XNetFeedbackReplyFormatter implements XPressNetMessageFormatter { 018 private static final String COLUMN_STATE = "ColumnState"; 019 private static final String MAKE_LABEL = "MakeLabel"; 020 private static final String POWER_STATE_ON = "PowerStateOn"; 021 private static final String POWER_STATE_OFF = "PowerStateOff"; 022 private static final String BEAN_NAME_TURNOUT = "BeanNameTurnout"; 023 private static final String X_NET_REPLY_NOT_OPERATED = "XNetReplyNotOperated"; 024 private static final String X_NET_REPLY_THROWN_LEFT = "XNetReplyThrownLeft"; 025 private static final String X_NET_REPLY_THROWN_RIGHT = "XNetReplyThrownRight"; 026 private static final String X_NET_REPLY_INVALID = "XNetReplyInvalid"; 027 private static final String X_NET_REPLY_CONTACT_LABEL = "XNetReplyContactLabel"; 028 029 @Override 030 public boolean handlesMessage(Message m) { 031 return m instanceof XNetReply && m.getElement(0) == XNetConstants.ACC_INFO_RESPONSE; 032 } 033 034 @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "cast is checked in handlesMessage") 035 @Override 036 public String formatMessage(Message m) { 037 if (!handlesMessage(m)) { 038 throw new IllegalArgumentException("Message not supported"); 039 } 040 StringBuilder text = new StringBuilder().append(Bundle.getMessage("XNetReplyFeedbackLabel")).append(" "); 041 XNetReply r = (XNetReply) m; 042 int numDataBytes = r.getElement(0) & 0x0f; 043 for (int i = 1; i < numDataBytes; i += 2) { 044 switch (r.getFeedbackMessageType(i)) { 045 case 0: 046 text.append(getTurnoutReplyMonitorString(i, "TurnoutWoFeedback",r)); 047 break; 048 case 1: 049 text.append(getTurnoutReplyMonitorString(i, "TurnoutWFeedback",r)); 050 break; 051 case 2: 052 text.append(getSensorFeedBackReplyMonitorString(r, i)); 053 break; 054 default: 055 text.append(r.getElement(i)).append(" ").append(r.getElement(i + 1)); 056 } 057 } 058 return text.toString(); 059 } 060 061 private String getSensorFeedBackReplyMonitorString(XNetReply r, int i) { 062 StringBuilder text = new StringBuilder(); 063 text.append(Bundle.getMessage("XNetReplyFeedbackEncoder")).append(" ").append(r.getFeedbackEncoderMsgAddr(i)); 064 boolean highnibble = ((r.getElement(i + 1) & 0x10) == 0x10); 065 text.append(" ").append(Bundle.getMessage(X_NET_REPLY_CONTACT_LABEL)).append(" ").append(highnibble ? 5 : 1); 066 067 text.append(" ").append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(COLUMN_STATE))).append(" ") 068 .append(((r.getElement(i + 1) & 0x01) == 0x01) ? Bundle.getMessage(POWER_STATE_ON) : Bundle.getMessage(POWER_STATE_OFF)); 069 text.append("; ").append(Bundle.getMessage(X_NET_REPLY_CONTACT_LABEL)).append(" ").append(highnibble ? 6 : 2); 070 071 text.append(" ").append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(COLUMN_STATE))).append(" ") 072 .append(((r.getElement(i + 1) & 0x02) == 0x02) ? Bundle.getMessage(POWER_STATE_ON) : Bundle.getMessage(POWER_STATE_OFF)); 073 text.append("; ").append(Bundle.getMessage(X_NET_REPLY_CONTACT_LABEL)).append(" ").append(highnibble ? 7 : 3); 074 075 text.append(" ").append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(COLUMN_STATE))).append(" ") 076 .append(((r.getElement(i + 1) & 0x04) == 0x04) ? Bundle.getMessage(POWER_STATE_ON) : Bundle.getMessage(POWER_STATE_OFF)); 077 text.append("; ").append(Bundle.getMessage(X_NET_REPLY_CONTACT_LABEL)).append(" ").append(highnibble ? 8 : 4); 078 079 text.append(" ").append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(COLUMN_STATE))).append(" ") 080 .append(((r.getElement(i + 1) & 0x08) == 0x08) ? Bundle.getMessage(POWER_STATE_ON) : Bundle.getMessage(POWER_STATE_OFF)); 081 text.append("; "); 082 return text.toString(); 083 } 084 085 private String getTurnoutReplyMonitorString(int startByte, String typeBundleKey,XNetReply r) { 086 StringBuilder text = new StringBuilder(); 087 int turnoutMsgAddr = r.getTurnoutMsgAddr(startByte); 088 Optional<FeedbackItem> feedBackOdd = r.selectTurnoutFeedback(turnoutMsgAddr); 089 if(feedBackOdd.isPresent()){ 090 FeedbackItem feedbackItem = feedBackOdd.get(); 091 text.append(singleTurnoutMonitorMessage(Bundle.getMessage(typeBundleKey), turnoutMsgAddr, feedbackItem)); 092 text.append(";"); 093 FeedbackItem pairedItem = feedbackItem.pairedAccessoryItem(); 094 text.append(singleTurnoutMonitorMessage("", turnoutMsgAddr + 1, pairedItem)); 095 096 } 097 return text.toString(); 098 } 099 100 private String singleTurnoutMonitorMessage(String prefix, int turnoutMsgAddr, FeedbackItem feedbackItem) { 101 StringBuilder outputBuilder = new StringBuilder(); 102 outputBuilder.append(prefix).append(" ") 103 .append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(BEAN_NAME_TURNOUT))).append(" ") 104 .append(turnoutMsgAddr).append(" ").append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(COLUMN_STATE))).append(" "); 105 switch (feedbackItem.getAccessoryStatus()){ 106 case 0: 107 outputBuilder.append(Bundle.getMessage(X_NET_REPLY_NOT_OPERATED)); // last items on line, no trailing space 108 break; 109 case 1: 110 outputBuilder.append(Bundle.getMessage(X_NET_REPLY_THROWN_LEFT)); 111 break; 112 case 2: 113 outputBuilder.append(Bundle.getMessage(X_NET_REPLY_THROWN_RIGHT)); 114 break; 115 default: 116 outputBuilder.append(Bundle.getMessage(X_NET_REPLY_INVALID)); 117 } 118 if(feedbackItem.getType()==1){ 119 outputBuilder.append(" "); 120 if(feedbackItem.isMotionComplete()){ 121 outputBuilder.append(Bundle.getMessage("XNetReplyMotionComplete")); 122 } else { 123 outputBuilder.append(Bundle.getMessage("XNetReplyMotionIncomplete")); 124 } 125 } 126 return outputBuilder.toString(); 127 } 128 129} 130