This commit is contained in:
Frank
2020-09-07 00:42:29 +02:00
parent 18e192b235
commit 7b8db597ef
5 changed files with 283 additions and 362 deletions

View File

@@ -141,18 +141,6 @@ function ARMYGROUP:SetPatrolAdInfinitum(switch)
return self return self
end end
--- Set default cruise speed. This is the speed a group will take by default if no speed is specified explicitly.
-- @param #ARMYGROUP self
-- @param #number Speed Speed in knots. Default 70% of max speed.
-- @return #ARMYGROUP self
function ARMYGROUP:SetSpeedCruise(Speed)
self.speedCruise=Speed and UTILS.KnotsToKmph(Speed) or self.speedMax*0.7
return self
end
--- Get coordinate of the closest road. --- Get coordinate of the closest road.
-- @param #ARMYGROUP self -- @param #ARMYGROUP self
-- @return Core.Point#COORDINATE Coordinate of a road closest to the group. -- @return Core.Point#COORDINATE Coordinate of a road closest to the group.
@@ -212,7 +200,26 @@ function ARMYGROUP:AddTaskAttackGroup(TargetGroup, WeaponExpend, WeaponType, Clo
end end
--- Check if the group is currently holding its positon.
-- @param #ARMYGROUP self
-- @return #boolean If true, group was ordered to hold.
function ARMYGROUP:IsHolding()
return self:Is("Holding")
end
--- Check if the group is currently cruising.
-- @param #ARMYGROUP self
-- @return #boolean If true, group cruising.
function ARMYGROUP:IsCruising()
return self:Is("Cruising")
end
--- Check if the group is currently on a detour.
-- @param #ARMYGROUP self
-- @return #boolean If true, group is on a detour
function ARMYGROUP:IsOnDetour()
return self:Is("OnDetour")
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Status -- Status
@@ -223,10 +230,10 @@ end
function ARMYGROUP:onbeforeStatus(From, Event, To) function ARMYGROUP:onbeforeStatus(From, Event, To)
if self:IsDead() then if self:IsDead() then
self:I(self.lid..string.format("Onbefore Status DEAD ==> false")) self:T(self.lid..string.format("Onbefore Status DEAD ==> false"))
return false return false
elseif self:IsStopped() then elseif self:IsStopped() then
self:I(self.lid..string.format("Onbefore Status STOPPED ==> false")) self:T(self.lid..string.format("Onbefore Status STOPPED ==> false"))
return false return false
end end
@@ -251,27 +258,36 @@ function ARMYGROUP:onafterStatus(From, Event, To)
self:_CheckDetectedUnits() self:_CheckDetectedUnits()
end end
-- Update position etc.
self:_UpdatePosition()
-- Current heading and position of the carrier. -- Check if group got stuck.
local hdg=self:GetHeading() self:_CheckStuck()
local pos=self:GetCoordinate()
local speed=self.group:GetVelocityKNOTS()
if self.verbose>=1 then
-- Get number of tasks and missions. -- Get number of tasks and missions.
local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks() local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks()
local nMissions=self:CountRemainingMissison() local nMissions=self:CountRemainingMissison()
-- Info text. local roe=self:GetROE()
local text=string.format("%s: Wp=%d/%d-->%d Speed=%.1f (%d) Heading=%03d ROE=%d Alarm=%d Formation=%s Tasks=%d Missions=%d", local alarm=self:GetAlarmstate()
fsmstate, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), speed, UTILS.MpsToKnots(self.speedWp or 0), hdg, self.option.ROE, self.option.Alarm, self.option.Formation, nTaskTot, nMissions) local speed=UTILS.MpsToKnots(self.velocity)
self:I(self.lid..text) local speedEx=UTILS.MpsToKnots(self:GetExpectedSpeed())
local formation=self.option.Formation
-- Info text.
local text=string.format("%s: Wp=%d/%d-->%d Speed=%.1f (%d) Heading=%03d ROE=%d Alarm=%d Formation=%s Tasks=%d Missions=%d",
fsmstate, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), speed, speedEx, self.heading, roe, alarm, formation, nTaskTot, nMissions)
self:I(self.lid..text)
end
else else
-- Info text. -- Info text.
local text=string.format("State %s: Alive=%s", fsmstate, tostring(self:IsAlive())) local text=string.format("State %s: Alive=%s", fsmstate, tostring(self:IsAlive()))
self:I(self.lid..text) self:T2(self.lid..text)
end end
@@ -305,19 +321,6 @@ function ARMYGROUP:onafterElementSpawned(From, Event, To, Element)
end end
--- On after "ElementDead" event.
-- @param #ARMYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #ARMYGROUP.Element Element The group element.
function ARMYGROUP:onafterElementDead(From, Event, To, Element)
self:T(self.lid..string.format("Element dead %s.", Element.name))
-- Set element status.
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD)
end
--- On after "Spawned" event. --- On after "Spawned" event.
-- @param #ARMYGROUP self -- @param #ARMYGROUP self
-- @param #string From From state. -- @param #string From From state.
@@ -326,10 +329,8 @@ end
function ARMYGROUP:onafterSpawned(From, Event, To) function ARMYGROUP:onafterSpawned(From, Event, To)
self:T(self.lid..string.format("Group spawned!")) self:T(self.lid..string.format("Group spawned!"))
-- TODO -- Update position.
self.traveldist=0 self:_UpdatePosition()
self.traveltime=timer.getAbsTime()
self.position=self:GetCoordinate()
if self.ai then if self.ai then
@@ -369,9 +370,6 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation)
-- Update route from this waypoint number onwards. -- Update route from this waypoint number onwards.
n=n or self:GetWaypointIndexNext(self.adinfinitum) n=n or self:GetWaypointIndexNext(self.adinfinitum)
-- Debug info.
--self:I(self.lid..string.format("FF Update route n=%d", n))
-- Update waypoint tasks, i.e. inject WP tasks into waypoint table. -- Update waypoint tasks, i.e. inject WP tasks into waypoint table.
self:_UpdateWaypointTasks(n) self:_UpdateWaypointTasks(n)
@@ -428,8 +426,6 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation)
end end
if wp.roaddist>100 and wp.action==ENUMS.Formation.Vehicle.OnRoad then if wp.roaddist>100 and wp.action==ENUMS.Formation.Vehicle.OnRoad then
env.info("FF Adding ON road waypoint")
--wp.roadcoord:MarkToAll("Added Road waypoint")
-- Waypoint is actually off road! -- Waypoint is actually off road!
wp.action=ENUMS.Formation.Vehicle.OffRoad wp.action=ENUMS.Formation.Vehicle.OffRoad
@@ -439,17 +435,8 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation)
table.insert(waypoints, wproad) table.insert(waypoints, wproad)
end end
--if wp.formation==ENUMS.Formation.Vehicle.OnRoad and wp.action~=ENUMS.Formation.Vehicle.OnRoad then --and not self.formationPerma~=ENUMS.Formation.Vehicle.OnRoad then
--[[
if wp.action==ENUMS.Formation.Vehicle.OnRoad and wp.roaddist>100 then
env.info("FF Adding ON road waypoint")
local wproad=wp.roadcoord:WaypointGround(wp.speed, ENUMS.Formation.Vehicle.OnRoad)
table.insert(waypoints, wproad)
end
]]
-- Debug info. -- Debug info.
self:I(string.format("WP %d %s: Speed=%d m/s, alt=%d m, Action=%s", i, wp.type, wp.speed, wp.alt, wp.action)) self:T(string.format("WP %d %s: Speed=%d m/s, alt=%d m, Action=%s", i, wp.type, wp.speed, wp.alt, wp.action))
-- Add waypoint. -- Add waypoint.
table.insert(waypoints, wp) table.insert(waypoints, wp)
@@ -463,7 +450,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation)
if #waypoints>2 then if #waypoints>2 then
self:I(self.lid..string.format("Updateing route: WP %d-->%d-->%d (#%d), Speed=%.1f knots, Formation=%s", self:T(self.lid..string.format("Updateing route: WP %d-->%d-->%d (#%d), Speed=%.1f knots, Formation=%s",
self.currentwp, n, #self.waypoints, #waypoints-2, UTILS.MpsToKnots(self.speedWp), tostring(self.option.Formation))) self.currentwp, n, #self.waypoints, #waypoints-2, UTILS.MpsToKnots(self.speedWp), tostring(self.option.Formation)))
-- Route group to all defined waypoints remaining. -- Route group to all defined waypoints remaining.
@@ -552,31 +539,6 @@ function ARMYGROUP:onafterCruise(From, Event, To, Speed, Formation)
end end
--- On after "Dead" event.
-- @param #ARMYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function ARMYGROUP:onafterDead(From, Event, To)
self:I(self.lid..string.format("Group dead!"))
-- Delete waypoints so they are re-initialized at the next spawn.
self.waypoints=nil
self.groupinitialized=false
-- Cancel all mission.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
self:MissionCancel(mission)
mission:GroupDead(self)
end
-- Stop
self:Stop()
end
--- On after Start event. Starts the ARMYGROUP FSM and event handlers. --- On after Start event. Starts the ARMYGROUP FSM and event handlers.
-- @param #ARMYGROUP self -- @param #ARMYGROUP self
-- @param #string From From state. -- @param #string From From state.
@@ -584,25 +546,14 @@ end
-- @param #string To To state. -- @param #string To To state.
function ARMYGROUP:onafterStop(From, Event, To) function ARMYGROUP:onafterStop(From, Event, To)
-- Check if group is still alive.
if self:IsAlive() then
-- Destroy group. No event is generated.
self.group:Destroy(false)
end
-- Handle events: -- Handle events:
self:UnHandleEvent(EVENTS.Birth) self:UnHandleEvent(EVENTS.Birth)
self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.Dead)
self:UnHandleEvent(EVENTS.RemoveUnit) self:UnHandleEvent(EVENTS.RemoveUnit)
-- Stop check timers. -- Call OPSGROUP function.
self.timerCheckZone:Stop() self:GetParent(self).onafterStop(self, From, Event, To)
self.timerQueueUpdate:Stop()
-- Stop FSM scheduler.
self.CallScheduler:Clear()
self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from database.")
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -662,8 +613,8 @@ function ARMYGROUP:OnEventDead(EventData)
local element=self:GetElementByName(unitname) local element=self:GetElementByName(unitname)
if element then if element then
self:I(self.lid..string.format("EVENT: Element %s dead ==> dead", element.name)) self:T(self.lid..string.format("EVENT: Element %s dead ==> destroyed", element.name))
self:ElementDead(element) self:ElementDestroyed(element)
end end
end end
@@ -685,7 +636,7 @@ function ARMYGROUP:OnEventRemoveUnit(EventData)
local element=self:GetElementByName(unitname) local element=self:GetElementByName(unitname)
if element then if element then
self:I(self.lid..string.format("EVENT: Element %s removed ==> dead", element.name)) self:T(self.lid..string.format("EVENT: Element %s removed ==> dead", element.name))
self:ElementDead(element) self:ElementDead(element)
end end
@@ -735,18 +686,8 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation
waypoint.roaddist=1000*1000 --1000 km. waypoint.roaddist=1000*1000 --1000 km.
end end
--[[
if waypoint.roaddist>100 and waypoint.action==ENUMS.Formation.Vehicle.OnRoad then
waypoint.formation=ENUMS.Formation.Vehicle.OnRoad
waypoint.action=ENUMS.Formation.Vehicle.OffRoad
else
waypoint.formation=waypoint.action
end
]]
-- Debug info. -- Debug info.
self:I(self.lid..string.format("Adding waypoint UID=%d (index=%d), Speed=%.1f knots, Dist2Road=%d m, Action=%s", waypoint.uid, wpnumber, Speed, waypoint.roaddist, waypoint.action)) self:T(self.lid..string.format("Adding waypoint UID=%d (index=%d), Speed=%.1f knots, Dist2Road=%d m, Action=%s", waypoint.uid, wpnumber, Speed, waypoint.roaddist, waypoint.action))
-- Update route. -- Update route.
if Updateroute==nil or Updateroute==true then if Updateroute==nil or Updateroute==true then
@@ -836,18 +777,20 @@ function ARMYGROUP:_InitGroup()
self.actype=unit:GetTypeName() self.actype=unit:GetTypeName()
-- Debug info. -- Debug info.
local text=string.format("Initialized Army Group %s:\n", self.groupname) if self.verbose>=1 then
text=text..string.format("Unit type = %s\n", self.actype) local text=string.format("Initialized Army Group %s:\n", self.groupname)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Elements = %d\n", #self.elements) text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
text=text..string.format("Waypoints = %d\n", #self.waypoints) text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles) text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))
text=text..string.format("FSM state = %s\n", self:GetState()) text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles)
text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) text=text..string.format("FSM state = %s\n", self:GetState())
text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) text=text..string.format("Is alive = %s\n", tostring(self:IsAlive()))
self:I(self.lid..text) text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated()))
self:I(self.lid..text)
end
-- Init done. -- Init done.
self.groupinitialized=true self.groupinitialized=true
@@ -885,7 +828,7 @@ function ARMYGROUP:SwitchFormation(Formation, Permanently)
self:__UpdateRoute(-1, nil, nil, Formation) self:__UpdateRoute(-1, nil, nil, Formation)
-- Debug info. -- Debug info.
self:I(self.lid..string.format("Switching formation to %s (permanently=%s)", self.option.Formation, tostring(Permanently))) self:T(self.lid..string.format("Switching formation to %s (permanently=%s)", self.option.Formation, tostring(Permanently)))
end end
@@ -897,6 +840,7 @@ end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -704,6 +704,21 @@ end
-- Status -- Status
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---- Update status.
-- @param #FLIHGTGROUP self
function FLIGHTGROUP:onbeforeStatus(From, Event, To)
if self:IsDead() then
self:T(self.lid..string.format("Onbefore Status DEAD ==> false"))
return false
elseif self:IsStopped() then
self:T(self.lid..string.format("Onbefore Status STOPPED ==> false"))
return false
end
return true
end
--- On after "Status" event. --- On after "Status" event.
-- @param #FLIGHTGROUP self -- @param #FLIGHTGROUP self
-- @param #string From From state. -- @param #string From From state.
@@ -714,6 +729,9 @@ function FLIGHTGROUP:onafterStatus(From, Event, To)
-- FSM state. -- FSM state.
local fsmstate=self:GetState() local fsmstate=self:GetState()
-- Update position.
self:_UpdatePosition()
--- ---
-- Detection -- Detection
--- ---
@@ -752,21 +770,27 @@ function FLIGHTGROUP:onafterStatus(From, Event, To)
end end
--- ---
-- Elements -- Group
--- ---
local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks()
local nMissions=self:CountRemainingMissison()
-- Short info. -- Short info.
if self.verbose>=1 then if self.verbose>=1 then
local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks()
local nMissions=self:CountRemainingMissison()
local text=string.format("Status %s [%d/%d]: Tasks=%d (%d,%d) Curr=%d, Missions=%s, Waypoint=%d/%d, Detected=%d, Home=%s, Destination=%s", local text=string.format("Status %s [%d/%d]: Tasks=%d (%d,%d) Curr=%d, Missions=%s, Waypoint=%d/%d, Detected=%d, Home=%s, Destination=%s",
fsmstate, #self.elements, #self.elements, nTaskTot, nTaskSched, nTaskWP, self.taskcurrent, nMissions, self.currentwp or 0, self.waypoints and #self.waypoints or 0, fsmstate, #self.elements, #self.elements, nTaskTot, nTaskSched, nTaskWP, self.taskcurrent, nMissions, self.currentwp or 0, self.waypoints and #self.waypoints or 0,
self.detectedunits:Count(), self.homebase and self.homebase:GetName() or "unknown", self.destbase and self.destbase:GetName() or "unknown") self.detectedunits:Count(), self.homebase and self.homebase:GetName() or "unknown", self.destbase and self.destbase:GetName() or "unknown")
self:I(self.lid..text) self:I(self.lid..text)
end end
-- Element status. ---
-- Elements
---
if self.verbose>=2 then if self.verbose>=2 then
local text="Elements:" local text="Elements:"
for i,_element in pairs(self.elements) do for i,_element in pairs(self.elements) do
@@ -801,25 +825,17 @@ function FLIGHTGROUP:onafterStatus(From, Event, To)
-- Distance travelled -- Distance travelled
--- ---
if self.verbose>=3 and self:IsAlive() and self.position then if self.verbose>=3 and self:IsAlive() then
local time=timer.getAbsTime()
-- Current position.
local position=self:GetCoordinate()
-- Travelled distance since last check. -- Travelled distance since last check.
local ds=self.position:Get3DDistance(position) local ds=self.travelds
-- Time interval. -- Time interval.
local dt=time-self.traveltime local dt=self.dTpositionUpdate
-- Speed. -- Speed.
local v=ds/dt local v=ds/dt
-- Add up travelled distance.
self.traveldist=self.traveldist+ds
-- Max fuel time remaining. -- Max fuel time remaining.
local TmaxFuel=math.huge local TmaxFuel=math.huge
@@ -853,10 +869,6 @@ function FLIGHTGROUP:onafterStatus(From, Event, To)
-- Log outut. -- Log outut.
self:I(self.lid..string.format("Travelled ds=%.1f km dt=%.1f s ==> v=%.1f knots. Fuel left for %.1f min", self.traveldist/1000, dt, UTILS.MpsToKnots(v), TmaxFuel/60)) self:I(self.lid..string.format("Travelled ds=%.1f km dt=%.1f s ==> v=%.1f knots. Fuel left for %.1f min", self.traveldist/1000, dt, UTILS.MpsToKnots(v), TmaxFuel/60))
-- Update parameters.
self.traveltime=time
self.position=position
end end
--- ---
@@ -911,8 +923,6 @@ function FLIGHTGROUP:onafterStatus(From, Event, To)
end end
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Events -- Events
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -1390,10 +1400,8 @@ end
function FLIGHTGROUP:onafterSpawned(From, Event, To) function FLIGHTGROUP:onafterSpawned(From, Event, To)
self:T(self.lid..string.format("Flight spawned")) self:T(self.lid..string.format("Flight spawned"))
-- TODO: general routine in opsgroup -- Update position.
self.traveldist=0 self:_UpdatePosition()
self.traveltime=timer.getAbsTime()
self.position=self:GetCoordinate()
if self.ai then if self.ai then
@@ -1577,7 +1585,7 @@ end
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
function FLIGHTGROUP:onafterLandedAt(From, Event, To) function FLIGHTGROUP:onafterLandedAt(From, Event, To)
self:I(self.lid..string.format("Flight landed at")) self:T(self.lid..string.format("Flight landed at"))
end end
@@ -2434,6 +2442,17 @@ function FLIGHTGROUP:onafterStop(From, Event, To)
--self.group:Destroy(false) --self.group:Destroy(false)
end end
-- Handle events:
self:UnHandleEvent(EVENTS.Birth)
self:UnHandleEvent(EVENTS.EngineStartup)
self:UnHandleEvent(EVENTS.Takeoff)
self:UnHandleEvent(EVENTS.Land)
self:UnHandleEvent(EVENTS.EngineShutdown)
self:UnHandleEvent(EVENTS.PilotDead)
self:UnHandleEvent(EVENTS.Ejection)
self:UnHandleEvent(EVENTS.Crash)
self:UnHandleEvent(EVENTS.RemoveUnit)
-- Remove flight from data base. -- Remove flight from data base.
_DATABASE.FLIGHTGROUPS[self.groupname]=nil _DATABASE.FLIGHTGROUPS[self.groupname]=nil

View File

@@ -290,7 +290,7 @@ function NAVYGROUP:CreateTurnIntoWind(starttime, stoptime, speed, uturn, offset)
return self return self
end end
if Tstop<=Tnow then if Tstop<=Tnow then
self:I(string.format("WARNING: Into wind stop time %s already over. Tnow=%s! Input rejected.", UTILS.SecondsToClock(Tstop), UTILS.SecondsToClock(Tnow))) self:E(string.format("WARNING: Into wind stop time %s already over. Tnow=%s! Input rejected.", UTILS.SecondsToClock(Tstop), UTILS.SecondsToClock(Tnow)))
return self return self
end end
@@ -389,10 +389,10 @@ end
function NAVYGROUP:onbeforeStatus(From, Event, To) function NAVYGROUP:onbeforeStatus(From, Event, To)
if self:IsDead() then if self:IsDead() then
self:I(self.lid..string.format("Onbefore Status DEAD ==> false")) self:T(self.lid..string.format("Onbefore Status DEAD ==> false"))
return false return false
elseif self:IsStopped() then elseif self:IsStopped() then
self:I(self.lid..string.format("Onbefore Status STOPPED ==> false")) self:T(self.lid..string.format("Onbefore Status STOPPED ==> false"))
return false return false
end end
@@ -518,19 +518,6 @@ function NAVYGROUP:onafterElementSpawned(From, Event, To, Element)
end end
--- On after "ElementDead" event.
-- @param #NAVYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #NAVYGROUP.Element Element The group element.
function NAVYGROUP:onafterElementDead(From, Event, To, Element)
self:T(self.lid..string.format("Element dead %s.", Element.name))
-- Set element status.
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD)
end
--- On after "Spawned" event. --- On after "Spawned" event.
-- @param #NAVYGROUP self -- @param #NAVYGROUP self
-- @param #string From From state. -- @param #string From From state.
@@ -539,10 +526,8 @@ end
function NAVYGROUP:onafterSpawned(From, Event, To) function NAVYGROUP:onafterSpawned(From, Event, To)
self:T(self.lid..string.format("Group spawned!")) self:T(self.lid..string.format("Group spawned!"))
-- TODO -- Update position.
self.traveldist=0 self:_UpdatePosition()
self.traveltime=timer.getAbsTime()
self.position=self:GetCoordinate()
if self.ai then if self.ai then
@@ -571,9 +556,6 @@ function NAVYGROUP:onafterSpawned(From, Event, To)
end end
-- Get orientation.
self.Corientlast=self.group:GetUnit(1):GetOrientationX()
-- Update route. -- Update route.
self:Cruise() self:Cruise()
@@ -742,7 +724,7 @@ function NAVYGROUP:onafterTurnIntoWind(From, Event, To, IntoWind)
local speed=math.max(IntoWind.Speed-vwind, 2) local speed=math.max(IntoWind.Speed-vwind, 2)
-- Debug info. -- Debug info.
self:I(self.lid..string.format("Steaming into wind: Heading=%03d Speed=%.1f Vwind=%.1f Vtot=%.1f knots, Tstart=%d Tstop=%d", IntoWind.Heading, speed, vwind, speed+vwind, IntoWind.Tstart, IntoWind.Tstop)) self:T(self.lid..string.format("Steaming into wind: Heading=%03d Speed=%.1f Vwind=%.1f Vtot=%.1f knots, Tstart=%d Tstop=%d", IntoWind.Heading, speed, vwind, speed+vwind, IntoWind.Tstart, IntoWind.Tstop))
local distance=UTILS.NMToMeters(1000) local distance=UTILS.NMToMeters(1000)
@@ -840,7 +822,7 @@ function NAVYGROUP:onafterDive(From, Event, To, Depth, Speed)
Depth=Depth or 50 Depth=Depth or 50
self:I(self.lid..string.format("Diving to %d meters", Depth)) self:T(self.lid..string.format("Diving to %d meters", Depth))
self.depth=Depth self.depth=Depth
@@ -888,35 +870,10 @@ end
-- @param #string To To state. -- @param #string To To state.
-- @param #number Distance Distance in meters where obstacle was detected. -- @param #number Distance Distance in meters where obstacle was detected.
function NAVYGROUP:onafterCollisionWarning(From, Event, To, Distance) function NAVYGROUP:onafterCollisionWarning(From, Event, To, Distance)
self:I(self.lid..string.format("Iceberg ahead in %d meters!", Distance or -1)) self:T(self.lid..string.format("Iceberg ahead in %d meters!", Distance or -1))
self.collisionwarning=true self.collisionwarning=true
end end
--- On after "Dead" event.
-- @param #NAVYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function NAVYGROUP:onafterDead(From, Event, To)
self:I(self.lid..string.format("Group dead!"))
-- Delete waypoints so they are re-initialized at the next spawn.
self.waypoints=nil
self.groupinitialized=false
-- Cancel all mission.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
self:MissionCancel(mission)
mission:GroupDead(self)
end
-- Stop
self:Stop()
end
--- On after Start event. Starts the NAVYGROUP FSM and event handlers. --- On after Start event. Starts the NAVYGROUP FSM and event handlers.
-- @param #NAVYGROUP self -- @param #NAVYGROUP self
-- @param #string From From state. -- @param #string From From state.
@@ -924,25 +881,14 @@ end
-- @param #string To To state. -- @param #string To To state.
function NAVYGROUP:onafterStop(From, Event, To) function NAVYGROUP:onafterStop(From, Event, To)
-- Check if group is still alive.
if self:IsAlive() then
-- Destroy group. No event is generated.
self.group:Destroy(false)
end
-- Handle events: -- Handle events:
self:UnHandleEvent(EVENTS.Birth) self:UnHandleEvent(EVENTS.Birth)
self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.Dead)
self:UnHandleEvent(EVENTS.RemoveUnit) self:UnHandleEvent(EVENTS.RemoveUnit)
-- Stop check timers. -- Call OPSGROUP function.
self.timerCheckZone:Stop() self:GetParent(self).onafterStop(self, From, Event, To)
self.timerQueueUpdate:Stop()
-- Stop FSM scheduler.
self.CallScheduler:Clear()
self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from database.")
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -1002,8 +948,8 @@ function NAVYGROUP:OnEventDead(EventData)
local element=self:GetElementByName(unitname) local element=self:GetElementByName(unitname)
if element then if element then
self:I(self.lid..string.format("EVENT: Element %s dead ==> dead", element.name)) self:T(self.lid..string.format("EVENT: Element %s dead ==> destroyed", element.name))
self:ElementDead(element) self:ElementDestroyed(element)
end end
end end
@@ -1025,7 +971,7 @@ function NAVYGROUP:OnEventRemoveUnit(EventData)
local element=self:GetElementByName(unitname) local element=self:GetElementByName(unitname)
if element then if element then
self:I(self.lid..string.format("EVENT: Element %s removed ==> dead", element.name)) self:T(self.lid..string.format("EVENT: Element %s removed ==> dead", element.name))
self:ElementDead(element) self:ElementDead(element)
end end
@@ -1173,18 +1119,20 @@ function NAVYGROUP:_InitGroup()
self.actype=unit:GetTypeName() self.actype=unit:GetTypeName()
-- Debug info. -- Debug info.
local text=string.format("Initialized Navy Group %s:\n", self.groupname) if self.verbose>=1 then
text=text..string.format("Unit type = %s\n", self.actype) local text=string.format("Initialized Navy Group %s:\n", self.groupname)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Elements = %d\n", #self.elements) text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
text=text..string.format("Waypoints = %d\n", #self.waypoints) text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos) text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))
text=text..string.format("FSM state = %s\n", self:GetState()) text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos)
text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) text=text..string.format("FSM state = %s\n", self:GetState())
text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) text=text..string.format("Is alive = %s\n", tostring(self:IsAlive()))
self:I(self.lid..text) text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated()))
self:I(self.lid..text)
end
-- Init done. -- Init done.
self.groupinitialized=true self.groupinitialized=true
@@ -1269,7 +1217,7 @@ function NAVYGROUP:_CheckFreePath(DistanceMax, dx)
local los=LoS(x) local los=LoS(x)
-- Debug message. -- Debug message.
self:I(self.lid..string.format("N=%d: xmin=%.1f xmax=%.1f x=%.1f d=%.3f los=%s", N, xmin, xmax, x, d, tostring(los))) self:T2(self.lid..string.format("N=%d: xmin=%.1f xmax=%.1f x=%.1f d=%.3f los=%s", N, xmin, xmax, x, d, tostring(los)))
if los and d<=eps then if los and d<=eps then
return x return x
@@ -1291,68 +1239,6 @@ function NAVYGROUP:_CheckFreePath(DistanceMax, dx)
return check() return check()
end end
--- Check for possible collisions between two coordinates.
-- @param #NAVYGROUP self
-- @param Core.Point#COORDINATE coordto Coordinate to which the collision is check.
-- @param Core.Point#COORDINATE coordfrom Coordinate from which the collision is check.
-- @return #boolean If true, surface type ahead is not deep water.
-- @return #number Max free distance in meters.
function NAVYGROUP:_CheckCollisionCoord(coordto, coordfrom)
-- Increment in meters.
local dx=100
-- From coordinate. Default 500 in front of the carrier.
local d=0
if coordfrom then
d=0
else
d=250
coordfrom=self:GetCoordinate():Translate(d, self:GetHeading())
end
-- Distance between the two coordinates.
local dmax=coordfrom:Get2DDistance(coordto)
-- Direction.
local direction=coordfrom:HeadingTo(coordto)
-- Scan path between the two coordinates.
local clear=true
while d<=dmax do
-- Check point.
local cp=coordfrom:Translate(d, direction)
-- Check if surface type is water.
if not cp:IsSurfaceTypeWater() then
-- Debug mark points.
if self.Debug or true then
local st=cp:GetSurfaceType()
cp:MarkToAll(string.format("Collision check surface type %d", st))
end
-- Collision WARNING!
clear=false
break
end
-- Increase distance.
d=d+dx
end
local text=""
if clear then
text=string.format("Path into direction %03d° is clear for the next %.1f NM.", direction, UTILS.MetersToNM(d))
else
text=string.format("Detected obstacle at distance %.1f NM into direction %03d°.", UTILS.MetersToNM(d), direction)
end
self:T(self.lid..text)
return not clear, d
end
--- Check if group is turning. --- Check if group is turning.
-- @param #NAVYGROUP self -- @param #NAVYGROUP self
function NAVYGROUP:_CheckTurning() function NAVYGROUP:_CheckTurning()
@@ -1365,7 +1251,7 @@ function NAVYGROUP:_CheckTurning()
local vNew=self.orientX --unit:GetOrientationX() local vNew=self.orientX --unit:GetOrientationX()
-- Last orientation from 30 seconds ago. -- Last orientation from 30 seconds ago.
local vLast=self.orientXLast --self.Corientlast or vNew local vLast=self.orientXLast
-- We only need the X-Z plane. -- We only need the X-Z plane.
vNew.y=0 ; vLast.y=0 vNew.y=0 ; vLast.y=0
@@ -1373,9 +1259,6 @@ function NAVYGROUP:_CheckTurning()
-- Angle between current heading and last time we checked ~30 seconds ago. -- Angle between current heading and last time we checked ~30 seconds ago.
local deltaLast=math.deg(math.acos(UTILS.VecDot(vNew,vLast)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vLast))) local deltaLast=math.deg(math.acos(UTILS.VecDot(vNew,vLast)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vLast)))
-- Last orientation becomes new orientation
--self.Corientlast=vNew
-- Carrier is turning when its heading changed by at least two degrees since last check. -- Carrier is turning when its heading changed by at least two degrees since last check.
local turning=math.abs(deltaLast)>=2 local turning=math.abs(deltaLast)>=2
@@ -1399,33 +1282,6 @@ function NAVYGROUP:_CheckTurning()
end end
--- Check if group got stuck.
-- @param #NAVYGROUP self
function NAVYGROUP:_CheckStuck()
if self:IsHolding() then
return
end
local holdtime=0
if self.holdtimestamp then
holdtime=timer.getTime()-self.holdtimestamp
end
local ExpectedSpeed=self:GetExpectedSpeed()
local speed=self:GetVelocity()
if speed<0.5 and ExpectedSpeed>0 then
if not self.holdtimestamp then
self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected", speed, ExpectedSpeed))
self.holdtimestamp=timer.getTime()
end
end
end
--- Check queued turns into wind. --- Check queued turns into wind.
-- @param #NAVYGROUP self -- @param #NAVYGROUP self

View File

@@ -54,8 +54,14 @@
-- @field #number Ndestroyed Number of destroyed units. -- @field #number Ndestroyed Number of destroyed units.
-- --
-- @field Core.Point#COORDINATE coordinate Current coordinate. -- @field Core.Point#COORDINATE coordinate Current coordinate.
-- @field Core.Point#COORDINATE position Position of the group at last status check. --
-- @field #number traveldist Distance traveled in meters. This is a lower bound! -- @field DCS#Vec3 position Position of the group at last status check.
-- @field DCS#Vec3 positionLast Backup of last position vec to monitor changes.
-- @field #number heading Heading of the group at last status check.
-- @field #number headingLast Backup of last heading to monitor changes.
-- @field DCS#Vec3 orientX Orientation at last status check.
-- @field DCS#Vec3 orientXLast Backup of last orientation to monitor changes.
-- @field #number traveldist Distance traveled in meters. This is a lower bound.
-- @field #number traveltime Time. -- @field #number traveltime Time.
-- --
-- @field Core.Astar#ASTAR Astar path finding. -- @field Core.Astar#ASTAR Astar path finding.
@@ -590,7 +596,7 @@ function OPSGROUP:Destroy(Delay)
if DCSGroup then if DCSGroup then
self:I(self.lid.."Destroying group") self:T(self.lid.."Destroying group")
-- Destroy DCS group. -- Destroy DCS group.
DCSGroup:destroy() DCSGroup:destroy()
@@ -650,8 +656,9 @@ end
--- Get current coordinate of the group. --- Get current coordinate of the group.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #boolean NewObject Create a new coordiante object.
-- @return Core.Point#COORDINATE The coordinate (of the first unit) of the group. -- @return Core.Point#COORDINATE The coordinate (of the first unit) of the group.
function OPSGROUP:GetCoordinate() function OPSGROUP:GetCoordinate(NewObject)
local vec3=self:GetVec3() local vec3=self:GetVec3()
@@ -663,7 +670,11 @@ function OPSGROUP:GetCoordinate()
self.coordinate.y=vec3.y self.coordinate.y=vec3.y
self.coordinate.z=vec3.z self.coordinate.z=vec3.z
return self.coordinate if NewObject then
local coord=COORDINATE:NewFromCoordinate(self.coordinate)
else
return self.coordinate
end
else else
self:E(self.lid.."WARNING: Group is not alive. Cannot get coordinate!") self:E(self.lid.."WARNING: Group is not alive. Cannot get coordinate!")
end end
@@ -725,6 +736,41 @@ function OPSGROUP:GetHeading()
return nil return nil
end end
--- Get current orientation of the first unit in the group.
-- @param #OPSGROUP self
-- @return DCS#Vec3 Orientation X parallel to where the "nose" is pointing.
-- @return DCS#Vec3 Orientation Y pointing "upwards".
-- @return DCS#Vec3 Orientation Z perpendicular to the "nose".
function OPSGROUP:GetOrientation()
if self:IsExist() then
local unit=self:GetDCSUnit()
if unit then
local pos=unit:getPosition()
return pos.x, pos.y, pos.z
end
else
self:E(self.lid.."WARNING: Group does not exist. Cannot get orientation!")
end
return nil
end
--- Get current orientation of the first unit in the group.
-- @param #OPSGROUP self
-- @return DCS#Vec3 Orientation X parallel to where the "nose" is pointing.
function OPSGROUP:GetOrientationX()
local X,Y,Z=self:GetOrientation()
return X
end
--- Check if task description is unique. --- Check if task description is unique.
@@ -2658,7 +2704,7 @@ end
-- @param #string To To state. -- @param #string To To state.
-- @param #OPSGROUP.Element Element The flight group element. -- @param #OPSGROUP.Element Element The flight group element.
function OPSGROUP:onafterElementDestroyed(From, Event, To, Element) function OPSGROUP:onafterElementDestroyed(From, Event, To, Element)
self:I(self.lid..string.format("Element destroyed %s", Element.name)) self:T(self.lid..string.format("Element destroyed %s", Element.name))
-- Cancel all missions. -- Cancel all missions.
for _,_mission in pairs(self.missionqueue) do for _,_mission in pairs(self.missionqueue) do
@@ -2683,7 +2729,7 @@ end
-- @param #string To To state. -- @param #string To To state.
-- @param #OPSGROUP.Element Element The flight group element. -- @param #OPSGROUP.Element Element The flight group element.
function OPSGROUP:onafterElementDead(From, Event, To, Element) function OPSGROUP:onafterElementDead(From, Event, To, Element)
self:I(self.lid..string.format("Element dead %s", Element.name)) self:T(self.lid..string.format("Element dead %s", Element.name))
-- Set element status. -- Set element status.
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD) self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD)
@@ -2695,7 +2741,7 @@ end
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
function OPSGROUP:onafterDead(From, Event, To) function OPSGROUP:onafterDead(From, Event, To)
self:T(self.lid..string.format("Flight dead!")) self:T(self.lid..string.format("Group dead!"))
-- Delete waypoints so they are re-initialized at the next spawn. -- Delete waypoints so they are re-initialized at the next spawn.
self.waypoints=nil self.waypoints=nil
@@ -2710,8 +2756,8 @@ function OPSGROUP:onafterDead(From, Event, To)
end end
-- Stop -- Stop in a sec.
self:Stop() self:__Stop(-1)
end end
--- On after "Stop" event. --- On after "Stop" event.
@@ -2721,17 +2767,6 @@ end
-- @param #string To To state. -- @param #string To To state.
function OPSGROUP:onafterStop(From, Event, To) function OPSGROUP:onafterStop(From, Event, To)
-- Handle events:
self:UnHandleEvent(EVENTS.Birth)
self:UnHandleEvent(EVENTS.EngineStartup)
self:UnHandleEvent(EVENTS.Takeoff)
self:UnHandleEvent(EVENTS.Land)
self:UnHandleEvent(EVENTS.EngineShutdown)
self:UnHandleEvent(EVENTS.PilotDead)
self:UnHandleEvent(EVENTS.Ejection)
self:UnHandleEvent(EVENTS.Crash)
self:UnHandleEvent(EVENTS.RemoveUnit)
-- Stop check timers. -- Stop check timers.
self.timerCheckZone:Stop() self.timerCheckZone:Stop()
self.timerQueueUpdate:Stop() self.timerQueueUpdate:Stop()
@@ -3915,26 +3950,43 @@ end
--- Check if all elements of the group have the same status (or are dead). --- Check if all elements of the group have the same status (or are dead).
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #string unitname Name of unit. -- @return #OPSGROUP self
function OPSGROUP:_UpdatePosition() function OPSGROUP:_UpdatePosition()
if self:IsAlive() then if self:IsAlive() then
self.positionLast=self.position or self:GetCoordinate() -- Backup last state to monitor differences.
self.positionLast=self.position or self:GetVec3()
self.headingLast=self.heading or self:GetHeading() self.headingLast=self.heading or self:GetHeading()
self.orientXLast=self.orientX or self.group:GetUnit(1):GetOrientationX() self.orientXLast=self.orientX or self:GetOrientationX()
self.velocityLast=self.velocity or self.group:GetVelocityMPS() self.velocityLast=self.velocity or self.group:GetVelocityMPS()
self.position=self:GetCoordinate() -- Current state.
self.position=self:GetVec3()
self.heading=self:GetHeading() self.heading=self:GetHeading()
self.orientX=self.group:GetUnit(1):GetOrientationX() self.orientX=self:GetOrientationX()
self.velocity=self.group:GetVelocityMPS() self.velocity=self:GetVelocity()
self.dTpositionUpdate=self.TpositionUpdate and self.TpositionUpdate-timer.getAbsTime() or 0 -- Update time.
self.TpositionUpdate=timer.getAbsTime() local Tnow=timer.getTime()
self.dTpositionUpdate=self.TpositionUpdate and Tnow-self.TpositionUpdate or 0
self.TpositionUpdate=Tnow
if not self.traveldist then
self.traveldist=0
end
self.travelds=UTILS.VecNorm(UTILS.VecSubstract(self.position, self.positionLast))
-- Add up travelled distance.
self.traveldist=self.traveldist+self.travelds
env.info(string.format("FF Traveled %.1f m", self.traveldist))
end end
return self
end end
--- Check if all elements of the group have the same status (or are dead). --- Check if all elements of the group have the same status (or are dead).
@@ -4483,6 +4535,56 @@ function OPSGROUP:_MissileCategoryName(categorynumber)
return cat return cat
end end
--- Check if group got stuck.
-- @param #OPSGROUP self
function OPSGROUP:_CheckStuck()
-- Holding means we are not stuck.
if self:IsHolding() then
return
end
-- Current time.
local Tnow=timer.getTime()
-- Expected speed in m/s.
local ExpectedSpeed=self:GetExpectedSpeed()
-- Current speed in m/s.
local speed=self:GetVelocity()
-- Check speed.
if speed<0.5 then
if ExpectedSpeed>0 and not self.stuckTimestamp then
self:T2(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected", speed, ExpectedSpeed))
self.stuckTimestamp=Tnow
self.stuckVec3=self:GetVec3()
end
else
-- Moving (again).
self.stuckTimestamp=nil
end
-- Somehow we are not moving...
if self.stuckTimestamp then
-- Time we are holding.
local holdtime=Tnow-self.stuckTimestamp
if holdtime>=5*60 then
self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime))
--TODO: Stuck event!
end
end
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -62,7 +62,7 @@
-- @field #SQUADRON -- @field #SQUADRON
SQUADRON = { SQUADRON = {
ClassName = "SQUADRON", ClassName = "SQUADRON",
verbose = 3, verbose = 0,
lid = nil, lid = nil,
name = nil, name = nil,
templatename = nil, templatename = nil,