/******************************************************************************* * SDR Trunk * Copyright (C) 2014 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 ******************************************************************************/ package decode.mpt1327; import message.Message; import sample.Listener; import source.config.SourceConfigTuner; import alias.AliasList; import audio.SquelchListener; import audio.SquelchListener.SquelchState; import controller.activity.CallEvent; import controller.activity.CallEvent.CallEventType; import controller.channel.Channel; import controller.channel.Channel.ChannelType; import controller.channel.ChannelMap; import controller.channel.ProcessingChain; import controller.state.AuxChannelState; import controller.state.ChannelState; import decode.config.DecodeConfigMPT1327; import decode.mpt1327.MPT1327Message.IdentType; public class MPT1327ChannelState extends ChannelState { private int mSite; private String mFromTalkgroup; private String mToTalkgroup; private int mChannelNumber; private ChannelType mChannelType; private ChannelMap mChannelMap; private MPT1327ActivitySummary mActivitySummary; public MPT1327ChannelState( ProcessingChain processingChain, AliasList aliasList, ChannelMap channelMap ) { super( processingChain, aliasList ); mActivitySummary = new MPT1327ActivitySummary( aliasList ); mChannelMap = channelMap; mChannelType = processingChain.getChannel().getChannelType(); } public void dispose() { super.dispose(); mActivitySummary.dispose(); mActivitySummary = null; mCurrentCallEvent = null; } public void setCurrentCall( MPT1327CallEvent event ) { mCurrentCallEvent = event; } @Override public String getActivitySummary() { StringBuilder sb = new StringBuilder(); sb.append( mActivitySummary.getSummary() ); for( AuxChannelState state: mAuxChannelStates ) { sb.append( state.getActivitySummary() ); sb.append( "\n\n" ); } return sb.toString(); } @Override public void receive( Message message ) { super.receive( message ); if( message instanceof MPT1327Message ) { mActivitySummary.receive( message ); MPT1327Message mpt = (MPT1327Message)message; if( mpt.isValid() ) { switch( mpt.getMessageType() ) { case ACK: IdentType identType = ( (MPT1327Message) message ).getIdent1Type(); if( identType == IdentType.REGI ) { mCallEventModel.add( new MPT1327CallEvent.Builder( CallEventType.REGISTER ) .aliasList( getProcessingChain().getAliasList() ) .channel( mChannelNumber ) .details( "REGISTERED ON NETWORK" ) .frequency( mChannelMap.getFrequency( mChannelNumber ) ) .from( mpt.getToID() ) .to( mpt.getFromID() ) .build() ); } else { mCallEventModel.add( new MPT1327CallEvent.Builder( CallEventType.ACKNOWLEDGE ) .aliasList( getProcessingChain().getAliasList() ) .channel( mChannelNumber ) .details( "ACK " + identType.getLabel() ) .frequency( mChannelMap.getFrequency( mChannelNumber ) ) .from( mpt.getFromID() ) .to( mpt.getToID() ) .build() ); } break; case AHYC: mCallEventModel.add( new MPT1327CallEvent.Builder( CallEventType.REQUEST ) .aliasList( getProcessingChain().getAliasList() ) .channel( mChannelNumber ) .details( ( (MPT1327Message) message ).getRequestString() ) .frequency( mChannelMap.getFrequency( mChannelNumber ) ) .from( mpt.getFromID() ) .to( mpt.getToID() ) .build() ); break; case AHYQ: mCallEventModel.add( new MPT1327CallEvent.Builder( CallEventType.STATUS ) .aliasList( getProcessingChain().getAliasList() ) .channel( mChannelNumber ) .details( mpt.getStatusMessage() ) .frequency( mChannelMap.getFrequency( mChannelNumber ) ) .from( mpt.getFromID() ) .to( mpt.getToID() ) .build() ); break; case ALH: int sys = mpt.getSiteID(); if( sys != 0 && sys != mSite ) { mSite = sys; broadcastChange( ChangedAttribute.CHANNEL_SITE_NUMBER ); } setState( State.CONTROL ); break; case GTC: if( mpt.isValidCall() ) { int channelNumber = mpt.getChannel(); if( !getProcessingChain().getChannel() .hasTrafficChannel( channelNumber ) ) { String aliasListName = mProcessingChain .getChannel().getAliasListName(); Channel traffic = getTrafficChannel( channelNumber, aliasListName ); /* Set the system and site to same as control channel */ traffic.setSystem( getProcessingChain().getChannel() .getSystem(), false ); traffic.setSite( getProcessingChain().getChannel() .getSite(), false ); /* Add the traffic channel to the parent control channel */ getProcessingChain().getChannel() .addTrafficChannel( channelNumber, traffic ); /* Start the traffic channel */ traffic.setEnabled( true ); /* Set traffic channel state info */ MPT1327ChannelState trafficState = (MPT1327ChannelState)traffic .getProcessingChain().getChannelState(); MPT1327ChannelState controlState = (MPT1327ChannelState)getProcessingChain() .getChannelState(); trafficState.setChannelMap( controlState.getChannelMap() ); trafficState.setChannelNumber( channelNumber ); trafficState.setFromTalkgroup( mpt.getFromID() ); trafficState.setToTalkgroup( mpt.getToID() ); /* Add this control channel as message listener on the * traffic channel so we can receive call tear-down * messages */ traffic.addListener( (Listener)this ); CallEventType type = traffic.isProcessing() ? CallEventType.CALL : CallEventType.CALL_NO_TUNER; /* * Set the traffic channel's call event model to * share the control channel's call event model */ traffic.getProcessingChain().getChannelState() .setCallEventModel( mCallEventModel ); MPT1327CallEvent callStartEvent = new MPT1327CallEvent.Builder( type ) .aliasList( traffic.getProcessingChain().getAliasList() ) .channel( channelNumber ) .frequency( mChannelMap.getFrequency( channelNumber ) ) .from( mpt.getFromID() ) .to( mpt.getToID() ) .build(); traffic.getProcessingChain().getChannelState() .setCurrentCallEvent( callStartEvent ); mCallEventModel.add( callStartEvent ); } } break; case CLEAR: case MAINT: if( mChannelType == ChannelType.TRAFFIC ) { fade( CallEventType.CALL_END ); } break; case HEAD_PLUS1: case HEAD_PLUS2: case HEAD_PLUS3: case HEAD_PLUS4: mCallEventModel.add( new MPT1327CallEvent.Builder( CallEventType.SDM ) .aliasList( mAliasList ) .details( mpt.getMessage() ) .from( mpt.getFromID() ) .to( mpt.getToID() ) .build() ); break; } } } } /** * Intercept the fade event so that we can generate a call end event */ @Override public void fade( final CallEventType type ) { /* * We can receive multiple call tear-down messages -- only post a call * end event for the one that can change the state to fade */ if( getState().canChangeTo( State.FADE ) ) { CallEvent current = getCurrentCallEvent(); if( current != null ) { mCallEventModel.setEnd( current ); } else { mCallEventModel.add( new MPT1327CallEvent.Builder( type ) .aliasList( getAliasList() ) .channel( mChannelNumber ) .frequency( mChannelMap.getFrequency( mChannelNumber ) ) .from( mFromTalkgroup ) .to( mToTalkgroup ) .build() ); } setCurrentCall( null ); } super.fade( type ); } /** * Make the ConventionalChannelState always unsquelched */ public void addListener( SquelchListener listener ) { super.addListener( listener ); super.setSquelchState( SquelchState.UNSQUELCH ); } public void reset() { mFromTalkgroup = null; broadcastChange( ChangedAttribute.FROM_TALKGROUP ); mToTalkgroup = null; broadcastChange( ChangedAttribute.TO_TALKGROUP ); super.reset(); } public int getSite() { return mSite; } public ChannelMap getChannelMap() { return mChannelMap; } public void setChannelMap( ChannelMap channelMap ) { mChannelMap = channelMap; } public String getFromTalkgroup() { return mFromTalkgroup; } /** * Set the talkgroup. This is used primarily for traffic channels since * the talkgroup will already have been identified prior to the traffic * channel being created. */ public void setFromTalkgroup( String fromTalkgroup ) { mFromTalkgroup = fromTalkgroup; broadcastChange( ChangedAttribute.FROM_TALKGROUP ); } public String getToTalkgroup() { return mToTalkgroup; } /** * Set the talkgroup. This is used primarily for traffic channels since * the talkgroup will already have been identified prior to the traffic * channel being created. */ public void setToTalkgroup( String toTalkgroup ) { mToTalkgroup = toTalkgroup; broadcastChange( ChangedAttribute.TO_TALKGROUP ); } public int getChannelNumber() { return mChannelNumber; } /** * Set the channel number. This is used primarily for traffic channels since * the channel will already have been identified prior to the traffic * channel being created. */ public void setChannelNumber( int channel ) { mChannelNumber = channel; broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); } public String getAliasListName() { return mProcessingChain.getChannel().getAliasListName(); } private Channel getTrafficChannel( int channelNumber, String aliasListName ) { Channel traffic = new Channel( "Traffic Channel " + channelNumber, ChannelType.TRAFFIC ); traffic.setResourceManager( getProcessingChain().getResourceManager() ); SourceConfigTuner source = new SourceConfigTuner(); long frequency = mChannelMap.getFrequency( channelNumber ); source.setFrequency( frequency ); traffic.setSourceConfiguration( source ); DecodeConfigMPT1327 decode = new DecodeConfigMPT1327(); traffic.setDecodeConfiguration( decode ); traffic.setAliasListName( aliasListName ); return traffic; } }