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 private void setIdentifiersFromSystemName(String systemName){ 057 String moduleAddressText = Z21CanBusAddress.getEncoderAddressString(systemName,_memo.getSystemPrefix()); 058 try{ 059 moduleAddress = Integer.parseInt(moduleAddressText); 060 } catch (NumberFormatException ex) { 061 // didn't parse as a decimal, check to see if network ID 062 // was used instead. 063 networkID = Integer.parseInt(moduleAddressText,16); 064 } 065 port = Z21CanBusAddress.getBitFromSystemName(systemName,_memo.getSystemPrefix()); 066 } 067 068 // the Z21 Listener interface 069 070 /** 071 * {@inheritDoc} 072 */ 073 @Override 074 public void reply(Z21Reply msg){ 075 // for incoming messages all the reporter cares about is 076 // LAN_CAN_DETECTOR messages. 077 if(msg.isCanReporterMessage()){ 078 int netID = ( msg.getElement(4)&0xFF) + ((msg.getElement(5)&0xFF) << 8); 079 int address = ( msg.getElement(6)&0xFF) + ((msg.getElement(7)&0xFF) << 8); 080 int msgPort = ( msg.getElement(8) & 0xFF); 081 if(!messageForReporter(address,netID,msgPort)) { 082 return; // not our messge. 083 } 084 int type = ( msg.getElement(9) & 0xFF); 085 if(type==0x11) { // restart the list. 086 log.trace("clear list, size {}",idTags.size()); 087 idTags.clear(); 088 notify(null); 089 } 090 int value1 = (msg.getElement(10)&0xFF) + ((msg.getElement(11)&0xFF) << 8); 091 int value2 = (msg.getElement(12)&0xFF) + ((msg.getElement(13)&0xFF) << 8); 092 RailCom tag = getRailComTagFromValue(value1); 093 if(tag != null ) { 094 log.trace("add tag {}",tag); 095 notify(tag); 096 idTags.add(tag); 097 // add the tag to the collection 098 tag = getRailComTagFromValue(value2); 099 if(tag != null ) { 100 log.trace("add tag {} ",tag); 101 notify(tag); 102 // add the tag to idTags 103 idTags.add(tag); 104 } 105 } 106 if(log.isDebugEnabled()){ 107 log.debug("after message, new list size {}",idTags.size()); 108 int i = 0; 109 for(Object id:idTags){ 110 log.debug("tag {}: {}",i++,id); 111 } 112 } 113 } 114 } 115 116 private boolean messageForReporter(int address,int netId,int msgPort){ 117 return (address == moduleAddress || netId == networkID) && msgPort == port; 118 } 119 /* 120 * private method to get and update a railcom tag based on the value 121 * bytes from the message. 122 */ 123 private RailCom getRailComTagFromValue(int value){ 124 DccLocoAddress l = Z21MessageUtils.getCanDetectorLocoAddress(value); 125 if (l != null ) { // 0 represents end of list or no railcom address. 126 // get the first locomotive address from the message. 127 log.debug("reporting tag for address 1 {}",l); 128 // see if there is a tag for this address. 129 RailCom tag = (RailCom) InstanceManager.getDefault(RailComManager.class).provideIdTag("" + l.getNumber()); 130 int direction = (0xC000&value); 131 switch (direction) { 132 case 0x8000: 133 tag.setOrientation(RailCom.Orientation.ORIENTA); 134 break; 135 case 0xC000: 136 tag.setOrientation(RailCom.Orientation.ORIENTB); 137 break; 138 default: 139 tag.setOrientation(RailCom.Orientation.UNKNOWN); 140 } 141 return tag; 142 } 143 return null; // address in the message indicates end of list. 144 } 145 146 /** 147 * {@inheritDoc} 148 */ 149 @Override 150 public void message(Z21Message msg){ 151 // we don't need to handle outgoing messages, so just ignore them. 152 } 153 154 // the CollectingReporter interface. 155 /** 156 * {@inheritDoc} 157 */ 158 @Override 159 public java.util.Collection<Object> getCollection(){ 160 return idTags; 161 } 162 163 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Z21CanReporter.class); 164 165}