From a56935f92cae422f48d7928980d1c25354586210 Mon Sep 17 00:00:00 2001 From: Denny Date: Sun, 31 Jul 2016 07:52:01 -0400 Subject: [PATCH] Closes #74 - adds support for vendor-specific (Motorola) TSBK patch group management. Processes patch group channel grants and updates and allocates voice channel processing. Logs call events for patch group add (register) and patch group delete (deregister). Also adds call event logging when there is a planned control channel shutdown--logged only once. --- src/module/decode/event/CallEvent.java | 1 + src/module/decode/p25/P25DecoderState.java | 162 +++++++++++++++--- .../motorola/PatchGroupVoiceChannelGrant.java | 3 + 3 files changed, 145 insertions(+), 21 deletions(-) diff --git a/src/module/decode/event/CallEvent.java b/src/module/decode/event/CallEvent.java index 28990ad9..ef7ce7d0 100644 --- a/src/module/decode/event/CallEvent.java +++ b/src/module/decode/event/CallEvent.java @@ -276,6 +276,7 @@ public abstract class CallEvent implements Comparable GROUP_CALL( "Group Call" ), ID_ANI( "ANI" ), ID_UNIQUE( "Unique ID" ), + NOTIFICATION( "Notification" ), PAGE( "Page" ), QUERY( "Query" ), REGISTER( "Register" ), diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index ef7b09e9..ffb5c5f0 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -17,16 +17,11 @@ ******************************************************************************/ package module.decode.p25; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.concurrent.ScheduledExecutorService; - +import alias.Alias; +import alias.AliasList; +import audio.metadata.Metadata; +import audio.metadata.MetadataType; +import controller.channel.Channel.ChannelType; import message.Message; import module.decode.DecoderType; import module.decode.event.CallEvent.CallEventType; @@ -79,6 +74,11 @@ import module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast; import module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcastExplicit; import module.decode.p25.message.tdu.lc.TDULinkControlMessage; import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.message.tsbk.motorola.MotorolaTSBKMessage; +import module.decode.p25.message.tsbk.motorola.PatchGroupAdd; +import module.decode.p25.message.tsbk.motorola.PatchGroupDelete; +import module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrant; +import module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrantUpdate; import module.decode.p25.message.tsbk.osp.control.AcknowledgeResponse; import module.decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; import module.decode.p25.message.tsbk.osp.control.AuthenticationCommand; @@ -128,15 +128,18 @@ import module.decode.state.DecoderStateEvent; import module.decode.state.DecoderStateEvent.Event; import module.decode.state.State; import module.decode.state.TrafficChannelAllocationEvent; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import alias.Alias; -import alias.AliasList; -import audio.metadata.Metadata; -import audio.metadata.MetadataType; -import controller.channel.Channel.ChannelType; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; public class P25DecoderState extends DecoderState { @@ -178,6 +181,7 @@ public class P25DecoderState extends DecoderState private ChannelType mChannelType; private Modulation mModulation; private boolean mIgnoreDataCalls; + private boolean mControlChannelShutdownLogged; private P25CallEvent mCurrentCallEvent; private List mCallDetectTalkgroups = new ArrayList<>(); @@ -265,7 +269,6 @@ public class P25DecoderState extends DecoderState * @param channel - channel number * @param from user id * @param to talkgroup or user id - * @param time current in millis * @return true if there is a call event */ public boolean hasCallEvent( String channel, String from, String to ) @@ -305,7 +308,6 @@ public class P25DecoderState extends DecoderState * Adds the channel and event to the current channel call map. If an entry * already exists, terminates the event and broadcasts the update. * - * @param channel for the call * @param event to place in the map */ public void registerCallEvent( P25CallEvent event ) @@ -322,7 +324,7 @@ public class P25DecoderState extends DecoderState mChannelCallMap.put( event.getChannel(), event ); } - public void updateCallEvent( String channel, String from, String to ) + private void updateCallEvent( String channel, String from, String to ) { P25CallEvent event = mChannelCallMap.get( channel ); @@ -1620,8 +1622,12 @@ public class P25DecoderState extends DecoderState break; } } + else if( tsbk.getVendor() == Vendor.MOTOROLA ) + { + processMotorolaTSBK((MotorolaTSBKMessage)tsbk); + } } - + /** * Process a Packet Data Unit message */ @@ -2697,7 +2703,121 @@ public class P25DecoderState extends DecoderState break; } } - + + /** + * Process Motorola vendor-specific Trunking Signaling Block messages + */ + private void processMotorolaTSBK(MotorolaTSBKMessage tsbk) + { + String channel; + String from; + String to; + + switch(((MotorolaTSBKMessage)tsbk).getMotorolaOpcode()) + { + case PATCH_GROUP_CHANNEL_GRANT: + PatchGroupVoiceChannelGrant gvcg = (PatchGroupVoiceChannelGrant)tsbk; + + channel = gvcg.getChannel(); + from = gvcg.getSourceAddress(); + to = gvcg.getPatchGroupAddress(); + + if( hasCallEvent( channel, from, to ) ) + { + updateCallEvent( channel, from, to ); + } + else + { + P25CallEvent event = new P25CallEvent.Builder( CallEventType.GROUP_CALL ) + .aliasList( getAliasList() ) + .channel( channel ) + .details( ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcg.isEmergency() ? " EMERGENCY" : "") + + " PATCH SESSION MODE:" + gvcg.getSessionMode().name()) + .frequency( gvcg.getDownlinkFrequency() ) + .from( from ) + .to( to ) + .build(); + + registerCallEvent( event ); + broadcast( event ); + } + + broadcast( new TrafficChannelAllocationEvent( this, + mChannelCallMap.get( channel ) ) ); + break; + case PATCH_GROUP_CHANNEL_GRANT_UPDATE: + PatchGroupVoiceChannelGrantUpdate gvcgu = (PatchGroupVoiceChannelGrantUpdate) tsbk; + + channel = gvcgu.getChannel1(); + to = gvcgu.getPatchGroupAddress1(); + + if( hasCallEvent( channel, null, to ) ) + { + updateCallEvent( channel, null, to ); + } + else + { + P25CallEvent event = new P25CallEvent.Builder( CallEventType.GROUP_CALL ) + .aliasList( getAliasList() ) + .channel( channel ) + .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + " UPDATE - GROUP 2:" + gvcgu.getPatchGroupAddress2() + + " DN:" + gvcgu.getDownlinkFrequency2()) + .frequency( gvcgu.getDownlinkFrequency1() ) + .to( to ) + .build(); + + registerCallEvent( event ); + broadcast( event ); + } + + broadcast( new TrafficChannelAllocationEvent( this, + mChannelCallMap.get( channel ) ) ); + break; + case PATCH_GROUP_ADD: + PatchGroupAdd pga = (PatchGroupAdd)tsbk; + + StringBuilder sb = new StringBuilder(); + + sb.append("PATCH GROUP:").append(pga.getPatchGroupAddress()); + sb.append(" 1:").append(pga.getGroupAddress1()); + sb.append(" 2:").append(pga.getGroupAddress2()); + sb.append(" 3:").append(pga.getGroupAddress3()); + + broadcast( new P25CallEvent.Builder( CallEventType.REGISTER ) + .aliasList( getAliasList() ) + .details( sb.toString() ) + .build() ); + break; + case PATCH_GROUP_DELETE: + PatchGroupDelete pgd = (PatchGroupDelete)tsbk; + + StringBuilder sbpgd = new StringBuilder(); + + sbpgd.append("PATCH GROUP:").append(pgd.getPatchGroupAddress()); + sbpgd.append(" 1:").append(pgd.getGroupAddress1()); + sbpgd.append(" 2:").append(pgd.getGroupAddress2()); + sbpgd.append(" 3:").append(pgd.getGroupAddress3()); + + broadcast( new P25CallEvent.Builder( CallEventType.DEREGISTER ) + .aliasList( getAliasList() ) + .details( sbpgd.toString() ) + .build() ); + break; + case CCH_PLANNED_SHUTDOWN: + if( !mControlChannelShutdownLogged ) + { + broadcast( new P25CallEvent.Builder( CallEventType.NOTIFICATION ) + .details( "PLANNED CONTROL CHANNEL SHUTDOWN" ) + .build() ); + + mControlChannelShutdownLogged = true; + } + break; + } + } + /** * Process a traffic channel allocation message */ diff --git a/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java index 6d7f5e5a..6b5a1cae 100644 --- a/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java @@ -63,6 +63,9 @@ public class PatchGroupVoiceChannelGrant extends MotorolaTSBKMessage sb.append( " DN:" + getDownlinkFrequency() ); sb.append( " UP:" + getUplinkFrequency() ); + + sb.append( " SESSION MODE:"); + sb.append( getSessionMode().name() ); return sb.toString(); }