Hierarchical IADS Code w/Shoot and Scoot Logic

All discussions & material related to Command's Lua interface

Moderators: angster, RoryAndersonCDT, michaelm75au, MOD_Command

Post Reply
User avatar
SeaQueen
Posts: 1436
Joined: Sat Apr 14, 2007 4:20 am
Location: Washington D.C.

Hierarchical IADS Code w/Shoot and Scoot Logic

Post by SeaQueen »

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.

Code: Select all

 -- 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'
 

Code: Select all

 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
 

Code: Select all

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

Code: Select all

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

Code: Select all

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

Code: Select all

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

Code: Select all

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

Code: Select all

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

Code: Select all

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

Code: Select all

 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
 

Code: Select all

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

Code: Select all

                 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.

Code: Select all

 -- 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
 

Code: Select all

 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                
 

Code: Select all

                     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)
 

Code: Select all

                         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.

Code: Select all

 -- 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
 

Code: Select all

 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
 

Code: Select all

 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
 

Code: Select all

     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!
User avatar
KLAB
Posts: 501
Joined: Tue Feb 27, 2007 5:24 pm

RE: Hierarchical IADS Code w/Shoot and Scoot Logic

Post by KLAB »

Wow! Thanks. Merry Christmas.
Parel803
Posts: 941
Joined: Thu Oct 10, 2019 3:39 pm
Location: Netherlands

RE: Hierarchical IADS Code w/Shoot and Scoot Logic

Post by Parel803 »

looks impressive, will try to learn from it.
merry Christmas
User avatar
SeaQueen
Posts: 1436
Joined: Sat Apr 14, 2007 4:20 am
Location: Washington D.C.

RE: Hierarchical IADS Code w/Shoot and Scoot Logic

Post by SeaQueen »

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
Posts: 2694
Joined: Wed Dec 27, 2017 12:59 pm

RE: Hierarchical IADS Code w/Shoot and Scoot Logic

Post by BDukes »

Very impressive. Thank you!

Mike
Don't call it a comeback...
User avatar
SeaQueen
Posts: 1436
Joined: Sat Apr 14, 2007 4:20 am
Location: Washington D.C.

RE: Hierarchical IADS Code w/Shoot and Scoot Logic

Post by SeaQueen »

Be advised, I have found some bugs in this during subsequent testing. I'll probably put out a revised version soon.
BDukes
Posts: 2694
Joined: Wed Dec 27, 2017 12:59 pm

RE: Hierarchical IADS Code w/Shoot and Scoot Logic

Post by BDukes »

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]
Don't call it a comeback...
User avatar
SeaQueen
Posts: 1436
Joined: Sat Apr 14, 2007 4:20 am
Location: Washington D.C.

RE: Hierarchical IADS Code w/Shoot and Scoot Logic

Post by 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.
BDukes
Posts: 2694
Joined: Wed Dec 27, 2017 12:59 pm

RE: Hierarchical IADS Code w/Shoot and Scoot Logic

Post by BDukes »

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[;)]
Don't call it a comeback...
User avatar
SeaQueen
Posts: 1436
Joined: Sat Apr 14, 2007 4:20 am
Location: Washington D.C.

RE: Hierarchical IADS Code w/Shoot and Scoot Logic

Post by SeaQueen »

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

Return to “Lua Legion”