mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-10 15:47:25 -05:00
557 lines
18 KiB
Java
557 lines
18 KiB
Java
/*******************************************************************************
|
|
* sdrtrunk
|
|
* Copyright (C) 2014-2017 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 module.decode.mpt1327;
|
|
|
|
import alias.Alias;
|
|
import alias.AliasList;
|
|
import alias.id.AliasIDType;
|
|
import channel.metadata.AliasedStringAttributeMonitor;
|
|
import channel.metadata.Attribute;
|
|
import channel.metadata.AttributeChangeRequest;
|
|
import channel.state.ChangeChannelTimeoutEvent;
|
|
import channel.state.DecoderState;
|
|
import channel.state.DecoderStateEvent;
|
|
import channel.state.DecoderStateEvent.Event;
|
|
import channel.state.State;
|
|
import channel.traffic.TrafficChannelAllocationEvent;
|
|
import controller.channel.Channel.ChannelType;
|
|
import controller.channel.map.ChannelMap;
|
|
import message.Message;
|
|
import module.decode.DecoderType;
|
|
import module.decode.config.DecodeConfiguration;
|
|
import module.decode.event.CallEvent;
|
|
import module.decode.event.CallEvent.CallEventType;
|
|
import module.decode.mpt1327.MPT1327Message.IdentType;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import util.StringUtils;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.TreeSet;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
|
|
public class MPT1327DecoderState extends DecoderState
|
|
{
|
|
private final static Logger mLog = LoggerFactory.getLogger(MPT1327DecoderState.class);
|
|
|
|
private TreeSet<String> mIdents = new TreeSet<String>();
|
|
private HashMap<String,ArrayList<String>> mGroups = new HashMap<String,ArrayList<String>>();
|
|
|
|
private String mSite;
|
|
private AliasedStringAttributeMonitor mFromAttribute;
|
|
private AliasedStringAttributeMonitor mToAttribute;
|
|
|
|
private int mChannelNumber;
|
|
private ChannelType mChannelType;
|
|
private ChannelMap mChannelMap;
|
|
|
|
private long mFrequency = 0;
|
|
private long mCallTimeout;
|
|
|
|
public MPT1327DecoderState(AliasList aliasList, ChannelMap channelMap, ChannelType channelType, long callTimeout)
|
|
{
|
|
super(aliasList);
|
|
|
|
mChannelMap = channelMap;
|
|
mChannelType = channelType;
|
|
mCallTimeout = callTimeout;
|
|
mFromAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM,
|
|
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.MPT1327);
|
|
mToAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO,
|
|
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.MPT1327);
|
|
}
|
|
|
|
@Override
|
|
public DecoderType getDecoderType()
|
|
{
|
|
return DecoderType.MPT1327;
|
|
}
|
|
|
|
@Override
|
|
public void start(ScheduledExecutorService executor)
|
|
{
|
|
}
|
|
|
|
@Override
|
|
public void stop()
|
|
{
|
|
}
|
|
|
|
public ChannelType getChannelType()
|
|
{
|
|
return mChannelType;
|
|
}
|
|
|
|
public void dispose()
|
|
{
|
|
super.dispose();
|
|
}
|
|
|
|
@Override
|
|
public void receive(Message message)
|
|
{
|
|
if(message.isValid())
|
|
{
|
|
if(message instanceof MPT1327Message)
|
|
{
|
|
MPT1327Message mpt = ((MPT1327Message) message);
|
|
|
|
switch(mpt.getMessageType())
|
|
{
|
|
case ACK:
|
|
mIdents.add(mpt.getFromID());
|
|
|
|
IdentType identType = mpt.getIdent1Type();
|
|
|
|
if(identType == IdentType.REGI)
|
|
{
|
|
broadcast(new MPT1327CallEvent.Builder(CallEventType.REGISTER)
|
|
.aliasList(getAliasList())
|
|
.channel(String.valueOf(mChannelNumber))
|
|
.details("REGISTERED ON NETWORK")
|
|
.frequency(mFrequency)
|
|
.from(mpt.getToID())
|
|
.to(mpt.getFromID())
|
|
.build());
|
|
}
|
|
else
|
|
{
|
|
broadcast(new MPT1327CallEvent.Builder(CallEventType.RESPONSE)
|
|
.aliasList(getAliasList())
|
|
.channel(String.valueOf(mChannelNumber))
|
|
.details("ACK " + identType.getLabel())
|
|
.frequency(mFrequency)
|
|
.from(mpt.getFromID())
|
|
.to(mpt.getToID())
|
|
.build());
|
|
}
|
|
|
|
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
|
|
break;
|
|
case ACKI:
|
|
mIdents.add(mpt.getFromID());
|
|
mIdents.add(mpt.getToID());
|
|
|
|
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
|
|
break;
|
|
case AHYC:
|
|
mIdents.add(mpt.getToID());
|
|
|
|
broadcast(new MPT1327CallEvent.Builder(CallEventType.COMMAND)
|
|
.aliasList(getAliasList())
|
|
.channel(String.valueOf(mChannelNumber))
|
|
.details(((MPT1327Message) message).getRequestString())
|
|
.frequency(mFrequency)
|
|
.from(mpt.getFromID())
|
|
.to(mpt.getToID())
|
|
.build());
|
|
|
|
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
|
|
break;
|
|
case AHYQ:
|
|
broadcast(new MPT1327CallEvent.Builder(CallEventType.STATUS)
|
|
.aliasList(getAliasList())
|
|
.channel(String.valueOf(mChannelNumber))
|
|
.details(mpt.getStatusMessage())
|
|
.frequency(mFrequency)
|
|
.from(mpt.getFromID())
|
|
.to(mpt.getToID())
|
|
.build());
|
|
|
|
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
|
|
break;
|
|
case ALH:
|
|
setSite(mpt.getSiteID());
|
|
broadcast(new DecoderStateEvent(this, Event.START, State.CONTROL));
|
|
break;
|
|
case GTC:
|
|
String from = mpt.getFromID();
|
|
String to = mpt.getToID();
|
|
|
|
if(from != null)
|
|
{
|
|
mIdents.add(from);
|
|
}
|
|
|
|
if(to != null)
|
|
{
|
|
mIdents.add(to);
|
|
}
|
|
|
|
/* Capture the idents that talk to each group */
|
|
if(from != null && to != null)
|
|
{
|
|
if(mGroups.containsKey(to))
|
|
{
|
|
ArrayList<String> talkgroups = mGroups.get(to);
|
|
|
|
if(!talkgroups.contains(from))
|
|
{
|
|
talkgroups.add(from);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ArrayList<String> talkgroups = new ArrayList<String>();
|
|
talkgroups.add(from);
|
|
|
|
mGroups.put(to, talkgroups);
|
|
}
|
|
}
|
|
|
|
int channel = mpt.getChannel();
|
|
|
|
long frequency = 0;
|
|
|
|
if(getChannelMap() != null)
|
|
{
|
|
frequency = getChannelMap().getFrequency(channel);
|
|
}
|
|
|
|
CallEvent event = new MPT1327CallEvent.Builder(CallEventType.CALL)
|
|
.aliasList(getAliasList())
|
|
.channel(String.valueOf(channel))
|
|
.details("GTC")
|
|
.frequency(frequency)
|
|
.from(mpt.getFromID())
|
|
.to(mpt.getToID())
|
|
.build();
|
|
|
|
broadcast(new TrafficChannelAllocationEvent(this, event));
|
|
break;
|
|
case HEAD_PLUS1:
|
|
case HEAD_PLUS2:
|
|
case HEAD_PLUS3:
|
|
case HEAD_PLUS4:
|
|
broadcast(new MPT1327CallEvent.Builder(CallEventType.SDM)
|
|
.aliasList(getAliasList())
|
|
.details(mpt.getMessage())
|
|
.from(mpt.getFromID())
|
|
.to(mpt.getToID())
|
|
.build());
|
|
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
|
|
break;
|
|
|
|
/* Traffic Channel Events */
|
|
case CLEAR:
|
|
mChannelNumber = mpt.getChannel();
|
|
|
|
broadcast(new DecoderStateEvent(this, Event.END, State.FADE));
|
|
break;
|
|
case MAINT:
|
|
if(mChannelType == ChannelType.STANDARD)
|
|
{
|
|
/**
|
|
* When we receive a MAINT message and we're configured
|
|
* as a standard channel, we need to apply the call
|
|
* timeout specified by the user. Otherwise we'll
|
|
* be using the shorter default call timeout
|
|
*/
|
|
broadcast(new ChangeChannelTimeoutEvent(this,
|
|
mChannelType, mCallTimeout));
|
|
|
|
if(mCurrentCallEvent == null)
|
|
{
|
|
mCurrentCallEvent = new MPT1327CallEvent.Builder(CallEventType.CALL)
|
|
.aliasList(getAliasList())
|
|
.channel(String.valueOf(mChannelNumber))
|
|
.details("MONITORED TRAFFIC CHANNEL")
|
|
.frequency(mFrequency)
|
|
.to(mpt.getToID())
|
|
.build();
|
|
|
|
broadcast(mCurrentCallEvent);
|
|
}
|
|
|
|
mToAttribute.process(mpt.getToID());
|
|
|
|
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void reset()
|
|
{
|
|
mIdents.clear();
|
|
|
|
resetState();
|
|
}
|
|
|
|
private void resetState()
|
|
{
|
|
mFromAttribute.reset();
|
|
mToAttribute.reset();
|
|
|
|
/**
|
|
* If this is a standard channel, reset the fade timeout to the default
|
|
* timeout. Once processing is underway, if we get a MAINT message,
|
|
* this indicates we're processing a traffic channel as a standard
|
|
* channel, so we'll issue a different call timeout then.
|
|
*/
|
|
if(mChannelType == ChannelType.STANDARD)
|
|
{
|
|
broadcast(new ChangeChannelTimeoutEvent(this, mChannelType,
|
|
DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000));
|
|
|
|
if(mCurrentCallEvent != null)
|
|
{
|
|
mCurrentCallEvent.end();
|
|
broadcast(mCurrentCallEvent);
|
|
mCurrentCallEvent = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public String getSite()
|
|
{
|
|
return mSite;
|
|
}
|
|
|
|
/**
|
|
* Set the site number.
|
|
*/
|
|
public void setSite(String site)
|
|
{
|
|
if(!StringUtils.isEqual(mSite, site))
|
|
{
|
|
mSite = site;
|
|
broadcast(new AttributeChangeRequest<String>(Attribute.NETWORK_ID_1, "SITE:" + getSite(),
|
|
getSiteAlias()));
|
|
}
|
|
}
|
|
|
|
public Alias getSiteAlias()
|
|
{
|
|
if(hasAliasList())
|
|
{
|
|
return getAliasList().getSiteID(mSite);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public ChannelMap getChannelMap()
|
|
{
|
|
return mChannelMap;
|
|
}
|
|
|
|
public void setChannelMap(ChannelMap channelMap)
|
|
{
|
|
mChannelMap = channelMap;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if(mChannelNumber != channel)
|
|
{
|
|
mChannelNumber = channel;
|
|
broadcast(new AttributeChangeRequest<String>(Attribute.CHANNEL_FREQUENCY_LABEL, String.valueOf(mChannelNumber)));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String getActivitySummary()
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
sb.append("Activity Summary - Decoder: MPT-1327\n");
|
|
sb.append(DIVIDER1);
|
|
sb.append("Site: ");
|
|
|
|
if(mSite != null)
|
|
{
|
|
sb.append(mSite);
|
|
|
|
if(hasAliasList())
|
|
{
|
|
Alias siteAlias = getAliasList().getSiteID(mSite);
|
|
|
|
if(siteAlias != null)
|
|
{
|
|
sb.append(" ").append(siteAlias.getName()).append("\n");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sb.append("Unknown\n");
|
|
}
|
|
|
|
sb.append(DIVIDER2).append("Talkgroups: ");
|
|
|
|
if(mGroups.isEmpty())
|
|
{
|
|
sb.append("None\n");
|
|
}
|
|
else
|
|
{
|
|
List<String> talkgroups = new ArrayList<String>(mGroups.keySet());
|
|
Collections.sort(talkgroups);
|
|
|
|
for(String talkgroup : talkgroups)
|
|
{
|
|
sb.append("\n ").append(talkgroup);
|
|
|
|
if(hasAliasList())
|
|
{
|
|
Alias alias = getAliasList().getMPT1327Alias(talkgroup);
|
|
|
|
if(alias != null)
|
|
{
|
|
sb.append(" ");
|
|
sb.append(alias.getName());
|
|
}
|
|
}
|
|
|
|
sb.append("\n");
|
|
|
|
ArrayList<String> members = mGroups.get(talkgroup);
|
|
Collections.sort(members);
|
|
|
|
for(String member : members)
|
|
{
|
|
sb.append(" >");
|
|
sb.append(member);
|
|
|
|
if(hasAliasList())
|
|
{
|
|
Alias alias = getAliasList().getMPT1327Alias(member);
|
|
|
|
if(alias != null)
|
|
{
|
|
sb.append(" ");
|
|
sb.append(alias.getName());
|
|
}
|
|
}
|
|
|
|
sb.append("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
sb.append(DIVIDER2).append("All Idents: ");
|
|
|
|
if(mIdents.isEmpty())
|
|
{
|
|
sb.append("None\n");
|
|
}
|
|
else
|
|
{
|
|
sb.append("\n");
|
|
|
|
Iterator<String> it = mIdents.iterator();
|
|
|
|
while(it.hasNext())
|
|
{
|
|
String ident = it.next();
|
|
|
|
sb.append("");
|
|
sb.append(ident);
|
|
sb.append(" ");
|
|
|
|
if(hasAliasList())
|
|
{
|
|
Alias alias = getAliasList().getMPT1327Alias(ident);
|
|
|
|
if(alias != null)
|
|
{
|
|
sb.append(alias.getName());
|
|
}
|
|
}
|
|
|
|
sb.append("\n");
|
|
}
|
|
}
|
|
|
|
return sb.toString();
|
|
}
|
|
|
|
@Override
|
|
public void init()
|
|
{
|
|
/* No initialization required */
|
|
}
|
|
|
|
@Override
|
|
public void receiveDecoderStateEvent(DecoderStateEvent event)
|
|
{
|
|
switch(event.getEvent())
|
|
{
|
|
case RESET:
|
|
resetState();
|
|
break;
|
|
case SOURCE_FREQUENCY:
|
|
mFrequency = event.getFrequency();
|
|
broadcast(new AttributeChangeRequest<Long>(Attribute.CHANNEL_FREQUENCY, event.getFrequency()));
|
|
break;
|
|
case TRAFFIC_CHANNEL_ALLOCATION:
|
|
if(event.getSource() != MPT1327DecoderState.this)
|
|
{
|
|
if(event instanceof TrafficChannelAllocationEvent)
|
|
{
|
|
TrafficChannelAllocationEvent allocationEvent =
|
|
(TrafficChannelAllocationEvent) event;
|
|
|
|
String channel = allocationEvent.getCallEvent().getChannel();
|
|
|
|
if(channel != null)
|
|
{
|
|
try
|
|
{
|
|
setChannelNumber(Integer.valueOf(channel));
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
//Do nothing, we couldn't parse the channel number
|
|
}
|
|
}
|
|
|
|
mFrequency = allocationEvent.getCallEvent().getFrequency();
|
|
broadcast(new AttributeChangeRequest<Long>(Attribute.CHANNEL_FREQUENCY,
|
|
allocationEvent.getCallEvent().getFrequency()));
|
|
|
|
mFromAttribute.process(allocationEvent.getCallEvent().getFromID());
|
|
mToAttribute.process(allocationEvent.getCallEvent().getToID());
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} |