Merge pull request #1751 from DSheirer/1188-fully-qualified-radio-identiifer-aliasing

#1188 APCO25 (ISSI) Fully Qualified Radio & Fully Qualified Talkgroup Aliasing
This commit is contained in:
Denny Sheirer
2023-11-29 02:34:43 -05:00
committed by GitHub
12 changed files with 1023 additions and 28 deletions

View File

@@ -30,11 +30,13 @@ import io.github.dsheirer.alias.id.legacy.mobileID.Min;
import io.github.dsheirer.alias.id.legacy.siteID.SiteID;
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
import io.github.dsheirer.alias.id.priority.Priority;
import io.github.dsheirer.alias.id.radio.P25FullyQualifiedRadio;
import io.github.dsheirer.alias.id.radio.Radio;
import io.github.dsheirer.alias.id.radio.RadioRange;
import io.github.dsheirer.alias.id.record.Record;
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.TalkgroupRange;
import io.github.dsheirer.alias.id.tone.TonesID;
@@ -77,6 +79,18 @@ public class AliasFactory
Min copyMin = new Min();
copyMin.setMin(originalMin.getMin());
return copyMin;
case P25_FULLY_QUALIFIED_RADIO_ID:
P25FullyQualifiedRadio originalP25 = (P25FullyQualifiedRadio) id;
P25FullyQualifiedRadio copyP25 = new P25FullyQualifiedRadio(originalP25.getWacn(),
originalP25.getSystem(), originalP25.getValue());
copyP25.setOverlap(originalP25.overlapProperty().get());
return copyP25;
case P25_FULLY_QUALIFIED_TALKGROUP:
P25FullyQualifiedTalkgroup originalFqt = (P25FullyQualifiedTalkgroup) id;
P25FullyQualifiedTalkgroup copyFqt = new P25FullyQualifiedTalkgroup(originalFqt.getWacn(),
originalFqt.getSystem(), originalFqt.getValue());
copyFqt.setOverlap(originalFqt.overlapProperty().get());
return copyFqt;
case PRIORITY:
Priority originalPriority = (Priority)id;
Priority copyPriority = new Priority();

View File

@@ -24,10 +24,12 @@ 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.priority.Priority;
import io.github.dsheirer.alias.id.radio.P25FullyQualifiedRadio;
import io.github.dsheirer.alias.id.radio.Radio;
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.TalkgroupRange;
import io.github.dsheirer.alias.id.tone.TonesID;
@@ -37,9 +39,11 @@ import io.github.dsheirer.identifier.dcs.DCSIdentifier;
import io.github.dsheirer.identifier.esn.ESNIdentifier;
import io.github.dsheirer.identifier.patch.PatchGroup;
import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
import io.github.dsheirer.identifier.radio.FullyQualifiedRadioIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.identifier.status.UnitStatusIdentifier;
import io.github.dsheirer.identifier.status.UserStatusIdentifier;
import io.github.dsheirer.identifier.talkgroup.FullyQualifiedTalkgroupIdentifier;
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.identifier.tone.ToneIdentifier;
import io.github.dsheirer.identifier.tone.ToneSequence;
@@ -155,6 +159,32 @@ public class AliasList
talkgroupRangeAliasList.add(talkgroupRange, alias);
break;
case P25_FULLY_QUALIFIED_RADIO_ID:
P25FullyQualifiedRadio qualifiedRadio = (P25FullyQualifiedRadio) id;
RadioAliasList p25RadioAliasList = mRadioProtocolMap.get(qualifiedRadio.getProtocol());
if(p25RadioAliasList == null)
{
p25RadioAliasList = new RadioAliasList();
mRadioProtocolMap.put(qualifiedRadio.getProtocol(), p25RadioAliasList);
}
p25RadioAliasList.add(qualifiedRadio, alias);
break;
case P25_FULLY_QUALIFIED_TALKGROUP:
P25FullyQualifiedTalkgroup qualifiedTalkgroup = (P25FullyQualifiedTalkgroup) id;
TalkgroupAliasList p25TalkgroupAliasList = mTalkgroupProtocolMap.get(qualifiedTalkgroup.getProtocol());
if(p25TalkgroupAliasList == null)
{
p25TalkgroupAliasList = new TalkgroupAliasList();
mTalkgroupProtocolMap.put(qualifiedTalkgroup.getProtocol(), p25TalkgroupAliasList);
}
p25TalkgroupAliasList.add(qualifiedTalkgroup, alias);
break;
case RADIO_ID:
Radio radio = (Radio)id;
@@ -444,7 +474,7 @@ public class AliasList
return toList(radioAliasList.getAlias(radio));
}
break;
case DCS:
case ESN:
if(identifier instanceof ESNIdentifier)
{
return toList(getESNAlias(((ESNIdentifier)identifier).getValue()));
@@ -623,6 +653,7 @@ public class AliasList
*/
public class TalkgroupAliasList
{
private Map<String,Alias> mFullyQualifiedTalkgroupAliasMap = new HashMap<>();
private Map<Integer,Alias> mTalkgroupAliasMap = new TreeMap<>();
private Map<TalkgroupRange, Alias> mTalkgroupRangeAliasMap = new HashMap<>();
@@ -632,6 +663,11 @@ public class AliasList
public Alias getAlias(TalkgroupIdentifier identifier)
{
if(identifier instanceof FullyQualifiedTalkgroupIdentifier fqti)
{
return mFullyQualifiedTalkgroupAliasMap.get(fqti.toString());
}
int value = identifier.getValue();
Alias mapValue = mTalkgroupAliasMap.get(value);
@@ -653,26 +689,55 @@ public class AliasList
public void add(Talkgroup talkgroup, Alias alias)
{
//Detect talkgroup collisions and set overlap flag for both
if(mTalkgroupAliasMap.containsKey(talkgroup.getValue()))
if(talkgroup instanceof P25FullyQualifiedTalkgroup fqt)
{
Alias existing = mTalkgroupAliasMap.get(talkgroup.getValue());
if(!existing.equals(alias))
//Detect collisions
if(mFullyQualifiedTalkgroupAliasMap.containsKey(fqt.getHashKey()))
{
talkgroup.setOverlap(true);
Alias existing = mFullyQualifiedTalkgroupAliasMap.get(fqt.getHashKey());
for(AliasID aliasID: existing.getAliasIdentifiers())
if(!existing.equals(alias))
{
if(aliasID instanceof Talkgroup && ((Talkgroup)aliasID).getValue() == talkgroup.getValue())
fqt.setOverlap(true);
for(AliasID aliasID: existing.getAliasIdentifiers())
{
aliasID.setOverlap(true);
if(aliasID instanceof P25FullyQualifiedTalkgroup existingFqt &&
existingFqt.getHashKey().contentEquals(fqt.getHashKey()))
{
aliasID.setOverlap(true);
}
}
}
}
else
{
mFullyQualifiedTalkgroupAliasMap.put(fqt.getHashKey(), alias);
}
}
else
{
//Detect talkgroup collisions and set overlap flag for both
if(mTalkgroupAliasMap.containsKey(talkgroup.getValue()))
{
Alias existing = mTalkgroupAliasMap.get(talkgroup.getValue());
mTalkgroupAliasMap.put(talkgroup.getValue(), alias);
if(!existing.equals(alias))
{
talkgroup.setOverlap(true);
for(AliasID aliasID: existing.getAliasIdentifiers())
{
if(aliasID instanceof Talkgroup && ((Talkgroup)aliasID).getValue() == talkgroup.getValue())
{
aliasID.setOverlap(true);
}
}
}
}
mTalkgroupAliasMap.put(talkgroup.getValue(), alias);
}
}
public void add(TalkgroupRange talkgroupRange, Alias alias)
@@ -705,6 +770,7 @@ public class AliasList
*/
public class RadioAliasList
{
private Map<String,Alias> mFullyQualifiedRadioAliasMap = new HashMap<>();
private Map<Integer,Alias> mRadioAliasMap = new TreeMap<>();
private Map<RadioRange, Alias> mRadioRangeAliasMap = new HashMap<>();
@@ -714,6 +780,11 @@ public class AliasList
public Alias getAlias(RadioIdentifier identifier)
{
if(identifier instanceof FullyQualifiedRadioIdentifier fqri)
{
return mFullyQualifiedRadioAliasMap.get(fqri.toString());
}
int value = identifier.getValue();
Alias mapValue = mRadioAliasMap.get(value);
@@ -735,26 +806,55 @@ public class AliasList
public void add(Radio radio, Alias alias)
{
//Detect collisions
if(mRadioAliasMap.containsKey(radio.getValue()))
if(radio instanceof P25FullyQualifiedRadio fqr)
{
Alias existing = mRadioAliasMap.get(radio.getValue());
if(!existing.equals(alias))
//Detect collisions
if(mFullyQualifiedRadioAliasMap.containsKey(fqr.getHashKey()))
{
radio.setOverlap(true);
Alias existing = mFullyQualifiedRadioAliasMap.get(fqr.getHashKey());
for(AliasID aliasID: existing.getAliasIdentifiers())
if(!existing.equals(alias))
{
if(aliasID instanceof Radio && ((Radio)aliasID).getValue() == radio.getValue())
fqr.setOverlap(true);
for(AliasID aliasID: existing.getAliasIdentifiers())
{
aliasID.setOverlap(true);
if(aliasID instanceof P25FullyQualifiedRadio existingFqr &&
existingFqr.getHashKey().contentEquals(fqr.getHashKey()))
{
aliasID.setOverlap(true);
}
}
}
}
else
{
mFullyQualifiedRadioAliasMap.put(fqr.getHashKey(), alias);
}
}
else
{
//Detect collisions
if(mRadioAliasMap.containsKey(radio.getValue()))
{
Alias existing = mRadioAliasMap.get(radio.getValue());
mRadioAliasMap.put(radio.getValue(), alias);
if(!existing.equals(alias))
{
radio.setOverlap(true);
for(AliasID aliasID: existing.getAliasIdentifiers())
{
if(aliasID instanceof Radio existingRadio && (existingRadio.getValue() == radio.getValue()))
{
aliasID.setOverlap(true);
}
}
}
}
mRadioAliasMap.put(radio.getValue(), alias);
}
}
public void add(RadioRange radioRange, Alias alias)

View File

@@ -35,11 +35,13 @@ import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID;
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID;
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
import io.github.dsheirer.alias.id.priority.Priority;
import io.github.dsheirer.alias.id.radio.P25FullyQualifiedRadio;
import io.github.dsheirer.alias.id.radio.Radio;
import io.github.dsheirer.alias.id.radio.RadioRange;
import io.github.dsheirer.alias.id.record.Record;
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.TalkgroupRange;
import io.github.dsheirer.alias.id.tone.TonesID;
@@ -61,6 +63,8 @@ import javafx.util.Callback;
@JsonSubTypes.Type(value = Min.class, name = "min"),
@JsonSubTypes.Type(value = MPT1327ID.class, name = "mpt1327ID"),
@JsonSubTypes.Type(value = NonRecordable.class, name = "nonRecordable"),
@JsonSubTypes.Type(value = P25FullyQualifiedRadio.class, name = "p25FullyQualifiedRadio"),
@JsonSubTypes.Type(value = P25FullyQualifiedTalkgroup.class, name = "p25FullyQualifiedTalkgroup"),
@JsonSubTypes.Type(value = Priority.class, name = "priority"),
@JsonSubTypes.Type(value = Radio.class, name = "radio"),
@JsonSubTypes.Type(value = RadioRange.class, name = "radioRange"),

View File

@@ -29,6 +29,8 @@ public enum AliasIDType
LOJACK("LoJack"),
LTR_NET_UID("LTR-Net UID"),
MIN("Passport MIN"),
P25_FULLY_QUALIFIED_RADIO_ID("P25 Fully Qualified Radio ID"),
P25_FULLY_QUALIFIED_TALKGROUP("P25 Fully Qualified Talkgroup"),
PRIORITY("Audio Priority"),
RADIO_ID("Radio ID"),
RADIO_ID_RANGE("Radio ID Range"),
@@ -56,7 +58,7 @@ public enum AliasIDType
//Values used by the View-By alias editor
public static EnumSet<AliasIDType> VIEW_BY_VALUES = EnumSet.of(TALKGROUP, TALKGROUP_RANGE, RADIO_ID, RADIO_ID_RANGE,
UNIT_STATUS, STATUS, TONES);
P25_FULLY_QUALIFIED_RADIO_ID, P25_FULLY_QUALIFIED_TALKGROUP, UNIT_STATUS, STATUS, TONES);
public String toString()
{

View File

@@ -0,0 +1,127 @@
/*
* *****************************************************************************
* 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.alias.id.radio;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.alias.id.AliasIDType;
import io.github.dsheirer.protocol.Protocol;
/**
* Fully qualified radio. Note: this is only for P25.
*/
public class P25FullyQualifiedRadio extends Radio
{
private int mWacn;
private int mSystem;
public P25FullyQualifiedRadio()
{
//No-arg JAXB constructor
}
/**
* Constructs an instance
* @param wacn for the radio
* @param system for the radio
* @param id for the radio
*/
public P25FullyQualifiedRadio(int wacn, int system, int id)
{
super(Protocol.APCO25, id);
mWacn = wacn;
mSystem = system;
}
@Override
public AliasIDType getType()
{
return AliasIDType.P25_FULLY_QUALIFIED_RADIO_ID;
}
/**
* WACN for this radio
* @return wacn
*/
@JacksonXmlProperty(isAttribute = true, localName = "wacn")
public int getWacn()
{
return mWacn;
}
/**
* Sets the WACN for this radio
* @param wacn for this radio
*/
public void setWacn(int wacn)
{
mWacn = wacn;
}
/**
* System for this radio
* @return system
*/
@JacksonXmlProperty(isAttribute = true, localName = "system")
public int getSystem()
{
return mSystem;
}
/**
* Sets the system for this radio
* @param system for this radio
*/
public void setSystem(int system)
{
mSystem = system;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("Fully Qualified Radio ID:").append(getWacn());
sb.append(".").append(getSystem());
sb.append(".").append(getValue());
sb.append(" Protocol:").append((getProtocol()));
if(!isValid())
{
sb.append(" **NOT VALID**");
}
return sb.toString();
}
/**
* Hashable string key for use in a lookup map
*/
@JsonIgnore
public String getHashKey()
{
StringBuilder sb = new StringBuilder();
sb.append(getWacn());
sb.append(".").append(getSystem());
sb.append(".").append(getValue());
return sb.toString();
}
}

View File

@@ -0,0 +1,128 @@
/*
* *****************************************************************************
* 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.alias.id.talkgroup;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.alias.id.AliasIDType;
import io.github.dsheirer.protocol.Protocol;
/**
* Fully qualified talkgroup. Note: this is only for P25.
*/
public class P25FullyQualifiedTalkgroup extends Talkgroup
{
private int mWacn;
private int mSystem;
public P25FullyQualifiedTalkgroup()
{
//No-arg JAXB constructor
}
/**
* Constructs an instance
* @param wacn for the radio
* @param system for the radio
* @param talkgroup value
*/
public P25FullyQualifiedTalkgroup(int wacn, int system, int talkgroup)
{
super(Protocol.APCO25, talkgroup);
mWacn = wacn;
mSystem = system;
}
@Override
public AliasIDType getType()
{
return AliasIDType.P25_FULLY_QUALIFIED_TALKGROUP;
}
/**
* WACN for this radio
* @return wacn
*/
@JacksonXmlProperty(isAttribute = true, localName = "wacn")
public int getWacn()
{
return mWacn;
}
/**
* Sets the WACN for this radio
* @param wacn for this radio
*/
public void setWacn(int wacn)
{
mWacn = wacn;
}
/**
* System for this radio
* @return system
*/
@JacksonXmlProperty(isAttribute = true, localName = "system")
public int getSystem()
{
return mSystem;
}
/**
* Sets the system for this radio
* @param system for this radio
*/
public void setSystem(int system)
{
mSystem = system;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("Fully Qualified Radio ID:").append(getWacn());
sb.append(".").append(getSystem());
sb.append(".").append(getValue());
sb.append(" Protocol:").append((getProtocol()));
if(!isValid())
{
sb.append(" **NOT VALID**");
}
return sb.toString();
}
/**
* Hashable string key for use in a lookup map
* @return
*/
@JsonIgnore
public String getHashKey()
{
StringBuilder sb = new StringBuilder();
sb.append(getWacn());
sb.append(".").append(getSystem());
sb.append(".").append(getValue());
return sb.toString();
}
}

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);
}
}
}
}

View File

@@ -37,7 +37,6 @@ public enum Form
DTMF,
ENCRYPTION_KEY,
ESN,
FULLY_QUALIFIED_IDENTIFIER,
IPV4_ADDRESS,
KNOX_TONE,
LOCATION,

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* 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
@@ -35,9 +35,6 @@ import io.github.dsheirer.identifier.configuration.SiteConfigurationIdentifier;
import io.github.dsheirer.identifier.configuration.SystemConfigurationIdentifier;
import io.github.dsheirer.identifier.decoder.DecoderLogicalChannelNameIdentifier;
import io.github.dsheirer.properties.SystemProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
@@ -47,6 +44,8 @@ import java.util.Date;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AudioMetadataUtils
{