001package jmri.jmrix.mqtt; 002 003import javax.annotation.Nonnull; 004import jmri.implementation.*; 005 006/** 007 * SignalMast implemented via MQTT messages 008 * <p> 009 * System name specifies the creation information: 010 * <pre> 011 * IF$mqm:basic:one-searchlight($0001) 012 * </pre> The name is a colon-separated series of terms: 013 * <ul> 014 * <li>IF$mqm - defines signal masts of this type 015 * <li>basic - name of the signaling system 016 * <li>one-searchlight - name of the particular aspect map 017 * <li>($0001) - small ordinal number for telling various signal masts apart 018 * apart 019 * </ul> 020 * 021 * @author Bob Jacobsen Copyright (C) 2009, 2021 022 */ 023public class MqttSignalMast extends AbstractSignalMast { 024 025 public MqttSignalMast(String systemName, String userName) { 026 super(systemName, userName); 027 configureFromName(systemName); 028 sendTopic = makeSendTopic(systemName); 029 mqttAdapter = jmri.InstanceManager.getDefault(MqttSystemConnectionMemo.class).getMqttAdapter(); 030 } 031 032 public MqttSignalMast(String systemName) { 033 super(systemName); 034 configureFromName(systemName); 035 sendTopic = makeSendTopic(systemName); 036 mqttAdapter = jmri.InstanceManager.getDefault(MqttSystemConnectionMemo.class).getMqttAdapter(); 037 } 038 039 @Nonnull 040 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "MS_PKGPROTECT", 041 justification = "Public accessibility for scripts that update the prefixes") 042 public static String sendTopicPrefix = "track/signalmast/"; // default for constructing topic; public for script access, set by config 043 044 public static void setSendTopicPrefix(@Nonnull String prefix) { 045 sendTopicPrefix = prefix; 046 log.debug("sendTopicPrefix set to {}", prefix); 047 } 048 049 protected String makeSendTopic(String systemName) { 050 String[] pieces = systemName.split("\\("); 051 if (pieces.length == 2) { 052 String result = pieces[1].substring(1, pieces[1].length()-1); // starts with ($) 053 return sendTopicPrefix+result; 054 } else { 055 log.warn("not just one '(' in {}", systemName); 056 return sendTopicPrefix+systemName; 057 } 058 } 059 060 private static final String mastType = "IF$mqm"; 061 062 private final String sendTopic; 063 private MqttAdapter mqttAdapter; 064 065 private void configureFromName(String systemName) { 066 // split out the basic information 067 String[] parts = systemName.split(":"); 068 if (parts.length < 3) { 069 log.error("SignalMast system name needs at least three parts: {}", systemName); 070 throw new IllegalArgumentException("System name needs at least three parts: " + systemName); 071 } 072 if (!parts[0].equals(mastType)) { 073 log.warn("SignalMast system name should start with {} but is {}", mastType, systemName); 074 } 075 076 String system = parts[1]; 077 078 String mast = parts[2]; 079 // new style 080 mast = mast.substring(0, mast.indexOf("(")); 081 setMastType(mast); 082 String tmp = parts[2].substring(parts[2].indexOf("($") + 2, parts[2].indexOf(")")); 083 try { 084 log.debug("Parse {} as integer from {}?", tmp, parts[2]); 085 int autoNumber = Integer.parseInt(tmp); 086 synchronized (MqttSignalMast.class) { 087 if (autoNumber > getLastRef()) { 088 setLastRef(autoNumber); 089 } 090 } 091 } catch (NumberFormatException e) { 092 log.debug("Auto generated SystemName {} does not have numeric form, skipping autoincrement", systemName); 093 } 094 configureSignalSystemDefinition(system); 095 configureAspectTable(system, mast); 096 } 097 098 @Override 099 public void setAspect(@Nonnull String aspect) { 100 // check it's a choice 101 if (!map.checkAspect(aspect)) { 102 // not a valid aspect 103 log.warn("attempting to set invalid aspect: {} on mast: {}", aspect, getDisplayName()); 104 throw new IllegalArgumentException("attempting to set invalid aspect: " + aspect + " on mast: " + getDisplayName()); 105 } else if (disabledAspects.contains(aspect)) { 106 log.warn("attempting to set an aspect that has been disabled: {} on mast: {}", aspect, getDisplayName()); 107 throw new IllegalArgumentException("attempting to set an aspect that has been disabled: " + aspect + " on mast: " + getDisplayName()); 108 } 109 log.debug("Setting aspect {}", aspect); 110 super.setAspect(aspect); 111 report(); 112 } 113 114 @Override 115 public void setHeld(boolean held) { 116 log.debug("Setting held {}", held); 117 super.setHeld(held); 118 report(); 119 } 120 121 @Override 122 public void setLit(boolean lit) { 123 log.debug("Setting lit {}", lit); 124 super.setLit(lit); 125 report(); 126 } 127 128 private void report() { 129 String msg = aspect+"; "; 130 msg = msg+ (getLit()?"Lit; ":"Unlit; "); 131 msg = msg+ (getHeld()?"Held":"Unheld"); 132 sendMessage(msg); 133 } 134 private void sendMessage(String c) { 135 log.debug("publishing \"{}\" on \"{}\"", c, sendTopic); 136 mqttAdapter.publish(sendTopic, c); 137 } 138 139 /** 140 * 141 * @param newVal for ordinal of all MqttSignalMasts in use 142 */ 143 protected static void setLastRef(int newVal) { 144 lastRef = newVal; 145 } 146 147 /** 148 * @return highest ordinal of all MqttSignalMasts in use 149 */ 150 public static int getLastRef() { 151 return lastRef; 152 } 153 154 /** 155 * Ordinal of all MqttSignalMasts to create unique system name. 156 */ 157 private static volatile int lastRef = 0; 158 159 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MqttSignalMast.class); 160 161}