diff --git a/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/build.gradle b/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/build.gradle index 8cab277..ccd84a6 100644 --- a/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/build.gradle +++ b/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/build.gradle @@ -21,6 +21,7 @@ dependencies { compile group: 'com.google.code.gson', name: 'gson', version: '2.8.6' implementation group: 'com.rabbitmq', name: 'amqp-client', version: '5.6.0' compile group: 'org.json', name: 'json', version: '20201115' + compile group: 'org.apache.httpcomponents.client5', name: 'httpclient5', version: '5.0' } diff --git a/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/McsSenderPlugin.java b/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/McsSenderPlugin.java index 3fa9c61..6c285be 100644 --- a/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/McsSenderPlugin.java +++ b/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/McsSenderPlugin.java @@ -27,6 +27,8 @@ import tak.server.plugins.dto.EntityDto; import tak.server.plugins.interfaces.MessageCallback; import tak.server.plugins.messagebroker.RabbitMQClient; +import tak.server.plugins.missionapi.TakServerCoTApi; +import tak.server.plugins.missionapi.TakServerCoTCallback; import tak.server.plugins.processing.MessageConsumer; import tak.server.plugins.processing.MessageProducer; import tak.server.plugins.processing.ProcessingMessage; @@ -48,7 +50,7 @@ public class McsSenderPlugin extends MessageSenderBase implements MessageCallbac private MessageConsumer _messageConsumer; private BlockingQueue _blockingQueue; private ExecutorService _executor = Executors.newFixedThreadPool(2); - + public static Boolean VerboseLogging = false; @SuppressWarnings("unchecked") @@ -116,7 +118,29 @@ public void messageReceived(String topic, String message){ return; } - takMessage = McsCoTConverter.convertToMessage(event, config); + //TODO - park it in prefs + String targetAddress = "127.0.0.1"; + int targetPort = 8080; + TakServerCoTApi.queryForCotEvent(targetAddress, targetPort, event.getUid(), + new TakServerCoTCallback(){ + @Override + public void cotResult(Boolean success, String cot) { + try { + Message eventMessage = null; + if (success) { + final Message parentMessage = getConverter().cotStringToDataMessage(cot, null, Integer.toString(System.identityHashCode(this))); + eventMessage = McsCoTConverter.convertToLinkedMessage(event, parentMessage.getPayload().getCotEvent(), config); + } + else + eventMessage = McsCoTConverter.convertToMessage(event, config); + + send(eventMessage); + } + catch (Exception exception) { + _logger.error("error converting to linked message", exception); + } + } + }); } else { EntityDto entity = McsCoTConverter.convertToEntity(message, config); @@ -126,6 +150,7 @@ public void messageReceived(String topic, String message){ } takMessage = McsCoTConverter.convertToMessage(entity, config); + send(takMessage); } if (takMessage == null){ @@ -136,7 +161,6 @@ public void messageReceived(String topic, String message){ if(VerboseLogging) _logger.info("TAK message converted: " + takMessage); - send(takMessage); } catch (Exception exception) { _logger.error("error converting message ", message, exception); } diff --git a/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/missionapi/TakServerCoTApi.java b/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/missionapi/TakServerCoTApi.java new file mode 100644 index 0000000..cdb5c02 --- /dev/null +++ b/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/missionapi/TakServerCoTApi.java @@ -0,0 +1,61 @@ +package tak.server.plugins.missionapi; + +import java.util.concurrent.Future; + +import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; +import org.apache.hc.client5.http.async.methods.SimpleHttpRequests; +import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.client5.http.impl.async.HttpAsyncClients; +import org.apache.hc.core5.concurrent.FutureCallback; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.reactor.IOReactorConfig; +import org.apache.hc.core5.util.Timeout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TakServerCoTApi { + private static final Logger logger = LoggerFactory.getLogger(TakServerCoTApi.class); + + public static void queryForCotEvent(String targetAddress, int targetPort, String uuid, TakServerCoTCallback callback) { + try { + final IOReactorConfig ioReactorConfig = IOReactorConfig.custom() + .setSoTimeout(Timeout.ofSeconds(5)) + .build(); + + final CloseableHttpAsyncClient client = HttpAsyncClients.custom() + .setIOReactorConfig(ioReactorConfig) + .build(); + + client.start(); + + String requestUri = "/Marti/api/cot/xml/" + uuid; + final HttpHost target = new HttpHost(targetAddress, targetPort); + final SimpleHttpRequest httpget = SimpleHttpRequests.get(target, requestUri); + logger.info("Executing request " + httpget.getMethod() + " " + httpget.getUri()); + final Future future = client.execute( + httpget, + new FutureCallback() { + @Override + public void completed(final SimpleHttpResponse response) { + callback.cotResult(response.getCode() == 200, response.getBody().getBodyText()); + } + + @Override + public void failed(final Exception ex) { + logger.error(target.toURI() + requestUri + "->" + ex); + callback.cotResult(false, ""); + } + + @Override + public void cancelled() { + logger.error(target.toURI() + requestUri + " cancelled"); + callback.cotResult(false, ""); + } + }); + } + catch (Exception e) { + logger.info("exception making HTTP request", e); + } + } +} diff --git a/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/missionapi/TakServerCoTCallback.java b/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/missionapi/TakServerCoTCallback.java new file mode 100644 index 0000000..e282345 --- /dev/null +++ b/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/missionapi/TakServerCoTCallback.java @@ -0,0 +1,5 @@ +package tak.server.plugins.missionapi; + +public interface TakServerCoTCallback { + void cotResult(Boolean success, String cot); +} diff --git a/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/utilities/McsCoTConverter.java b/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/utilities/McsCoTConverter.java index 2d51440..e62f78b 100644 --- a/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/utilities/McsCoTConverter.java +++ b/TAK Server Plugin/Plugin/src/takserver-sender-plugin-mcs/src/main/java/tak/server/plugins/utilities/McsCoTConverter.java @@ -32,7 +32,39 @@ public class McsCoTConverter { public static Message convertToMessage(EventDto event, PluginConfiguration configuration) { Message.Builder messageBuilder = MessageOuterClass.Message.newBuilder(); - TakMessage.Builder payloadBuilder = messageBuilder.getPayloadBuilder(); + buildEventMessage(event, messageBuilder, configuration); + + return messageBuilder.build(); + } + + public static Message convertToLinkedMessage(EventDto event, CotEvent linkedCotEvent, PluginConfiguration configuration) { + Message.Builder messageBuilder = MessageOuterClass.Message.newBuilder(); + buildEventMessage(event, messageBuilder, configuration); + + TakMessage.Builder payloadBuilder = messageBuilder.getPayloadBuilder(); + CotEvent.Builder cotEventBuilder = payloadBuilder.getCotEventBuilder(); + DetailOuterClass.Detail.Builder detailBuilder = cotEventBuilder.getDetailBuilder(); + + Instant instant = Instant.now(); + Long timeMs = instant.toEpochMilli() ; + cotEventBuilder.setUid(event.getUid() + "-alert-" + timeMs.toString()); + + cotEventBuilder.setLat(linkedCotEvent.getLat()); + cotEventBuilder.setLon(linkedCotEvent.getLon()); + + String existingDetail = detailBuilder.getXmlDetail(); + String linkDetail = ""; + //Keep it simple for now + String linkXML = "" + .replace("PARENT_TYPE", linkedCotEvent.getType()) + .replace("PARENT_UUID", linkedCotEvent.getUid()); + detailBuilder.setXmlDetail(existingDetail + linkXML); + + return messageBuilder.build(); + } + + public static void buildEventMessage(EventDto event, Message.Builder messageBuilder, PluginConfiguration configuration) { + TakMessage.Builder payloadBuilder = messageBuilder.getPayloadBuilder(); CotEvent.Builder cotEventBuilder = payloadBuilder.getCotEventBuilder(); DetailOuterClass.Detail.Builder detailBuilder = cotEventBuilder.getDetailBuilder(); @@ -49,34 +81,14 @@ public static Message convertToMessage(EventDto event, PluginConfiguration confi Long timeMs = instant.toEpochMilli() ; Long staleMs = timeMs + (5 * 60 * 1000); //Five minutes - cotEventBuilder.setType("a-u"); - cotEventBuilder.setHow("m-r"); + cotEventBuilder.setType("b-m-p-s-p-i");//Bits-mapping-designated point-sensor-point-interest + cotEventBuilder.setHow("m-r");//Machine-relayed cotEventBuilder.setSendTime(timeMs); cotEventBuilder.setStartTime(timeMs); cotEventBuilder.setStaleTime(staleMs); - - if(configuration.containsProperty("simulateAlertLocation") - && (boolean)configuration.getProperty("simulateAlertLocation") == true) { - if (event.getPoint().getLat() == 0.0 || event.getPoint().getLon() == 0.0) { - //27.6615493 -81.2769707 - Generally around Avon Park - Random r = new Random(); - double tempLat = 27.6615493 + r.nextDouble() * .08; - double tempLon = -81.2769707 + r.nextDouble() * .08; - - cotEventBuilder.setLat(tempLat); - cotEventBuilder.setLon(tempLon); - } - else { - cotEventBuilder.setLat(event.getPoint().getLat()); - cotEventBuilder.setLon(event.getPoint().getLon()); - } - } - else { - cotEventBuilder.setLat(event.getPoint().getLat()); - cotEventBuilder.setLon(event.getPoint().getLon()); - } - + cotEventBuilder.setLat(event.getPoint().getLat()); + cotEventBuilder.setLon(event.getPoint().getLon()); cotEventBuilder.setHae(9999999); cotEventBuilder.setCe(9999999); @@ -90,8 +102,6 @@ public static Message convertToMessage(EventDto event, PluginConfiguration confi detailJsonObject.put(FROM_MCS, "true"); String xmlDetailData = XML.toString(detailJsonObject); detailBuilder.setXmlDetail(xmlDetailData); - - return messageBuilder.build(); } public static Message convertToMessage(EntityDto entity, PluginConfiguration configuration) { diff --git a/TAK Server Plugin/RabbitMQ Client/Program.cs b/TAK Server Plugin/RabbitMQ Client/Program.cs index 6d43ecc..ad21606 100644 --- a/TAK Server Plugin/RabbitMQ Client/Program.cs +++ b/TAK Server Plugin/RabbitMQ Client/Program.cs @@ -20,9 +20,13 @@ class Program private static string MESSAGE = "{\"uid\":\"CustomIDForTAKServerTest.Entity\",\"type\":\"a-f-G-U-C\",\"time\":\"1614187736429\",\"start\":\"1614187736429\",\"stale\":\"1614191352000\",\"how\":\"m-g\",\"point\":{\"lat\":\"39.0495\",\"lon\":\"-85.7445\",\"hae\":\"9999999\",\"ce\":\"9999999\",\"le\":\"9999999\"},\"detail\":{\"milsym2525C\":\"SFGPUCI*****\", \"video\":\"https://cdn.bitdegree.org/learn/Pexels%20Videos%203373.mp4?raw=true\", \"image\": \"IMAGEPLACEHOLDER\", \"feedType\":\"OSH_SENSOR\",\"serviceDetails\":{\"serviceUrl\":\"THE SERVICE URL\",\"provider\":{\"providerName\":\"SOME NAME\",\"providerData\":\"SOME DATA\"},\"offeringID\":\"THE OFFERING ID\",\"temporalData\":\"THE TEMPORAL DATA\"}}}"; - private static string EVENT_MESSAGE = "{\"cot_uid\":\"CustomIDForTAKServerTest.Alert\",\"message\":\"This is a demo alert from a test client (no location)\",\"type\":\"info\"}"; + //private static string EVENT_MESSAGE = "{\"cot_uid\":\"CustomIDForTAKServerTest.Alert\",\"message\":\"This is a demo alert from a test client (no location)\",\"type\":\"info\"}"; + + private static string EVENT_MESSAGE = "{\"cot_uid\":\"CustomIDForTAKServerTest.Entity\",\"message\":\"This is a demo alert from a test client at TIME\",\"type\":\"info\"}"; + //ANDROID-1e159fc8250eb070 + //private static string EVENT_MESSAGE = "{\"cot_uid\":\"ANDROID-1e159fc8250eb070\",\"message\":\"This is a demo alert from a test client (no location)\",\"type\":\"info\"}"; - private static string EVENT_MESSAGE_WITH_LOCATION = "{\"cot_uid\":\"CustomIDForTAKServerTest.Alert\",\"message\":\"This is a demo alert from a test client\",\"type\":\"info\",\"point\":{\"lat\":\"30.4299511\",\"lon\":\"-86.6061093\",\"hae\":\"9999999\",\"ce\":\"9999999\",\"le\":\"9999999\"}}"; + private static string EVENT_MESSAGE_WITH_LOCATION = "{\"cot_uid\":\"CustomIDForTAKServerTest.Entity\",\"message\":\"This is a demo alert from a test client\",\"type\":\"info\",\"point\":{\"lat\":\"30.4299511\",\"lon\":\"-86.6061093\",\"hae\":\"9999999\",\"ce\":\"9999999\",\"le\":\"9999999\"}}"; private static string IMAGE_URL = @"https://i.picsum.photos/id/1025/4951/3301.jpg?hmac=_aGh5AtoOChip_iaMo8ZvvytfEojcgqbCH7dzaz-H8Y"; @@ -66,7 +70,7 @@ static void Main(string[] args) body: body); Console.WriteLine(" [x] Sent {0}", message); - await Task.Delay(10000); + await Task.Delay(5000); } } catch (Exception ex) @@ -82,15 +86,16 @@ static void Main(string[] args) { while (true) { - var body = Encoding.UTF8.GetBytes(EVENT_MESSAGE); + var message = EVENT_MESSAGE.Replace("TIME", DateTime.UtcNow.ToString("HH:mm:ss")); + var body = Encoding.UTF8.GetBytes(message); channel.BasicPublish(exchange: EXCHANGE, routingKey: EVENT_ROUTING_KEY, basicProperties: null, body: body); - Console.WriteLine(" [x] Sent {0}", EVENT_MESSAGE); - await Task.Delay(5000); + Console.WriteLine(" [x] Sent {0}", message); + await Task.Delay(20000); } } catch (Exception ex)