001package jmri.jmrix.bidib; 002 003import java.util.List; 004import jmri.InstanceManager; 005import jmri.RailCom; 006import jmri.RailComManager; 007 008import org.bidib.jbidibc.messages.AddressData; 009import org.bidib.jbidibc.core.DefaultMessageListener; 010import org.bidib.jbidibc.core.MessageListener; 011import org.bidib.jbidibc.messages.enums.OccupationState; 012import org.bidib.jbidibc.messages.utils.NodeUtils; 013 014import org.slf4j.Logger; 015import org.slf4j.LoggerFactory; 016 017/** 018 * This class implements the Reporter Manager interface 019 * for BiDiB railcom feedback. 020 * <p> 021 * Reports from this reporter are of the type jmri.RailCom. 022 * 023 * @author Paul Bender Copyright (C) 2016 024 * @author Eckart Meyer Copyright (C) 2019-2023 025 * 026 * based on jmri.jmrix.z21.Z21reporter 027 */ 028public class BiDiBReporter extends jmri.implementation.AbstractRailComReporter implements BiDiBNamedBeanInterface { 029 030 BiDiBAddress addr; 031 private final char typeLetter; 032 private BiDiBTrafficController tc = null; 033 MessageListener messageListener = null; 034 035 /** 036 * Create a reporter instance. 037 * 038 * @param systemName name to be created 039 * @param mgr Reporter Manager, we get the memo object and the type letter (R) from the manager 040 */ 041 public BiDiBReporter(String systemName, BiDiBReporterManager mgr) { 042 super(systemName); 043 tc = mgr.getMemo().getBiDiBTrafficController(); 044 log.debug("New Reporter: {}", systemName); 045 addr = new BiDiBAddress(systemName, mgr.typeLetter(), mgr.getMemo()); 046 log.info("New REPORTER created: {} -> {}", systemName, addr); 047 typeLetter = mgr.typeLetter(); 048 049 createReporterListener(); 050 } 051 052 /** 053 * {@inheritDoc} 054 */ 055 @Override 056 public BiDiBAddress getAddr() { 057 return addr; 058 } 059 060 /** 061 * {@inheritDoc} 062 */ 063 @Override 064 public void nodeNew() { 065 //create a new BiDiBAddress 066 addr = new BiDiBAddress(getSystemName(), typeLetter, tc.getSystemConnectionMemo()); 067 if (addr.isValid()) { 068 log.info("new reporter address created: {} -> {}", getSystemName(), addr); 069 } 070 } 071 072 /** 073 * {@inheritDoc} 074 */ 075 @Override 076 public void nodeLost() { 077 notify_loco(null); 078 } 079 080 /** 081 * Notify loco address 082 * 083 * @param tag found tag 084 */ 085 public void notify_loco(RailCom tag){ 086 //log.trace("tag: {}", tag); 087 super.notify(tag); 088 } 089 090 private void createReporterListener() { 091 // create message listener for RailCom messages 092 messageListener = new DefaultMessageListener() { 093 @Override 094 public void address(byte[] address, int messageNum, int detectorNumber, List<AddressData> addressData) { 095 log.trace("address: node UID: {}, node addr: {}, address: {}, detectorNumber: {}, addressData: {}", addr.getNodeUID(), addr.getNodeAddr(), address, detectorNumber, addressData); 096 if (NodeUtils.isAddressEqual(addr.getNodeAddr(), address) && addr.getAddr() == detectorNumber) { 097 log.info("REPORTER address was signalled, locos: {}, BM Number: {}, node: {}", addressData, detectorNumber, addr); 098 if (addressData.size() > 0) { 099 for (AddressData l : addressData) { 100 log.debug("loco addr: {}", l); 101 if (l.getAddress() > 0) { 102 RailCom tag = (RailCom) InstanceManager.getDefault(RailComManager.class).provideIdTag("" + l.getAddress()); 103 //tag.setActualSpeed(msg.getRailComSpeed(i)); 104 notify_loco(tag); 105 } 106 else { 107 notify_loco(null); 108 } 109 } 110 } 111 else { 112 notify_loco(null); 113 } 114 } 115 } 116 // occupation free is catched here to report the loco has left - obviusly an "address" message is not sent then 117 @Override 118 public void occupation(byte[] address, int messageNum, int detectorNumber, OccupationState occupationState, Integer timestamp) { 119 //log.trace("occupation: node UID: {}, node addr: {}, address: {}, detectorNumber: {}, occ state: {}, timestamp: {}", addr.getNodeUID(), addr.getNodeAddr(), address, detectorNumber, occupationState, timestamp); 120 if (NodeUtils.isAddressEqual(addr.getNodeAddr(), address) && addr.getAddr() == detectorNumber) { 121 if (occupationState == OccupationState.FREE) { 122 log.info("REPORTER occupation free signalled, state: {}, BM Number: {}, node: {}", occupationState, detectorNumber, addr); 123 notify_loco(null); 124 } 125 } 126 } 127 @Override 128 public void occupancyMultiple(byte[] address, int messageNum, int baseAddress, int detectorCount, byte[] detectorData) { 129 log.trace("occupation: node UID: {}, node addr: {}, address: {}, baseAddress: {}, detectorCount: {}, occ states: {}", 130 addr.getNodeUID(), addr.getNodeAddr(), address, baseAddress, 131 detectorCount, detectorData); 132 if (NodeUtils.isAddressEqual(addr.getNodeAddr(), address) && !addr.isPortAddr() && addr.getAddr() >= baseAddress && addr.getAddr() < (baseAddress + detectorCount)) { 133 // TODO: This is very inefficent, since this function is called for each sensor! We should place the listener at a more central instance like the sensor manager 134 // our address is in the data bytes. Check which byte and then check, if the correspondent bit is set. 135 //log.trace("multiple occupation was signalled, states: {}, BM base Number: {}, BM count: {}, node: {}", ByteUtils.bytesToHex(detectorData), baseAddress, detectorCount, addr); 136 int relAddr = addr.getAddr() - baseAddress; 137 byte b = detectorData[ relAddr / 8]; 138 boolean isOccupied = (b & (1 << (relAddr % 8))) != 0; 139 log.info("REPORTER multi occupation was signalled, state: {}, BM addr: {}, node: {}", isOccupied ? "OCCUPIED" : "FREE", addr.getAddr(), addr); 140 if (!isOccupied) { 141 notify_loco(null); 142 } 143 } 144 } 145 }; 146 tc.addMessageListener(messageListener); 147 } 148 149 /** 150 * {@inheritDoc} 151 * 152 * Remove the Message Listener for this reporter 153 */ 154 @Override 155 public void dispose() { 156 if (messageListener != null) { 157 tc.removeMessageListener(messageListener); 158 messageListener = null; 159 } 160 super.dispose(); 161 } 162 163 // initialize logging 164 private final static Logger log = LoggerFactory.getLogger(BiDiBReporter.class); 165 166}