001package jmri.jmrix.roco.z21; 002 003import java.util.ArrayList; 004import jmri.CollectingReporter; 005import jmri.DccLocoAddress; 006import jmri.InstanceManager; 007import jmri.RailCom; 008import jmri.RailComManager; 009 010/** 011 * Z21CanReporter implements the Reporter Manager interface 012 * for Can connected reporters on Roco Z21 systems. 013 * <p> 014 * Reports from this reporter are of the type jmri.RailCom. 015 * 016 * @author Paul Bender Copyright (C) 2016 017 */ 018public class Z21CanReporter extends jmri.implementation.AbstractRailComReporter implements Z21Listener,CollectingReporter { 019 020 private Z21SystemConnectionMemo _memo; 021 022 private int networkID=0; // CAN network ID associated with this reporter's module. 023 private int moduleAddress=-1; // User assigned address associated with this reporter's module. 024 private int port; // module port (0-7) associated with this reporter. 025 026 private ArrayList<Object> idTags; 027 028 /** 029 * Create a new Z21CanReporter. 030 * 031 * @param systemName the system name of the new reporter. 032 * @param userName the user name of the new reporter. 033 * @param memo an instance of Z21SystemConnectionMemo this reporter 034 * is associated with. 035 * 036 */ 037 public Z21CanReporter(String systemName,String userName,Z21SystemConnectionMemo memo){ 038 super(systemName,userName); 039 _memo = memo; 040 _memo.getTrafficController().addz21Listener(this); 041 //Address format passed is in the form of moduleAddress:pin 042 try { 043 setIdentifiersFromSystemName(systemName); 044 } catch (NumberFormatException ex) { 045 log.debug("Unable to convert {} into the cab and input format of nn:xx",systemName); 046 throw new IllegalArgumentException("requires mm:pp format address."); 047 } 048 idTags = new ArrayList<>(); 049 // request an update from the layout 050 //if(networkID!=0){ 051 //leave commented out for now, causing loop that needs investigation. 052 // _memo.getTrafficController().sendz21Message(Z21Message.getLanCanDetector(networkID),this); 053 //} 054 } 055 056 /** 057 * {@inheritDoc} 058 */ 059 @Override 060 public boolean hasExitReports() { 061 return true; 062 } 063 064 private void setIdentifiersFromSystemName(String systemName){ 065 String moduleAddressText = Z21CanBusAddress.getEncoderAddressString(systemName,_memo.getSystemPrefix()); 066 try{ 067 moduleAddress = Integer.parseInt(moduleAddressText); 068 } catch (NumberFormatException ex) { 069 // didn't parse as a decimal, check to see if network ID 070 // was used instead. 071 networkID = Integer.parseInt(moduleAddressText,16); 072 } 073 port = Z21CanBusAddress.getBitFromSystemName(systemName,_memo.getSystemPrefix()); 074 } 075 076 // the Z21 Listener interface 077 078 /** 079 * {@inheritDoc} 080 */ 081 @Override 082 public void reply(Z21Reply msg){ 083 // for incoming messages all the reporter cares about is 084 // LAN_CAN_DETECTOR messages. 085 if(msg.isCanReporterMessage()){ 086 int netID = ( msg.getElement(4)&0xFF) + ((msg.getElement(5)&0xFF) << 8); 087 int address = ( msg.getElement(6)&0xFF) + ((msg.getElement(7)&0xFF) << 8); 088 int msgPort = ( msg.getElement(8) & 0xFF); 089 if(!messageForReporter(address,netID,msgPort)) { 090 return; // not our messge. 091 } 092 int type = ( msg.getElement(9) & 0xFF); 093 if(type==0x11) { // restart the list. 094 log.trace("clear list, size {}",idTags.size()); 095 idTags.clear(); 096 notify(null); 097 } 098 int value1 = (msg.getElement(10)&0xFF) + ((msg.getElement(11)&0xFF) << 8); 099 int value2 = (msg.getElement(12)&0xFF) + ((msg.getElement(13)&0xFF) << 8); 100 RailCom tag = getRailComTagFromValue(value1); 101 if(tag != null ) { 102 log.trace("add tag {}",tag); 103 notify(tag); 104 idTags.add(tag); 105 // add the tag to the collection 106 tag = getRailComTagFromValue(value2); 107 if(tag != null ) { 108 log.trace("add tag {} ",tag); 109 notify(tag); 110 // add the tag to idTags 111 idTags.add(tag); 112 } 113 } 114 if(log.isDebugEnabled()){ 115 log.debug("after message, new list size {}",idTags.size()); 116 int i = 0; 117 for(Object id:idTags){ 118 log.debug("tag {}: {}",i++,id); 119 } 120 } 121 } 122 } 123 124 private boolean messageForReporter(int address,int netId,int msgPort){ 125 return (address == moduleAddress || netId == networkID) && msgPort == port; 126 } 127 /* 128 * private method to get and update a railcom tag based on the value 129 * bytes from the message. 130 */ 131 private RailCom getRailComTagFromValue(int value){ 132 DccLocoAddress l = Z21MessageUtils.getCanDetectorLocoAddress(value); 133 if (l != null ) { // 0 represents end of list or no railcom address. 134 // get the first locomotive address from the message. 135 log.debug("reporting tag for address 1 {}",l); 136 // see if there is a tag for this address. 137 RailCom tag = (RailCom) InstanceManager.getDefault(RailComManager.class).provideIdTag("" + l.getNumber()); 138 int direction = (0xC000&value); 139 switch (direction) { 140 case 0x8000: 141 tag.setOrientation(RailCom.Orientation.EAST); 142 break; 143 case 0xC000: 144 tag.setOrientation(RailCom.Orientation.WEST); 145 break; 146 default: 147 tag.setOrientation(RailCom.Orientation.UNKNOWN); 148 } 149 return tag; 150 } 151 return null; // address in the message indicates end of list. 152 } 153 154 /** 155 * {@inheritDoc} 156 */ 157 @Override 158 public void message(Z21Message msg){ 159 // we don't need to handle outgoing messages, so just ignore them. 160 } 161 162 // the CollectingReporter interface. 163 /** 164 * {@inheritDoc} 165 */ 166 @Override 167 public java.util.Collection<Object> getCollection(){ 168 return idTags; 169 } 170 171 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Z21CanReporter.class); 172 173}