Hierarchical IADS Code w/Shoot and Scoot Logic (Full Version)

All Forums >> [New Releases from Matrix Games] >> Command: Modern Operations series >> Mods and Scenarios >> Lua Legion



Message


SeaQueen -> Hierarchical IADS Code w/Shoot and Scoot Logic (12/24/2021 4:56:12 PM)

I wrote this code to allow scenario designers to build a flexible model of a hierarchical IADS that would include the possibility of clipping the kill chain by striking various echelon command posts. IADS elements will move periodically, following their emission period.

This first part is intended to be executed on scenario load. It defines the IADS hierarchy and initializes the state table for its components.

-- initialization

asvRadarGuid = 'GWKW9V-0HME4FG8F0I72'
bgdCPguid = 'GWKW9V-0HME4FGAIAD73'


sa10rgtCPguid = 'GWKW9V-0HME4FG8F0HPC'
sa10bnCPguid = 'GWKW9V-0HME4FG95CU8I'
sa10bmRadarGuid = 'GWKW9V-0HME4FG8F0HPJ'
sa10Bn1Guid='GWKW9V-0HME4FG8F0I36'
sa10Bn2Guid='GWKW9V-0HME4FG8F0I0B'
sa10Bn3Guid='GWKW9V-0HME4FG8F0HPO'

sa21rgtCPguid = 'GWKW9V-0HME4FGAI6D4S'
sa21bnCPguid = 'GWKW9V-0HME4FGAHSGSH'
sa21bmRadarGuid = 'GWKW9V-0HME4FGAHTDR6'
sa21Bn1Guid= 'GWKW9V-0HME4FGAHRUG7'
sa21Bn2Guid= 'GWKW9V-0HME4FGAHS03V'
sa21Bn3Guid= 'GWKW9V-0HME4FGAHS15G'

pantsirCpyCP = 'GWKW9V-0HME4FGBBM6A5'
pantsirCpyBMRadr = 'GWKW9V-0HME4FGBBPDRI'
pantsirPlt1 = 'GWKW9V-0HME4FGBBM05M'
pantsirPlt2 = 'GWKW9V-0HME4FGBBM1RT'
pantsirPlt3 = 'GWKW9V-0HME4FGBBM2I5'

pantsirBMMaxRange = 135 -- Max launch range of a FLATFACE-E Radar
pantsirRange = 10 -- Max launch range of an SA-22

sa10BMRMaxRange = 325 -- Max range for a BIG BIRD B
sa10Range = 40 -- Max launch range of an SA-10

sa21BMRMaxRange = 325 -- Max range for a BIG BIRD D
sa21Range = 215 -- Max launch range of an SA-21

-- weapons control states
wcsFree = 0
wcsTight = 1
wcsHold = 2

sa10AirDefenseRgt = {
commandPost=sa10rgtCPguid, 
equipment={sa10bmRadarGuid}, 
rdrRng=0.5*sa10BMRMaxRange, 
emitTime = 8, 
tearDownTime = 2, 
repositionTime = 2, 
setUpTime = 2, 
standByTime = 2, 
subordinates={ sa10AirDefenseBns }}

sa10AirDefenseBns = {
commandPost=sa10bnCPguid, 
equipment={sa10Bn1Guid, 
sa10Bn2Guid, sa10Bn3Guid}, 
rdrRng=0.8*sa10Range, 
emitTime = 8, 
tearDownTime = 2, 
repositionTime = 2, 
setUpTime = 2, 
standByTime = 2, 
subordinates={}}

sa21AirDefenseRgt = {
commandPost=sa21rgtCPguid, 
equipment={sa21bmRadarGuid }, 
rdrRng=0.75*sa21BMRMaxRange, 
emitTime = 8, 
tearDownTime = 2, 
repositionTime = 2, 
setUpTime = 2, 
standByTime = 2, 
subordinates={ sa21AirDefenseBns }}

sa21AirDefenseBns = {
commandPost=sa21bnCPguid, 
equipment={sa21Bn1Guid, sa21Bn2Guid, sa21Bn3Guid}, 
rdrRng=0.8*sa21Range, 
emitTime = 8, 
tearDownTime = 2, 
repositionTime = 2, 
setUpTime = 2, 
standByTime = 2, 
subordinates={}}

pansirAirDefenseCpy = {
commandPost=pantsirCpyCP, 
equipment={pantsirCpyBMRadr}, 
rdrRng=0.8*pantsirBMMaxRange, 
emitTime = 8, 
tearDownTime = 2, 
repositionTime = 2, 
setUpTime = 2, 
standByTime = 2, 
subordinates={ pantsirAirDefensePlts }}

pantsirAirDefensePlts =  {
commandPost=pantsirCpyCP, 
equipment={pantsirPlt1, pantsirPlt2, pantsirPlt3}, 
rdrRng=0.8*pantsirRange, 
emitTime = 8, 
tearDownTime = 2, 
repositionTime = 2, 
setUpTime = 2, 
standByTime = 2, 
subordinates={}}

airDefenseBrigade = {
commandPost=bgdCPguid , 
equipment={}, 
rdrRng=0,
emitTime = 8, 
tearDownTime = 2, 
repositionTime = 2, 
setUpTime = 2, 
standByTime = 2, 
subordinates={ sa21AirDefenseRgt, sa10AirDefenseRgt }}

unitStates = {}

function initializeUnitStates(unit, thisSide)
    initialized = false

    if(ScenEdit_GetUnit({side=mySide, guid=unit.commandPost}) ~= nil) then        
        if(unit.subordinates ~= nil) then
            for s, sub in ipairs(unit.subordinates) do                
                initializeUnitStates(sub, thisSide)
            end 
        end        
        if(unit.equipment ~= {}) then
            for e, eqp in ipairs(unit.equipment) do
                timeToCompleteEvolution = unit.emitTime + unit.tearDownTime + unit.repositionTime + unit.setUpTime + unit.standByTime

                timeToCeaseEmission = unit.emitTime
                timeToCompleteTearDown = timeToCeaseEmission + unit.tearDownTime
                timeToCompleteRelocation = timeToCompleteTearDown + unit.repositionTime
                timeToCompleteSetUp = timeToCompleteRelocation + unit.setUpTime
                timeToStandBy = timeToCompleteSetUp + unit.standByTime

                time = math.random(0, timeToCompleteEvolution )
                if time < timeToCeaseEmission then 
                    unitStates[eqp] = { state="canEmit",  minutesInState = time }
                elseif ( (time >= timeToCeaseEmission) and ( time < timeToCompleteTearDown )) then
                    unitStates[eqp] = { state="tearingDown", minutesInState = (time - timeToCeaseEmission) }
                elseif ( ( time >= timeToCompleteTearDown ) and (time < timeToCompleteRelocation )) then
                    unitStates[eqp] = { state="repositioning", minutesInState = (time - timeToCompleteTearDown) }
                elseif ( (time >= timeToCompleteRelocation ) and ( time < timeToCompleteSetUp )) then
                    unitStates[eqp] = { state="settingUp", minutesInState = (time - timeToCompleteRelocation) }
                else
                    unitStates[eqp] = { state="standingBy", minutesInState = (time - timeToCompleteSetUp ) }                
                end
            end
        end
        return true
    else
        return false -- return false if unit is improperly defined
    end
end

initializeUnitStates(airDefenseBrigade)


The next bit of code is intended to executed every minute. It manages whether whether the IADS components are setting up, tearing down, moving, or whether they could emit should a worthwhile target come close enough to make it worth lighting up one's organic radars.

-- execute every minute

function randomWaypoint(currentPosition, dist)
    math.randomseed( os.time() ) -- removes correlations (not)

    r = math.random(0, 359)
    print(r)
    newpos=World_GetPointFromBearing( {latitude=tostring(currentPosition.latitude), longitude=tostring(currentPosition.longitude), distance = dist, bearing = r} )
    print(newpos)
    --wpt = {latitude = newpos.latitude, longitude = newpos.longitude}
    wpt = {TypeOf = 'ManualPlottedCourseWaypoint', latitude = newpos.latitude, longitude = newpos.longitude}
    
    return wpt
end

function manageUnitEMCONandMobilityStates(unit, thisSide)
    if(ScenEdit_GetUnit({side=thisSide, guid=unit.commandPost}) ~= nil) then        
        if(unit.subordinates ~= nil) then
            for s, sub in ipairs(unit.subordinates) do                
                manageUnitEMCONandMobilityStates(sub, thisSide)
            end 
        end
        if(next(unit.equipment) ~= nil) then
            timeToCompleteEvolution = unit.emitTime + unit.tearDownTime + unit.repositionTime + unit.setUpTime + unit.standByTime
            for e, eqp in ipairs(unit.equipment) do
                if( unitStates[eqp] ~= nil) then
                    unitStates[eqp].minutesInState = unitStates[eqp].minutesInState + 1          
                    print(eqp..": state = "..unitStates[eqp].state..", minutesinstate = "..unitStates[eqp].minutesInState)
                    if unitStates[eqp].state == 'canEmit' then
                        ScenEdit_SetUnit({side=thisSide, guid=eqp, course={}, speed = 0, holdposition=true  })
                        if unitStates[eqp].minutesInState >= unit.emitTime then                            
                            unitStates[eqp].state = 'tearingDown'
                            unitStates[eqp].minutesInState = 0 
                            break
                        end                

                    elseif unitStates[eqp].state == 'tearingDown' then 
                        ScenEdit_SetUnit({side=thisSide, guid=eqp, course={}, speed = 0, holdposition=true  })
                        if unitStates[eqp].minutesInState >= unit.tearDownTime then
                            unitStates[eqp].state = 'repositioning'
                            unitStates[eqp].minutesInState = 0
                            break
                        end               
                    elseif unitStates[eqp].state == 'repositioning' then
                        repositionSpeed = 35 -- NM/hr
                        u = ScenEdit_GetUnit({side=thisside, guid=eqp})
                        if ( unitStates[eqp].minutesInState < unit.repositionTime) then   
                            newPosition = randomWaypoint( {latitude=u.latitude, longitude=u.longitude}, (repositionSpeed * unit.repositionTime / 60) )                            
                            --print(u.name..": latitude= "..newPosition.latitude..", longitude= "..newPosition.longitude)
                            ScenEdit_SetUnit({side=thisSide, guid=eqp, course={newPosition}, speed = repositionSpeed, holdposition=false  })                        
                            print(u.course)

                        else
                            if unitStates[eqp].minutesInState >= unit.repositionTime then                                
                                unitStates[eqp].state = 'settingUp' 
                                unitStates[eqp].minutesInState = 0                                                  
                                break
                            end
                        end                    
                    elseif unitStates[eqp].state == 'settingUp' then
                        ScenEdit_SetUnit({side=thisSide, guid=eqp, course={}, speed = 0, holdposition=true  })
                        if unitStates[eqp].minutesInState >= unit.setUpTime then                            
                            unitStates[eqp].state = 'standingBy' 
                            unitStates[eqp].minutesInState = 0
                            break
                        end
                    elseif unitStates[eqp].state == 'standingBy' then
                        ScenEdit_SetUnit({side=thisSide, guid=eqp, course={}, speed = 0, holdposition=true  })
                        if unitStates[eqp].minutesInState >= timeToCompleteEvolution then
                            unitStates[eqp].state = 'canEmit'
                            unitStates[eqp].minutesInState = 0
                            break
                        end
                    end
                end
            end
        end
    end
    return true
end

manageUnitEMCONandMobilityStates( airDefenseBrigade )




This third part is intended to execute every 15 seconds. It recursively descends the hierarchy tree and determines if a unit component is in a state where it could emit, then adjusts its WCS and EMCON appropriately.
-- execute every 15 seconds

function equipmentCanEmit(elementGuid)
    canEmit = false
    if(unitStates[elementGuid] ~=  nil) then
        if( unitStates[elementGuid].state == "canEmit" ) then
            canEmit = true
        end
    end
    return canEmit
end

function atLeastOneContactIsInRangeOfEquipment(list, unitGuid, range)
    cntctInRng = false
    for c, cntc in ipairs(contactList) do
        trk = ScenEdit_GetContact({side=mySide, guid=cntc.guid})      
        trkRng = Tool_Range(unitGuid, {latitude=trk.latitude, longitude=trk.longitude})
        print(trkRng..", "..range)
        if (trkRng <= range) then
            cntctInRng = true;
        end
    end
    return cntctInRng
end

function manageIADSEchelonEmconAndWCS(unit, thisSide)
    if(ScenEdit_GetUnit({side=mySide, guid=unit.commandPost}) ~= nil) then        
        if(unit.subordinates ~= nil) then
            for s, sub in ipairs(unit.subordinates) do                
                manageIADSEchelonEmconAndWCS(sub, thisSide)
            end 
        end
        contactList = ScenEdit_GetContacts(thisSide)
        if(unit.equipment ~= {}) then
            for e, eqp in ipairs(unit.equipment) do
                if( ScenEdit_GetUnit({side=thisSide, guid=eqp}) ~= nil ) then
                    u=ScenEdit_GetUnit({side=thisSide, guid=eqp})
                    if( atLeastOneContactIsInRangeOfEquipment(contactList, eqp, unit.rdrRng)  and equipmentCanEmit(eqp) ) then
                        print("Turning on radar.")
                        ScenEdit_SetEMCON('Unit', eqp, "Radar=Active;Sonar=Passive;OECM=Passive")
                        ScenEdit_SetDoctrine({side = thisSide, unitname=u.name }, { weapon_control_status_air = wcsTight  })
                    else
                        print("Turning off radar.")
                        ScenEdit_SetEMCON('Unit', eqp, "Radar=Passive;Sonar=Passive;OECM=Passive")
                        ScenEdit_SetDoctrine({side = thisSide, unitname=u.name }, { weapon_control_status_air = wcsHold  })
                    end        
                end
            end
        end
        return true

    else
        -- execute alternative emcon/wcs management plan (currently none)
        return false -- return false if echelon command post is destroyed
    end
end

manageIADSEchelonEmconAndWCS(airDefenseBrigade, "RUS")




Merry Christmas!




KLAB -> RE: Hierarchical IADS Code w/Shoot and Scoot Logic (12/24/2021 9:46:52 PM)

Wow! Thanks. Merry Christmas.




Parel803 -> RE: Hierarchical IADS Code w/Shoot and Scoot Logic (12/25/2021 11:38:57 AM)

looks impressive, will try to learn from it.
merry Christmas




SeaQueen -> RE: Hierarchical IADS Code w/Shoot and Scoot Logic (12/25/2021 2:20:39 PM)

If you have any questions, please ask. I created a tree data structure that is intended to reflect the various echelons of the IADS network. It should be very flexible, and can reflect a wide variety of network topologies. On initialization or at regular intervals, it recursively descends the tree and performs various administrative actions (changing the values of variables) and then asks the SAM site to move or emit based on the administrative actions and the current picture. It's very simple, and it should be easy to customize to many different IADS behaviors.




BDukes -> RE: Hierarchical IADS Code w/Shoot and Scoot Logic (12/26/2021 9:42:16 PM)

Very impressive. Thank you!

Mike




SeaQueen -> RE: Hierarchical IADS Code w/Shoot and Scoot Logic (12/27/2021 9:28:59 AM)

Be advised, I have found some bugs in this during subsequent testing. I'll probably put out a revised version soon.




BDukes -> RE: Hierarchical IADS Code w/Shoot and Scoot Logic (12/27/2021 3:10:03 PM)


quote:

ORIGINAL: SeaQueen

Be advised, I have found some bugs in this during subsequent testing. I'll probably put out a revised version soon.


Waa I want my money back[8D]




SeaQueen -> RE: Hierarchical IADS Code w/Shoot and Scoot Logic (12/27/2021 11:33:11 PM)

You’re welcome to fix it yourself. I don’t generally post code with the idea that it could be used without substantial customization.




BDukes -> RE: Hierarchical IADS Code w/Shoot and Scoot Logic (12/28/2021 1:04:46 PM)

quote:

ORIGINAL: SeaQueen

You’re welcome to fix it yourself. I don’t generally post code with the idea that it could be used without substantial customization.


SQ its free code. I can't complain and you're awesome!

Probably should have started with that.[:)] Internet don't do tone well[;)]




SeaQueen -> RE: Hierarchical IADS Code w/Shoot and Scoot Logic (12/30/2021 12:30:09 PM)

Thank you. Hopefully you've put it to good use by now. ;-)




Page: [1]

Valid CSS!




Forum Software © ASPPlayground.NET Advanced Edition 2.4.5 ANSI
1.390625