org.bidib.wizard.dcca.client.model.DccAdvDecoderModel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bidibwizard-dcca-client Show documentation
Show all versions of bidibwizard-dcca-client Show documentation
jBiDiB BiDiB Wizard DCC-A client POM
package org.bidib.wizard.dcca.client.model;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.swing.SwingUtilities;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.messages.DccAInfoIndexedString;
import org.bidib.jbidibc.messages.DccAInfoShortGui;
import org.bidib.jbidibc.messages.DccAInfoShortInfo;
import org.bidib.jbidibc.messages.DecoderIdAddressData;
import org.bidib.jbidibc.messages.enums.AddressMode;
import org.bidib.jbidibc.messages.enums.DccAOpCodeBm;
import org.bidib.jbidibc.messages.enums.DccAdvSelectInfoOpCode;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jgoodies.binding.beans.Model;
/**
* This model holds the state of a DccAdv decoder in the system.
*
*/
public class DccAdvDecoderModel extends Model {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(DccAdvDecoderModel.class);
public static final String PROPERTY_CURRENT_ANSWER_RETRY_COUNT = "currentAnswerRetryCount";
public static final String PROPERTY_DECODER_UNIQUE_ID = "decoderUniqueId";
public static final String PROPERTY_DECODER_WISH_ADDRESS = "decoderWishAddress";
public static final String PROPERTY_DECODER_ADDRESS = "decoderAddress";
public static final String PROPERTY_SHORTNAME = "shortName";
public static final String PROPERTY_SHORTINFO = "shortInfo";
public static final String PROPERTY_SHORTGUI = "shortGui";
public static final String PROPERTY_FULLNAME = "fullName";
public static final String PROPERTY_PRODUCTNAME = "productName";
public static final String PROPERTY_FIRMWAREID = "firmwareId";
public static final String PROPERTY_DETECTIONSTATUS = "detectionStatus";
public static final int MAX_ANSWER_RETRY_COUNT = 3;
private int currentAnswerRetryCount;
private int maxAnswerRetryCount = MAX_ANSWER_RETRY_COUNT;
private DecoderIdAddressData decoderUniqueId;
private Integer decoderWishAddress;
private Integer decoderAddress;
private AddressMode decoderAddressMode;
private DccAInfoShortInfo shortInfo;
private DccAInfoShortGui shortGui;
// expecting 7 data packets to complete
private IndexedStringCollector fullName = new IndexedStringCollector(7);
// expecting 2 data packets to complete
private IndexedStringCollector productName = new IndexedStringCollector(2);
// expecting 2 data packets to complete
private IndexedStringCollector shortName = new IndexedStringCollector(2);
// expecting 2 data packets to complete
private IndexedStringCollector firmwareId = new IndexedStringCollector(2);
private DccAOpCodeBm lastReceivedOpCode;
private boolean[] addressPartsSet = new boolean[2];
private final Object checkDataCompleteLock = new Object();
public enum DetectionStatus {
UNKNOWN, NEW_DID, GET_INFO, ASSIGNED, UNRESPONSIVE;
}
private static class IndexedStringCollector {
private SortedSet indexList =
new TreeSet<>(Comparator.comparing(DccAInfoIndexedString::getIndex));
private final int expectedItemsToComplete;
// public IndexedStringCollector() {
// this(1);
// }
public IndexedStringCollector(int expectedItemsToComplete) {
this.expectedItemsToComplete = expectedItemsToComplete;
}
// /**
// * @return the expectedItemsToComplete
// */
// public int getExpectedItemsToComplete() {
// return expectedItemsToComplete;
// }
public boolean isDataComplete() {
return expectedItemsToComplete == indexList.size();
}
public void addIndexedString(final DccAInfoIndexedString string) {
DccAInfoIndexedString oldValue = IterableUtils.find(indexList, new Predicate() {
@Override
public boolean evaluate(DccAInfoIndexedString instance) {
return instance.getIndex() == string.getIndex();
}
});
if (oldValue != null) {
LOGGER.info("Remove old value: {}", oldValue);
indexList.remove(oldValue);
}
indexList.add(string);
}
public String getString() {
// concat the string values
StringBuilder sb = new StringBuilder();
for (DccAInfoIndexedString string : indexList) {
sb.append(string.getData());
}
return sb.toString();
}
public void setString(String fullName) {
indexList.clear();
indexList.add(new DccAInfoIndexedString(0, fullName));
}
}
private DetectionStatus detectionStatus = DetectionStatus.UNKNOWN;
public DccAdvDecoderModel() {
}
/**
* @return the maxAnswerRetryCount
*/
public int getMaxAnswerRetryCount() {
return maxAnswerRetryCount;
}
/**
* @param maxAnswerRetryCount
* the maxAnswerRetryCount to set
*/
public void setMaxAnswerRetryCount(int maxAnswerRetryCount) {
this.maxAnswerRetryCount = maxAnswerRetryCount;
}
/**
* @return the currentAnswerRetryCount
*/
public int getCurrentAnswerRetryCount() {
return currentAnswerRetryCount;
}
/**
* @param currentAnswerRetryCount
* the currentAnswerRetryCount to set
*/
public void setCurrentAnswerRetryCount(int currentAnswerRetryCount) {
int oldValue = this.currentAnswerRetryCount;
this.currentAnswerRetryCount = currentAnswerRetryCount;
edtFirePropertyChange(PROPERTY_CURRENT_ANSWER_RETRY_COUNT, oldValue, this.currentAnswerRetryCount);
}
/**
* Increment the answer retry count.
*/
public void incCurrentAnswerRetryCount() {
int oldValue = this.currentAnswerRetryCount;
this.currentAnswerRetryCount++;
LOGGER.info("Incremented the currentAnswerRetryCount: {}", currentAnswerRetryCount);
edtFirePropertyChange(PROPERTY_CURRENT_ANSWER_RETRY_COUNT, oldValue, this.currentAnswerRetryCount);
}
/**
* @return the max answer retry count is exceeded
*/
public boolean isMaxAnswerRetryExceeded() {
return currentAnswerRetryCount > maxAnswerRetryCount;
}
/**
* @return the vendorId
*/
public DecoderIdAddressData getDecoderUniqueId() {
return decoderUniqueId;
}
/**
* @param decoderUniqueData
* the did to set
*/
public void setDecoderUniqueId(DecoderIdAddressData decoderUniqueData) {
DecoderIdAddressData oldValue = this.decoderUniqueId;
this.decoderUniqueId = decoderUniqueData;
edtFirePropertyChange(PROPERTY_DECODER_UNIQUE_ID, oldValue, this.decoderUniqueId);
}
/**
* @return the decoderUniqueId of the decoder or {@code null}
*/
public Long getDecoderDid() {
if (decoderUniqueId != null) {
return decoderUniqueId.getDid();
}
return null;
}
/**
* @return the manufacturer id of the decoder or {@code null}
*/
public Integer getManufacturerId() {
if (decoderUniqueId != null) {
return decoderUniqueId.getManufacturedId();
}
return null;
}
public String getFormattedDecoderUniqueId() {
if (decoderUniqueId != null) {
return String.format("%04X %08X", decoderUniqueId.getManufacturedId(), decoderUniqueId.getDid());
}
return null;
}
public Integer getDecoderWishAddress() {
return decoderWishAddress;
}
/**
* @param decoderWishAddress
* the decoderAddress to set
*/
public void setDecoderWishAddress(Integer decoderWishAddress) {
LOGGER.info("Set the decoder wish address: {}", decoderWishAddress);
Integer oldValue = this.decoderWishAddress;
this.decoderWishAddress = decoderWishAddress;
edtFirePropertyChange(PROPERTY_DECODER_WISH_ADDRESS, oldValue, this.decoderWishAddress);
}
/**
* @return the decoderAddress
*/
public Integer getDecoderAddress() {
return decoderAddress;
}
/**
* @param decoderAddress
* the decoderAddress to set
*/
public void setDecoderAddress(Integer decoderAddress) {
LOGGER.info("Set the decoder address: {}", decoderAddress);
Integer oldValue = this.decoderAddress;
this.decoderAddress = decoderAddress;
if (decoderAddress == null) {
addressPartsSet[0] = false;
addressPartsSet[1] = false;
}
else {
addressPartsSet[0] = true;
addressPartsSet[1] = true;
}
edtFirePropertyChange(PROPERTY_DECODER_ADDRESS, oldValue, this.decoderAddress);
}
/**
* @param decoderAddressLowValue
* the decoderAddress low value to set
*/
public void setDecoderAddressLowValue(Integer decoderAddressLowValue) {
LOGGER.info("Set the decoder address low value: {}", decoderAddressLowValue);
if (decoderAddress == null) {
decoderAddress = 0;
}
decoderAddress = ByteUtils.toInt((decoderAddress >> 8), decoderAddressLowValue);
addressPartsSet[0] = true;
}
/**
* @param decoderAddressHighValue
* the decoderAddress high value to set
*/
public void setDecoderAddressHighValue(Integer decoderAddressHighValue) {
LOGGER.info("Set the decoder address high value: {}", decoderAddressHighValue);
if (decoderAddress == null) {
decoderAddress = 0;
}
decoderAddress = ByteUtils.toInt(decoderAddressHighValue, decoderAddress);
addressPartsSet[1] = true;
}
public boolean isAddressComplete() {
boolean addressComplete = false;
if (AddressMode.SHORT == decoderAddressMode) {
// short address only needs low byte
addressComplete = addressPartsSet[0];
}
else {
addressComplete = (addressPartsSet[0] && addressPartsSet[1]);
}
return addressComplete;
}
/**
* @param addressMode
* the addressMode to set
*/
public void setDecoderAddressMode(AddressMode addressMode) {
this.decoderAddressMode = addressMode;
}
/**
* @return the addressMode
*/
public AddressMode getDecoderAddressMode() {
return decoderAddressMode;
}
/**
* @return the detectionStatus
*/
public DetectionStatus getDetectionStatus() {
if (detectionStatus == null) {
return DetectionStatus.UNKNOWN;
}
return detectionStatus;
}
/**
* @param detectionStatus
* the detectionStatus to set
*/
public void setDetectionStatus(DetectionStatus detectionStatus) {
DetectionStatus oldValue = this.detectionStatus;
this.detectionStatus = detectionStatus;
edtFirePropertyChange(PROPERTY_DETECTIONSTATUS, oldValue, this.detectionStatus);
}
private void edtFirePropertyChange(final String propertyName, final Object oldValue, final Object newValue) {
if (SwingUtilities.isEventDispatchThread()) {
super.firePropertyChange(propertyName, oldValue, newValue);
}
else {
SwingUtilities.invokeLater(() -> {
try {
super.firePropertyChange(propertyName, oldValue, newValue);
}
catch (Exception ex) {
LOGGER
.warn("Fire property changed failed, propertyName: {}, oldValue: {}, newValue: {}",
propertyName, oldValue, newValue, ex);
}
});
}
}
/**
* @return the shortInfo
*/
public DccAInfoShortInfo getShortInfo() {
return shortInfo;
}
/**
* @param shortInfo
* the shortInfo to set
*/
public void setShortInfo(DccAInfoShortInfo shortInfo) {
DccAInfoShortInfo oldValue = this.shortInfo;
this.shortInfo = shortInfo;
edtFirePropertyChange(PROPERTY_SHORTINFO, oldValue, this.shortInfo);
setDecoderWishAddress(shortInfo.getDemandedLocoAddress());
// update the las received opcode
setLastReceivedOpCode(DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTINFO);
}
/**
* @return the shortGui
*/
public DccAInfoShortGui getShortGui() {
return shortGui;
}
/**
* @param shortGui
* the shortGui to set
*/
public void setShortGui(DccAInfoShortGui shortGui) {
DccAInfoShortGui oldValue = this.shortGui;
this.shortGui = shortGui;
edtFirePropertyChange(PROPERTY_SHORTGUI, oldValue, this.shortGui);
// update the las received opcode
setLastReceivedOpCode(DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTGUI);
}
/**
* @return the fullName
*/
public String getFullName() {
return fullName.getString();
}
/**
* @param fullName
* the fullName to set
*/
public void setFullName(DccAInfoIndexedString fullName) {
String oldValue = this.fullName.getString();
LOGGER.info("Update the fullname: {}", fullName);
if (StringUtils.isBlank(fullName.getData())) {
LOGGER.info("No data delivered in fullName.");
// return;
}
this.fullName.addIndexedString(fullName);
edtFirePropertyChange(PROPERTY_FULLNAME, oldValue, this.fullName.getString());
// update the las received opcode
setLastReceivedOpCode(DccAOpCodeBm.BIDIB_DCCA_INFO_FULLNAME);
}
/**
* @param fullName
* the fullName to set
*/
public void setFullName(String fullName) {
String oldValue = this.fullName.getString();
this.fullName.setString(fullName);
edtFirePropertyChange(PROPERTY_FULLNAME, oldValue, this.fullName.getString());
}
/**
* @return the productName
*/
public String getProductName() {
return productName.getString();
}
/**
* @param productName
* the productName to set
*/
public void setProductName(DccAInfoIndexedString productName) {
String oldValue = this.productName.getString();
if (StringUtils.isBlank(productName.getData())) {
LOGGER.info("No data delivered in productName.");
// return;
}
this.productName.addIndexedString(productName);
edtFirePropertyChange(PROPERTY_PRODUCTNAME, oldValue, this.productName.getString());
// update the las received opcode
setLastReceivedOpCode(DccAOpCodeBm.BIDIB_DCCA_INFO_PRODUCTNAME);
}
/**
* @param productName
* the productName to set
*/
public void setProductName(String productName) {
String oldValue = this.productName.getString();
this.productName.setString(productName);
edtFirePropertyChange(PROPERTY_PRODUCTNAME, oldValue, this.productName.getString());
}
/**
* @return the shortName
*/
public String getShortName() {
return shortName.getString();
}
/**
* @param shortName
* the shortName to set
*/
public void setShortName(DccAInfoIndexedString shortName) {
String oldValue = this.shortName.getString();
if (StringUtils.isBlank(shortName.getData())) {
LOGGER.info("No data delivered in shortName.");
// return;
}
this.shortName.addIndexedString(shortName);
edtFirePropertyChange(PROPERTY_SHORTNAME, oldValue, this.shortName.getString());
// update the las received opcode
setLastReceivedOpCode(DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTNAME);
}
/**
* @param shortName
* the shortName to set
*/
public void setShortName(String shortName) {
String oldValue = this.shortName.getString();
this.shortName.setString(shortName);
edtFirePropertyChange(PROPERTY_SHORTNAME, oldValue, this.shortName.getString());
}
/**
* @return the firmwareId
*/
public String getFirmwareId() {
return firmwareId.getString();
}
/**
* @param firmwareId
* the firmwareId to set
*/
public void setFirmwareId(DccAInfoIndexedString firmwareId) {
String oldValue = this.firmwareId.getString();
if (StringUtils.isBlank(firmwareId.getData())) {
LOGGER.info("No data delivered in firmwareId.");
// return;
}
this.firmwareId.addIndexedString(firmwareId);
edtFirePropertyChange(PROPERTY_FIRMWAREID, oldValue, this.firmwareId.getString());
// update the las received opcode
setLastReceivedOpCode(DccAOpCodeBm.BIDIB_DCCA_INFO_FIRMWAREID);
}
/**
* @param firmwareId
* the firmwareId to set
*/
public void setFirmwareId(String firmwareId) {
String oldValue = this.firmwareId.getString();
this.firmwareId.setString(firmwareId);
edtFirePropertyChange(PROPERTY_FIRMWAREID, oldValue, this.firmwareId.getString());
}
/**
* @return the lastReceivedOpCode
*/
public DccAOpCodeBm getLastReceivedOpCode() {
return lastReceivedOpCode;
}
/**
* @param lastReceivedOpCode
* the lastReceivedOpCode to set
*/
public void setLastReceivedOpCode(DccAOpCodeBm lastReceivedOpCode) {
LOGGER.info("Set the last received opCode: {}", lastReceivedOpCode);
this.lastReceivedOpCode = lastReceivedOpCode;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DccAdvDecoderModel) {
DccAdvDecoderModel other = (DccAdvDecoderModel) obj;
if (decoderUniqueId != null && Objects.equals(decoderUniqueId, other.decoderUniqueId)) {
return true;
}
if (decoderAddress != null && Objects.equals(decoderAddress, other.decoderAddress)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
int hashCode = super.hashCode();
if (decoderUniqueId != null) {
hashCode += decoderUniqueId.hashCode();
}
// if (decoderAddress != null) {
// hashCode += decoderAddress.hashCode();
// }
// ignore decoder address
return hashCode;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("DccAdvDecoderModel[");
sb.append("decoderUniqueId=").append(decoderUniqueId);
sb.append(", decoderAddress=").append(decoderAddress).append("]");
return sb.toString();
}
public boolean checkAllDataReceived(DccAOpCodeBm lastReceivedOpCode) {
boolean allDataReceived = isInfoComplete(lastReceivedOpCode);
// check the data of the other incompleteOpCodes before trigger
if (allDataReceived && !hasIncompleteOpCodes()) {
LOGGER.info("All data of the last expected bmOpCode was received: {}", lastReceivedOpCode);
return allDataReceived;
}
LOGGER
.info("Check all data received: {}, but return false because incomplete data was detected.",
allDataReceived);
return false;
}
private boolean isInfoComplete(DccAOpCodeBm infoOpCode) {
boolean isInfoComplete = false;
switch (infoOpCode) {
case BIDIB_DCCA_INFO_FIRMWAREID:
isInfoComplete = firmwareId.isDataComplete();
break;
case BIDIB_DCCA_INFO_PRODUCTNAME:
isInfoComplete = productName.isDataComplete();
break;
case BIDIB_DCCA_INFO_SHORTNAME:
isInfoComplete = shortName.isDataComplete();
break;
case BIDIB_DCCA_INFO_FULLNAME:
isInfoComplete = fullName.isDataComplete();
break;
case BIDIB_DCCA_INFO_SHORTGUI:
isInfoComplete = shortGui != null;
break;
case BIDIB_DCCA_INFO_SHORTINFO:
isInfoComplete = shortInfo != null;
break;
default:
break;
}
return isInfoComplete;
}
private static final DccAOpCodeBm[] REQUIRED_OPCODEBM =
new DccAOpCodeBm[] { DccAOpCodeBm.BIDIB_DCCA_INFO_FIRMWAREID, DccAOpCodeBm.BIDIB_DCCA_INFO_FULLNAME,
DccAOpCodeBm.BIDIB_DCCA_INFO_PRODUCTNAME, DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTNAME,
DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTINFO, DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTGUI };
private List incompleteOpCodes = new ArrayList<>();
public DccAOpCodeBm[] getIncompleteOpCodes() {
LOGGER.info("The incomplete opCodes: {}", incompleteOpCodes);
return incompleteOpCodes.toArray(new DccAOpCodeBm[0]);
}
private boolean hasIncompleteOpCodes() {
for (DccAOpCodeBm opCodeBm : incompleteOpCodes) {
if (!isInfoComplete(opCodeBm)) {
LOGGER.info("Incomplete opCodeBm found: {}", opCodeBm);
return true;
}
}
return false;
}
public DccAOpCodeBm[] checkIncompleteOpCodes() {
incompleteOpCodes.clear();
for (DccAOpCodeBm opCodeBm : REQUIRED_OPCODEBM) {
if (!isInfoComplete(opCodeBm)) {
LOGGER.info("Incomplete opCodeBm found: {}", opCodeBm);
incompleteOpCodes.add(opCodeBm);
}
}
LOGGER.info("The info data is complete: {}", incompleteOpCodes.isEmpty());
return incompleteOpCodes.toArray(new DccAOpCodeBm[0]);
}
/**
* @return the checkDataCompleteLock
*/
public Object getCheckDataCompleteLock() {
return checkDataCompleteLock;
}
private DccAdvSelectInfoOpCode lastRequestedDccAdvSelectInfoOpCode;
private DccAOpCodeBm lastExpectedReceiveBmOpCode;
public void setLastRequestedInfoOpCode(DccAdvSelectInfoOpCode dccAdvSelectInfoOpCode) {
LOGGER.info("Set the last requested info opCode: {}", dccAdvSelectInfoOpCode);
this.lastRequestedDccAdvSelectInfoOpCode = dccAdvSelectInfoOpCode;
switch (dccAdvSelectInfoOpCode) {
case BIDIB_DCCA_SPACE_FULLNAME:
lastExpectedReceiveBmOpCode = DccAOpCodeBm.BIDIB_DCCA_INFO_FULLNAME;
break;
case BIDIB_DCCA_SPACE_FIRMWARE_ID:
lastExpectedReceiveBmOpCode = DccAOpCodeBm.BIDIB_DCCA_INFO_FIRMWAREID;
break;
case BIDIB_DCCA_SPACE_PRODUCTNAME:
lastExpectedReceiveBmOpCode = DccAOpCodeBm.BIDIB_DCCA_INFO_PRODUCTNAME;
break;
case BIDIB_DCCA_SPACE_SHORTGUI:
lastExpectedReceiveBmOpCode = DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTGUI;
break;
case BIDIB_DCCA_SPACE_SHORTINFO:
lastExpectedReceiveBmOpCode = DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTINFO;
break;
case BIDIB_DCCA_SPACE_SHORTNAME:
lastExpectedReceiveBmOpCode = DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTNAME;
break;
default:
LOGGER.warn("Unhandled infoOpCode in setLastRequestedInfoOpCode: {}", dccAdvSelectInfoOpCode);
break;
}
LOGGER.info("Current lastExpectedReceiveBmOpCode: {}", lastExpectedReceiveBmOpCode);
}
public DccAOpCodeBm getLastExpectedReceiveBmOpCode() {
return lastExpectedReceiveBmOpCode;
}
public DccAdvSelectInfoOpCode getLastRequestedInfoOpCode() {
return lastRequestedDccAdvSelectInfoOpCode;
}
private int assignRetryCounter;
public int getAssignRetryCounter() {
return assignRetryCounter;
}
public void incAssignRetryCounter() {
assignRetryCounter++;
LOGGER.info("Incremented the assignRetryCounter: {}", assignRetryCounter);
}
}