pydcs / dcs

Digital Combat Simulator Python mission framework
GNU Lesser General Public License v3.0
165 stars 80 forks source link

datalink support #335

Open DanAlbert opened 1 year ago

DanAlbert commented 1 year ago

2.9 includes fancy new datalink features. FlyingUnit needs to be updated to handle it.

I was hoping some of the information (max number of donors and team members) would be something we could export, but it doesn't look like it. There is a connectDatalinks field of the aircraft Lua that is a list of supported datalinks (using the real names rather than the DCS names, so "IDM" rather than SMDL, "Link16" rather than TNDL, and "SADL" instead of A10CDL), and a datalinks field which maps those to some lua files that define some of the datalink behaviors, but there isn't any defined constant we can pull from those (the F-16 one has a hard coded if donors < 4, so I doubt there is a constant elsewhere that I'm overlooking).

It may be best to not try to limit the number of team members or donors (or whatever limits might exist for IDM and SADL) and leave that up to the application. In Liberation that'll work fine for us since we're used to maintaining non-exported metadata for this kind of thing.

The good news is that pydcs doesn't need this to be compatible with 2.9 (not for serializing missions for 2.9 anyway, I haven't tried deserializing a miz created with DCS 2.9). DCS will automatically configure datalink sensibly if we do nothing, but this is needed for pydcs callers to configure it manually.

datalink.miz.txt (renamed for github's filter) is a mission with several flights set up. Each is a 2-ship, with the group name <callsign> <number>, pilots named <callsign> <number>-<member>, voice callsign label/number set the way Wags said to (first and last letter of the callsign, so "Jedi 1-1" has callsign label "JI" and number 11). The setup is:

Hawg 1:

image

Hawg 2:

image

Hawg 3:

image

Devil 1 (hornets):

image

Jedi 1 (vipers):

image

Saber 1 (apaches):

image

Saber 2 (apaches):

image

Buff 1 is a flight of B-52s, which do participate in datalink but are AI only. They don't have the donor/team datalink tab but do have callsign labels/numbers/STNs.

DanAlbert commented 1 year ago

For SADL, the miz has this for Hawg 1-1:

["datalinks"] = 
{
    ["SADL"] = 
    {
        ["settings"] = 
        {
            ["flightLead"] = true,
            ["AirKey"] = 10,
            ["GatewayKey"] = 8,
        }, -- end of ["settings"]
        ["network"] = 
        {
            ["teamMembers"] = 
            {
                [1] = 
                {
                    ["missionUnitId"] = 1,
                }, -- end of [1]
                [2] = 
                {
                    ["missionUnitId"] = 2,
                }, -- end of [2]
                [4] = 
                {
                    ["missionUnitId"] = 12,
                }, -- end of [4]
                [3] = 
                {
                    ["missionUnitId"] = 11,
                }, -- end of [3]
            }, -- end of ["teamMembers"]
            ["donors"] = 
            {
                [1] = 
                {
                    ["missionUnitId"] = 13,
                }, -- end of [1]
                [2] = 
                {
                    ["missionUnitId"] = 14,
                }, -- end of [2]
            }, -- end of ["donors"]
        }, -- end of ["network"]
    }, -- end of ["SADL"]
}, -- end of ["datal

Link-16 for Jedi 1-1:

["datalinks"] = 
{
    ["Link16"] = 
    {
        ["settings"] = 
        {
            ["flightLead"] = true,
            ["transmitPower"] = 3,
            ["specialChannel"] = 1,
            ["fighterChannel"] = 1,
            ["missionChannel"] = 1,
        }, -- end of ["settings"]
        ["network"] = 
        {
            ["teamMembers"] = 
            {
                [1] = 
                {
                    ["TDOA"] = true,
                    ["missionUnitId"] = 3,
                }, -- end of [1]
                [2] = 
                {
                    ["TDOA"] = true,
                    ["missionUnitId"] = 4,
                }, -- end of [2]
                [4] = 
                {
                    ["TDOA"] = false,
                    ["missionUnitId"] = 6,
                }, -- end of [4]
                [3] = 
                {
                    ["TDOA"] = false,
                    ["missionUnitId"] = 5,
                }, -- end of [3]
            }, -- end of ["teamMembers"]
            ["donors"] = 
            {
                [1] = 
                {
                    ["missionUnitId"] = 7,
                }, -- end of [1]
                [2] = 
                {
                    ["missionUnitId"] = 8,
                }, -- end of [2]
            }, -- end of ["donors"]
        }, -- end of ["network"]
    }, -- end of ["Link16"]
}, -- end of ["datalinks"]

The AI B-52 flight does not have a datalinks field.

IDM is the most complicated, but it's mostly just the defaults repeated per-preset. Saber 1-1:

["datalinks"] = 
{
    ["IDM"] = 
    {
        ["settings"] = 
        {
            ["presets"] = 
            {
                [7] = 
                {
                    ["primaryFreq"] = 0,
                    ["NoAcknowledgmentRetries"] = 2,
                    ["LB_Net"] = true,
                    ["autoAcknowledgment"] = true,
                    ["callSign"] = "PRE 7",
                    ["presetName"] = "PRESET 7",
                }, -- end of [7]
                [1] = 
                {
                    ["primaryFreq"] = 1,
                    ["NoAcknowledgmentRetries"] = 2,
                    ["LB_Net"] = true,
                    ["autoAcknowledgment"] = true,
                    ["callSign"] = "PRE 1",
                    ["presetName"] = "PRESET 1",
                }, -- end of [1]
                [2] = 
                {
                    ["primaryFreq"] = 2,
                    ["NoAcknowledgmentRetries"] = 2,
                    ["LB_Net"] = true,
                    ["autoAcknowledgment"] = true,
                    ["callSign"] = "PRE 2",
                    ["presetName"] = "PRESET 2",
                }, -- end of [2]
                [4] = 
                {
                    ["primaryFreq"] = 4,
                    ["NoAcknowledgmentRetries"] = 2,
                    ["LB_Net"] = true,
                    ["autoAcknowledgment"] = true,
                    ["callSign"] = "PRE 4",
                    ["presetName"] = "PRESET 4",
                }, -- end of [4]
                [8] = 
                {
                    ["primaryFreq"] = 0,
                    ["NoAcknowledgmentRetries"] = 2,
                    ["LB_Net"] = true,
                    ["autoAcknowledgment"] = true,
                    ["callSign"] = "PRE 8",
                    ["presetName"] = "PRESET 8",
                }, -- end of [8]
                [9] = 
                {
                    ["primaryFreq"] = 0,
                    ["NoAcknowledgmentRetries"] = 2,
                    ["LB_Net"] = false,
                    ["autoAcknowledgment"] = true,
                    ["callSign"] = "PRE 9",
                    ["presetName"] = "PRESET 9",
                }, -- end of [9]
                [5] = 
                {
                    ["primaryFreq"] = 0,
                    ["NoAcknowledgmentRetries"] = 2,
                    ["LB_Net"] = true,
                    ["autoAcknowledgment"] = true,
                    ["callSign"] = "PRE 5",
                    ["presetName"] = "PRESET 5",
                }, -- end of [5]
                [10] = 
                {
                    ["primaryFreq"] = 0,
                    ["NoAcknowledgmentRetries"] = 2,
                    ["LB_Net"] = false,
                    ["autoAcknowledgment"] = true,
                    ["callSign"] = "PRE10",
                    ["presetName"] = "PRESET10",
                }, -- end of [10]
                [3] = 
                {
                    ["primaryFreq"] = 3,
                    ["NoAcknowledgmentRetries"] = 2,
                    ["LB_Net"] = true,
                    ["autoAcknowledgment"] = true,
                    ["callSign"] = "PRE 3",
                    ["presetName"] = "PRESET 3",
                }, -- end of [3]
                [6] = 
                {
                    ["primaryFreq"] = 0,
                    ["NoAcknowledgmentRetries"] = 2,
                    ["LB_Net"] = true,
                    ["autoAcknowledgment"] = true,
                    ["callSign"] = "PRE 6",
                    ["presetName"] = "PRESET 6",
                }, -- end of [6]
            }, -- end of ["presets"]
        }, -- end of ["settings"]
        ["network"] = 
        {
            ["presets"] = 
            {
                [7] = 
                {
                    ["members"] = 
                    {
                        [1] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 9,
                        }, -- end of [1]
                        [2] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 10,
                        }, -- end of [2]
                    }, -- end of ["members"]
                }, -- end of [7]
                [1] = 
                {
                    ["members"] = 
                    {
                        [1] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 9,
                        }, -- end of [1]
                        [2] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 10,
                        }, -- end of [2]
                        [4] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 16,
                        }, -- end of [4]
                        [3] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 15,
                        }, -- end of [3]
                    }, -- end of ["members"]
                }, -- end of [1]
                [2] = 
                {
                    ["members"] = 
                    {
                        [1] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 9,
                        }, -- end of [1]
                        [2] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 10,
                        }, -- end of [2]
                    }, -- end of ["members"]
                }, -- end of [2]
                [4] = 
                {
                    ["members"] = 
                    {
                        [1] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 9,
                        }, -- end of [1]
                        [2] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 10,
                        }, -- end of [2]
                    }, -- end of ["members"]
                }, -- end of [4]
                [8] = 
                {
                    ["members"] = 
                    {
                        [1] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 9,
                        }, -- end of [1]
                        [2] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 10,
                        }, -- end of [2]
                    }, -- end of ["members"]
                }, -- end of [8]
                [9] = 
                {
                    ["members"] = 
                    {
                        [1] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 9,
                        }, -- end of [1]
                        [2] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 10,
                        }, -- end of [2]
                    }, -- end of ["members"]
                }, -- end of [9]
                [5] = 
                {
                    ["members"] = 
                    {
                        [1] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 9,
                        }, -- end of [1]
                        [2] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 10,
                        }, -- end of [2]
                    }, -- end of ["members"]
                }, -- end of [5]
                [10] = 
                {
                    ["members"] = 
                    {
                        [1] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 9,
                        }, -- end of [1]
                        [2] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 10,
                        }, -- end of [2]
                    }, -- end of ["members"]
                }, -- end of [10]
                [3] = 
                {
                    ["members"] = 
                    {
                        [1] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 9,
                        }, -- end of [1]
                        [2] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 10,
                        }, -- end of [2]
                    }, -- end of ["members"]
                }, -- end of [3]
                [6] = 
                {
                    ["members"] = 
                    {
                        [1] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 9,
                        }, -- end of [1]
                        [2] = 
                        {
                            ["PRI_value"] = true,
                            ["TM_value"] = true,
                            ["missionUnitId"] = 10,
                        }, -- end of [2]
                    }, -- end of ["members"]
                }, -- end of [6]
            }, -- end of ["presets"]
        }, -- end of ["network"]
    }, -- end of ["IDM"]
}, -- end of ["datalinks"]
DanAlbert commented 1 year ago

As far as what we export into planes.py, the connectDatalinks field is only present for flyable aircraft. AI aircraft which can participate in the network for player flights do have this though:

        "attribute":
          1: 1
          2: 1
          3: 4
          4: 23
          5: "Strategic bombers"
          6: "Refuelable"
          7: "Datalink"
          8: "Link16"
          9: "All"
          10: "NonAndLightArmoredUnits"
          11: "NonArmoredUnits"
          12: "Air"
          13: "Planes"
          14: "Battle airplanes"
          15: "Bombers"

There doesn't appear to be a similar attribute for IDM, and SADL aircraft actually also report Link16 there. It's probably worth exposing both of these fields so callers can programmatically determine which network an aircraft can participate in... if it weren't for the SADL aircraft reporting Link16. I can't find any data in the export that shows that the A-10C (the old one) is SADL and not Link16, but the ME gets this right.

DanAlbert commented 1 year ago

lol, the ME has this:

function fillListsForManualAdd()
    currentUnitsTable = getUnitsGroupsWithAddPropName("SADL_TN")

There actually isn't any property that defines this. It's based on whether or not the aircraft has a property (as in AddPropAircraft) with a matching name.

The names are STN_L16, SADL_TN, and TN_IDM_LB.

magwo commented 11 months ago

Good research. My "squadron" is using my Mission Buzzer which both deserializes and serializes these things. I'm not actually sure whether it's currently working, as I suspect that the sensible DCS defaults may be masking the missing data.

magwo commented 10 months ago

Here's something to be aware of: https://forum.dcs.world/topic/340172-link16-dont-work-when-starting-cold/

The latest patch changed a lot of datalink code and apparently broke some stuff, so implementing and verifying correctness of this could be challenging at the moment. I expect to see a DCS hotfix or patch a few weeks from now.