├── dpp.cot
├── Track.cs
├── protobuf
│ ├── status.proto
│ ├── group.proto
│ ├── track.proto
│ ├── precisionlocation.proto
│ ├── takmessage.proto
│ ├── contact.proto
│ ├── takv.proto
│ ├── takcontrol.proto
│ ├── cotevent.proto
│ ├── detail.proto
│ └── protocol.txt
├── Status.cs
├── TakControl.cs
├── Group.cs
├── Contact.cs
├── Point.cs
├── PrecisionLocation.cs
├── Takv.cs
├── dpp.cot.csproj
├── Detail.cs
├── CotDescriptions.tt
├── Extensions.cs
├── CotPredicates.tt
├── Message.cs
├── Event.cs
├── CotPredicates.cs
└── CotDescriptions.cs
├── readme.md
├── .github
└── workflows
│ ├── ci.yml
│ └── release.yml
├── dpp.cot.Tests
├── GeoTests.cs
├── CotTypeTests.cs
├── dpp.cot.Tests.csproj
├── Helpers.cs
└── SerializationTests.cs
├── LICENSE
├── .gitattributes
├── cot.sln
└── .gitignore
/dpp.cot/Track.cs:
--------------------------------------------------------------------------------
1 | namespace dpp.cot
2 | {
3 | public class Track
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # dpp.cot [](https://github.com/darkplusplus/cot/actions/workflows/ci.yml)
2 |
3 | a library for handling cursor-on-target messages.
4 |
--------------------------------------------------------------------------------
/dpp.cot/protobuf/status.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | option optimize_for = LITE_RUNTIME;
3 |
4 | package atakmap.commoncommo.protobuf.v1;
5 |
6 | // All items are required unless otherwise noted!
7 | // "required" means if they are missing on send, the conversion
8 | // to the message format will be rejected and fall back to opaque
9 | // XML representation
10 | message Status {
11 | uint32 battery = 1; // battery=
12 | }
13 |
--------------------------------------------------------------------------------
/dpp.cot/protobuf/group.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | option optimize_for = LITE_RUNTIME;
3 |
4 | package atakmap.commoncommo.protobuf.v1;
5 |
6 | // All items are required unless otherwise noted!
7 | // "required" means if they are missing on send, the conversion
8 | // to the message format will be rejected and fall back to opaque
9 | // XML representation
10 | message Group {
11 | string name = 1; // name=
12 | string role = 2; // role=
13 | }
14 |
--------------------------------------------------------------------------------
/dpp.cot/protobuf/track.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | option optimize_for = LITE_RUNTIME;
3 |
4 | package atakmap.commoncommo.protobuf.v1;
5 |
6 | // All items are required unless otherwise noted!
7 | // "required" means if they are missing on send, the conversion
8 | // to the message format will be rejected and fall back to opaque
9 | // XML representation
10 | message Track {
11 | double speed = 1; // speed=
12 | double course = 2; // course=
13 | }
14 |
--------------------------------------------------------------------------------
/dpp.cot/protobuf/precisionlocation.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | option optimize_for = LITE_RUNTIME;
3 |
4 | package atakmap.commoncommo.protobuf.v1;
5 |
6 | // All items are required unless otherwise noted!
7 | // "required" means if they are missing on send, the conversion
8 | // to the message format will be rejected and fall back to opaque
9 | // XML representation
10 | message PrecisionLocation {
11 | string geopointsrc = 1; // geopointsrc=
12 | string altsrc = 2; // altsrc=
13 | }
14 |
--------------------------------------------------------------------------------
/dpp.cot/protobuf/takmessage.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | option optimize_for = LITE_RUNTIME;
3 |
4 | import "cotevent.proto";
5 | import "takcontrol.proto";
6 |
7 | package atakmap.commoncommo.protobuf.v1;
8 |
9 | // Top level message sent for TAK Messaging Protocol Version 1.
10 | message TakMessage {
11 | // Optional - if omitted, continue using last reported control
12 | // information
13 | TakControl takControl = 1;
14 |
15 | // Optional - if omitted, no event data in this message
16 | CotEvent cotEvent = 2;
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/dpp.cot/protobuf/contact.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | option optimize_for = LITE_RUNTIME;
3 |
4 | package atakmap.commoncommo.protobuf.v1;
5 |
6 | // All items are required unless otherwise noted!
7 | // "required" means if they are missing on send, the conversion
8 | // to the message format will be rejected and fall back to opaque
9 | // XML representation
10 | message Contact {
11 | // Endpoint is optional; if missing/empty do not populate.
12 | string endpoint = 1; // endpoint=
13 | string callsign = 2; // callsign=
14 | }
15 |
--------------------------------------------------------------------------------
/dpp.cot/protobuf/takv.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | option optimize_for = LITE_RUNTIME;
3 |
4 | package atakmap.commoncommo.protobuf.v1;
5 |
6 | // All items are required unless otherwise noted!
7 | // "required" means if they are missing on send, the conversion
8 | // to the message format will be rejected and fall back to opaque
9 | // XML representation
10 | message Takv {
11 | string device = 1; // device=
12 | string platform = 2; // platform=
13 | string os = 3; // os=
14 | string version = 4; // version=
15 | }
16 |
--------------------------------------------------------------------------------
/dpp.cot/Status.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Xml.Serialization;
8 |
9 | namespace dpp.cot
10 | {
11 | [ProtoContract()]
12 | public partial class Status : IExtensible
13 | {
14 | private IExtension __pbn__extensionData;
15 | IExtension IExtensible.GetExtensionObject(bool createIfMissing)
16 | => Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
17 |
18 | [ProtoMember(1, Name = @"battery")]
19 | [XmlAttribute(AttributeName = "battery")]
20 | public uint Battery { get; set; }
21 |
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - name: Checkout
16 | uses: actions/checkout@v2
17 |
18 | - name: Setup .NET
19 | uses: actions/setup-dotnet@v1
20 | with:
21 | dotnet-version: 5.0.x
22 |
23 | - name: Restore dependencies
24 | run: dotnet restore
25 |
26 | - name: Build
27 | run: dotnet build --no-restore
28 |
29 | - name: Test
30 | run: dotnet test --no-build --verbosity normal
31 |
32 | - name: Publish
33 | run: dotnet publish
34 |
--------------------------------------------------------------------------------
/dpp.cot/TakControl.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using System.ComponentModel;
3 |
4 | namespace dpp.cot
5 | {
6 | [ProtoContract()]
7 | public partial class TakControl : IExtensible
8 | {
9 | private IExtension __pbn__extensionData;
10 | IExtension IExtensible.GetExtensionObject(bool createIfMissing)
11 | => Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
12 |
13 | [ProtoMember(1)]
14 | public uint minProtoVersion { get; set; }
15 |
16 | [ProtoMember(2)]
17 | public uint maxProtoVersion { get; set; }
18 |
19 | [ProtoMember(3)]
20 | [DefaultValue("")]
21 | public string contactUid { get; set; } = "";
22 | }
23 | }
--------------------------------------------------------------------------------
/dpp.cot/protobuf/takcontrol.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | option optimize_for = LITE_RUNTIME;
3 |
4 | package atakmap.commoncommo.protobuf.v1;
5 |
6 | // TAK Protocol control message
7 | // This specifies to a recipient what versions
8 | // of protocol elements this sender supports during
9 | // decoding.
10 | message TakControl {
11 | // Lowest TAK protocol version supported
12 | // If not filled in (reads as 0), version 1 is assumed
13 | uint32 minProtoVersion = 1;
14 |
15 | // Highest TAK protocol version supported
16 | // If not filled in (reads as 0), version 1 is assumed
17 | uint32 maxProtoVersion = 2;
18 |
19 | // UID of the sending contact. May be omitted if
20 | // this message is paired in a TakMessage with a CotEvent
21 | // and the CotEvent contains this information
22 | string contactUid = 3;
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/dpp.cot.Tests/GeoTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xunit;
3 |
4 | namespace dpp.cot.Tests
5 | {
6 | public class GeoTests
7 | {
8 | [Fact]
9 | public void DistanceExtensionInMilesTest()
10 | {
11 | var evt = new cot.Event();
12 | evt.Point.Lat = 36.12;
13 | evt.Point.Lon = -86.67;
14 |
15 | var m = evt.GetDistanceMiles(33.94, -118.4);
16 | Assert.Equal(1793.57342023, Math.Round(m, 8));
17 | }
18 |
19 | [Fact]
20 | public void DistanceExtensionInKilometersTest()
21 | {
22 | var evt = new cot.Event();
23 | evt.Point.Lat = 36.12;
24 | evt.Point.Lon = -86.67;
25 |
26 | var km = evt.GetDistanceKilometers(33.94, -118.4);
27 | Assert.Equal(2887.25995061, Math.Round(km, 8));
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/dpp.cot/Group.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Xml.Serialization;
9 |
10 | namespace dpp.cot
11 | {
12 | [ProtoContract()]
13 | public partial class Group : IExtensible
14 | {
15 | private IExtension __pbn__extensionData;
16 | IExtension IExtensible.GetExtensionObject(bool createIfMissing)
17 | => Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
18 |
19 | [ProtoMember(1, Name = @"name")]
20 | [DefaultValue("")]
21 | [XmlAttribute(AttributeName = @"name")]
22 | public string Name { get; set; } = "";
23 |
24 | [ProtoMember(2, Name = @"role")]
25 | [DefaultValue("")]
26 | [XmlAttribute(AttributeName = @"role")]
27 | public string Role { get; set; } = "";
28 |
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/dpp.cot/Contact.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Xml.Serialization;
9 |
10 | namespace dpp.cot
11 | {
12 | [ProtoContract()]
13 | public partial class Contact : IExtensible
14 | {
15 | private IExtension __pbn__extensionData;
16 | IExtension IExtensible.GetExtensionObject(bool createIfMissing)
17 | => Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
18 |
19 | [ProtoMember(1, Name = @"endpoint")]
20 | [DefaultValue("")]
21 | [XmlAttribute(AttributeName = "endpoint")]
22 | public string Endpoint { get; set; } = "";
23 |
24 | [ProtoMember(2, Name = @"callsign")]
25 | [DefaultValue("")]
26 | [XmlAttribute(AttributeName = "callsign")]
27 | public string Callsign { get; set; } = "";
28 |
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/dpp.cot/Point.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Xml.Serialization;
8 |
9 | namespace dpp.cot
10 | {
11 | [XmlRoot(ElementName = "point")]
12 | public class Point
13 | {
14 | [ProtoMember(10, Name = @"lat")]
15 | [XmlAttribute(AttributeName = "lat")]
16 | public double Lat { get; set; } = 0;
17 |
18 | [ProtoMember(11, Name = @"lon")]
19 | [XmlAttribute(AttributeName = "lon")]
20 | public double Lon { get; set; } = 0;
21 |
22 | [ProtoMember(12, Name = @"ce")]
23 | [XmlAttribute(AttributeName = "ce")]
24 | public double Ce { get; set; } = 9999999.0;
25 |
26 | [ProtoMember(13, Name = @"hae")]
27 | [XmlAttribute(AttributeName = "hae")]
28 | public double Hae { get; set; } = 9999999.0;
29 |
30 | [ProtoMember(14, Name = @"le")]
31 | [XmlAttribute(AttributeName = "le")]
32 | public double Le { get; set; } = 9999999.0;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/dpp.cot.Tests/CotTypeTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xunit;
3 |
4 | namespace dpp.cot.Tests
5 | {
6 | public class CotTypeTests
7 | {
8 | [Theory]
9 | [InlineData(Helpers.SimplePayload, "a-.-A-M")]
10 | [InlineData(Helpers.SimplePayload, "a-h-A-M")]
11 | [InlineData(Helpers.SimplePayload, CotPredicates.air)]
12 | [InlineData(Helpers.EudPayload, CotPredicates.ground)]
13 | public void PredicateTest(string corpus, string predicate)
14 | {
15 | var evt = cot.Event.Parse(corpus);
16 |
17 | Assert.True(evt.IsA(predicate));
18 | }
19 |
20 | [Theory]
21 | [InlineData(Helpers.SimplePayload, "UTILITY (MEDIUM)")]
22 | [InlineData(Helpers.EudPayload, "COMBAT")]
23 | public void DescriptionTest(string corpus, string description)
24 | {
25 | var evt = cot.Event.Parse(corpus);
26 |
27 | Assert.Equal(description, evt.GetDescription());
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/dpp.cot/PrecisionLocation.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Xml.Serialization;
9 |
10 | namespace dpp.cot
11 | {
12 | [ProtoContract()]
13 | public partial class PrecisionLocation : IExtensible
14 | {
15 | private IExtension __pbn__extensionData;
16 | IExtension IExtensible.GetExtensionObject(bool createIfMissing)
17 | => Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
18 |
19 | [ProtoMember(1, Name = @"geopointsrc")]
20 | [DefaultValue("")]
21 | [XmlAttribute(AttributeName = "geopointsrc")]
22 | public string Geopointsrc { get; set; } = "";
23 |
24 | [ProtoMember(2, Name = @"altsrc")]
25 | [DefaultValue("")]
26 | [XmlAttribute(AttributeName = "altsrc")]
27 | public string Altsrc { get; set; } = "";
28 |
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/dpp.cot.Tests/dpp.cot.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net5.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 | all
15 |
16 |
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 | all
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 darkplusplus
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/dpp.cot/Takv.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using System.ComponentModel;
3 | using System.Xml.Serialization;
4 |
5 | namespace dpp.cot
6 | {
7 | [ProtoContract()]
8 | public partial class Takv : IExtensible
9 | {
10 | private IExtension __pbn__extensionData;
11 | IExtension IExtensible.GetExtensionObject(bool createIfMissing)
12 | => Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
13 |
14 | [ProtoMember(1, Name = @"device")]
15 | [DefaultValue("")]
16 | [XmlAttribute(AttributeName = "device")]
17 | public string Device { get; set; } = "";
18 |
19 | [ProtoMember(2, Name = @"platform")]
20 | [DefaultValue("")]
21 | [XmlAttribute(AttributeName = "platform")]
22 | public string Platform { get; set; } = "";
23 |
24 | [ProtoMember(3, Name = @"os")]
25 | [DefaultValue("")]
26 | [XmlAttribute(AttributeName = "os")]
27 | public string Os { get; set; } = "";
28 |
29 | [ProtoMember(4, Name = @"version")]
30 | [DefaultValue("")]
31 | [XmlAttribute(AttributeName = "version")]
32 | public string Version { get; set; } = "";
33 | }
34 | }
--------------------------------------------------------------------------------
/dpp.cot.Tests/Helpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace dpp.cot.Tests
8 | {
9 | public static class Helpers
10 | {
11 | public const string SimplePayload = @"";
12 | public const string EudPayload = @"<__group name='Blue' role='Team Member'/>";
13 | }
14 | }
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | push:
5 | tags:
6 | - v*
7 |
8 | jobs:
9 | build:
10 |
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v2
16 |
17 | - name: Setup .NET
18 | uses: actions/setup-dotnet@v1
19 | with:
20 | dotnet-version: 5.0.x
21 |
22 | - name: Restore dependencies
23 | run: dotnet restore
24 |
25 | - name: Build
26 | run: dotnet build -c Release --no-restore --version-suffix="$GITHUB_REF_NAME.$GITHUB_RUN_NUMBER"
27 |
28 | - name: Publish
29 | run: dotnet publish -c Release --no-build --version-suffix="$GITHUB_REF_NAME.$GITHUB_RUN_NUMBER"
30 |
31 | - name: Pack
32 | run: dotnet pack -c Release --no-build --version-suffix="$GITHUB_REF_NAME.$GITHUB_RUN_NUMBER"
33 |
34 | - name: Upload Artifacts
35 | uses: actions/upload-artifact@v2
36 | with:
37 | name: dpp.cot
38 | path: dpp.cot/bin/*/*.nupkg
39 |
40 | - name: Publish To Nuget
41 | run: dotnet nuget push dpp.cot/bin/Release/*.nupkg -k $NUGET_AUTH_TOKEN -s https://api.nuget.org/v3/index.json
42 | env:
43 | NUGET_AUTH_TOKEN: ${{ secrets.NUGET_SECRET_DPP_COT }}
44 |
--------------------------------------------------------------------------------
/dpp.cot/dpp.cot.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net5.0
5 | 1.0.5.0
6 | 1.0.5.0
7 | 1.0.5
8 |
9 |
10 |
11 |
12 | TextTemplatingFileGenerator
13 | CotDescriptions.cs
14 |
15 |
16 | TextTemplatingFileGenerator
17 | CotPredicates.cs
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | True
28 | True
29 | CotDescriptions.tt
30 |
31 |
32 | True
33 | True
34 | CotPredicates.tt
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/dpp.cot/protobuf/cotevent.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option optimize_for = LITE_RUNTIME;
4 |
5 | package atakmap.commoncommo.protobuf.v1;
6 |
7 | import "detail.proto";
8 |
9 | // A note about timestamps:
10 | // Uses "timeMs" units, which is number of milliseconds since
11 | // 1970-01-01 00:00:00 UTC
12 | //
13 | // All items are required unless otherwise noted!
14 | // "required" means if they are missing in the XML during outbound
15 | // conversion to protobuf, the message will be
16 | // rejected
17 | message CotEvent {
18 | //
19 |
20 | string type = 1; //
21 |
22 | string access = 2; // optional
23 | string qos = 3; // optional
24 | string opex = 4; // optional
25 |
26 | string uid = 5; //
27 | uint64 sendTime = 6; // converted to timeMs
28 | uint64 startTime = 7; // converted to timeMs
29 | uint64 staleTime = 8; // converted to timeMs
30 | string how = 9; //
31 |
32 | //
33 | double lat = 10; //
34 | double lon = 11; //
35 | double hae = 12; // use 999999 for unknown
36 | double ce = 13; // use 999999 for unknown
37 | double le = 14; // use 999999 for unknown
38 |
39 | // comprises children of
40 | // This is optional - if omitted, then the cot message
41 | // had no data under
42 | Detail detail = 15;
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/dpp.cot/Detail.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Xml.Serialization;
9 |
10 | namespace dpp.cot
11 | {
12 | [ProtoContract]
13 | public class Detail : IExtensible
14 | {
15 | private IExtension __pbn__extensionData;
16 | IExtension IExtensible.GetExtensionObject(bool createIfMissing)
17 | => Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
18 |
19 | [ProtoMember(1)]
20 | [DefaultValue("")]
21 | public string xmlDetail { get; set; } = "";
22 |
23 | [ProtoMember(2, Name = @"contact")]
24 | [XmlElement(ElementName = "contact", IsNullable = true)]
25 | public Contact Contact { get; set; }
26 |
27 | [ProtoMember(3, Name = @"group")]
28 | [XmlElement(ElementName = "__group", IsNullable = true)]
29 | public Group Group { get; set; }
30 |
31 | [ProtoMember(4)]
32 | [XmlElement(ElementName = @"precisionlocation", IsNullable = true)]
33 | public PrecisionLocation PrecisionLocation { get; set; }
34 |
35 | [ProtoMember(5, Name = @"status")]
36 | [XmlElement(ElementName = "status", IsNullable = true)]
37 | public Status Status { get; set; }
38 |
39 | [ProtoMember(6, Name = @"takv")]
40 | [XmlElement(ElementName = "takv", IsNullable = true)]
41 | public Takv Takv { get; set; }
42 |
43 | [ProtoMember(7, Name = @"track")]
44 | [XmlElement(ElementName = "track", IsNullable = true)]
45 | public Track Track { get; set; }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/dpp.cot/CotDescriptions.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="true" language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ assembly name="System.Xml" #>
4 | <#@ import namespace="System.Linq" #>
5 | <#@ import namespace="System.Text" #>
6 | <#@ import namespace="System.Collections.Generic" #>
7 | <#@ import namespace="System.Text.RegularExpressions" #>
8 | <#@ import namespace="System.Xml.XPath" #>
9 | <#@ output extension=".cs" #>
10 | <#
11 | var f = this.Host.ResolvePath("CoTtypes.xml");
12 | var doc = new System.Xml.XPath.XPathDocument(f);
13 | var nav = doc.CreateNavigator();
14 | var nodes = nav.Select("/types/cot");
15 | #>
16 | using System;
17 | using System.Collections.Generic;
18 | using System.Linq;
19 | using System.Text;
20 | using System.Threading.Tasks;
21 | using System.Text.RegularExpressions;
22 |
23 | namespace dpp.cot
24 | {
25 | public static class CotDescriptions
26 | {
27 | private static readonly Dictionary CotDescriptionsMap = new Dictionary
28 | {
29 | <#
30 | while(nodes.MoveNext())
31 | {
32 | var cot = nodes.Current.SelectSingleNode("@cot").Value;
33 | var desc = nodes.Current.SelectSingleNode("@desc").Value;
34 | #>
35 | { "<#= cot #>", "<#= desc #>"},
36 | <#
37 | }
38 | #>
39 |
40 | };
41 |
42 | public static string GetDescription(this Event e)
43 | {
44 | var t = e.Type;
45 | if (t.StartsWith("a-"))
46 | {
47 | t = t.Remove(2, 1).Insert(2, ".");
48 | }
49 | if (t.StartsWith("b-g-") || t.StartsWith("b-r-"))
50 | {
51 | t = t.Remove(4, 1).Insert(4, ".");
52 | }
53 |
54 | return CotDescriptionsMap.TryGetValue(t, out var value) ? value : "Unknown";
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/dpp.cot/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.RegularExpressions;
6 | using System.Threading.Tasks;
7 |
8 | namespace dpp.cot
9 | {
10 | public static class Extensions
11 | {
12 | public static bool IsA(string p, string t)
13 | {
14 | var regex = new Regex(p);
15 | return regex.Matches(t).Count > 0;
16 | }
17 |
18 | public static bool IsA(this Event e, string p)
19 | {
20 | var regex = new Regex(p);
21 | return regex.Matches(e.Type).Count > 0;
22 | }
23 |
24 | private static double GetDistanceTo(double R, double lat1, double lon1, double lat2, double lon2)
25 | {
26 | // implementaion of the Haversine distance formula
27 |
28 | var rLat1 = Math.PI * lat1 / 180.0;
29 | var rLat2 = Math.PI * lat2 / 180.0;
30 | var rLon1 = Math.PI * lon1 / 180.0;
31 | var rLon2 = Math.PI * lon2 / 180.0;
32 |
33 | var dLat = rLat2 - rLat1;
34 | var dLon = rLon2 - rLon1;
35 |
36 | var d = Math.Asin(Math.Sqrt(Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(rLat1) * Math.Cos(rLat2) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2)));
37 | return 2 * R * d;
38 | }
39 |
40 | public static double GetDistanceMiles(this Event e, double lat, double lon)
41 | {
42 | var rMiles = 3958.8;
43 | return GetDistanceTo(rMiles, e.Point.Lat, e.Point.Lon, lat, lon);
44 | }
45 | public static double GetDistanceKilometers(this Event e, double lat, double lon)
46 | {
47 | var rKilometers = 6372.8;
48 | return GetDistanceTo(rKilometers, e.Point.Lat, e.Point.Lon, lat, lon);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/dpp.cot/CotPredicates.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="true" language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ assembly name="System.Xml" #>
4 | <#@ import namespace="System.Linq" #>
5 | <#@ import namespace="System.Text" #>
6 | <#@ import namespace="System.Collections.Generic" #>
7 | <#@ import namespace="System.Text.RegularExpressions" #>
8 | <#@ import namespace="System.Xml.XPath" #>
9 | <#@ output extension=".cs" #>
10 | <#
11 | var f = this.Host.ResolvePath("CoTtypes.xml");
12 | var doc = new System.Xml.XPath.XPathDocument(f);
13 | var nav = doc.CreateNavigator();
14 | var nodes = nav.Select("/types/is");
15 |
16 | var predicates = new Dictionary();
17 |
18 | var reservedWords = new List(){ "true", "false" };
19 |
20 | while(nodes.MoveNext())
21 | {
22 | var what = nodes.Current.SelectSingleNode("@what").Value;
23 | var match = nodes.Current.SelectSingleNode("@match").Value;
24 |
25 | // cleanup dirty characters
26 | what = what.Replace(".", "_");
27 | what = what.Replace("-", "_");
28 | what = what.Replace("+", "_");
29 |
30 | if (reservedWords.Contains(what)) {continue; };
31 | predicates[what] = match;
32 | }
33 | #>
34 | using System;
35 | using System.Collections.Generic;
36 | using System.Linq;
37 | using System.Text;
38 | using System.Threading.Tasks;
39 | using System.Text.RegularExpressions;
40 |
41 | namespace dpp.cot
42 | {
43 | public static class CotPredicates
44 | {
45 | <#
46 | foreach(var predicate in predicates)
47 | {
48 | #>
49 | public const string <#= predicate.Key #> = "<#= predicate.Value #>";
50 | <#
51 | }
52 | #>
53 |
54 | // types not in CoTtypes.xml
55 | public const string t_ping = "t-x-c-t";
56 | public const string t_pong = "t-x-c-t-r";
57 | public const string t_remote_disconnect = "t-x-d-d";
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/dpp.cot/Message.cs:
--------------------------------------------------------------------------------
1 | using ProtoBuf;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace dpp.cot
10 | {
11 | [ProtoContract()]
12 | public partial class Message : IExtensible
13 | {
14 | // versioning bytes for header
15 | // magic|version|magic|data
16 | private const byte magic = 0xef;
17 | private const byte v0_xml = 0x00;
18 | private const byte v1_protobuf = 0x01;
19 |
20 | private IExtension __pbn__extensionData;
21 | IExtension IExtensible.GetExtensionObject(bool createIfMissing)
22 | => Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
23 |
24 | [ProtoMember(1)]
25 | public TakControl Control { get; set; }
26 |
27 | [ProtoMember(2)]
28 | public Event Event { get; set; }
29 |
30 | public static Message Parse(byte[] data, int offset, int length)
31 | {
32 | string msg;
33 | Event e;
34 |
35 | if (data[0] == magic && data[2] == magic)
36 | {
37 | switch (data[1])
38 | {
39 | case v0_xml:
40 | msg = Encoding.UTF8.GetString(data, (int)offset + 3, (int)length);
41 | e = Event.Parse(msg);
42 |
43 | return new Message
44 | {
45 | Event = e,
46 | Control = new TakControl()
47 | {
48 | minProtoVersion = v0_xml,
49 | maxProtoVersion = v0_xml,
50 | contactUid = e.Uid,
51 | }
52 | };
53 |
54 | case v1_protobuf:
55 | using (var ms = new MemoryStream(data, offset + 3, length))
56 | {
57 | return ProtoBuf.Serializer.Deserialize(ms);
58 | }
59 |
60 | default:
61 | throw new NotImplementedException($"Unknown protocol version. Version={data[1]:X}");
62 | }
63 | }
64 |
65 | // No magic bytes detected to specify version, assume it's just utf8 xml
66 | msg = Encoding.UTF8.GetString(data, (int)offset, (int)length);
67 | e = Event.Parse(msg);
68 |
69 | return new Message
70 | {
71 | Event = e,
72 | Control = new TakControl()
73 | {
74 | minProtoVersion = 0,
75 | maxProtoVersion = 0,
76 | contactUid = e.Uid,
77 | }
78 | };
79 | }
80 |
81 | public string ToXmlString()
82 | {
83 | return this.Event.ToXmlString();
84 | }
85 |
86 | public byte[] ToXmlBytes()
87 | {
88 | var header = new byte[]{
89 | magic,
90 | v0_xml,
91 | magic,
92 | };
93 | var data = Encoding.UTF8.GetBytes(this.Event.ToXmlString());
94 |
95 | return header.Concat(data).ToArray();
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/dpp.cot/protobuf/detail.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | option optimize_for = LITE_RUNTIME;
3 |
4 | package atakmap.commoncommo.protobuf.v1;
5 |
6 | import "contact.proto";
7 | import "group.proto";
8 | import "precisionlocation.proto";
9 | import "status.proto";
10 | import "takv.proto";
11 | import "track.proto";
12 |
13 | // CotEvent detail
14 | // The strong typed message fields are optional. If used, they *MUST* adhere
15 | // to the requirements of the message (see their proto file) and
16 | // their XML source element used to populate the message MUST
17 | // be omitted from the xmlDetail.
18 | // WHOLE ELEMENTS MUST BE CONVERTED TO MESSAGES. Do not try to
19 | // put part of the data from a given element into one of the messages
20 | // and put other parts of the data in an element of xmlDetail! This applies
21 | // especially if you add new things to the XML representation which do not
22 | // have a place in the equivalent protobuf message. Instead, omit the
23 | // message and put the entire element in xmlDetail!
24 | //
25 | // xmlDetail is optional. If omitted, all Detail data has been
26 | // converted to the strongly typed message fields.
27 | // If present, this contains any remaining detail data that has NOT been
28 | // included in one of the strongly typed message fields. To process the
29 | // xmlDetail, the following rules MUST be followed:
30 | // Senders of this message MUST:
31 | // 1. Remove child elements used to populate the other message
32 | // fields. If the same child element appears more times than an
33 | // associated message field(s) is intended to encompass, or if any
34 | // error occurs mapping to the message equivalent, do not remove
35 | // the element(s) in question and do not populate the message
36 | // equivalent.
37 | // 2. If no data under remains, STOP - do not populate
38 | // xmlDetail
39 | // 3. Serialize the remaining XML tree under ....
40 | // as XML in UTF-8 encoding
41 | // 4. Remove the and element tags
42 | // 5. Remove the XML header
43 | // 6. Place the result in xmlDetail
44 | // Receivers of this message MUST do the equivalent of the following:
45 | // 1. If the field is not present (zero length), stop - do nothing
46 | // 2. Prepend and append
47 | // 3. Prepend an XML header for UTF-8 encoding, version 1.0
48 | // ( or similar)
49 | // 4. Read the result, expecting a valid XML document with a document
50 | // root of
51 | // 5. Merge in XML equivalents of each of the strongly typed
52 | // messages present in this Detail message.
53 | // In the event that a sending application does not follow
54 | // sending rule #1 above properly and data for the same element
55 | // appears in xmlDetail, the data in xmlDetail should be left alone
56 | // and the data in the equivalent message should ignored.
57 |
58 | message Detail {
59 | string xmlDetail = 1;
60 |
61 | //
62 | Contact contact = 2;
63 |
64 | // <__group>
65 | Group group = 3;
66 |
67 | //
68 | PrecisionLocation precisionLocation = 4;
69 |
70 | //
71 | Status status = 5;
72 |
73 | //
74 | Takv takv = 6;
75 |
76 | //