-- ***************************************************************************** -- ** Awacs ** -- ********************************************************* -- region AwacsConfigFunctions -- @type AwacsCallsignConfig -- @field #string alias Alias for the AWACS. -- @field #number name CallSign name for the AWACS. -- @field #number number CallSign number (1-7) for the AWACS. --- Parse an Awacs Callsign config Object. -- @param #JsonObject config Config object to parse -- @param #string parser_name Parser name ("AIRBASE", "AIRBOSS", etc...). -- @return #AwacsCallsignConfig awacsCallsignConfigJson Parsed CallSign object function ParseAwacsCallsignConfigJson(config, parser_name) local awacsCallsignConfigJson = {} local default_number = 1 -- ************************************************************************** -- name -- ************************************************************************** if type(config.name) == "number" then if table.count_value(CALLSIGN.AWACS, config.name) > 0 then awacsCallsignConfigJson.name = config.name else Jtff_log.error("Awacs Callsign name is not a valid CallSign name, skipping awacs configuration", parser_name) return {} end else Jtff_log.error("Awacs Callsign name is not valid, skipping awacs configuration", parser_name) return {} end -- ************************************************************************** -- alias -- ************************************************************************** if type(config.alias) == "string" then awacsCallsignConfigJson.alias = config.alias else Jtff_log.warn("Awacs Callsign alias is not a string, defaulting to Awacs name", parser_name) awacsCallsignConfigJson.alias = awacsCallsignConfigJson.name end -- ************************************************************************** -- number -- ************************************************************************** if type(config.number) == "number" then if config.number >= 1 and config.number <= 7 then awacsCallsignConfigJson.number = config.number else Jtff_log.warn( string.format("Awacs Callsign number is not a valid number (1-7), defaulting number to %d", default_number), parser_name) awacsCallsignConfigJson.number = default_number end else Jtff_log.warn(string.format("Awacs Callsign number is not a number, defaulting number to %d", default_number), parser_name) awacsCallsignConfigJson.number = default_number end return awacsCallsignConfigJson end -- @type AwacsConfig -- @field #boolean enable Enable awacs creation. -- @field #string type Awacs type. -- @field #string airbaseName Airbase name. -- @field #boolean autospawn Autospawn Awacs. -- @field #RacetrackConfig racetrack Racetrack configuration. -- @field #number nbEscort Number of escort aircraft. -- @field #number missionmaxduration Maximum mission duration in minutes. -- @field #tacanConfig tacan TACAN configuration. -- @field #radioConfig radio Radio configuration. -- @field #number fuelLowThreshold Fuel low threshold in %. -- @field #AwacsCallsignConfig callsign Callsign configuration. -- @field #SquawkConfig squawk Squawk configuration. --- Parse Awacs config Object. -- @param #JsonObject config Config object to parse -- @return #AwacsConfig awacsConfigJson Parsed AwacsConfig object function ParseAwacsConfigJson(config) local json = require('Scripts/json') local parser_name = "AWACS" local default_autospawn = false local default_nbEscort = 1 local default_missionmaxduration = 240 local default_fuelLowThreshold = 30 -- ************************************************************************** -- enable -- ************************************************************************** local awacsConfigJson = { enable = config.enable or false, } -- ************************************************************************** -- type -- ************************************************************************** if type(config.type) == "string" then awacsConfigJson.type = config.type else Jtff_log.error("Awacs type is not a string, skipping awacs configuration", parser_name) config.enable = false return config end -- ************************************************************************** -- airbaseName -- ************************************************************************** if type(config.airbaseName) == "string" then if type(AIRBASE:FindByName(config.airbaseName)) == 'nil' then Jtff_log.error( string.format( "Airbase %s not found, skipping awacs configuration", config.airbaseName ), parser_name ) config.enable = false return config else awacsConfigJson.airbaseName = config.airbaseName end else Jtff_log.error("Airbase warehouse is not a string", parser_name) config.enable = false return config end -- ************************************************************************** -- autospawn -- ************************************************************************** if type(config.autospawn) == "boolean" then awacsConfigJson.autospawn = config.autospawn else Jtff_log.warn("Awacs %s autospawn not defined : defaulting", parser_name) awacsConfigJson.autospawn = default_autospawn end -- ************************************************************************** -- racetrack -- ************************************************************************** if type(config.racetrack) == "table" then awacsConfigJson.racetrack = ParseRacetrackConfigJson(config.racetrack, parser_name) if next(awacsConfigJson.racetrack) == nil then Jtff_log.error("Awacs racetrack is not a valid Racetrack object, skipping awacs configuration", parser_name) config.enable = false return config end else Jtff_log.error("Awacs racetrack is not a table, skipping awacs configuration", parser_name) config.enable = false return config end -- ************************************************************************** -- nbEscort -- ************************************************************************** if type(config.nbEscort) == "number" then awacsConfigJson.nbEscort = config.nbEscort else Jtff_log.warn(string.format("Awacs nbEscort is not a number, defaulting to %d", default_nbEscort), parser_name) awacsConfigJson.nbEscort = default_nbEscort end -- ************************************************************************** -- missionmaxduration -- ************************************************************************** if type(config.missionmaxduration) == "number" then awacsConfigJson.missionmaxduration = config.missionmaxduration else Jtff_log.warn( string.format("Awacs missionmaxduration is not a number, defaulting to %d", default_missionmaxduration), parser_name) awacsConfigJson.missionmaxduration = default_missionmaxduration end -- ************************************************************************** -- tacan -- ************************************************************************** if type(config.tacan) == "table" then awacsConfigJson.tacan = ParseTacanConfigJson(config.tacan, parser_name) else Jtff_log.error("Awacs tacan is not a table, skiping TACAN awacs configuration", parser_name) awacsConfigJson.tacan = {} end -- ************************************************************************** -- radio -- ************************************************************************** if type(config.radio) == "table" then awacsConfigJson.radio = ParseRadioConfigJson(config.radio, parser_name) if next(awacsConfigJson.radio) == nil then Jtff_log.error("Awacs radio is not a valid Radio object, skipping awacs configuration", parser_name) config.enable = false return config end else Jtff_log.error("Awacs radio is not a table, skipping awacs configuration", parser_name) config.enable = false return config end -- ************************************************************************** -- fuelLowThreshold -- ************************************************************************** if type(config.fuelLowThreshold) == "number" then awacsConfigJson.fuelLowThreshold = config.fuelLowThreshold else Jtff_log.warn( string.format("Awacs fuelLowThreshold is not a number, defaulting to %d %%", default_fuelLowThreshold), parser_name) awacsConfigJson.fuelLowThreshold = default_fuelLowThreshold end -- ************************************************************************** -- callsign -- ************************************************************************** if type(config.callsign) == "table" then awacsConfigJson.callsign = ParseAwacsCallsignConfigJson(config.callsign, parser_name) if next(awacsConfigJson.callsign) == nil then Jtff_log.error("Awacs callsign is not a valid Callsign object, skipping awacs configuration", parser_name) config.enable = false return config end else Jtff_log.error("Awacs callsign is not a table, skipping awacs configuration", parser_name) config.enable = false return config end -- ************************************************************************** -- squawk -- ************************************************************************** if type(config.squawk) == "table" then awacsConfigJson.squawk = ParseSquawkConfigJson(config.squawk, parser_name) else Jtff_log.warn("Awacs squawk is not a table, defaulting awacs squawk configuration", parser_name) awacsConfigJson.squawk = ParseSquawkConfigJson({}, parser_name) end Jtff_log.debug( string.format( "parsed Awacs config for %s Awacs, resulting config :\n%s", config.type or "", json:encode( awacsConfigJson, { indent = true } ) ), parser_name ) return awacsConfigJson end -- endregion AwacsConfigFunctions -- region AwacsFunctions --- Generate an Awacs mission object. -- @param #AwacsConfig awacsconfig Awacs configuration object. -- @param #boolean manageEscort Manage escort mission creation. -- @return #AUFTRAG awacsmission AWACS mission object, #AIRWING airwing Airwing object. function GenerateAwacsMission(awacsconfig, manageEscort) local generateEscortMission = false if manageEscort == true then generateEscortMission = true end local airwing = FindAirwingByAirbaseName(awacsconfig.airbaseName) local awacsmission = nil if airwing == nil then Jtff_log.error( string.format( "Airwing not found for Airbase %s, skipping awacs mission generation", awacsconfig.airbaseName ), "AWACS" ) return nil,nil else local function isAnchorCoordValid(MGRSString, autospawn) if autospawn == true then return true end if #(UTILS.Split(MGRSString," ")) ~= 5 then return false end return true end local orbitCoord = {x=0, y=0, z=0} if (isAnchorCoordValid(awacsconfig.racetrack.coordinate, awacsconfig.autospawn) == true) then orbitCoord = COORDINATE:NewFromMGRSString(awacsconfig.racetrack.coordinate) else orbitCoord = AIRBASE:FindByName(awacsconfig.airbaseName):GetCoordinate() end awacsmission = AUFTRAG:NewAWACS( orbitCoord, awacsconfig.racetrack.fl * 100, awacsconfig.racetrack.speed, awacsconfig.racetrack.heading, awacsconfig.racetrack.leg ) or {} awacsmission:SetDuration(awacsconfig.missionmaxduration*60) awacsmission:SetReturnToLegion(true) awacsmission:SetRequiredAssets(1) awacsmission:SetName( string.format( "Awacs-%s", awacsconfig.type ) ) awacsmission:SetRadio( awacsconfig.radio.freq, awacsconfig.radio.modulation ) if next(awacsconfig.tacan) ~= nil then awacsmission:SetTACAN( awacsconfig.tacan.channel, awacsconfig.tacan.morse, nil, awacsconfig.tacan.band ) end awacsmission:SetEPLRS(true) awacsmission:SetEmission(false) awacsmission:SetVerbosity(JTFF_verbosity_levels[JTFF_LOGLEVEL]) awacsmission.additionalData = { minEscort = awacsconfig.nbEscort, maxEscort = awacsconfig.nbEscort, awacsType = awacsconfig.type, awacsDuration = awacsconfig.missionmaxduration, callsign = awacsconfig.callsign, awacsTacan = awacsconfig.tacan, awacsRadio = awacsconfig.radio, awacsRacetrack = awacsconfig.racetrack, awacsFuelLowThreshold = awacsconfig.fuelLowThreshold, } function awacsmission:OnAfterScheduled(From, Event, To) local awacsOpsGroup = self:GetOpsGroups()[1] local airwing = awacsOpsGroup:GetAirwing() awacsOpsGroup:SwitchCallsign( self.additionalData.callsign.name, self.additionalData.callsign.number ) awacsOpsGroup:SetFuelLowThreshold(self.additionalData.awacsFuelLowThreshold) awacsOpsGroup:SetFuelCriticalThreshold(self.additionalData.awacsFuelLowThreshold-10) awacsOpsGroup:SetFuelLowRefuel(true) awacsOpsGroup:SetFuelLowRTB(false) awacsOpsGroup:SetFuelCriticalRTB(true) if (Jtff_map_marker[awacsOpsGroup:GetName()]) then COORDINATE:RemoveMark(Jtff_map_marker[awacsOpsGroup:GetName()]) end if(next(self.additionalData.awacsTacan) ~= nil) then Jtff_map_marker[awacsOpsGroup:GetName()] = COORDINATE:NewFromMGRSString(self.additionalData.awacsRacetrack.coordinate):MarkToCoalition( string.format( 'OnDemand Awacs %s - TCN %i\nFL %i at %i knots\nFreq %.2f MHz\nOn station for %i minutes\nRacetrack : %i ° for %i nm', self.additionalData.awacsType, self.additionalData.awacsTacan.channel, UTILS.Round(self.additionalData.awacsRacetrack.fl , 0), self.additionalData.awacsRacetrack.speed, self.additionalData.awacsRadio.freq, self.additionalData.awacsDuration, self.additionalData.awacsRacetrack.heading, self.additionalData.awacsRacetrack.leg ), awacsOpsGroup:GetCoalition(), true, 'OnDemand Awacs %s is Activated' ) else Jtff_map_marker[awacsOpsGroup:GetName()] = COORDINATE:NewFromMGRSString(self.additionalData.awacsRacetrack.coordinate):MarkToCoalition( string.format( 'OnDemand Awacs %s\nFL %i at %i knots\nFreq %.2f MHz\nOn station for %i minutes\nRacetrack : %i ° for %i nm', self.additionalData.awacsType, UTILS.Round(self.additionalData.awacsRacetrack.fl , 0), self.additionalData.awacsRacetrack.speed, self.additionalData.awacsRadio.freq, self.additionalData.awacsDuration, self.additionalData.awacsRacetrack.heading, self.additionalData.awacsRacetrack.leg ), awacsOpsGroup:GetCoalition(), true, 'OnDemand Awacs %s is Activated' ) end function awacsOpsGroup:OnBeforeRTB(From, Event, To, Airbase, speed, holdspeed) COORDINATE:RemoveMark(Jtff_map_marker[self:GetName()]) end function awacsOpsGroup:OnAfterDead(From, Event, To) COORDINATE:RemoveMark(Jtff_map_marker[self:GetName()]) end function awacsOpsGroup:OnAfterElementDestroyed(From, Event, To, Element) COORDINATE:RemoveMark(Jtff_map_marker[self:GetName()]) end if self.additionalData.maxEscort > 0 and generateEscortMission == true then local escortMission = AUFTRAG:NewESCORT( awacsOpsGroup, {x=250, y=100, z=400}, 35, {'Air'} ) escortMission:SetReturnToLegion(true) escortMission:SetRequiredAssets(self.additionalData.minEscort, self.additionalData.maxEscort) escortMission:SetReinforce(3) escortMission:SetName(string.format("Escort-Awacs-%s", self.additionalData.awacsType)) escortMission:SetEPLRS(true) escortMission:SetEmission(true) escortMission:SetVerbosity(JTFF_verbosity_levels[JTFF_LOGLEVEL]) escortMission:SetFormation(ENUMS.Formation.FixedWing.EchelonRight.Close) escortMission:SetMissionAltitude(20000) escortMission:SetMissionWaypointRandomization(UTILS.NMToMeters(20)) escortMission:SetProhibitAfterburnerExecutePhase() escortMission:SetTime( 300, ( self.additionalData.awacsDuration * 60 ) + 1200 ) function escortMission:OnAfterScheduled(From, Event, To) for _, opsGroup in ipairs(self:GetOpsGroups()) do opsGroup:SetFuelLowThreshold(30) opsGroup:SetFuelCriticalThreshold(20) opsGroup:SetFuelLowRefuel(true) opsGroup:SetFuelLowRTB(false) opsGroup:SetFuelCriticalRTB(true) opsGroup:SetJettisonEmptyTanks(false) opsGroup:SetJettisonWeapons(false) end end airwing:AddMission(escortMission) end end return awacsmission,airwing end end --- Trigger an Awacs mission. -- @param #table objAwacs AWACS object. function TriggerAwacsMission(objAwacs) objAwacs.airwing:AddMission(objAwacs.mission) Jtff_log.info( string.format( 'Awacs %s triggered', objAwacs.customconfig.type ), "AWACS" ) end -- endregion AwacsFunctions AwacsArray = {} MenuCoalitionAwacs = {} for _k, _coalition in pairs(coalition.side) do MenuCoalitionAwacs[UTILS.GetCoalitionName(_coalition)] = MENU_COALITION:New(_coalition, "Awacs", MenuCoalition[UTILS.GetCoalitionName(_coalition)]) end local compteur = #AwacsArray for index, _currentAwacsConfigObject in ipairs(AwacsConfig) do local awacsconfig = ParseAwacsConfigJson(_currentAwacsConfigObject) if awacsconfig.enable == true then compteur = compteur + 1 Jtff_log.info( string.format( 'configuration AWACS %s: ', awacsconfig.type ), "AWACS" ) local awacsmission, airwing = GenerateAwacsMission(awacsconfig, true) if awacsmission == nil or airwing == nil then Jtff_log.error( string.format( "Airwing not found for Airbase %s, skipping Awacs Registration", awacsconfig.airbaseName ), "AWACS" ) else AwacsArray[compteur] = { customconfig = awacsconfig, mission = awacsmission, airwing = airwing, } if awacsconfig.autospawn == true then Jtff_log.info( string.format( 'autospawn Awacs %s', awacsconfig.type ), "AWACS" ) TriggerAwacsMission(AwacsArray[compteur]) -- AwacsArray[compteur].airwing:AddMission(TanAwacsArraykersArray[compteur].mission) else Jtff_log.info( string.format( 'OnDemand Awacs %s Registering', awacsconfig.type ), "AWACS" ) end end end end -- ***************************************************************************** -- ** OnDemand Awacs ** -- ********************************************************* function TriggerOnDemandAwacs(type, askedDuration, askedFL, askedSpeed, askedAnchorCoord, askedOrbitHeading,askedOrbitLeg) if (AwacsConfig) then for index, objAwacs in ipairs(AwacsArray) do if (objAwacs.customconfig.type == type) then Jtff_log.debug( string.format( 'OnDemandAwacs : Found type %s Awacs !', type ), "AWACS" ) if (askedAnchorCoord) then objAwacs.customconfig.racetrack.coordinate = askedAnchorCoord:ToStringMGRS() end if (askedFL and askedFL > 0) then objAwacs.customconfig.racetrack.fl = askedFL end if (askedSpeed and askedSpeed > 0) then objAwacs.customconfig.racetrack.speed = askedSpeed end if (askedDuration ~= nil and askedDuration ~= 0) then objAwacs.customconfig.missionmaxduration = askedDuration end if (askedOrbitHeading) then if (askedOrbitLeg) then --heading et Leg demandés objAwacs.customconfig.racetrack.heading = askedOrbitHeading % 360 objAwacs.customconfig.racetrack.leg = math.max(10, askedOrbitLeg) else --heading demandé et leg non demandé objAwacs.customconfig.racetrack.heading = askedOrbitHeading % 360 objAwacs.customconfig.racetrack.leg = 30 end end local oldMission = objAwacs.airwing:GetMissionByID(objAwacs.mission.auftragsnummer) if oldMission then Jtff_log.info( string.format( 'Mission %s already registered to Airwing %s, rerouting now', objAwacs.mission:GetName(), objAwacs.airwing:GetName() ), "AWACS" ) local objOpsGroup = objAwacs.mission:GetOpsGroups()[1] Jtff_log.debug( string.format( 'OpsGroup executing previous mission %s ID=%d is %s', oldMission:GetName(), oldMission.auftragsnummer, objOpsGroup:GetName() ), "AWACS" ) objAwacs.mission, objAwacs.airwing = GenerateAwacsMission(objAwacs.customconfig, false) Jtff_log.debug( string.format( 'Next Mission generated %s ID=%d for airwing %s', objAwacs.mission:GetName(), objAwacs.mission.auftragsnummer, objAwacs.airwing:GetName() ), "AWACS" ) objOpsGroup:AddMission(objAwacs.mission) Jtff_log.debug( string.format( 'Added next Mission %s generated ID=%d to group %s : %d mission(s) active(s)', objAwacs.mission:GetName(), objAwacs.mission.auftragsnummer, objOpsGroup:GetName(), #(objOpsGroup.missionqueue) ), "AWACS" ) -- objAwacs.airwing:AddMission(objAwacs.mission) objAwacs.mission:AssignLegion(objAwacs.airwing) table.insert(objAwacs.airwing.missionqueue, objAwacs.mission) oldMission:SetTime(nil, 5) Jtff_log.debug( string.format( 'Stopped old Mission %s (ID=%d) from group %s : %d mission(s) active(s)', oldMission:GetName(), oldMission.auftragsnummer, objOpsGroup:GetName(), #(objOpsGroup.missionqueue) ), "AWACS" ) else Jtff_log.info( string.format( 'Mission %s not registered to Airwing %s, spawning now', objAwacs.mission:GetName(), objAwacs.airwing:GetName() ), "AWACS" ) objAwacs.mission, objAwacs.airwing = GenerateAwacsMission(objAwacs.customconfig, true) objAwacs.airwing:AddMission(objAwacs.mission) end end end end end --local RestrToCoal = nil local AwacsMarkHandler = {} function AwacsMarkHandler:onEvent(event) local awacsCmdSymbol = "-" if event.id == 25 then --trigger.action.outText(" ", 0, true) elseif (event.id == 27 and string.find(event.text, awacsCmdSymbol)) then --if (event.coalition == RestrToCoal or RestrToCoal == nil) then local full local remString local cmd local param1 local param1Start local param2 local param2Start local param3 local param3Start local param4 local param4Start local param5 local param5Start local param6 local param6Start local mcoord = COORDINATE:New(event.pos.x, event.pos.y, event.pos.z) full = string.sub(event.text, 2) if (string.find(full, awacsCmdSymbol)) then param1Start = string.find(full, awacsCmdSymbol) cmd = string.sub(full, 0, param1Start-1) remString = string.sub(full, param1Start+1) if (string.find(remString, awacsCmdSymbol)) then param2Start = string.find(remString, awacsCmdSymbol) param1 = string.sub(remString, 0, param2Start-1) remString = string.sub(remString, param2Start+1) if string.find(remString, awacsCmdSymbol) then param3Start = string.find(remString, awacsCmdSymbol) param2 = string.sub(remString, 0, param3Start-1) remString = string.sub(remString, param3Start+1) if string.find(remString, awacsCmdSymbol) then param4Start = string.find(remString, awacsCmdSymbol) param3 = string.sub(remString, 0, param4Start-1) remString = string.sub(remString, param4Start+1) if string.find(remString, awacsCmdSymbol) then param5Start = string.find(remString, awacsCmdSymbol) param4 = string.sub(remString, 0, param5Start-1) remString = string.sub(remString, param5Start+1) if string.find(remString, awacsCmdSymbol) then param6Start = string.find(remString, awacsCmdSymbol) param5 = string.sub(remString, 0, param6Start-1) param6 = string.sub(remString, param6Start+1) else param5 = remString end else param4 = remString end else param3 = remString end else param2 = remString end else param1 = remString end else cmd = full end if Log_levels[JTFF_LOGLEVEL] <= Log_levels['debug'] then trigger.action.outText("Full Text = " .. full, 10) trigger.action.outText("Command = " .. cmd, 10) if param1 ~= nil then trigger.action.outText("type = " .. param1, 10) end if param2 ~= nil then trigger.action.outText("Duration = " .. param2, 10) end if param3 ~= nil then trigger.action.outText("FlightLevel = " .. param3, 10) end if param4 ~= nil then trigger.action.outText("Speed = " .. param4, 10) end if param5 ~= nil then trigger.action.outText("OrbitHeading = " .. param5, 10) end if param6 ~= nil then trigger.action.outText("OrbitLeg = " .. param6, 10) end end if string.find(cmd, "awacs") then if Log_levels[JTFF_LOGLEVEL] <= Log_levels['debug'] then trigger.action.outText("DEBUG: On Demand Awacs Started!", 10) end TriggerOnDemandAwacs( param1, tonumber(param2), tonumber(param3), tonumber(param4), mcoord, tonumber(param5), tonumber(param6) ) end end end world.addEventHandler(AwacsMarkHandler)