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