#1188 Add support for P25 fully qualified radio and talkgroup identifiers to aliases and alias lists. Resolve issue with ESN alias lookups.

This commit is contained in:
Dennis Sheirer
2023-11-27 04:40:19 -05:00
parent 3f2ec612d5
commit 804c2e6359
12 changed files with 1023 additions and 28 deletions

View File

@@ -37,11 +37,13 @@ import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
import io.github.dsheirer.alias.id.dcs.Dcs;
import io.github.dsheirer.alias.id.esn.Esn;
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
import io.github.dsheirer.alias.id.radio.P25FullyQualifiedRadio;
import io.github.dsheirer.alias.id.radio.Radio;
import io.github.dsheirer.alias.id.radio.RadioFormatter;
import io.github.dsheirer.alias.id.radio.RadioRange;
import io.github.dsheirer.alias.id.status.UnitStatusID;
import io.github.dsheirer.alias.id.status.UserStatusID;
import io.github.dsheirer.alias.id.talkgroup.P25FullyQualifiedTalkgroup;
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
import io.github.dsheirer.alias.id.talkgroup.TalkgroupFormatter;
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
@@ -684,8 +686,10 @@ public class AliasItemEditor extends Editor<Alias>
Menu p25Menu = new ProtocolMenu(Protocol.APCO25);
p25Menu.getItems().add(new AddTalkgroupItem(Protocol.APCO25));
p25Menu.getItems().add(new AddTalkgroupRangeItem(Protocol.APCO25));
p25Menu.getItems().add(new AddP25FullyQualifiedTalkgroupItem());
p25Menu.getItems().add(new AddRadioIdItem(Protocol.APCO25));
p25Menu.getItems().add(new AddRadioIdRangeItem(Protocol.APCO25));
p25Menu.getItems().add(new AddP25FullyQualifiedRadioIdItem());
p25Menu.getItems().add(new SeparatorMenuItem());
p25Menu.getItems().add(new AddUserStatusItem());
p25Menu.getItems().add(new AddUnitStatusItem());
@@ -1322,6 +1326,44 @@ public class AliasItemEditor extends Editor<Alias>
}
}
/**
* Menu Item for adding a new protocol-specific Fully Qualified Radio ID alias identifier
*/
public class AddP25FullyQualifiedRadioIdItem extends MenuItem
{
public AddP25FullyQualifiedRadioIdItem()
{
super("Fully Qualified Radio ID");
setOnAction(event -> {
P25FullyQualifiedRadio radioId = new P25FullyQualifiedRadio();
radioId.setProtocol(Protocol.APCO25);
getIdentifiersList().getItems().add(radioId);
getIdentifiersList().getSelectionModel().select(radioId);
getIdentifiersList().scrollTo(radioId);
modifiedProperty().set(true);
});
}
}
/**
* Menu Item for adding a new protocol-specific Fully Qualified Talkgroup alias identifier
*/
public class AddP25FullyQualifiedTalkgroupItem extends MenuItem
{
public AddP25FullyQualifiedTalkgroupItem()
{
super("Fully Qualified Talkgroup");
setOnAction(event -> {
P25FullyQualifiedTalkgroup talkgroup = new P25FullyQualifiedTalkgroup();
talkgroup.setProtocol(Protocol.APCO25);
getIdentifiersList().getItems().add(talkgroup);
getIdentifiersList().getSelectionModel().select(talkgroup);
getIdentifiersList().scrollTo(talkgroup);
modifiedProperty().set(true);
});
}
}
/**
* Menu Item for adding a new protocol-specific Radio ID range alias identifier
*/
@@ -1505,7 +1547,25 @@ public class AliasItemEditor extends Editor<Alias>
if(item != null)
{
if(item instanceof Talkgroup)
if(item instanceof P25FullyQualifiedTalkgroup fqt)
{
StringBuilder sb = new StringBuilder();
sb.append("APCO-25 Fully Qualified Talkgroup:").append(fqt.getWacn());
sb.append(".").append(fqt.getSystem());
sb.append(".").append(fqt.getValue());
if(fqt.overlapProperty().get())
{
sb.append(" - Error: Overlap");
}
if(!fqt.isValid())
{
sb.append(" **NOT VALID**");
}
setText(sb.toString());
}
else if(item instanceof Talkgroup)
{
Talkgroup talkgroup = (Talkgroup)item;
Protocol protocol = talkgroup.getProtocol();
@@ -1552,6 +1612,24 @@ public class AliasItemEditor extends Editor<Alias>
}
setText(sb.toString());
}
else if(item instanceof P25FullyQualifiedRadio fqr)
{
StringBuilder sb = new StringBuilder();
sb.append("APCO-25 Fully Qualified Radio ID:").append(fqr.getWacn());
sb.append(".").append(fqr.getSystem());
sb.append(".").append(fqr.getValue());
if(fqr.overlapProperty().get())
{
sb.append(" - Error: Overlap");
}
if(!fqr.isValid())
{
sb.append(" **NOT VALID**");
}
setText(sb.toString());
}
else if(item instanceof Radio)
{
Radio radio = (Radio)item;

View File

@@ -41,6 +41,10 @@ public class IdentifierEditorFactory
return new EsnEditor();
case LOJACK:
return new LojackEditor();
case P25_FULLY_QUALIFIED_RADIO_ID:
return new P25FullyQualifiedRadioIdEditor(userPreferences);
case P25_FULLY_QUALIFIED_TALKGROUP:
return new P25FullyQualifiedTalkgroupEditor(userPreferences);
case RADIO_ID:
return new RadioIdEditor(userPreferences);
case RADIO_ID_RANGE:

View File

@@ -0,0 +1,270 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.gui.playlist.alias.identifier;
import io.github.dsheirer.alias.id.radio.P25FullyQualifiedRadio;
import io.github.dsheirer.gui.control.HexFormatter;
import io.github.dsheirer.gui.control.IntegerFormatter;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.identifier.IntegerFormat;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.HPos;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Editor for P25 fully qualified radio alias identifiers
*/
public class P25FullyQualifiedRadioIdEditor extends IdentifierEditor<P25FullyQualifiedRadio>
{
private static final Logger mLog = LoggerFactory.getLogger(P25FullyQualifiedRadioIdEditor.class);
private UserPreferences mUserPreferences;
private Label mProtocolLabel;
private TextField mWacnField;
private TextField mSystemField;
private TextField mRadioField;
private TextFormatter<Integer> mWacnTextFormatter;
private TextFormatter<Integer> mSystemTextFormatter;
private TextFormatter<Integer> mRadioTextFormatter;
private WacnValueChangeListener mWacnValueChangeListener = new WacnValueChangeListener();
private SystemValueChangeListener mSystemValueChangeListener = new SystemValueChangeListener();
private RadioValueChangeListener mRadioValueChangeListener = new RadioValueChangeListener();
/**
* Constructs an instance
* @param userPreferences for determining user preferred talkgroup formats
*/
public P25FullyQualifiedRadioIdEditor(UserPreferences userPreferences)
{
mUserPreferences = userPreferences;
GridPane gridPane = new GridPane();
gridPane.setHgap(5);
gridPane.setVgap(3);
GridPane.setConstraints(getProtocolLabel(), 0, 0);
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("WACN");
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);
GridPane.setConstraints(getWacnField(), 2, 0);
gridPane.getChildren().add(getWacnField());
Label systemLabel = new Label("System");
GridPane.setHalignment(systemLabel, HPos.RIGHT);
GridPane.setConstraints(systemLabel, 3, 0);
gridPane.getChildren().add(systemLabel);
GridPane.setConstraints(getSystemField(), 4, 0);
gridPane.getChildren().add(getSystemField());
Label radioLabel = new Label("Radio ID");
GridPane.setHalignment(radioLabel, HPos.RIGHT);
GridPane.setConstraints(radioLabel, 5, 0);
gridPane.getChildren().add(radioLabel);
GridPane.setConstraints(getRadioField(), 6, 0);
gridPane.getChildren().add(getRadioField());
getChildren().add(gridPane);
}
@Override
public void setItem(P25FullyQualifiedRadio item)
{
super.setItem(item);
P25FullyQualifiedRadio fqr = getItem();
getProtocolLabel().setDisable(fqr == null);
getWacnField().setDisable(fqr == null);
getSystemField().setDisable(fqr == null);
getRadioField().setDisable(fqr == null);
if(fqr != null)
{
getProtocolLabel().setText(fqr.getProtocol().toString());
updateTextFormatter();
}
else
{
getWacnField().setText(null);
getSystemField().setText(null);
getRadioField().setText(null);
}
modifiedProperty().set(false);
}
private void updateTextFormatter()
{
if(mWacnTextFormatter != null)
{
mWacnTextFormatter.valueProperty().removeListener(mWacnValueChangeListener);
}
if(mSystemTextFormatter != null)
{
mSystemTextFormatter.valueProperty().removeListener(mSystemValueChangeListener);
}
if(mRadioTextFormatter != null)
{
mRadioTextFormatter.valueProperty().removeListener(mRadioValueChangeListener);
}
IntegerFormat format = mUserPreferences.getTalkgroupFormatPreference().getTalkgroupFormat(getItem().getProtocol());
if(format == IntegerFormat.DECIMAL && (mRadioTextFormatter == null || !(mRadioTextFormatter instanceof IntegerFormatter)))
{
mWacnTextFormatter = new IntegerFormatter(0,0xFFFFF);
mSystemTextFormatter = new IntegerFormatter(0,0xFFF);
mRadioTextFormatter = new IntegerFormatter(0,0xFFFFFF);
mWacnField.setTooltip(new Tooltip("Format: 0 - 1048575"));
mSystemField.setTooltip(new Tooltip("Format: 0 - 4095"));
mRadioField.setTooltip(new Tooltip("Format: 0 - 16777215"));
}
else if(format == IntegerFormat.HEXADECIMAL && (mRadioTextFormatter == null || !(mRadioTextFormatter instanceof HexFormatter)))
{
mWacnTextFormatter = new HexFormatter(0,0xFFFFF);
mSystemTextFormatter = new HexFormatter(0,0xFFF);
mRadioTextFormatter = new HexFormatter(0,0xFFFFFF);
mWacnField.setTooltip(new Tooltip("Format: 0 - FFFFF"));
mSystemField.setTooltip(new Tooltip("Format: 0 - FFF"));
mRadioField.setTooltip(new Tooltip("Format: 0 - FFFFFF"));
}
mWacnField.setTextFormatter(mWacnTextFormatter);
mSystemField.setTextFormatter(mSystemTextFormatter);
mRadioField.setTextFormatter(mRadioTextFormatter);
mWacnTextFormatter.setValue(getItem() != null ? getItem().getWacn() : null);
mSystemTextFormatter.setValue(getItem() != null ? getItem().getSystem() : null);
mRadioTextFormatter.setValue(getItem() != null ? getItem().getValue() : null);
mWacnTextFormatter.valueProperty().addListener(mWacnValueChangeListener);
mSystemTextFormatter.valueProperty().addListener(mSystemValueChangeListener);
mRadioTextFormatter.valueProperty().addListener(mRadioValueChangeListener);
}
@Override
public void save()
{
//no-op
}
@Override
public void dispose()
{
//no-op
}
private Label getProtocolLabel()
{
if(mProtocolLabel == null)
{
mProtocolLabel = new Label();
}
return mProtocolLabel;
}
private TextField getWacnField()
{
if(mWacnField == null)
{
mWacnField = new TextField();
mWacnField.setTextFormatter(mWacnTextFormatter);
}
return mWacnField;
}
private TextField getSystemField()
{
if(mSystemField == null)
{
mSystemField = new TextField();
mSystemField.setTextFormatter(mSystemTextFormatter);
}
return mSystemField;
}
private TextField getRadioField()
{
if(mRadioField == null)
{
mRadioField = new TextField();
mRadioField.setTextFormatter(mRadioTextFormatter);
}
return mRadioField;
}
public class WacnValueChangeListener implements ChangeListener<Integer>
{
@Override
public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue)
{
if(getItem() != null)
{
getItem().setWacn(newValue != null ? newValue : 0);
modifiedProperty().set(true);
}
}
}
public class SystemValueChangeListener implements ChangeListener<Integer>
{
@Override
public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue)
{
if(getItem() != null)
{
getItem().setSystem(newValue != null ? newValue : 0);
modifiedProperty().set(true);
}
}
}
public class RadioValueChangeListener implements ChangeListener<Integer>
{
@Override
public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue)
{
if(getItem() != null)
{
getItem().setValue(newValue != null ? newValue : 0);
modifiedProperty().set(true);
}
}
}
}

View File

@@ -0,0 +1,270 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.gui.playlist.alias.identifier;
import io.github.dsheirer.alias.id.talkgroup.P25FullyQualifiedTalkgroup;
import io.github.dsheirer.gui.control.HexFormatter;
import io.github.dsheirer.gui.control.IntegerFormatter;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.identifier.IntegerFormat;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.HPos;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Editor for P25 fully qualified talkgroup alias identifiers
*/
public class P25FullyQualifiedTalkgroupEditor extends IdentifierEditor<P25FullyQualifiedTalkgroup>
{
private static final Logger mLog = LoggerFactory.getLogger(P25FullyQualifiedTalkgroupEditor.class);
private UserPreferences mUserPreferences;
private Label mProtocolLabel;
private TextField mWacnField;
private TextField mSystemField;
private TextField mTalkgroupField;
private TextFormatter<Integer> mWacnTextFormatter;
private TextFormatter<Integer> mSystemTextFormatter;
private TextFormatter<Integer> mTalkgroupTextFormatter;
private WacnValueChangeListener mWacnValueChangeListener = new WacnValueChangeListener();
private SystemValueChangeListener mSystemValueChangeListener = new SystemValueChangeListener();
private TalkgroupValueChangeListener mTalkgroupValueChangeListener = new TalkgroupValueChangeListener();
/**
* Constructs an instance
* @param userPreferences for determining user preferred talkgroup formats
*/
public P25FullyQualifiedTalkgroupEditor(UserPreferences userPreferences)
{
mUserPreferences = userPreferences;
GridPane gridPane = new GridPane();
gridPane.setHgap(5);
gridPane.setVgap(3);
GridPane.setConstraints(getProtocolLabel(), 0, 0);
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("WACN");
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);
GridPane.setConstraints(getWacnField(), 2, 0);
gridPane.getChildren().add(getWacnField());
Label systemLabel = new Label("System");
GridPane.setHalignment(systemLabel, HPos.RIGHT);
GridPane.setConstraints(systemLabel, 3, 0);
gridPane.getChildren().add(systemLabel);
GridPane.setConstraints(getSystemField(), 4, 0);
gridPane.getChildren().add(getSystemField());
Label radioLabel = new Label("Talkgroup");
GridPane.setHalignment(radioLabel, HPos.RIGHT);
GridPane.setConstraints(radioLabel, 5, 0);
gridPane.getChildren().add(radioLabel);
GridPane.setConstraints(getTalkgroupField(), 6, 0);
gridPane.getChildren().add(getTalkgroupField());
getChildren().add(gridPane);
}
@Override
public void setItem(P25FullyQualifiedTalkgroup item)
{
super.setItem(item);
P25FullyQualifiedTalkgroup fqt = getItem();
getProtocolLabel().setDisable(fqt == null);
getWacnField().setDisable(fqt == null);
getSystemField().setDisable(fqt == null);
getTalkgroupField().setDisable(fqt == null);
if(fqt != null)
{
getProtocolLabel().setText(fqt.getProtocol().toString());
updateTextFormatter();
}
else
{
getWacnField().setText(null);
getSystemField().setText(null);
getTalkgroupField().setText(null);
}
modifiedProperty().set(false);
}
private void updateTextFormatter()
{
if(mWacnTextFormatter != null)
{
mWacnTextFormatter.valueProperty().removeListener(mWacnValueChangeListener);
}
if(mSystemTextFormatter != null)
{
mSystemTextFormatter.valueProperty().removeListener(mSystemValueChangeListener);
}
if(mTalkgroupTextFormatter != null)
{
mTalkgroupTextFormatter.valueProperty().removeListener(mTalkgroupValueChangeListener);
}
IntegerFormat format = mUserPreferences.getTalkgroupFormatPreference().getTalkgroupFormat(getItem().getProtocol());
if(format == IntegerFormat.DECIMAL && (mTalkgroupTextFormatter == null || !(mTalkgroupTextFormatter instanceof IntegerFormatter)))
{
mWacnTextFormatter = new IntegerFormatter(0,0xFFFFF);
mSystemTextFormatter = new IntegerFormatter(0,0xFFF);
mTalkgroupTextFormatter = new IntegerFormatter(0,0xFFFF);
mWacnField.setTooltip(new Tooltip("Format: 0 - 1048575"));
mSystemField.setTooltip(new Tooltip("Format: 0 - 4095"));
mTalkgroupField.setTooltip(new Tooltip("Format: 0 - 65535"));
}
else if(format == IntegerFormat.HEXADECIMAL && (mTalkgroupTextFormatter == null || !(mTalkgroupTextFormatter instanceof HexFormatter)))
{
mWacnTextFormatter = new HexFormatter(0,0xFFFFF);
mSystemTextFormatter = new HexFormatter(0,0xFFF);
mTalkgroupTextFormatter = new HexFormatter(0,0xFFFFFF);
mWacnField.setTooltip(new Tooltip("Format: 0 - FFFFF"));
mSystemField.setTooltip(new Tooltip("Format: 0 - FFF"));
mTalkgroupField.setTooltip(new Tooltip("Format: 0 - FFFF"));
}
mWacnField.setTextFormatter(mWacnTextFormatter);
mSystemField.setTextFormatter(mSystemTextFormatter);
mTalkgroupField.setTextFormatter(mTalkgroupTextFormatter);
mWacnTextFormatter.setValue(getItem() != null ? getItem().getWacn() : null);
mSystemTextFormatter.setValue(getItem() != null ? getItem().getSystem() : null);
mTalkgroupTextFormatter.setValue(getItem() != null ? getItem().getValue() : null);
mWacnTextFormatter.valueProperty().addListener(mWacnValueChangeListener);
mSystemTextFormatter.valueProperty().addListener(mSystemValueChangeListener);
mTalkgroupTextFormatter.valueProperty().addListener(mTalkgroupValueChangeListener);
}
@Override
public void save()
{
//no-op
}
@Override
public void dispose()
{
//no-op
}
private Label getProtocolLabel()
{
if(mProtocolLabel == null)
{
mProtocolLabel = new Label();
}
return mProtocolLabel;
}
private TextField getWacnField()
{
if(mWacnField == null)
{
mWacnField = new TextField();
mWacnField.setTextFormatter(mWacnTextFormatter);
}
return mWacnField;
}
private TextField getSystemField()
{
if(mSystemField == null)
{
mSystemField = new TextField();
mSystemField.setTextFormatter(mSystemTextFormatter);
}
return mSystemField;
}
private TextField getTalkgroupField()
{
if(mTalkgroupField == null)
{
mTalkgroupField = new TextField();
mTalkgroupField.setTextFormatter(mTalkgroupTextFormatter);
}
return mTalkgroupField;
}
public class WacnValueChangeListener implements ChangeListener<Integer>
{
@Override
public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue)
{
if(getItem() != null)
{
getItem().setWacn(newValue != null ? newValue : 0);
modifiedProperty().set(true);
}
}
}
public class SystemValueChangeListener implements ChangeListener<Integer>
{
@Override
public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue)
{
if(getItem() != null)
{
getItem().setSystem(newValue != null ? newValue : 0);
modifiedProperty().set(true);
}
}
}
public class TalkgroupValueChangeListener implements ChangeListener<Integer>
{
@Override
public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue)
{
if(getItem() != null)
{
getItem().setValue(newValue != null ? newValue : 0);
modifiedProperty().set(true);
}
}
}
}