Adds event type as a filtering option in the Events tab

This commit is contained in:
Christopher Varner
2021-11-11 13:13:13 -05:00
committed by Dennis Sheirer
parent adfc8b2d6f
commit d246ffff30
39 changed files with 2686 additions and 452 deletions

View File

@@ -25,19 +25,24 @@ import io.github.dsheirer.gui.power.ChannelPowerPanel;
import io.github.dsheirer.icon.IconModel; import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.module.decode.event.DecodeEventPanel; import io.github.dsheirer.module.decode.event.DecodeEventPanel;
import io.github.dsheirer.module.decode.event.MessageActivityPanel; import io.github.dsheirer.module.decode.event.MessageActivityPanel;
import io.github.dsheirer.module.decode.event.filter.lastheard.LastHeardPanel;
import io.github.dsheirer.module.decode.event.filter.lastseen.LastSeenPanel;
import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.settings.SettingsManager;
import java.awt.Color; import java.awt.Color;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import javax.swing.JPanel; import javax.swing.*;
import java.awt.*;
public class NowPlayingPanel extends JPanel public class NowPlayingPanel extends JPanel
{ {
private ChannelMetadataPanel mChannelMetadataPanel; private ChannelMetadataPanel mChannelMetadataPanel;
private ChannelDetailPanel mChannelDetailPanel; private ChannelDetailPanel mChannelDetailPanel;
private DecodeEventPanel mDecodeEventPanel; private DecodeEventPanel mDecodeEventPanel;
private LastSeenPanel mLastSeenPanel;
private LastHeardPanel mLastHeardPanel;
private MessageActivityPanel mMessageActivityPanel; private MessageActivityPanel mMessageActivityPanel;
private ChannelPowerPanel mChannelPowerPanel; private ChannelPowerPanel mChannelPowerPanel;
@@ -50,6 +55,8 @@ public class NowPlayingPanel extends JPanel
{ {
mChannelDetailPanel = new ChannelDetailPanel(playlistManager.getChannelProcessingManager()); mChannelDetailPanel = new ChannelDetailPanel(playlistManager.getChannelProcessingManager());
mDecodeEventPanel = new DecodeEventPanel(iconModel, userPreferences, playlistManager.getAliasModel()); mDecodeEventPanel = new DecodeEventPanel(iconModel, userPreferences, playlistManager.getAliasModel());
mLastSeenPanel = new LastSeenPanel(iconModel, userPreferences, playlistManager.getAliasModel());
mLastHeardPanel = new LastHeardPanel(iconModel, userPreferences, playlistManager.getAliasModel());
mMessageActivityPanel = new MessageActivityPanel(userPreferences); mMessageActivityPanel = new MessageActivityPanel(userPreferences);
mChannelMetadataPanel = new ChannelMetadataPanel(playlistManager, iconModel, userPreferences); mChannelMetadataPanel = new ChannelMetadataPanel(playlistManager, iconModel, userPreferences);
mChannelPowerPanel = new ChannelPowerPanel(playlistManager, settingsManager); mChannelPowerPanel = new ChannelPowerPanel(playlistManager, settingsManager);
@@ -64,15 +71,13 @@ public class NowPlayingPanel extends JPanel
JideTabbedPane tabbedPane = new JideTabbedPane(); JideTabbedPane tabbedPane = new JideTabbedPane();
tabbedPane.addTab("Details", mChannelDetailPanel); tabbedPane.addTab("Details", mChannelDetailPanel);
tabbedPane.addTab("Events", mDecodeEventPanel); tabbedPane.addTab("Events", mDecodeEventPanel);
tabbedPane.addTab("Last Seen", mLastSeenPanel);
tabbedPane.addTab("Last Heard", mLastHeardPanel);
tabbedPane.addTab("Messages", mMessageActivityPanel); tabbedPane.addTab("Messages", mMessageActivityPanel);
tabbedPane.addTab("Channel", mChannelPowerPanel); tabbedPane.addTab("Channel", mChannelPowerPanel);
tabbedPane.setFont(this.getFont()); tabbedPane.setFont(this.getFont());
tabbedPane.setForeground(Color.BLACK); tabbedPane.setForeground(Color.BLACK);
//Register state change listener to toggle visibility state for channel tab to turn-on/off FFT processing
tabbedPane.addChangeListener(e -> mChannelPowerPanel.setPanelVisible(tabbedPane.getSelectedIndex() == tabbedPane
.indexOfComponent(mChannelPowerPanel)));
JideSplitPane splitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT); JideSplitPane splitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT);
splitPane.setShowGripper(true); splitPane.setShowGripper(true);
splitPane.add(mChannelMetadataPanel); splitPane.add(mChannelMetadataPanel);
@@ -81,6 +86,8 @@ public class NowPlayingPanel extends JPanel
mChannelMetadataPanel.addProcessingChainSelectionListener(mChannelDetailPanel); mChannelMetadataPanel.addProcessingChainSelectionListener(mChannelDetailPanel);
mChannelMetadataPanel.addProcessingChainSelectionListener(mDecodeEventPanel); mChannelMetadataPanel.addProcessingChainSelectionListener(mDecodeEventPanel);
mChannelMetadataPanel.addProcessingChainSelectionListener(mLastSeenPanel);
mChannelMetadataPanel.addProcessingChainSelectionListener(mLastHeardPanel);
mChannelMetadataPanel.addProcessingChainSelectionListener(mMessageActivityPanel); mChannelMetadataPanel.addProcessingChainSelectionListener(mMessageActivityPanel);
mChannelMetadataPanel.addProcessingChainSelectionListener(mChannelPowerPanel); mChannelMetadataPanel.addProcessingChainSelectionListener(mChannelPowerPanel);
} }

View File

@@ -314,6 +314,7 @@ public class DMRDecoderState extends TimeslotDecoderState
DecodeEvent smsEvent = DMRDecodeEvent.builder(packet.getTimestamp()) DecodeEvent smsEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.SMS.name()) .eventDescription(DecodeEventType.SMS.name())
.eventType(DecodeEventType.DATA_PACKET)
.identifiers(mic) .identifiers(mic)
.timeslot(packet.getTimeslot()) .timeslot(packet.getTimeslot())
.details("SMS:" + hyteraSmsPacket.getSMS()) .details("SMS:" + hyteraSmsPacket.getSMS())
@@ -327,6 +328,7 @@ public class DMRDecoderState extends TimeslotDecoderState
DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(packet.getTimestamp()) DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.UNKNOWN_PACKET.name()) .eventDescription(DecodeEventType.UNKNOWN_PACKET.name())
.eventType(DecodeEventType.DATA_PACKET)
.identifiers(mic) .identifiers(mic)
.timeslot(packet.getTimeslot()) .timeslot(packet.getTimeslot())
.details("HYTERA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString()) .details("HYTERA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString())
@@ -337,6 +339,7 @@ public class DMRDecoderState extends TimeslotDecoderState
{ {
DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp()) DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.DATA_PACKET.name()) .eventDescription(DecodeEventType.DATA_PACKET.name())
.eventType(DecodeEventType.DATA_PACKET)
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers())) .identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
.timeslot(packet.getTimeslot()) .timeslot(packet.getTimeslot())
.details(packet.toString()) .details(packet.toString())
@@ -505,14 +508,8 @@ public class DMRDecoderState extends TimeslotDecoderState
case STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_PAYLOAD: case STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_PAYLOAD:
if(csbk instanceof Acknowledge) if(csbk instanceof Acknowledge)
{ {
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE,
.eventDescription(DecodeEventType.RESPONSE.toString()) ((Acknowledge) csbk).getReason().toString()));
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(((Acknowledge)csbk).getReason().toString())
.build();
broadcast(ackEvent);
} }
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ACTIVE, getTimeslot())); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ACTIVE, getTimeslot()));
break; break;
@@ -520,14 +517,8 @@ public class DMRDecoderState extends TimeslotDecoderState
case STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_TSCC: case STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_TSCC:
if(csbk instanceof Acknowledge) if(csbk instanceof Acknowledge)
{ {
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE,
.eventDescription(DecodeEventType.RESPONSE.toString()) ((Acknowledge) csbk).getReason().toString()));
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(((Acknowledge)csbk).getReason().toString())
.build();
broadcast(ackEvent);
} }
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot())); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break; break;
@@ -537,33 +528,16 @@ public class DMRDecoderState extends TimeslotDecoderState
switch(((Ahoy)csbk).getServiceKind()) switch(((Ahoy)csbk).getServiceKind())
{ {
case AUTHENTICATE_REGISTER_RADIO_CHECK_SERVICE: case AUTHENTICATE_REGISTER_RADIO_CHECK_SERVICE:
DecodeEvent registerEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND, DecodeEventType.REGISTER.toString()));
.eventDescription(DecodeEventType.COMMAND.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(DecodeEventType.REGISTER.toString())
.build();
broadcast(registerEvent);
break; break;
case CANCEL_CALL_SERVICE: case CANCEL_CALL_SERVICE:
DecodeEvent cancelEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND, "CANCEL CALL"));
.eventDescription(DecodeEventType.COMMAND.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("CANCEL CALL")
.build();
broadcast(cancelEvent);
break; break;
case SUPPLEMENTARY_SERVICE: case SUPPLEMENTARY_SERVICE:
if(csbk instanceof StunReviveKill) if(csbk instanceof StunReviveKill)
{ {
DecodeEvent stunEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND,
.eventDescription(DecodeEventType.COMMAND.toString()) ((StunReviveKill)csbk).getCommand() + " RADIO"));
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(((StunReviveKill)csbk).getCommand() + " RADIO")
.build();
broadcast(stunEvent);
} }
break; break;
case FULL_DUPLEX_MS_TO_MS_PACKET_CALL_SERVICE: case FULL_DUPLEX_MS_TO_MS_PACKET_CALL_SERVICE:
@@ -577,15 +551,9 @@ public class DMRDecoderState extends TimeslotDecoderState
if(csbk instanceof ServiceRadioCheck) if(csbk instanceof ServiceRadioCheck)
{ {
ServiceRadioCheck src = (ServiceRadioCheck)csbk; ServiceRadioCheck src = (ServiceRadioCheck)csbk;
broadcast(getDecodeEvent(csbk, DecodeEventType.RADIO_CHECK,
DecodeEvent checkEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) src.getServiceDescription() + " SERVICE FOR " +
.eventDescription(DecodeEventType.RADIO_CHECK.toString()) (src.isTalkgroupTarget() ? "TALKGROUP" : "RADIO")));
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(src.getServiceDescription() + " SERVICE FOR " +
(src.isTalkgroupTarget() ? "TALKGROUP" : "RADIO"))
.build();
broadcast(checkEvent);
} }
break; break;
} }
@@ -599,14 +567,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(aloha.hasRadioIdentifier()) if(aloha.hasRadioIdentifier())
{ {
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE, "Aloha Acknowledge"));
.eventDescription(DecodeEventType.RESPONSE.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("Aloha Acknowledge")
.build();
broadcast(ackEvent);
resetState(); resetState();
} }
} }
@@ -618,24 +579,13 @@ public class DMRDecoderState extends TimeslotDecoderState
switch(((Announcement)csbk).getAnnouncementType()) switch(((Announcement)csbk).getAnnouncementType())
{ {
case MASS_REGISTRATION: case MASS_REGISTRATION:
DecodeEvent massEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) broadcast(getDecodeEvent(csbk, DecodeEventType.REGISTER, "MASS REGISTRATION"));
.eventDescription(DecodeEventType.REGISTER.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("MASS REGISTRATION")
.build();
broadcast(massEvent);
break; break;
case VOTE_NOW_ADVICE: case VOTE_NOW_ADVICE:
if(csbk instanceof VoteNowAdvice) if(csbk instanceof VoteNowAdvice)
{ {
DecodeEvent voteEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND,
.eventDescription(DecodeEventType.COMMAND.toString()) "VOTE NOW FOR " + ((VoteNowAdvice)csbk).getVotedSystemIdentityCode()));
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("VOTE NOW FOR " + ((VoteNowAdvice)csbk).getVotedSystemIdentityCode())
.build();
broadcast(voteEvent);
} }
break; break;
} }
@@ -653,13 +603,8 @@ public class DMRDecoderState extends TimeslotDecoderState
case STANDARD_PROTECT: case STANDARD_PROTECT:
if(csbk instanceof Protect) if(csbk instanceof Protect)
{ {
DecodeEvent protectEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND,
.eventDescription(DecodeEventType.COMMAND.toString()) "PROTECT: " + ((Protect)csbk).getProtectKind()));
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("PROTECT: " + ((Protect)csbk).getProtectKind())
.build();
broadcast(protectEvent);
} }
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL, getTimeslot())); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL, getTimeslot()));
break; break;
@@ -714,13 +659,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers())) if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{ {
event = DMRDecodeEvent.builder(csbk.getTimestamp()) event = getDecodeEvent(csbk, DecodeEventType.DATA_CALL, channel, mergedIdentifiers);
.channel(channel)
.details(csbk.getOpcode().getLabel())
.eventDescription(DecodeEventType.DATA_CALL.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event); mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
} }
else else
@@ -753,13 +692,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers())) if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{ {
event = DMRDecodeEvent.builder(csbk.getTimestamp()) event = getDecodeEvent(csbk, DecodeEventType.CALL_GROUP, channel, mergedIdentifiers);
.channel(channel)
.details(csbk.getOpcode().getLabel())
.eventDescription(DecodeEventType.CALL_GROUP.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event); mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
} }
else else
@@ -791,13 +724,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers())) if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{ {
event = DMRDecodeEvent.builder(csbk.getTimestamp()) event = getDecodeEvent(csbk, DecodeEventType.CALL_UNIT_TO_UNIT, channel, mergedIdentifiers);
.channel(channel)
.details(csbk.getOpcode().getLabel())
.eventDescription(DecodeEventType.CALL_UNIT_TO_UNIT.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event); mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
} }
else else
@@ -817,15 +744,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(cmAloha.hasRadioIdentifier()) if(cmAloha.hasRadioIdentifier())
{ {
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE, "Aloha Acknowledge"));
.eventDescription(DecodeEventType.RESPONSE.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("Aloha Acknowledge")
.build();
broadcast(ackEvent);
resetState(); resetState();
} }
} }
@@ -850,13 +769,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers())) if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{ {
event = DMRDecodeEvent.builder(csbk.getTimestamp()) event = getDecodeEvent(csbk, DecodeEventType.DATA_CALL, channel, mergedIdentifiers);
.channel(channel)
.details(csbk.getOpcode().getLabel())
.eventDescription(DecodeEventType.DATA_CALL.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event); mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
} }
else else
@@ -909,13 +822,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(isStale(detectedEvent, csbk.getTimestamp(), csbk.getIdentifiers())) if(isStale(detectedEvent, csbk.getTimestamp(), csbk.getIdentifiers()))
{ {
detectedEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) detectedEvent = getDecodeEvent(csbk, DecodeEventType.CALL_GROUP, channel, mergedIdentifiers);
.channel(channel)
.details(csbk.getOpcode().getLabel())
.eventDescription(DecodeEventType.CALL_GROUP.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), detectedEvent); mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), detectedEvent);
} }
else else
@@ -945,6 +852,27 @@ public class DMRDecoderState extends TimeslotDecoderState
} }
} }
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, DMRChannel channel, IdentifierCollection mergedIdentifiers) {
return DMRDecodeEvent.builder(csbk.getTimestamp())
.channel(channel)
.eventType(decodeEventType)
.details(csbk.getOpcode().getLabel())
.eventDescription(decodeEventType.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
}
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, String details) {
return DMRDecodeEvent.builder(csbk.getTimestamp())
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(details)
.build();
}
/** /**
* Creates a copy of the current identifier collection, removes any USER class identifiers and loads the identifiers * Creates a copy of the current identifier collection, removes any USER class identifiers and loads the identifiers
* argument values into the collection. * argument values into the collection.

View File

@@ -325,12 +325,14 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
int lsn = channel.getLogicalSlotNumber(); int lsn = channel.getLogicalSlotNumber();
DMRChannelGrantEvent event = mLSNGrantEventMap.get(lsn); DMRChannelGrantEvent event = mLSNGrantEventMap.get(lsn);
DecodeEventType decodeEventType = getEventType(opcode, identifierCollection, encrypted);
if(isStale(event, timestamp, identifierCollection)) //Create new event if(isStale(event, timestamp, identifierCollection)) //Create new event
{ {
event = DMRChannelGrantEvent.channelGrantBuilder(timestamp) event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
.channel(channel) .channel(channel)
.eventDescription(getEventType(opcode, identifierCollection, encrypted).toString()) .eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : "")) .details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.identifiers(identifierCollection) .identifiers(identifierCollection)
.build(); .build();
@@ -351,7 +353,8 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
event = DMRChannelGrantEvent.channelGrantBuilder(timestamp) event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
.channel(channel) .channel(channel)
.eventDescription(getEventType(opcode, identifierCollection, encrypted).toString() + " - Continue") .eventType(decodeEventType)
.eventDescription(decodeEventType.toString() + " - Continue")
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : "")) .details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.identifiers(identifierCollection) .identifiers(identifierCollection)
.build(); .build();
@@ -391,7 +394,7 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
{ {
if(event.getEventDescription() == null) if(event.getEventDescription() == null)
{ {
event.setEventDescription(getEventType(opcode, identifierCollection, encrypted) + IGNORED); event.setEventDescription(decodeEventType + IGNORED);
} }
else if(!event.getEventDescription().endsWith(IGNORED)) else if(!event.getEventDescription().endsWith(IGNORED))
{ {

View File

@@ -21,6 +21,7 @@ package io.github.dsheirer.module.decode.dmr.event;
import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
import io.github.dsheirer.module.decode.event.DecodeEventType;
/** /**
* DMR Channel Grant Event. * DMR Channel Grant Event.
@@ -74,6 +75,7 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
{ {
protected long mTimeStart; protected long mTimeStart;
protected long mDuration; protected long mDuration;
protected DecodeEventType mDecodeEventType;
protected String mEventDescription; protected String mEventDescription;
protected IdentifierCollection mIdentifierCollection; protected IdentifierCollection mIdentifierCollection;
protected DMRChannel mChannel; protected DMRChannel mChannel;
@@ -121,7 +123,7 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
/** /**
* Sets the channel descriptor for this event * Sets the channel descriptor for this event
* @param channelDescriptor * @param channel
*/ */
public DMRChannelGrantDecodeEventBuilder channel(DMRChannel channel) public DMRChannelGrantDecodeEventBuilder channel(DMRChannel channel)
{ {
@@ -129,6 +131,15 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
return this; return this;
} }
/**
* Sets the Decode Event type for this event.
* @param eventType
*/
public DMRChannelGrantDecodeEventBuilder eventType(DecodeEventType eventType) {
mDecodeEventType = eventType;
return this;
}
/** /**
* Sets the event description text * Sets the event description text
* @param description of the event * @param description of the event
@@ -170,6 +181,7 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
decodeEvent.setTimeslot(mChannel.getTimeslot()); decodeEvent.setTimeslot(mChannel.getTimeslot());
decodeEvent.setDetails(mDetails); decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration); decodeEvent.setDuration(mDuration);
decodeEvent.setEventType(mDecodeEventType);
decodeEvent.setEventDescription(mEventDescription); decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection); decodeEvent.setIdentifierCollection(mIdentifierCollection);
return decodeEvent; return decodeEvent;

View File

@@ -313,6 +313,10 @@ public class DecodeEvent implements IDecodeEvent
return this; return this;
} }
/**
* Sets the Decode Event type for this event.
* @param eventType
*/
public DecodeEventBuilder eventType(DecodeEventType eventType) { public DecodeEventBuilder eventType(DecodeEventType eventType) {
mDecodeEventType = eventType; mDecodeEventType = eventType;
return this; return this;
@@ -374,6 +378,7 @@ public class DecodeEvent implements IDecodeEvent
decodeEvent.setChannelDescriptor(mChannelDescriptor); decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails); decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration); decodeEvent.setDuration(mDuration);
decodeEvent.setEventType(mDecodeEventType);
decodeEvent.setEventDescription(mEventDescription); decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection); decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setProtocol(mProtocol); decodeEvent.setProtocol(mProtocol);

View File

@@ -14,7 +14,7 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/> * along with this program. If not, see <http://www.gnu.org/licenses/>
* **************************************************************************** * *****************************************************************************
*/ */
package io.github.dsheirer.module.decode.event; package io.github.dsheirer.module.decode.event;
@@ -23,6 +23,7 @@ public enum DecodeEventType
{ {
AFFILIATE("Affiliate"), AFFILIATE("Affiliate"),
ANNOUNCEMENT("Announcement"), ANNOUNCEMENT("Announcement"),
ACKNOWLEDGE("Acknowledge"),
AUTOMATIC_REGISTRATION_SERVICE("Motorola ARS"), AUTOMATIC_REGISTRATION_SERVICE("Motorola ARS"),
CALL("Call"), CALL("Call"),
CALL_ENCRYPTED("Encrypted Call"), CALL_ENCRYPTED("Encrypted Call"),
@@ -32,6 +33,7 @@ public enum DecodeEventType
CALL_PATCH_GROUP_ENCRYPTED("Encrypted Patch Call"), CALL_PATCH_GROUP_ENCRYPTED("Encrypted Patch Call"),
CALL_ALERT("Call Alert"), CALL_ALERT("Call Alert"),
CALL_DETECT("Call Detect"), CALL_DETECT("Call Detect"),
CALL_IN_PROGRESS("Call In Progress"),
CALL_DO_NOT_MONITOR("Call-Do Not Monitor"), CALL_DO_NOT_MONITOR("Call-Do Not Monitor"),
CALL_END("Call End"), CALL_END("Call End"),
CALL_INTERCONNECT("Telephone Call"), CALL_INTERCONNECT("Telephone Call"),

View File

@@ -103,6 +103,7 @@ public class PlottableDecodeEvent extends DecodeEvent
private long mTimeStart; private long mTimeStart;
private long mDuration; private long mDuration;
private String mEventDescription; private String mEventDescription;
private DecodeEventType mDecodeEventType;
private IdentifierCollection mIdentifierCollection; private IdentifierCollection mIdentifierCollection;
private IChannelDescriptor mChannelDescriptor; private IChannelDescriptor mChannelDescriptor;
private String mDetails; private String mDetails;
@@ -190,6 +191,15 @@ public class PlottableDecodeEvent extends DecodeEvent
return this; return this;
} }
/**
* Sets the Decode Event type for this event.
* @param eventType
*/
public PlottableDecodeEventBuilder eventType(DecodeEventType eventType) {
mDecodeEventType = eventType;
return this;
}
/** /**
* Sets the location for the plottable event * Sets the location for the plottable event
*/ */
@@ -226,6 +236,7 @@ public class PlottableDecodeEvent extends DecodeEvent
decodeEvent.setChannelDescriptor(mChannelDescriptor); decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails); decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration); decodeEvent.setDuration(mDuration);
decodeEvent.setEventType(mDecodeEventType);
decodeEvent.setEventDescription(mEventDescription); decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection); decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setProtocol(mProtocol); decodeEvent.setProtocol(mProtocol);

View File

@@ -1,7 +1,25 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 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.module.decode.event.filter; package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType; import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays; import java.util.Arrays;
public class DecodedCommandEventFilter extends EventFilter public class DecodedCommandEventFilter extends EventFilter
@@ -11,6 +29,7 @@ public class DecodedCommandEventFilter extends EventFilter
super("Commands", Arrays.asList( super("Commands", Arrays.asList(
DecodeEventType.ANNOUNCEMENT, DecodeEventType.ANNOUNCEMENT,
DecodeEventType.STATION_ID, DecodeEventType.STATION_ID,
DecodeEventType.ACKNOWLEDGE,
DecodeEventType.PAGE, DecodeEventType.PAGE,
DecodeEventType.QUERY, DecodeEventType.QUERY,
DecodeEventType.RADIO_CHECK, DecodeEventType.RADIO_CHECK,

View File

@@ -0,0 +1,17 @@
package io.github.dsheirer.module.decode.event.filter.lastheard;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
public class LastHeardFilterSet extends FilterSet<IDecodeEvent>
{
public LastHeardFilterSet(AliasModel aliasModel, IconModel iconModel)
{
super("Last Heard To Channel");
addFilter(new LastHeardIconFilter("Icon", aliasModel, iconModel));
}
}

View File

@@ -0,0 +1,77 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.module.decode.event.filter.lastheard;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.module.HistoryModule;
import io.github.dsheirer.module.decode.event.DecodeEventHistoryPreloadData;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.IDecodeEventListener;
import io.github.dsheirer.sample.Listener;
/**
* Decode event history module. Maintains a history of decode events and constrains the total history size.
*/
public class LastHeardHistory extends HistoryModule<IDecodeEvent> implements IDecodeEventListener
{
/**
* Constructs an instance
*/
public LastHeardHistory(int historySize)
{
super(historySize);
}
/**
* Implements the IDecodeEventListener interface - delegates to receive(event) method.
*/
@Override
public Listener<IDecodeEvent> getDecodeEventListener()
{
return this;
}
/**
* Process preload data
*/
@Subscribe
public void process(DecodeEventHistoryPreloadData preloadData)
{
for(IDecodeEvent decodeEvent: preloadData.getData())
{
receive(decodeEvent);
}
}
/**
* Processes a request for decode event history and posts the response back
* to the processing chain event bus so that any of the modules can receive that history.
*
* Note: this is principally used by the DMR decoder for Capacity+ REST channel rotation to transfer the decode
* event history from a traffic channel conversion to the new rest channel.
*
* @param request for decode event history
*/
@Subscribe
public void process(LastHeardHistoryRequest request)
{
getInterModuleEventBus().post(new LastHeardHistoryResponse(this));
}
}

View File

@@ -0,0 +1,35 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.module.decode.event.filter.lastheard;
import io.github.dsheirer.module.ModuleEventBusMessage;
/**
* Request for decode event history from a processing chain for the specified channel.
*/
public class LastHeardHistoryRequest extends ModuleEventBusMessage
{
/**
* Constructs an instance
*/
public LastHeardHistoryRequest()
{
}
}

View File

@@ -0,0 +1,47 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.module.decode.event.filter.lastheard;
import io.github.dsheirer.module.ModuleEventBusMessage;
/**
* Response to decode event history request from a processing chain for the specified channel.
*/
public class LastHeardHistoryResponse extends ModuleEventBusMessage
{
private LastHeardHistory mLastHeardHistory;
/**
* Constructs an instance
* @param history that has a decode event history
*/
public LastHeardHistoryResponse(LastHeardHistory history)
{
mLastHeardHistory = history;
}
/**
* Decode event history module
*/
public LastHeardHistory getDecodeEventHistory()
{
return mLastHeardHistory;
}
}

View File

@@ -0,0 +1,97 @@
package io.github.dsheirer.module.decode.event.filter.lastheard;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import javafx.collections.ObservableList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class LastHeardIconFilter extends Filter<IDecodeEvent>
{
private AliasModel mAliasModel;
private Map<String, FilterElement<String>> mElements = new HashMap<>();
public LastHeardIconFilter(String name, AliasModel aliasModel, IconModel iconModel)
{
super(name);
this.mAliasModel = aliasModel;
ObservableList<Icon> icons = iconModel.iconsProperty();
for (Icon icon : icons) {
mElements.put(icon.getName(), new FilterElement<>(icon.getName()));
}
}
@Override
public boolean passes(IDecodeEvent iDecodeEvent)
{
if (mEnabled && canProcess(iDecodeEvent))
{
return matchAliasToFilter(iDecodeEvent)
.map(FilterElement::isEnabled)
.orElse(false);
}
return false;
}
public Optional<FilterElement<String>> matchAliasToFilter(IDecodeEvent iDecodeEvent)
{
IdentifierCollection identifierCollection = iDecodeEvent.getIdentifierCollection();
List<Identifier> identifiers = identifierCollection.getIdentifiers(Role.TO);
if (iDecodeEvent.getDuration() > 0 && identifiers != null && !identifiers.isEmpty())
{
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
if (aliasList != null)
{
for(Identifier identifier: identifiers)
{
if (identifier.getForm() == Form.TALKGROUP)
{
List<Alias> aliases = aliasList.getAliases(identifier);
if (!aliases.isEmpty())
{
for (Alias alias : aliases) {
if (alias.getIconName() == null)
{
// Return any to match aliases with no icon defined.
return Optional.of(mElements.get(IconModel.DEFAULT_ICON));
}
else
{
return Optional.of(mElements.get(alias.getIconName()));
}
}
}
}
}
}
}
return Optional.empty();
}
@Override
public boolean canProcess(IDecodeEvent iDecodeEvent)
{
return true;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<>(mElements.values());
}
}

View File

@@ -0,0 +1,292 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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.module.decode.event.filter.lastheard;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.table.AbstractTableModel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class LastHeardModel extends AbstractTableModel implements Listener<IDecodeEvent>, EventFilterProvider<IDecodeEvent>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(LastHeardModel.class);
public static final int COLUMN_TIME = 0;
public static final int COLUMN_DURATION = 1;
public static final int COLUMN_EVENT = 2;
public static final int COLUMN_FROM_ID = 3;
public static final int COLUMN_FROM_ALIAS = 4;
public static final int COLUMN_TO_ID = 5;
public static final int COLUMN_TO_ALIAS = 6;
public static final int COLUMN_CHANNEL = 7;
public static final int COLUMN_FREQUENCY = 8;
public static final int COLUMN_DETAILS = 9;
protected int mMaxMessages = 500;
protected List<IDecodeEvent> mEvents = new ArrayList<>();
protected FilterSet<IDecodeEvent> mLastSeenFilterSet;
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
public LastHeardModel(AliasModel aliasModel, IconModel iconModel)
{
mLastSeenFilterSet = new LastHeardFilterSet(aliasModel, iconModel);
MyEventBus.getGlobalEventBus().register(this);
}
/**
* Receives preference update notifications via the event bus
* @param preferenceType that was updated
*/
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.DECODE_EVENT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_TIME);
}
}
else if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_FROM_ID);
fireTableCellUpdated(row, COLUMN_TO_ID);
}
}
}
/**
* Access the complete list of events managed by this model.
*/
public List<IDecodeEvent> getEvents()
{
return new ArrayList<>(mEvents);
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
Iterator<IDecodeEvent> it = mEvents.iterator();
while(it.hasNext())
{
it.remove();
}
}
public void clear()
{
mEvents.clear();
fireTableDataChanged();
}
/**
* Clears all events from this model and loads the events argument
*/
public void clearAndSet(List<IDecodeEvent> events)
{
mEvents.clear();
mEvents.addAll(events);
fireTableDataChanged();
}
public void reset()
{
dispose();
fireTableDataChanged();
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
public void setMaxMessageCount(int count)
{
mMaxMessages = count;
}
/**
* Adds, updates or deletes the event from the model. Producers can send
* the same call event multiple times to indicate that information in the
* event is updated. Producers can also mark the event as invalid and the
* event will be removed from the model.
*/
public void receive(final IDecodeEvent event)
{
if (mLastSeenFilterSet != null && !mLastSeenFilterSet.passes(event))
{
return;
}
if(!mEvents.contains(event))
{
mEvents.add(0, event);
fireTableRowsInserted(0, 0);
prune();
}
else
{
int row = mEvents.indexOf(event);
fireTableRowsUpdated(row, row);
}
}
private void prune()
{
while(mEvents.size() > mMaxMessages)
{
int index = mEvents.size() - 1;
mEvents.remove(index);
fireTableRowsDeleted(index, index);
}
}
@Override
public FilterSet<IDecodeEvent> getFilterSet() {
return mLastSeenFilterSet;
}
@Override
public void setFilterSet(FilterSet<IDecodeEvent> filterSet) {
this.mLastSeenFilterSet = filterSet;
}
@Override
public int getRowCount()
{
return mEvents.size();
}
@Override
public int getColumnCount()
{
return mHeaders.length;
}
public String getColumnName(int column)
{
return mHeaders[column];
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
synchronized(mEvents)
{
IDecodeEvent event = mEvents.get(rowIndex);
if(event != null)
{
switch(columnIndex)
{
case COLUMN_TIME:
return event.getTimeStart();
case COLUMN_DURATION:
return event.getDuration();
case COLUMN_EVENT:
return event.getEventDescription();
case COLUMN_FROM_ID:
return event.getIdentifierCollection();
case COLUMN_FROM_ALIAS:
return event.getIdentifierCollection();
case COLUMN_TO_ID:
return event.getIdentifierCollection();
case COLUMN_TO_ALIAS:
return event.getIdentifierCollection();
case COLUMN_CHANNEL:
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
if(channelDescriptor != null)
{
if(event.hasTimeslot())
{
return channelDescriptor.toString() + " TS:" + event.getTimeslot();
}
else
{
return channelDescriptor.toString();
}
}
else
{
if(event.hasTimeslot())
{
return "TS:" + event.getTimeslot();
}
else
{
return null;
}
}
case COLUMN_FREQUENCY:
return event.getChannelDescriptor();
case COLUMN_DETAILS:
return event.getDetails();
}
}
}
return null;
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
switch(columnIndex)
{
case COLUMN_DURATION:
case COLUMN_TIME:
return Long.class;
case COLUMN_DETAILS:
case COLUMN_EVENT:
return String.class;
case COLUMN_FREQUENCY:
case COLUMN_FROM_ALIAS:
case COLUMN_FROM_ID:
case COLUMN_TO_ALIAS:
case COLUMN_TO_ID:
return IdentifierCollection.class;
case COLUMN_CHANNEL:
return String.class;
}
return super.getColumnClass(columnIndex);
}
}

View File

@@ -0,0 +1,482 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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.module.decode.event.filter.lastheard;
import com.google.common.base.Joiner;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.AllPassFilter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.module.decode.event.DecodeEventHistory;
import io.github.dsheirer.module.decode.event.DecodeEventModel;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.filter.EventClearButton;
import io.github.dsheirer.module.decode.event.filter.EventClearHandler;
import io.github.dsheirer.module.decode.event.filter.EventFilterButton;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.sample.Listener;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class LastHeardPanel extends JPanel implements Listener<ProcessingChain>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(LastHeardPanel.class);
private static final String TABLE_PREFERENCE_KEY = "decode.lastseen.panel";
private JTable mTable;
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private LastHeardModel mLastHeardModel;
private DecodeEventHistory mCurrentEventHistory;
private JScrollPane mEmptyScroller;
private IconModel mIconModel;
private AliasModel mAliasModel;
private UserPreferences mUserPreferences;
private TimestampCellRenderer mTimestampCellRenderer;
private LastSeenManagementPanel mLastSeenManagementPanel;
/**
* View for call event table
* @param iconModel to display alias icons in table rows
*/
public LastHeardPanel(IconModel iconModel, UserPreferences userPreferences, AliasModel aliasModel)
{
MyEventBus.getGlobalEventBus().register(this);
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
mIconModel = iconModel;
mAliasModel = aliasModel;
mLastHeardModel = new LastHeardModel(aliasModel, iconModel);
mUserPreferences = userPreferences;
mTimestampCellRenderer = new TimestampCellRenderer();
mTable = new JTable(mLastHeardModel);
mTable.setAutoCreateRowSorter(true);
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
mLastSeenManagementPanel = new LastSeenManagementPanel();
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
updateCellRenderers();
add(mLastSeenManagementPanel, "span,growx");
mEmptyScroller = new JScrollPane(mTable);
add(mEmptyScroller);
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
}
/**
* Receives preference update notifications via the event bus
* @param preferenceType that was updated
*/
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
EventQueue.invokeLater(() -> mTimestampCellRenderer.updatePreferences());
}
}
private void updateCellRenderers()
{
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME)
.setCellRenderer(mTimestampCellRenderer);
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION)
.setCellRenderer(new DurationCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL)
.setCellRenderer(new ChannelDescriptorCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY)
.setCellRenderer(new FrequencyCellRenderer());
}
@Override
public void receive(final ProcessingChain processingChain)
{
if(mCurrentEventHistory != null)
{
mCurrentEventHistory.removeListener(mLastHeardModel);
}
EventQueue.invokeLater(() -> {
if(processingChain != null)
{
mCurrentEventHistory = processingChain.getDecodeEventHistory();
mLastHeardModel.clearAndSet(mCurrentEventHistory.getItems());
processingChain.getDecodeEventHistory().addListener(mLastHeardModel);
mLastSeenManagementPanel.enableButtons();
}
else
{
mCurrentEventHistory = null;
mLastHeardModel.clearAndSet(Collections.emptyList());
mLastSeenManagementPanel.disableButtons();
}
});
}
public class LastSeenManagementPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private EventFilterButton<IDecodeEvent> mFilterButton;
private EventClearButton mEventClearButton;
public LastSeenManagementPanel()
{
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
initializeFilterButton();
initializeClearButton();
disableButtons();
add(mFilterButton);
add(mEventClearButton);
}
public void enableButtons()
{
mFilterButton.setEnabled(true);
mEventClearButton.setEnabled(true);
}
public void disableButtons()
{
mFilterButton.setEnabled(false);
mEventClearButton.setEnabled(false);
}
private void initializeFilterButton()
{
EventFilterProvider<IDecodeEvent> filterProvider = (EventFilterProvider<IDecodeEvent>) mTable.getModel();
FilterSet<IDecodeEvent> filterSet = new FilterSet<>(new AllPassFilter<>());
if (filterProvider != null) {
filterSet = filterProvider.getFilterSet();
}
mFilterButton = new EventFilterButton<>("Last Heard To Channel Filter Editor", filterSet);
}
private void initializeClearButton() {
mEventClearButton = new EventClearButton(
((LastHeardModel) mTable.getModel()).getMaxMessageCount()
);
mEventClearButton.setEventClearHandler(new EventClearHandler() {
@Override
public void onHistoryLimitChanged(int newHistoryLimit) {
((LastHeardModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
}
@Override
public void onClearHistoryClicked() {
((LastHeardModel) mTable.getModel()).clear();
}
});
}
}
/**
* Custom cell renderer for displaying identifiers from an identifier collection
*/
public class IdentifierCellRenderer extends DefaultTableCellRenderer
{
protected Role mRole;
/**
* Constructs an instance of the cell renderer.
*
* @param role of the identifier
*/
public IdentifierCellRenderer(Role role)
{
mRole = role;
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(value instanceof IdentifierCollection)
{
List<Identifier> identifiers = ((IdentifierCollection)value).getIdentifiers(mRole);
label.setText(format(identifiers));
}
else
{
label.setText(null);
}
return label;
}
/**
* Formats a list of identifiers as a comma separated list of values
* @param identifiers to format
* @return formatted list or null
*/
protected String format(List<Identifier> identifiers)
{
if(identifiers == null || identifiers.isEmpty())
{
return null;
}
StringBuilder sb = new StringBuilder();
for(Identifier identifier: identifiers)
{
if(sb.length() > 0)
{
sb.append(",");
}
if(identifier.getForm() == Form.TALKGROUP)
{
sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier));
}
else
{
sb.append(identifier);
}
}
return sb.toString();
}
}
/**
* Cell renderer for identifier aliases
*/
public class AliasedIdentifierCellRenderer extends DefaultTableCellRenderer
{
private Role mRole;
/**
* Constructs an instance of the cell renderer.
*
* @param role of the identifier
*/
public AliasedIdentifierCellRenderer(Role role)
{
mRole = role;
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Color color = mTable.getForeground();
ImageIcon icon = null;
String text = null;
if(value instanceof IdentifierCollection)
{
IdentifierCollection identifierCollection = (IdentifierCollection)value;
List<Identifier> identifiers = identifierCollection.getIdentifiers(mRole);
if(identifiers != null && !identifiers.isEmpty())
{
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
if(aliasList != null)
{
StringBuilder sb = new StringBuilder();
for(Identifier identifier: identifiers)
{
List<Alias> aliases = aliasList.getAliases(identifier);
if(!aliases.isEmpty())
{
if(sb.length() > 0)
{
sb.append(",");
}
sb.append(Joiner.on(", ").skipNulls().join(aliases));
color = aliases.get(0).getDisplayColor();
icon = mIconModel.getIcon(aliases.get(0).getIconName(), IconModel.DEFAULT_ICON_SIZE);
}
}
text = sb.toString();
}
}
}
label.setText(text);
label.setForeground(color);
label.setIcon(icon);
return label;
}
}
public class TimestampCellRenderer extends DefaultTableCellRenderer
{
private SimpleDateFormat mTimestampFormatter;
public TimestampCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
updatePreferences();
}
public void updatePreferences()
{
mTimestampFormatter = mUserPreferences.getDecodeEventPreference().getTimestampFormat().getFormatter();
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(value instanceof Long)
{
label.setText(mTimestampFormatter.format(new Date((long)value)));
}
else
{
label.setText(null);
}
return label;
}
}
public class DurationCellRenderer extends DefaultTableCellRenderer
{
private DecimalFormat mDecimalFormat = new DecimalFormat("0.0");
public DurationCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
String formatted = null;
if(value instanceof Long)
{
long duration = (long)value;
if(duration > 0)
{
formatted = mDecimalFormat.format((double)duration / 1e3d);
}
}
label.setText(formatted);
return label;
}
}
/**
* Frequency value cell renderer
*/
public class FrequencyCellRenderer extends DefaultTableCellRenderer
{
private DecimalFormat mFrequencyFormatter = new DecimalFormat("0.00000");
public FrequencyCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
String formatted = null;
if(value instanceof IChannelDescriptor)
{
IChannelDescriptor channelDescriptor = (IChannelDescriptor)value;
long frequency = channelDescriptor.getDownlinkFrequency();
if(frequency > 0)
{
formatted = mFrequencyFormatter.format(frequency / 1e6d);
}
}
label.setText(formatted);
return label;
}
}
/**
* Channel descriptor value cell renderer
*/
public class ChannelDescriptorCellRenderer extends DefaultTableCellRenderer
{
public ChannelDescriptorCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
}
}

View File

@@ -0,0 +1,17 @@
package io.github.dsheirer.module.decode.event.filter.lastseen;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
public class LastSeenFilterSet extends FilterSet<IDecodeEvent>
{
public LastSeenFilterSet(AliasModel aliasModel, IconModel iconModel)
{
super("Last Seen From Radio");
addFilter(new LastSeenIconFilter("Icon", aliasModel, iconModel));
}
}

View File

@@ -0,0 +1,77 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.module.decode.event.filter.lastseen;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.module.HistoryModule;
import io.github.dsheirer.module.decode.event.DecodeEventHistoryPreloadData;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.IDecodeEventListener;
import io.github.dsheirer.sample.Listener;
/**
* Decode event history module. Maintains a history of decode events and constrains the total history size.
*/
public class LastSeenHistory extends HistoryModule<IDecodeEvent> implements IDecodeEventListener
{
/**
* Constructs an instance
*/
public LastSeenHistory(int historySize)
{
super(historySize);
}
/**
* Implements the IDecodeEventListener interface - delegates to receive(event) method.
*/
@Override
public Listener<IDecodeEvent> getDecodeEventListener()
{
return this;
}
/**
* Process preload data
*/
@Subscribe
public void process(DecodeEventHistoryPreloadData preloadData)
{
for(IDecodeEvent decodeEvent: preloadData.getData())
{
receive(decodeEvent);
}
}
/**
* Processes a request for decode event history and posts the response back
* to the processing chain event bus so that any of the modules can receive that history.
*
* Note: this is principally used by the DMR decoder for Capacity+ REST channel rotation to transfer the decode
* event history from a traffic channel conversion to the new rest channel.
*
* @param request for decode event history
*/
@Subscribe
public void process(LastSeenHistoryRequest request)
{
getInterModuleEventBus().post(new LastSeenHistoryResponse(this));
}
}

View File

@@ -0,0 +1,35 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.module.decode.event.filter.lastseen;
import io.github.dsheirer.module.ModuleEventBusMessage;
/**
* Request for decode event history from a processing chain for the specified channel.
*/
public class LastSeenHistoryRequest extends ModuleEventBusMessage
{
/**
* Constructs an instance
*/
public LastSeenHistoryRequest()
{
}
}

View File

@@ -0,0 +1,47 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.module.decode.event.filter.lastseen;
import io.github.dsheirer.module.ModuleEventBusMessage;
/**
* Response to decode event history request from a processing chain for the specified channel.
*/
public class LastSeenHistoryResponse extends ModuleEventBusMessage
{
private LastSeenHistory mLastSeenHistory;
/**
* Constructs an instance
* @param history that has a decode event history
*/
public LastSeenHistoryResponse(LastSeenHistory history)
{
mLastSeenHistory = history;
}
/**
* Decode event history module
*/
public LastSeenHistory getDecodeEventHistory()
{
return mLastSeenHistory;
}
}

View File

@@ -0,0 +1,96 @@
package io.github.dsheirer.module.decode.event.filter.lastseen;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import javafx.collections.ObservableList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class LastSeenIconFilter extends Filter<IDecodeEvent>
{
private AliasModel mAliasModel;
private Map<String, FilterElement<String>> mElements = new HashMap<>();
public LastSeenIconFilter(String name, AliasModel aliasModel, IconModel iconModel)
{
super(name);
this.mAliasModel = aliasModel;
ObservableList<Icon> icons = iconModel.iconsProperty();
for (Icon icon : icons) {
mElements.put(icon.getName(), new FilterElement<>(icon.getName()));
}
}
@Override
public boolean passes(IDecodeEvent iDecodeEvent)
{
if (mEnabled && canProcess(iDecodeEvent))
{
return matchAliasToFilter(iDecodeEvent)
.map(FilterElement::isEnabled)
.orElse(false);
}
return false;
}
public Optional<FilterElement<String>> matchAliasToFilter(IDecodeEvent iDecodeEvent)
{
IdentifierCollection identifierCollection = iDecodeEvent.getIdentifierCollection();
List<Identifier> identifiers = identifierCollection.getIdentifiers();
if (identifiers != null && !identifiers.isEmpty())
{
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
if (aliasList != null)
{
for(Identifier identifier: identifiers)
{
if (identifier.getForm() == Form.RADIO)
{
List<Alias> aliases = aliasList.getAliases(identifier);
if (!aliases.isEmpty())
{
for (Alias alias : aliases) {
if (alias.getIconName() == null)
{
// Return any to match aliases with no icon defined.
return Optional.of(mElements.get(IconModel.DEFAULT_ICON));
}
else
{
return Optional.of(mElements.get(alias.getIconName()));
}
}
}
}
}
}
}
return Optional.empty();
}
@Override
public boolean canProcess(IDecodeEvent iDecodeEvent)
{
return true;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<>(mElements.values());
}
}

View File

@@ -0,0 +1,289 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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.module.decode.event.filter.lastseen;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.sample.Listener;
import javax.swing.table.AbstractTableModel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class LastSeenModel extends AbstractTableModel implements Listener<IDecodeEvent>, EventFilterProvider<IDecodeEvent>
{
private static final long serialVersionUID = 1L;
public static final int COLUMN_TIME = 0;
public static final int COLUMN_DURATION = 1;
public static final int COLUMN_EVENT = 2;
public static final int COLUMN_FROM_ID = 3;
public static final int COLUMN_FROM_ALIAS = 4;
public static final int COLUMN_TO_ID = 5;
public static final int COLUMN_TO_ALIAS = 6;
public static final int COLUMN_CHANNEL = 7;
public static final int COLUMN_FREQUENCY = 8;
public static final int COLUMN_DETAILS = 9;
protected int mMaxMessages = 500;
protected List<IDecodeEvent> mEvents = new ArrayList<>();
protected FilterSet<IDecodeEvent> mLastSeenFilterSet;
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
public LastSeenModel(AliasModel aliasModel, IconModel iconModel)
{
mLastSeenFilterSet = new LastSeenFilterSet(aliasModel, iconModel);
MyEventBus.getGlobalEventBus().register(this);
}
/**
* Receives preference update notifications via the event bus
* @param preferenceType that was updated
*/
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.DECODE_EVENT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_TIME);
}
}
else if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_FROM_ID);
fireTableCellUpdated(row, COLUMN_TO_ID);
}
}
}
/**
* Access the complete list of events managed by this model.
*/
public List<IDecodeEvent> getEvents()
{
return new ArrayList<>(mEvents);
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
Iterator<IDecodeEvent> it = mEvents.iterator();
while(it.hasNext())
{
it.remove();
}
}
public void clear()
{
mEvents.clear();
fireTableDataChanged();
}
/**
* Clears all events from this model and loads the events argument
*/
public void clearAndSet(List<IDecodeEvent> events)
{
mEvents.clear();
mEvents.addAll(events);
fireTableDataChanged();
}
public void reset()
{
dispose();
fireTableDataChanged();
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
public void setMaxMessageCount(int count)
{
mMaxMessages = count;
}
/**
* Adds, updates or deletes the event from the model. Producers can send
* the same call event multiple times to indicate that information in the
* event is updated. Producers can also mark the event as invalid and the
* event will be removed from the model.
*/
public void receive(final IDecodeEvent event)
{
if (mLastSeenFilterSet != null && !mLastSeenFilterSet.passes(event))
{
return;
}
if(!mEvents.contains(event))
{
mEvents.add(0, event);
fireTableRowsInserted(0, 0);
prune();
}
else
{
int row = mEvents.indexOf(event);
fireTableRowsUpdated(row, row);
}
}
private void prune()
{
while(mEvents.size() > mMaxMessages)
{
int index = mEvents.size() - 1;
mEvents.remove(index);
fireTableRowsDeleted(index, index);
}
}
@Override
public FilterSet<IDecodeEvent> getFilterSet() {
return mLastSeenFilterSet;
}
@Override
public void setFilterSet(FilterSet<IDecodeEvent> filterSet) {
this.mLastSeenFilterSet = filterSet;
}
@Override
public int getRowCount()
{
return mEvents.size();
}
@Override
public int getColumnCount()
{
return mHeaders.length;
}
public String getColumnName(int column)
{
return mHeaders[column];
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
synchronized(mEvents)
{
IDecodeEvent event = mEvents.get(rowIndex);
if(event != null)
{
switch(columnIndex)
{
case COLUMN_TIME:
return event.getTimeStart();
case COLUMN_DURATION:
return event.getDuration();
case COLUMN_EVENT:
return event.getEventDescription();
case COLUMN_FROM_ID:
return event.getIdentifierCollection();
case COLUMN_FROM_ALIAS:
return event.getIdentifierCollection();
case COLUMN_TO_ID:
return event.getIdentifierCollection();
case COLUMN_TO_ALIAS:
return event.getIdentifierCollection();
case COLUMN_CHANNEL:
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
if(channelDescriptor != null)
{
if(event.hasTimeslot())
{
return channelDescriptor.toString() + " TS:" + event.getTimeslot();
}
else
{
return channelDescriptor.toString();
}
}
else
{
if(event.hasTimeslot())
{
return "TS:" + event.getTimeslot();
}
else
{
return null;
}
}
case COLUMN_FREQUENCY:
return event.getChannelDescriptor();
case COLUMN_DETAILS:
return event.getDetails();
}
}
}
return null;
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
switch(columnIndex)
{
case COLUMN_DURATION:
case COLUMN_TIME:
return Long.class;
case COLUMN_DETAILS:
case COLUMN_EVENT:
return String.class;
case COLUMN_FREQUENCY:
case COLUMN_FROM_ALIAS:
case COLUMN_FROM_ID:
case COLUMN_TO_ALIAS:
case COLUMN_TO_ID:
return IdentifierCollection.class;
case COLUMN_CHANNEL:
return String.class;
}
return super.getColumnClass(columnIndex);
}
}

View File

@@ -0,0 +1,482 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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.module.decode.event.filter.lastseen;
import com.google.common.base.Joiner;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.AllPassFilter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.module.decode.event.DecodeEventHistory;
import io.github.dsheirer.module.decode.event.DecodeEventModel;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.filter.EventClearButton;
import io.github.dsheirer.module.decode.event.filter.EventClearHandler;
import io.github.dsheirer.module.decode.event.filter.EventFilterButton;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.sample.Listener;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class LastSeenPanel extends JPanel implements Listener<ProcessingChain>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(LastSeenPanel.class);
private static final String TABLE_PREFERENCE_KEY = "decode.lastseen.panel";
private JTable mTable;
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private LastSeenModel mLastSeenModel;
private DecodeEventHistory mCurrentEventHistory;
private JScrollPane mEmptyScroller;
private IconModel mIconModel;
private AliasModel mAliasModel;
private UserPreferences mUserPreferences;
private TimestampCellRenderer mTimestampCellRenderer;
private LastSeenManagementPanel mLastSeenManagementPanel;
/**
* View for call event table
* @param iconModel to display alias icons in table rows
*/
public LastSeenPanel(IconModel iconModel, UserPreferences userPreferences, AliasModel aliasModel)
{
MyEventBus.getGlobalEventBus().register(this);
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
mIconModel = iconModel;
mAliasModel = aliasModel;
mLastSeenModel = new LastSeenModel(aliasModel, iconModel);
mUserPreferences = userPreferences;
mTimestampCellRenderer = new TimestampCellRenderer();
mTable = new JTable(mLastSeenModel);
mTable.setAutoCreateRowSorter(true);
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
mLastSeenManagementPanel = new LastSeenManagementPanel();
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
updateCellRenderers();
add(mLastSeenManagementPanel, "span,growx");
mEmptyScroller = new JScrollPane(mTable);
add(mEmptyScroller);
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
}
/**
* Receives preference update notifications via the event bus
* @param preferenceType that was updated
*/
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
EventQueue.invokeLater(() -> mTimestampCellRenderer.updatePreferences());
}
}
private void updateCellRenderers()
{
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME)
.setCellRenderer(mTimestampCellRenderer);
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION)
.setCellRenderer(new DurationCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL)
.setCellRenderer(new ChannelDescriptorCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY)
.setCellRenderer(new FrequencyCellRenderer());
}
@Override
public void receive(final ProcessingChain processingChain)
{
if(mCurrentEventHistory != null)
{
mCurrentEventHistory.removeListener(mLastSeenModel);
}
EventQueue.invokeLater(() -> {
if(processingChain != null)
{
mCurrentEventHistory = processingChain.getDecodeEventHistory();
mLastSeenModel.clearAndSet(mCurrentEventHistory.getItems());
processingChain.getDecodeEventHistory().addListener(mLastSeenModel);
mLastSeenManagementPanel.enableButtons();
}
else
{
mCurrentEventHistory = null;
mLastSeenModel.clearAndSet(Collections.emptyList());
mLastSeenManagementPanel.disableButtons();
}
});
}
public class LastSeenManagementPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private EventFilterButton<IDecodeEvent> mFilterButton;
private EventClearButton mEventClearButton;
public LastSeenManagementPanel()
{
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
initializeFilterButton();
initializeClearButton();
disableButtons();
add(mFilterButton);
add(mEventClearButton);
}
public void enableButtons()
{
mFilterButton.setEnabled(true);
mEventClearButton.setEnabled(true);
}
public void disableButtons()
{
mFilterButton.setEnabled(false);
mEventClearButton.setEnabled(false);
}
private void initializeFilterButton()
{
EventFilterProvider<IDecodeEvent> filterProvider = (EventFilterProvider<IDecodeEvent>) mTable.getModel();
FilterSet<IDecodeEvent> filterSet = new FilterSet<>(new AllPassFilter<>());
if (filterProvider != null) {
filterSet = filterProvider.getFilterSet();
}
mFilterButton = new EventFilterButton<>("Last Seen From Radio Filter Editor", filterSet);
}
private void initializeClearButton() {
mEventClearButton = new EventClearButton(
((LastSeenModel) mTable.getModel()).getMaxMessageCount()
);
mEventClearButton.setEventClearHandler(new EventClearHandler() {
@Override
public void onHistoryLimitChanged(int newHistoryLimit) {
((LastSeenModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
}
@Override
public void onClearHistoryClicked() {
((LastSeenModel) mTable.getModel()).clear();
}
});
}
}
/**
* Custom cell renderer for displaying identifiers from an identifier collection
*/
public class IdentifierCellRenderer extends DefaultTableCellRenderer
{
protected Role mRole;
/**
* Constructs an instance of the cell renderer.
*
* @param role of the identifier
*/
public IdentifierCellRenderer(Role role)
{
mRole = role;
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(value instanceof IdentifierCollection)
{
List<Identifier> identifiers = ((IdentifierCollection)value).getIdentifiers(mRole);
label.setText(format(identifiers));
}
else
{
label.setText(null);
}
return label;
}
/**
* Formats a list of identifiers as a comma separated list of values
* @param identifiers to format
* @return formatted list or null
*/
protected String format(List<Identifier> identifiers)
{
if(identifiers == null || identifiers.isEmpty())
{
return null;
}
StringBuilder sb = new StringBuilder();
for(Identifier identifier: identifiers)
{
if(sb.length() > 0)
{
sb.append(",");
}
if(identifier.getForm() == Form.TALKGROUP)
{
sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier));
}
else
{
sb.append(identifier);
}
}
return sb.toString();
}
}
/**
* Cell renderer for identifier aliases
*/
public class AliasedIdentifierCellRenderer extends DefaultTableCellRenderer
{
private Role mRole;
/**
* Constructs an instance of the cell renderer.
*
* @param role of the identifier
*/
public AliasedIdentifierCellRenderer(Role role)
{
mRole = role;
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Color color = mTable.getForeground();
ImageIcon icon = null;
String text = null;
if(value instanceof IdentifierCollection)
{
IdentifierCollection identifierCollection = (IdentifierCollection)value;
List<Identifier> identifiers = identifierCollection.getIdentifiers(mRole);
if(identifiers != null && !identifiers.isEmpty())
{
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
if(aliasList != null)
{
StringBuilder sb = new StringBuilder();
for(Identifier identifier: identifiers)
{
List<Alias> aliases = aliasList.getAliases(identifier);
if(!aliases.isEmpty())
{
if(sb.length() > 0)
{
sb.append(",");
}
sb.append(Joiner.on(", ").skipNulls().join(aliases));
color = aliases.get(0).getDisplayColor();
icon = mIconModel.getIcon(aliases.get(0).getIconName(), IconModel.DEFAULT_ICON_SIZE);
}
}
text = sb.toString();
}
}
}
label.setText(text);
label.setForeground(color);
label.setIcon(icon);
return label;
}
}
public class TimestampCellRenderer extends DefaultTableCellRenderer
{
private SimpleDateFormat mTimestampFormatter;
public TimestampCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
updatePreferences();
}
public void updatePreferences()
{
mTimestampFormatter = mUserPreferences.getDecodeEventPreference().getTimestampFormat().getFormatter();
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(value instanceof Long)
{
label.setText(mTimestampFormatter.format(new Date((long)value)));
}
else
{
label.setText(null);
}
return label;
}
}
public class DurationCellRenderer extends DefaultTableCellRenderer
{
private DecimalFormat mDecimalFormat = new DecimalFormat("0.0");
public DurationCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
String formatted = null;
if(value instanceof Long)
{
long duration = (long)value;
if(duration > 0)
{
formatted = mDecimalFormat.format((double)duration / 1e3d);
}
}
label.setText(formatted);
return label;
}
}
/**
* Frequency value cell renderer
*/
public class FrequencyCellRenderer extends DefaultTableCellRenderer
{
private DecimalFormat mFrequencyFormatter = new DecimalFormat("0.00000");
public FrequencyCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
String formatted = null;
if(value instanceof IChannelDescriptor)
{
IChannelDescriptor channelDescriptor = (IChannelDescriptor)value;
long frequency = channelDescriptor.getDownlinkFrequency();
if(frequency > 0)
{
formatted = mFrequencyFormatter.format(frequency / 1e6d);
}
}
label.setText(formatted);
return label;
}
}
/**
* Channel descriptor value cell renderer
*/
public class ChannelDescriptorCellRenderer extends DefaultTableCellRenderer
{
public ChannelDescriptorCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
}
}

View File

@@ -2,19 +2,24 @@
* ***************************************************************************** * *****************************************************************************
* Copyright (C) 2014-2023 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 * * Copyright (C) 2014-2019 Dennis Sheirer
* the Free Software Foundation, either version 3 of the License, or * *
* (at your option) any later version. * * 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/>
* * *****************************************************************************
* *
* 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.module.decode.fleetsync2; package io.github.dsheirer.module.decode.fleetsync2;
@@ -107,13 +112,7 @@ public class Fleetsync2DecoderState extends DecoderState
case ANI: case ANI:
case EMERGENCY: case EMERGENCY:
case LONE_WORKER_EMERGENCY: case LONE_WORKER_EMERGENCY:
DecodeEvent aniEvent = DecodeEvent.builder(fleetsync.getTimestamp()) DecodeEvent aniEvent = getDecodeEvent(fleetsync, getDecodeEventType(fleetsync.getMessageType()));
.channel(getCurrentChannel())
.eventDescription(fleetsync.getMessageType().toString())
.details(fleetsync.toString())
.identifiers(getIdentifierCollection().copyOf())
.build();
broadcast(aniEvent); broadcast(aniEvent);
broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.CALL)); broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.CALL));
break; break;
@@ -121,18 +120,14 @@ public class Fleetsync2DecoderState extends DecoderState
case PAGING: case PAGING:
case STATUS: case STATUS:
case UNKNOWN: case UNKNOWN:
DecodeEvent statusEvent = DecodeEvent.builder(fleetsync.getTimestamp()) DecodeEvent statusEvent = getDecodeEvent(fleetsync, getDecodeEventType(fleetsync.getMessageType()));
.channel(getCurrentChannel())
.eventDescription(fleetsync.getMessageType().toString())
.details(fleetsync.toString())
.identifiers(getIdentifierCollection().copyOf())
.build();
broadcast(statusEvent); broadcast(statusEvent);
broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.DATA)); broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.DATA));
break; break;
case GPS: case GPS:
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(fleetsync.getTimestamp()) PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(fleetsync.getTimestamp())
.channel(getCurrentChannel()) .channel(getCurrentChannel())
.eventType(DecodeEventType.GPS)
.eventDescription(DecodeEventType.GPS.toString()) .eventDescription(DecodeEventType.GPS.toString())
.details(fleetsync.toString()) .details(fleetsync.toString())
.identifiers(getIdentifierCollection().copyOf()) .identifiers(getIdentifierCollection().copyOf())
@@ -147,6 +142,37 @@ public class Fleetsync2DecoderState extends DecoderState
} }
} }
private DecodeEvent getDecodeEvent(Fleetsync2Message fleetsync, DecodeEventType eventType) {
return DecodeEvent.builder(fleetsync.getTimestamp())
.channel(getCurrentChannel())
.eventType(eventType)
.eventDescription(fleetsync.getMessageType().toString())
.details(fleetsync.toString())
.identifiers(getIdentifierCollection().copyOf())
.protocol(Protocol.FLEETSYNC)
.build();
}
private DecodeEventType getDecodeEventType(FleetsyncMessageType fleetsyncMessageType) {
switch (fleetsyncMessageType) {
case ANI:
return DecodeEventType.ID_ANI;
case EMERGENCY:
case LONE_WORKER_EMERGENCY:
return DecodeEventType.EMERGENCY;
case ACKNOWLEDGE:
return DecodeEventType.ACKNOWLEDGE;
case PAGING:
return DecodeEventType.PAGE;
case STATUS:
return DecodeEventType.STATUS;
case GPS:
return DecodeEventType.GPS;
default:
return DecodeEventType.UNKNOWN;
}
}
/** /**
* Responds to reset events issued by the channel state * Responds to reset events issued by the channel state
*/ */

View File

@@ -30,6 +30,7 @@ import io.github.dsheirer.identifier.MutableIdentifierCollection;
import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent; import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -74,6 +75,7 @@ public class LJ1200DecoderState extends DecoderState
DecodeEvent event = DecodeEvent.builder(System.currentTimeMillis()) DecodeEvent event = DecodeEvent.builder(System.currentTimeMillis())
.identifiers(ic) .identifiers(ic)
.eventType(DecodeEventType.DATA_PACKET)
.channel(getCurrentChannel()) .channel(getCurrentChannel())
.details("LOJACK") .details("LOJACK")
.eventDescription(lj.toString()) .eventDescription(lj.toString())
@@ -93,6 +95,7 @@ public class LJ1200DecoderState extends DecoderState
DecodeEvent transponderEvent = DecodeEvent.builder(System.currentTimeMillis()) DecodeEvent transponderEvent = DecodeEvent.builder(System.currentTimeMillis())
.identifiers(ic) .identifiers(ic)
.eventType(DecodeEventType.GPS)
.channel(getCurrentChannel()) .channel(getCurrentChannel())
.details("LOJACK TRANSPONDER") .details("LOJACK TRANSPONDER")
.eventDescription(transponder.toString()) .eventDescription(transponder.toString())

View File

@@ -0,0 +1,49 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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.module.decode.ltrnet;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.protocol.Protocol;
public class LTRNetDecodeEvent extends DecodeEvent
{
/**
* Constructs an LTR NET decode event
* @param start
*/
public LTRNetDecodeEvent(long start)
{
super(start);
setProtocol(Protocol.LTR_NET);
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
decodeEventBuilder.protocol(Protocol.LTR_NET);
return decodeEventBuilder;
}
}

View File

@@ -38,6 +38,7 @@ import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.MessageDirection; import io.github.dsheirer.message.MessageDirection;
import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent; import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.ltrnet.channel.LtrNetChannel; import io.github.dsheirer.module.decode.ltrnet.channel.LtrNetChannel;
import io.github.dsheirer.module.decode.ltrnet.identifier.LtrNetRadioIdentifier; import io.github.dsheirer.module.decode.ltrnet.identifier.LtrNetRadioIdentifier;
import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessage; import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessage;
@@ -168,8 +169,7 @@ public class LTRNetDecoderState extends DecoderState
{ {
getIdentifierCollection().remove(IdentifierClass.USER); getIdentifierCollection().remove(IdentifierClass.USER);
getIdentifierCollection().update(talkgroup); getIdentifierCollection().update(talkgroup);
mCurrentCallEvent = DecodeEvent.builder(timestamp) mCurrentCallEvent = LTRNetDecodeEvent.builder(timestamp)
.protocol(Protocol.LTR_NET)
.channel(getCurrentChannel()) .channel(getCurrentChannel())
.identifiers(getIdentifierCollection().copyOf()) .identifiers(getIdentifierCollection().copyOf())
.build(); .build();
@@ -181,16 +181,19 @@ public class LTRNetDecoderState extends DecoderState
if(talkgroup.getTalkgroup() == 253) if(talkgroup.getTalkgroup() == 253)
{ {
mCurrentCallEvent.setEventType(DecodeEventType.REGISTER);
mCurrentCallEvent.setEventDescription("Register"); mCurrentCallEvent.setEventDescription("Register");
broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
} }
else if(talkgroup.getTalkgroup() == 254) else if(talkgroup.getTalkgroup() == 254)
{ {
mCurrentCallEvent.setEventType(DecodeEventType.STATION_ID);
mCurrentCallEvent.setEventDescription("FCC CWID"); mCurrentCallEvent.setEventDescription("FCC CWID");
broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
} }
else else
{ {
mCurrentCallEvent.setEventType(DecodeEventType.CALL);
mCurrentCallEvent.setEventDescription("Call"); mCurrentCallEvent.setEventDescription("Call");
broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
} }
@@ -203,16 +206,7 @@ public class LTRNetDecoderState extends DecoderState
if(decodeEvent == null) if(decodeEvent == null)
{ {
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); decodeEvent = getDecodeEvent(talkgroup, channel, timestamp, DecodeEventType.CALL_DETECT);
ic.remove(IdentifierClass.USER);
ic.update(talkgroup);
decodeEvent = DecodeEvent.builder(timestamp)
.eventDescription("Call Detect")
.channel(mChannelMap.get(channel))
.protocol(Protocol.LTR_NET)
.identifiers(ic)
.build();
mCallDetectMap.put(channel, decodeEvent); mCallDetectMap.put(channel, decodeEvent);
} }
else else
@@ -223,16 +217,7 @@ public class LTRNetDecoderState extends DecoderState
if(eventTalkgroup == null || !eventTalkgroup.equals(talkgroup) || if(eventTalkgroup == null || !eventTalkgroup.equals(talkgroup) ||
(timestamp - decodeEvent.getTimeStart() - decodeEvent.getDuration() > 2000)) (timestamp - decodeEvent.getTimeStart() - decodeEvent.getDuration() > 2000))
{ {
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); decodeEvent = getDecodeEvent(talkgroup, channel, timestamp, DecodeEventType.CALL_DETECT);
ic.remove(IdentifierClass.USER);
ic.update(talkgroup);
decodeEvent = DecodeEvent.builder(timestamp)
.eventDescription("Call Detect")
.channel(mChannelMap.get(channel))
.protocol(Protocol.LTR_NET)
.identifiers(ic)
.build();
mCallDetectMap.put(channel, decodeEvent); mCallDetectMap.put(channel, decodeEvent);
} }
} }
@@ -334,13 +319,9 @@ public class LTRNetDecoderState extends DecoderState
mESNIdentifiers.add(registrationRequestEsn.getESN()); mESNIdentifiers.add(registrationRequestEsn.getESN());
} }
broadcast(DecodeEvent.builder(message.getTimestamp()) broadcast(getDecodeEvent(
.eventDescription("Registration Request") message, DecodeEventType.REQUEST,
.details(registrationRequestEsn.toString()) "Registration Request", registrationRequestEsn.toString()));
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
.protocol(Protocol.LTR_NET)
.build());
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
} }
@@ -356,13 +337,9 @@ public class LTRNetDecoderState extends DecoderState
mESNIdentifiers.add(registrationRequestEsn.getESN()); mESNIdentifiers.add(registrationRequestEsn.getESN());
} }
broadcast(DecodeEvent.builder(message.getTimestamp()) broadcast(getDecodeEvent(
.eventDescription("Registration Request") message, DecodeEventType.REQUEST,
.details(registrationRequestEsn.toString()) "Registration Request", registrationRequestEsn.toString()));
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
.protocol(Protocol.LTR_NET)
.build());
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
} }
@@ -375,13 +352,9 @@ public class LTRNetDecoderState extends DecoderState
getIdentifierCollection().update(requestAccess.getTalkgroup()); getIdentifierCollection().update(requestAccess.getTalkgroup());
mTalkgroups.add(requestAccess.getTalkgroup()); mTalkgroups.add(requestAccess.getTalkgroup());
broadcast(DecodeEvent.builder(message.getTimestamp()) broadcast(getDecodeEvent(
.eventDescription("Access Request") message, DecodeEventType.REQUEST,
.details(requestAccess.toString()) "Access Request", requestAccess.toString()));
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
.protocol(Protocol.LTR_NET)
.build());
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
} }
@@ -470,13 +443,8 @@ public class LTRNetDecoderState extends DecoderState
ic.remove(IdentifierClass.USER); ic.remove(IdentifierClass.USER);
ic.update(message.getIdentifiers()); ic.update(message.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp()) broadcast(getDecodeEvent(message, DecodeEventType.RESPONSE,
.protocol(Protocol.LTR_NET) "Registration Accept", registrationAccept.toString()));
.channel(getCurrentChannel())
.identifiers(ic)
.eventDescription("Registration Accept")
.details(registrationAccept.toString())
.build());
} }
break; break;
case OSW_SITE_ID: case OSW_SITE_ID:
@@ -509,6 +477,29 @@ public class LTRNetDecoderState extends DecoderState
} }
} }
private DecodeEvent getDecodeEvent(IMessage message, DecodeEventType decodeEventType, String description, String details) {
return LTRNetDecodeEvent.builder(message.getTimestamp())
.eventType(decodeEventType)
.eventDescription(description)
.details(details)
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
.build();
}
private DecodeEvent getDecodeEvent(LTRTalkgroup talkgroup, int channel, long timestamp, DecodeEventType decodeEventType) {
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(talkgroup);
return LTRNetDecodeEvent.builder(timestamp)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
.channel(mChannelMap.get(channel))
.identifiers(ic)
.build();
}
@Override @Override
public String getActivitySummary() public String getActivitySummary()
{ {

View File

@@ -0,0 +1,49 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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.module.decode.ltrstandard;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.protocol.Protocol;
public class LTRStandardDecodeEvent extends DecodeEvent
{
/**
* Constructs an LTR Standard decode event
* @param start
*/
public LTRStandardDecodeEvent(long start)
{
super(start);
setProtocol(Protocol.LTR);
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
decodeEventBuilder.protocol(Protocol.LTR);
return decodeEventBuilder;
}
}

View File

@@ -35,6 +35,7 @@ import io.github.dsheirer.identifier.talkgroup.LTRTalkgroup;
import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent; import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.ltrstandard.channel.LtrChannel; import io.github.dsheirer.module.decode.ltrstandard.channel.LtrChannel;
import io.github.dsheirer.module.decode.ltrstandard.message.Call; import io.github.dsheirer.module.decode.ltrstandard.message.Call;
import io.github.dsheirer.module.decode.ltrstandard.message.CallEnd; import io.github.dsheirer.module.decode.ltrstandard.message.CallEnd;
@@ -97,10 +98,10 @@ public class LTRStandardDecoderState extends DecoderState
mCurrentTalkgroup = start.getTalkgroup(); mCurrentTalkgroup = start.getTalkgroup();
getIdentifierCollection().remove(IdentifierClass.USER); getIdentifierCollection().remove(IdentifierClass.USER);
getIdentifierCollection().update(start.getTalkgroup()); getIdentifierCollection().update(start.getTalkgroup());
mCurrentCallEvent = DecodeEvent.builder(start.getTimestamp()) mCurrentCallEvent = LTRStandardDecodeEvent.builder(start.getTimestamp())
.protocol(Protocol.LTR)
.identifiers(getIdentifierCollection().copyOf()) .identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel()) .channel(getCurrentChannel())
.eventType(DecodeEventType.CALL)
.eventDescription("Call") .eventDescription("Call")
.build(); .build();
} }

View File

@@ -0,0 +1,49 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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.module.decode.mdc1200;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.protocol.Protocol;
public class MDCDecodeEvent extends DecodeEvent
{
/**
* Constucts a P25 decode event
* @param start
*/
public MDCDecodeEvent(long start)
{
super(start);
setProtocol(Protocol.MDC1200);
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
decodeEventBuilder.protocol(Protocol.MDC1200);
return decodeEventBuilder;
}
}

View File

@@ -29,7 +29,7 @@ import io.github.dsheirer.identifier.IdentifierClass;
import io.github.dsheirer.identifier.MutableIdentifierCollection; import io.github.dsheirer.identifier.MutableIdentifierCollection;
import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent; import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.mdc1200.identifier.MDC1200Identifier; import io.github.dsheirer.module.decode.mdc1200.identifier.MDC1200Identifier;
import java.util.Iterator; import java.util.Iterator;
@@ -114,7 +114,8 @@ public class MDCDecoderState extends DecoderState
ic.remove(IdentifierClass.USER); ic.remove(IdentifierClass.USER);
ic.update(message.getIdentifiers()); ic.update(message.getIdentifiers());
broadcast(DecodeEvent.builder(mdc.getTimestamp()) broadcast(MDCDecodeEvent.builder(mdc.getTimestamp())
.eventType(getDecodeEventType(type))
.eventDescription(type.getLabel()) .eventDescription(type.getLabel())
.details(mdc.toString()) .details(mdc.toString())
.identifiers(ic) .identifiers(ic)
@@ -195,4 +196,22 @@ public class MDCDecoderState extends DecoderState
break; break;
} }
} }
private DecodeEventType getDecodeEventType(MDCMessageType messageType) {
switch(messageType)
{
case ANI:
return DecodeEventType.ID_ANI;
case ACKNOWLEDGE:
return DecodeEventType.RESPONSE;
case EMERGENCY:
return DecodeEventType.EMERGENCY;
case PAGING:
return DecodeEventType.PAGE;
case STATUS:
return DecodeEventType.STATUS;
default:
return DecodeEventType.UNKNOWN;
}
}
} }

View File

@@ -21,6 +21,7 @@ package io.github.dsheirer.module.decode.mpt1327;
import io.github.dsheirer.channel.IChannelDescriptor; import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.p25.P25DecodeEvent; import io.github.dsheirer.module.decode.p25.P25DecodeEvent;
import io.github.dsheirer.protocol.Protocol; import io.github.dsheirer.protocol.Protocol;
@@ -49,6 +50,7 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
protected long mTimeStart; protected long mTimeStart;
protected long mDuration; protected long mDuration;
protected String mEventDescription; protected String mEventDescription;
protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection; protected IdentifierCollection mIdentifierCollection;
protected IChannelDescriptor mChannelDescriptor; protected IChannelDescriptor mChannelDescriptor;
protected String mDetails; protected String mDetails;
@@ -93,6 +95,15 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
return this; return this;
} }
/**
* Sets the Decode Event type for this event.
* @param eventType
*/
public MPT1327ChannelGrantDecodeEventBuilder eventType(DecodeEventType eventType) {
mDecodeEventType = eventType;
return this;
}
/** /**
* Sets the event description text * Sets the event description text
* @param description of the event * @param description of the event
@@ -134,6 +145,7 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
decodeEvent.setChannelDescriptor(mChannelDescriptor); decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails); decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration); decodeEvent.setDuration(mDuration);
decodeEvent.setEventType(mDecodeEventType);
decodeEvent.setEventDescription(mEventDescription); decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection); decodeEvent.setIdentifierCollection(mIdentifierCollection);
return decodeEvent; return decodeEvent;

View File

@@ -0,0 +1,49 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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.module.decode.mpt1327;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.protocol.Protocol;
public class MPT1327DecodeEvent extends DecodeEvent
{
/**
* Constructs a MPT1327 decode event
* @param start
*/
public MPT1327DecodeEvent(long start)
{
super(start);
setProtocol(Protocol.MPT1327);
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
decodeEventBuilder.protocol(Protocol.MPT1327);
return decodeEventBuilder;
}
}

View File

@@ -30,8 +30,8 @@ import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.DecodeConfiguration; import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.event.DecodeEvent; import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel; import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.util.StringUtils; import io.github.dsheirer.util.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -110,28 +110,11 @@ public class MPT1327DecoderState extends DecoderState
if(identType == MPT1327Message.IdentType.REGI) if(identType == MPT1327Message.IdentType.REGI)
{ {
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(getDecodeEvent(message, mpt, DecodeEventType.REGISTER, null));
ic.remove(IdentifierClass.USER);
ic.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Register")
.identifiers(ic)
.build());
} }
else else
{ {
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(getDecodeEvent(message, mpt, DecodeEventType.RESPONSE, "ACK " + identType.getLabel()));
ic.remove(IdentifierClass.USER);
ic.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Response")
.details("ACK " + identType.getLabel())
.identifiers(ic)
.build());
} }
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
@@ -143,45 +126,15 @@ public class MPT1327DecoderState extends DecoderState
case ACKT: case ACKT:
case ACKV: case ACKV:
case ACKX: case ACKX:
MutableIdentifierCollection icACK = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(getDecodeEvent(message, mpt, DecodeEventType.ACKNOWLEDGE, mpt.getMessageType().getDescription()));
icACK.remove(IdentifierClass.USER);
icACK.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Acknowledge")
.details(mpt.getMessageType().getDescription())
.identifiers(icACK)
.build());
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break; break;
case AHYC: case AHYC:
MutableIdentifierCollection icAHY = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(getDecodeEvent(message, mpt, DecodeEventType.COMMAND, "Send Short Data Message"));
icAHY.remove(IdentifierClass.USER);
icAHY.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Command")
.details("Send Short Data Message")
.identifiers(icAHY)
.build());
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break; break;
case AHYQ: case AHYQ:
MutableIdentifierCollection icAHYQ = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(getDecodeEvent(message, mpt, DecodeEventType.COMMAND, "Send Status Message"));
icAHYQ.remove(IdentifierClass.USER);
icAHYQ.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Command")
.details("Send Status Message")
.identifiers(icAHYQ)
.build());
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break; break;
case ALH: case ALH:
@@ -197,41 +150,22 @@ public class MPT1327DecoderState extends DecoderState
case GTC: case GTC:
if(mMPT1327TrafficChannelManager != null) if(mMPT1327TrafficChannelManager != null)
{ {
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); MutableIdentifierCollection ic = getUpdatedMutableIdentifierCollection(mpt);
ic.remove(IdentifierClass.USER);
ic.update(mpt.getIdentifiers());
mMPT1327TrafficChannelManager.processChannelGrant(mpt, ic); mMPT1327TrafficChannelManager.processChannelGrant(mpt, ic);
} }
else else
{ {
MPT1327Channel channel = MPT1327Channel.create(mpt.getChannel()); MPT1327Channel channel = MPT1327Channel.create(mpt.getChannel());
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); DecodeEvent decodeEvent = getDecodeEvent(message, mpt, DecodeEventType.CALL_DETECT, mpt.getMessage());
ic.remove(IdentifierClass.USER); decodeEvent.setChannelDescriptor(channel);
ic.update(mpt.getIdentifiers()); broadcast(decodeEvent);
broadcast(DecodeEvent.builder(mpt.getTimestamp())
.eventDescription("Call Detect")
.details(mpt.getMessage())
.channel(channel)
.identifiers(ic)
.build());
} }
break; break;
case HEAD_PLUS1: case HEAD_PLUS1:
case HEAD_PLUS2: case HEAD_PLUS2:
case HEAD_PLUS3: case HEAD_PLUS3:
case HEAD_PLUS4: case HEAD_PLUS4:
MutableIdentifierCollection icHEAD = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(getDecodeEvent(message, mpt, DecodeEventType.SDM, mpt.getMessage()));
icHEAD.remove(IdentifierClass.USER);
icHEAD.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Short Data Message")
.details(mpt.getMessage())
.identifiers(icHEAD)
.build());
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break; break;
@@ -250,14 +184,7 @@ public class MPT1327DecoderState extends DecoderState
// timeout specified by the user. Otherwise we'll be using the shorter default call timeout // timeout specified by the user. Otherwise we'll be using the shorter default call timeout
broadcast(new ChangeChannelTimeoutEvent(this, mChannelType, mCallTimeoutMilliseconds)); broadcast(new ChangeChannelTimeoutEvent(this, mChannelType, mCallTimeoutMilliseconds));
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(getDecodeEvent(message, mpt, DecodeEventType.CALL_IN_PROGRESS, null));
ic.remove(IdentifierClass.USER);
ic.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(mpt.getTimestamp())
.identifiers(ic)
.eventDescription("Call In Progress")
.build());
broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
} }
break; break;
@@ -268,6 +195,24 @@ public class MPT1327DecoderState extends DecoderState
} }
} }
private DecodeEvent getDecodeEvent(IMessage message, MPT1327Message mpt, DecodeEventType decodeEventType, String details)
{
MutableIdentifierCollection ic = getUpdatedMutableIdentifierCollection(mpt);
return MPT1327DecodeEvent.builder(message.getTimestamp())
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
.details(details)
.identifiers(ic)
.build();
}
private MutableIdentifierCollection getUpdatedMutableIdentifierCollection(MPT1327Message mpt) {
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(mpt.getIdentifiers());
return ic;
}
public void reset() public void reset()
{ {
super.reset(); super.reset();

View File

@@ -30,6 +30,7 @@ import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role; import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.decode.config.DecodeConfiguration; import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.event.DecodeEvent; import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.event.IDecodeEvent; import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.IDecodeEventProvider; import io.github.dsheirer.module.decode.event.IDecodeEventProvider;
import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel; import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel;
@@ -105,6 +106,7 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen
MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent.mpt1327Builder(mpt1327Message.getTimestamp()) MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent.mpt1327Builder(mpt1327Message.getTimestamp())
.channel(mpt1327Channel) .channel(mpt1327Channel)
.eventType(DecodeEventType.CALL)
.eventDescription("Call") .eventDescription("Call")
.details("Traffic Channel Grant") .details("Traffic Channel Grant")
.identifiers(identifierCollection) .identifiers(identifierCollection)

View File

@@ -26,7 +26,7 @@ import io.github.dsheirer.protocol.Protocol;
public class P25DecodeEvent extends DecodeEvent public class P25DecodeEvent extends DecodeEvent
{ {
/** /**
* Constucts a P25 decode event * Constructs a P25 decode event
* @param start * @param start
*/ */
public P25DecodeEvent(long start) public P25DecodeEvent(long start)

View File

@@ -471,36 +471,19 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
if(mac instanceof QueuedResponse) if(mac instanceof QueuedResponse)
{ {
QueuedResponse qr = (QueuedResponse)mac; QueuedResponse qr = (QueuedResponse)mac;
broadcast(message, mac, DecodeEventType.RESPONSE,
MutableIdentifierCollection icQueuedResponse = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); "QUEUED - " + qr.getQueuedResponseServiceType() +
icQueuedResponse.remove(IdentifierClass.USER); " REASON:" + qr.getQueuedResponseReason() + " ADDL:" + qr.getAdditionalInfo());
icQueuedResponse.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.RESPONSE.toString())
.details("QUEUED - " + qr.getQueuedResponseServiceType() +
" REASON:" + qr.getQueuedResponseReason() + " ADDL:" + qr.getAdditionalInfo())
.identifiers(icQueuedResponse)
.build());
} }
} }
private void processEFCA(MacMessage message, MacStructure mac) { private void processEFCA(MacMessage message, MacStructure mac) {
if(mac instanceof ExtendedFunctionCommand) if(mac instanceof ExtendedFunctionCommand)
{ {
ExtendedFunctionCommand efc = (ExtendedFunctionCommand)mac; ExtendedFunctionCommand efc = (ExtendedFunctionCommand)mac;
broadcast(message, mac, DecodeEventType.COMMAND,
MutableIdentifierCollection icExtendedFunction = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); "EXTENDED FUNCTION: " + efc.getExtendedFunction() + " ARGUMENTS:" + efc.getArguments());
icExtendedFunction.remove(IdentifierClass.USER);
icExtendedFunction.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("EXTENDED FUNCTION: " + efc.getExtendedFunction() + " ARGUMENTS:" + efc.getArguments())
.identifiers(icExtendedFunction)
.build());
} }
} }
@@ -508,16 +491,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
if(mac instanceof DenyResponse) if(mac instanceof DenyResponse)
{ {
DenyResponse dr = (DenyResponse)mac; DenyResponse dr = (DenyResponse)mac;
MutableIdentifierCollection icDenyResponse = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(message, mac, DecodeEventType.RESPONSE,
icDenyResponse.remove(IdentifierClass.USER); "DENY: " + dr.getDeniedServiceType() + " REASON:" + dr.getDenyReason() + " ADDL:" + dr.getAdditionalInfo());
icDenyResponse.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.RESPONSE.toString())
.details("DENY: " + dr.getDeniedServiceType() + " REASON:" + dr.getDenyReason() + " ADDL:" + dr.getAdditionalInfo())
.identifiers(icDenyResponse)
.build());
} }
} }
@@ -525,17 +500,9 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
if(mac instanceof RadioUnitMonitorCommandExtended) if(mac instanceof RadioUnitMonitorCommandExtended)
{ {
RadioUnitMonitorCommandExtended rumce = (RadioUnitMonitorCommandExtended)mac; RadioUnitMonitorCommandExtended rumce = (RadioUnitMonitorCommandExtended)mac;
MutableIdentifierCollection icRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(message, mac, DecodeEventType.COMMAND,
icRequest.remove(IdentifierClass.USER); "RADIO UNIT MONITOR" + (rumce.isSilentMonitor() ? " (STEALTH)" : "") +
icRequest.update(mac.getIdentifiers()); " TIME:" + rumce.getTransmitTime() + "MULTIPLIER:" + rumce.getTransmitMultiplier());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("RADIO UNIT MONITOR" + (rumce.isSilentMonitor() ? " (STEALTH)" : "") +
" TIME:" + rumce.getTransmitTime() + "MULTIPLIER:" + rumce.getTransmitMultiplier())
.identifiers(icRequest)
.build());
} }
} }
@@ -543,16 +510,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
if(mac instanceof StatusUpdateExtended) if(mac instanceof StatusUpdateExtended)
{ {
StatusUpdateExtended sue = (StatusUpdateExtended)mac; StatusUpdateExtended sue = (StatusUpdateExtended)mac;
MutableIdentifierCollection icStatusUpdate = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(message, mac, DecodeEventType.STATUS,
icStatusUpdate.remove(IdentifierClass.USER); "STATUS UPDATE - UNIT:" + sue.getUnitStatus() + " USER:" + sue.getUserStatus());
icStatusUpdate.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.STATUS.toString())
.details("STATUS UPDATE - UNIT:" + sue.getUnitStatus() + " USER:" + sue.getUserStatus())
.identifiers(icStatusUpdate)
.build());
} }
} }
@@ -560,48 +519,23 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
if(mac instanceof ExtendedFunctionCommandExtended) if(mac instanceof ExtendedFunctionCommandExtended)
{ {
ExtendedFunctionCommandExtended efce = (ExtendedFunctionCommandExtended)mac; ExtendedFunctionCommandExtended efce = (ExtendedFunctionCommandExtended)mac;
broadcast(message, mac, DecodeEventType.COMMAND,
MutableIdentifierCollection icExtendedFunction = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); "EXTENDED FUNCTION: " + efce.getExtendedFunction() + " ARGUMENTS:" + efce.getArguments());
icExtendedFunction.remove(IdentifierClass.USER);
icExtendedFunction.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("EXTENDED FUNCTION: " + efce.getExtendedFunction() + " ARGUMENTS:" + efce.getArguments())
.identifiers(icExtendedFunction)
.build());
} }
} }
private void processCallAlert(MacMessage message, MacStructure mac) { private void processCallAlert(MacMessage message, MacStructure mac) {
MutableIdentifierCollection icCallAlert = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(message, mac, DecodeEventType.CALL_ALERT, null);
icCallAlert.remove(IdentifierClass.USER);
icCallAlert.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.CALL_ALERT.toString())
.identifiers(icCallAlert)
.build());
} }
private void processRUMCE(MacMessage message, MacStructure mac) { private void processRUMCE(MacMessage message, MacStructure mac) {
if(mac instanceof RadioUnitMonitorCommandEnhanced) if(mac instanceof RadioUnitMonitorCommandEnhanced)
{ {
RadioUnitMonitorCommandEnhanced rumc = (RadioUnitMonitorCommandEnhanced)mac; RadioUnitMonitorCommandEnhanced rumc = (RadioUnitMonitorCommandEnhanced)mac;
MutableIdentifierCollection icRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(message, mac, DecodeEventType.COMMAND,
icRequest.remove(IdentifierClass.USER); "RADIO UNIT MONITOR" + (rumc.isStealthMode() ? " (STEALTH)" : "") +
icRequest.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("RADIO UNIT MONITOR" + (rumc.isStealthMode() ? " (STEALTH)" : "") +
" ENCRYPTION:" + rumc.getEncryption() + " ENCRYPTION:" + rumc.getEncryption() +
" TIME:" + rumc.getTransmitTime()) " TIME:" + rumc.getTransmitTime());
.identifiers(icRequest)
.build());
} }
} }
@@ -609,16 +543,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
if(mac instanceof StatusUpdateAbbreviated) if(mac instanceof StatusUpdateAbbreviated)
{ {
StatusUpdateAbbreviated sua = (StatusUpdateAbbreviated)mac; StatusUpdateAbbreviated sua = (StatusUpdateAbbreviated)mac;
MutableIdentifierCollection icStatusUpdate = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(message, mac, DecodeEventType.STATUS,
icStatusUpdate.remove(IdentifierClass.USER); "STATUS UPDATE - UNIT:" + sua.getUnitStatus() + " USER:" + sua.getUserStatus());
icStatusUpdate.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.STATUS.toString())
.details("STATUS UPDATE - UNIT:" + sua.getUnitStatus() + " USER:" + sua.getUserStatus())
.identifiers(icStatusUpdate)
.build());
} }
} }
@@ -626,16 +552,9 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
if(mac instanceof SNDCPDataChannelGrant) if(mac instanceof SNDCPDataChannelGrant)
{ {
SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)mac; SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)mac;
MutableIdentifierCollection icGrant = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(message, mac,
icGrant.remove(IdentifierClass.USER); sdcg.getServiceOptions().isEncrypted() ? DecodeEventType.DATA_CALL_ENCRYPTED : DecodeEventType.DATA_CALL,
icGrant.update(mac.getIdentifiers()); "SNDCP CHANNEL GRANT " + sdcg.getServiceOptions());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(sdcg.getChannel())
.eventDescription(sdcg.getServiceOptions().isEncrypted() ? DecodeEventType.DATA_CALL_ENCRYPTED.toString() : DecodeEventType.DATA_CALL.toString())
.details("SNDCP CHANNEL GRANT " + sdcg.getServiceOptions())
.identifiers(icGrant)
.build());
} }
} }
@@ -643,17 +562,9 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
if(mac instanceof RadioUnitMonitorCommand) if(mac instanceof RadioUnitMonitorCommand)
{ {
RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)mac; RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)mac;
MutableIdentifierCollection icRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); broadcast(message, mac, DecodeEventType.COMMAND,
icRequest.remove(IdentifierClass.USER); "RADIO UNIT MONITOR" + (rumc.isSilentMonitor() ? " (STEALTH)" : "") +
icRequest.update(mac.getIdentifiers()); " TIME:" + rumc.getTransmitTime() + " MULTIPLIER:" + rumc.getTransmitMultiplier());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("RADIO UNIT MONITOR" + (rumc.isSilentMonitor() ? " (STEALTH)" : "") +
" TIME:" + rumc.getTransmitTime() + " MULTIPLIER:" + rumc.getTransmitMultiplier())
.identifiers(icRequest)
.build());
} }
} }
@@ -661,19 +572,9 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
if(mac instanceof MacRelease) if(mac instanceof MacRelease)
{ {
MacRelease mr = (MacRelease)mac; MacRelease mr = (MacRelease)mac;
MutableIdentifierCollection icMacRelease = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icMacRelease.remove(IdentifierClass.USER);
icMacRelease.update(mac.getIdentifiers());
closeCurrentCallEvent(message.getTimestamp(), true, message.getMacPduType()); closeCurrentCallEvent(message.getTimestamp(), true, message.getMacPduType());
broadcast(message, mac, DecodeEventType.COMMAND,
broadcast(P25DecodeEvent.builder(message.getTimestamp()) (mr.isForcedPreemption() ? "FORCED " : "") + "CALL PREEMPTION" + (mr.isTalkerPreemption() ? " BY USER" : ""));
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details((mr.isForcedPreemption() ? "FORCED " : "") + "CALL PREEMPTION" + (mr.isTalkerPreemption() ? " BY USER" : ""))
.identifiers(icMacRelease)
.build());
} }
} }
@@ -681,17 +582,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
if(mac instanceof PowerControlSignalQuality) if(mac instanceof PowerControlSignalQuality)
{ {
PowerControlSignalQuality pcsq = (PowerControlSignalQuality)mac; PowerControlSignalQuality pcsq = (PowerControlSignalQuality)mac;
broadcast(message, mac, DecodeEventType.COMMAND,
MutableIdentifierCollection icPowerControl = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); "ADJUST TRANSMIT POWER - RF:" + pcsq.getRFLevel() + " BER:" + pcsq.getBitErrorRate());
icPowerControl.remove(IdentifierClass.USER);
icPowerControl.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("ADJUST TRANSMIT POWER - RF:" + pcsq.getRFLevel() + " BER:" + pcsq.getBitErrorRate())
.identifiers(icPowerControl)
.build());
} }
} }
@@ -699,34 +591,44 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
if(mac instanceof IndividualPagingMessage) if(mac instanceof IndividualPagingMessage)
{ {
IndividualPagingMessage ipm = (IndividualPagingMessage)mac; IndividualPagingMessage ipm = (IndividualPagingMessage)mac;
MutableIdentifierCollection icIndividualPaging = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icIndividualPaging.remove(IdentifierClass.USER);
icIndividualPaging.update(mac.getIdentifiers());
boolean priority = ipm.isTalkgroupPriority1() || ipm.isTalkgroupPriority2() || boolean priority = ipm.isTalkgroupPriority1() || ipm.isTalkgroupPriority2() ||
ipm.isTalkgroupPriority3() || ipm.isTalkgroupPriority4(); ipm.isTalkgroupPriority3() || ipm.isTalkgroupPriority4();
broadcast(message, mac, DecodeEventType.PAGE,
broadcast(P25DecodeEvent.builder(message.getTimestamp()) (priority ? "PRIORITY " : "") + "USER PAGE");
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.PAGE.toString())
.details((priority ? "PRIORITY " : "") + "USER PAGE")
.identifiers(icIndividualPaging)
.build());
} }
} }
private void broadcast(MacMessage message, MacStructure mac, IChannelDescriptor currentChannel, DecodeEventType page, String s) { private void broadcast(MacMessage message, MacStructure mac, IChannelDescriptor currentChannel, DecodeEventType eventType, String details) {
MutableIdentifierCollection collection = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); MutableIdentifierCollection collection = getUpdatedMutableIdentifierCollection(mac);
collection.remove(IdentifierClass.USER);
collection.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp()) broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(currentChannel) .channel(currentChannel)
.eventDescription(page.toString()) .eventType(eventType)
.details(s) .eventDescription(eventType.toString())
.details(details)
.identifiers(collection) .identifiers(collection)
.build()); .build());
} }
private void broadcast(MacMessage message, MacStructure structure, DecodeEventType eventType, String details) {
MutableIdentifierCollection icQueuedResponse = getUpdatedMutableIdentifierCollection(structure);
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventType(eventType)
.eventDescription(eventType.toString())
.details(details)
.identifiers(icQueuedResponse)
.build());
}
private MutableIdentifierCollection getUpdatedMutableIdentifierCollection(MacStructure mac) {
MutableIdentifierCollection icQueuedResponse = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icQueuedResponse.remove(IdentifierClass.USER);
icQueuedResponse.update(mac.getIdentifiers());
return icQueuedResponse;
}
private void processUTUVCGUExtended(MacStructure mac) { private void processUTUVCGUExtended(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantUpdateExtended) if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantUpdateExtended)
{ {

View File

@@ -0,0 +1,49 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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.module.decode.passport;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.protocol.Protocol;
public class PassportDecodeEvent extends DecodeEvent
{
/**
* Constructs a Passport decode event
* @param start
*/
public PassportDecodeEvent(long start)
{
super(start);
setProtocol(Protocol.PASSPORT);
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
decodeEventBuilder.protocol(Protocol.PASSPORT);
return decodeEventBuilder;
}
}

View File

@@ -34,6 +34,7 @@ import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.MessageType; import io.github.dsheirer.message.MessageType;
import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent; import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.passport.identifier.PassportRadioId; import io.github.dsheirer.module.decode.passport.identifier.PassportRadioId;
import io.github.dsheirer.module.decode.passport.identifier.PassportTalkgroup; import io.github.dsheirer.module.decode.passport.identifier.PassportTalkgroup;
import io.github.dsheirer.protocol.Protocol; import io.github.dsheirer.protocol.Protocol;
@@ -158,7 +159,8 @@ public class PassportDecoderState extends DecoderState
} }
} }
mCurrentDecodeEvent = DecodeEvent.builder(passport.getTimestamp()) mCurrentDecodeEvent = PassportDecodeEvent.builder(passport.getTimestamp())
.eventType(passport.getMessageType() == MessageType.CA_STRT ? DecodeEventType.CALL : DecodeEventType.DATA_CALL)
.eventDescription(passport.getMessageType() == MessageType.CA_STRT ? "Call" : "Data") .eventDescription(passport.getMessageType() == MessageType.CA_STRT ? "Call" : "Data")
.identifiers(getIdentifierCollection().copyOf()) .identifiers(getIdentifierCollection().copyOf())
.details(passport.toString()) .details(passport.toString())
@@ -177,7 +179,8 @@ public class PassportDecoderState extends DecoderState
!isSameTalkgroup(to, getToIdentifier(callDetect.getIdentifierCollection())) || !isSameTalkgroup(to, getToIdentifier(callDetect.getIdentifierCollection())) ||
callDetect.getTimeStart() < (passport.getTimestamp() - 45000)) callDetect.getTimeStart() < (passport.getTimestamp() - 45000))
{ {
callDetect = DecodeEvent.builder(passport.getTimestamp()) callDetect = PassportDecodeEvent.builder(passport.getTimestamp())
.eventType(DecodeEventType.CALL_DETECT)
.eventDescription("Call Detect") .eventDescription("Call Detect")
.identifiers(new IdentifierCollection(passport.getIdentifiers())) .identifiers(new IdentifierCollection(passport.getIdentifiers()))
// .channel(...) // .channel(...)
@@ -218,7 +221,8 @@ public class PassportDecoderState extends DecoderState
} }
break; break;
case RA_REGI: case RA_REGI:
broadcast(DecodeEvent.builder(passport.getTimestamp()) broadcast(PassportDecodeEvent.builder(passport.getTimestamp())
.eventType(DecodeEventType.REGISTER)
.eventDescription("Register") .eventDescription("Register")
.identifiers(new IdentifierCollection(passport.getIdentifiers())) .identifiers(new IdentifierCollection(passport.getIdentifiers()))
.build()); .build());

View File

@@ -30,14 +30,17 @@ import io.github.dsheirer.identifier.MutableIdentifierCollection;
import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent; import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.event.PlottableDecodeEvent; import io.github.dsheirer.module.decode.event.PlottableDecodeEvent;
import io.github.dsheirer.module.decode.tait.identifier.TaitIdentifier; import io.github.dsheirer.module.decode.tait.identifier.TaitIdentifier;
import io.github.dsheirer.protocol.Protocol;
import org.jdesktop.swingx.mapviewer.GeoPosition; import org.jdesktop.swingx.mapviewer.GeoPosition;
import java.util.TreeSet; import java.util.TreeSet;
public class Tait1200DecoderState extends DecoderState public class Tait1200DecoderState extends DecoderState
{ {
public static final Protocol PROTOCOL_TAIT_1200 = Protocol.TAIT1200;
private TreeSet<TaitIdentifier> mIdents = new TreeSet<>(); private TreeSet<TaitIdentifier> mIdents = new TreeSet<>();
public Tait1200DecoderState() public Tait1200DecoderState()
@@ -95,6 +98,8 @@ public class Tait1200DecoderState extends DecoderState
ic.update(message.getIdentifiers()); ic.update(message.getIdentifiers());
PlottableDecodeEvent event = PlottableDecodeEvent.plottableBuilder(gps.getTimestamp()) PlottableDecodeEvent event = PlottableDecodeEvent.plottableBuilder(gps.getTimestamp())
.protocol(PROTOCOL_TAIT_1200)
.eventType(DecodeEventType.GPS)
.eventDescription("GPS") .eventDescription("GPS")
.identifiers(ic) .identifiers(ic)
.location(position) .location(position)
@@ -117,6 +122,8 @@ public class Tait1200DecoderState extends DecoderState
ic.update(message.getIdentifiers()); ic.update(message.getIdentifiers());
broadcast(DecodeEvent.builder(ani.getTimestamp()) broadcast(DecodeEvent.builder(ani.getTimestamp())
.protocol(PROTOCOL_TAIT_1200)
.eventType(DecodeEventType.ID_ANI)
.eventDescription("ANI") .eventDescription("ANI")
.identifiers(ic) .identifiers(ic)
.details("Automatic Number Identification") .details("Automatic Number Identification")