package org.yamcs.tctm;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamcs.ConfigurationException;
import org.yamcs.YConfiguration;
import org.yamcs.protobuf.Pvalue.ParameterData;
import com.google.common.util.concurrent.AbstractExecutionThreadService;
/**
* Receives PP data via UDP.
*
* The UDP packets are protobuf encoded ParameterData.
* We don't use any checksum, assume it's done by UDP.
*
* @author nm
*
*/
public class UdpParameterDataLink extends AbstractExecutionThreadService implements ParameterDataLink {
private volatile int validDatagramCount = 0;
private volatile int invalidDatagramCount = 0;
private volatile boolean disabled=false;
private DatagramSocket udpSocket;
private String group="239.192.0.1";
private int port=31002;
ParameterSink ppListener;
private Logger log=LoggerFactory.getLogger(this.getClass().getName());
int MAX_LENGTH=10*1024;
DatagramPacket datagram = new DatagramPacket(new byte[MAX_LENGTH], MAX_LENGTH);
/**
* Creates a new UDP data link
* @param instance
* @param name
* @param config
* @throws ConfigurationException if port is not defined in the config
*/
public UdpParameterDataLink(String instance, String name, Map<String, Object> config) throws ConfigurationException {
port = YConfiguration.getInt(config, "port");
}
@Override
public void startUp() throws IOException {
udpSocket = new DatagramSocket(port);
}
@Override
public void run() {
while(isRunning()) {
ParameterData pd = getNextData();
if(pd!=null) {
ppListener.updateParams(pd.getGenerationTime(), pd.getGroup(), pd.getSeqNum(), pd.getParameterList());
}
}
}
/**
*
* Called to retrieve the next packet.
* It blocks in readinng on the UDP socket
* @return anything that looks as a valid packet, just the size is taken into account to decide if it's valid or not
*/
public ParameterData getNextData() {
while(isRunning() && disabled) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return null;
}
}
try {
udpSocket.receive(datagram);
ParameterData.Builder pdb = ParameterData.newBuilder().mergeFrom(datagram.getData(), datagram.getOffset(), datagram.getLength());
validDatagramCount++;
return pdb.build();
} catch (IOException e) {
log.warn("exception when receiving parameter data: {}'", e.getMessage());
invalidDatagramCount++;
}
return null;
}
@Override
public String getLinkStatus() {
return disabled?"DISABLED":"OK";
}
/**
* returns statistics with the number of datagram received and the number of invalid datagrams
*/
@Override
public String getDetailedStatus() {
if(disabled) {
return "DISABLED";
} else {
return String.format("OK (%s:%d)\nValid datagrams received: %d\nInvalid datagrams received: %d",
group, port, validDatagramCount, invalidDatagramCount);
}
}
/**
* Sets the disabled to true such that getNextPacket ignores the received datagrams
*/
@Override
public void disable() {
disabled=true;
}
/**
* Sets the disabled to false such that getNextPacket does not ignore the received datagrams
*/
@Override
public void enable() {
disabled=false;
}
@Override
public boolean isDisabled() {
return disabled;
}
@Override
public long getDataCount() {
return validDatagramCount;
}
@Override
public void setParameterSink(ParameterSink ppListener) {
this.ppListener = ppListener;
}
}