--INTRUCTIONS: -- Thie file contains code for 2 tests: a simple evet generation test (which works) -- and a complex event generation mechanism that fails to work. -- -- The complex code fails ONLY when the function triggered by an event, deletes the event during execution. -- remove the line that does that delete, adn the code works. -- -- CleanupScriptEvent = function( eventName ) local e,p = pcall(ScenEdit_GetEvent,eventName); if(e == true and p ~=nil) then --remove all the actions connected to this event! for k,v in pairs(p.actions) do local aName = '' if (v.LuaScript ~= nil) then aName = v.LuaScript.Description elseif (v.ChangeMissionStatus ~= nil) then aName = v.ChangeMissionStatus.Description elseif (v.NewStatus ~= nil) then aName = v.NewStatus.Description elseif (v.EndScenario ~= nil) then aName = v.EndScenario.Description elseif (v.Message ~= nil) then aName = v.Message.Description elseif (v.Text ~= nil) then aName = v.Text.Description elseif (v.Points ~= nil) then aName = v.Points.Description elseif (v.SideID ~= nil) then aName = v.SideID.Description elseif (v.TeleportInArea ~= nil) then aName = v.TeleportInArea.Description else aName = nil end if (aName ~= nil) then --print('disconnecting action: '..aName..'from event: '..eventName) ScenEdit_SetEventAction(eventName,{mode='remove',name=aName}) --print('destroying action: '..aName) ScenEdit_SetAction({mode='remove',name=aName}) end end --remove all the triggers connected to this event! for k,v in pairs(p.triggers) do local tName = '' if (v.Points ~= nil) then tName = v.Points.Description elseif (v.RandomTime) then tName = v.RandomTime.Description elseif (v.RegularTime) then tName = v.RegularTime.Description elseif (v.ScenEnded) then tName = v.ScenEnded.Description elseif (v.ScenLoaded) then tName = v.ScenLoaded.Description elseif (v.Time) then tName = v.Time.Description elseif (v.UnitDamaged) then tName = v.UnitDamaged.Description elseif (v.UnitDestroyed) then tName = v.UnitDestroyed.Description elseif (v.UnitDetected) then tName = v.UnitDetected.Description elseif (v.UnitEmissions) then tName = v.UnitEmissions.Description elseif (v.UnitEntersArea) then tName = v.UnitEntersArea.Description elseif (v.UnitRemainsInArea) then tName = v.UnitRemainsInArea.Description elseif (v.UnitBaseStatus) then tName = v.UnitBaseStatus.Description elseif (v.UnitCargoMoved) then tName = v.UnitCargoMoved.Description else tName = nil end if (tName ~= nil) then --print('disconnecting tigger: '..tName..'from event: '..eventName) ScenEdit_SetEventTrigger(eventName,{mode='remove',name=tName}) --print('destroying tiggger: '..tName) ScenEdit_SetTrigger({mode='remove',name=tName}) end end --remove all the conditions connected to this event! for k,v in pairs(p.conditions) do local cName = '' if (v.LuaScript~= nil) then cName = v.LuaScript.Description elseif (v.ScenHasStarted) then cName = v.ScenHasStarted.Description elseif (v.SidePosture) then cName = v.SidePosture.Description else cName = nil end if (cName ~= nil) then --print('disconnecting condition: '..cName..'from event: '..eventName) ScenEdit_SetEventCondition(eventName,{mode='remove',name=cName}) --print('destroying condition: '..cName) ScenEdit_SetCondition({mode='remove',name=cName}) end end --remove the event ScenEdit_SetEvent(eventName, {mode='remove'}) end end getEventName = function(guid) return guid..'_start_movement' end splitString = function(str, pat) local t = {} local fpat = "(.-)" .. pat if pat == '.' then fpat = "(.-)%." --fix for when pattern is just a dot. end local last_end = 1 local s, e, cap = string.find(str, fpat, last_end, false) while s do if s ~= 1 or cap ~= "" then table.insert(t,cap) end last_end = e+1 s, e, cap = string.find(str, fpat, last_end, false) end if last_end <= #str then cap = str:sub(last_end) table.insert(t, cap) end return t end --return the time string for urrent time plus some specified number of seconds. AddSecondsFromNow = function(seconds) --create and set the trigger (time) local time = ScenEdit_CurrentTime() local offSet = 62135596801 --number of seconds from 01-01-0001 to 01-01-1970 local newTime = (time + offSet + seconds)*10000000 local retTime = string.format("%18.0f",newTime) return retTime end --returns the start times to use for event generation... EventTimes = function(timeParam) local delaySecs = 0 if timeParam ~= nil and type(timeParam == 'string') then local sTimeData = splitString(timeParam,":") if (sTimeData ~= nil) then if type(sTimeData) == 'table' and #sTimeData == 3 then local sHours = tonumber(sTimeData[1]) local sMinutes = tonumber(sTimeData[2]) local sSeconds = tonumber(sTimeData[3]) if (sHours and sMinutes and sSeconds) then local curTimeStr = ScenEdit_CurrentLocalTime() local curTimeData = splitString(curTimeStr,":") local curHours = tonumber(curTimeData[1]) local curMinutes = tonumber(curTimeData[2]) local curSeconds = tonumber(curTimeData[3]) local curTime = curHours * 3600 + curMinutes * 60 + curSeconds local sTime = sHours * 3600 + sMinutes * 60 + sSeconds if (curTime < sTime) then -- start time is later today... delaySecs = (sTime - curTime) elseif (curTime > sTime) then --start time is in 'past' meaning it is tomorrow. delaySecs = (sTime+24*3600 - curTime) end end end end end return AddSecondsFromNow(delaySecs),AddSecondsFromNow(delaySecs + 60) end --this function simply generates a Time event that tells the ground unit to move. DisplaceUnit = function(uGuid, offsetLat, offsetLon, timeString) unit = ScenEdit_GetUnit({guid=uGuid}) local destLat = unit.latitude + offsetLat local destLon = unit.longitude + offsetLon local eName = getEventName(uGuid) --set the course for the unit. (actually starts it moving. local script = "ScenEdit_SetUnit({guid = '"..unit.guid.."',".. "\r\n".." course = {[1] = {TypeOf = 'ManualPlottedCourseWaypoint', ".. "\r\n".." latitude = "..destLat..", ".. "\r\n".." longitude = "..destLon.."}},".. "\r\n".." throttle = 'Full'}) " --compute the time for the event local eTime, lTime = EventTimes(timeString) --create the first follow-on event ScenEdit_SetEvent(eName, {mode='add', isRepeatable = true, isActive= active}) ScenEdit_SetTrigger({name=eName, Mode='add', type='Time', Time=eTime}) ScenEdit_SetEventTrigger(eName, {mode = 'add', name = eName}) --create and set the action (execute thes script passed in) ScenEdit_SetAction({mode='add', type='LuaScript', name=eName, ScriptText=script }) ScenEdit_SetEventAction(eName, {mode = 'add', name = eName}) end --sets up data for all tests SetupTestData = function() ScenEdit_AddSide({name = 'US'}) ScenEdit_AddUnit({guid='test-0001', side = 'US', name = 'TEST UNIT 1', type = 'Facility', dbid=2784, latitude=19.004100276608, longitude=110.24596193077, }) ScenEdit_AddUnit({guid='test-0002', side = 'US', name = 'TEST UNIT 2', type = 'Facility', dbid=2784, latitude=19.002716269866, longitude=110.16686023683, }) ScenEdit_AddUnit({guid='test-0003', side = 'US', name = 'TEST UNIT 3', type = 'Facility', dbid=2784, latitude=19.003217518340, longitude=110.08610097175, }) ScenEdit_AddUnit({guid='test-0004', side = 'US', name = 'TEST UNIT 4', type = 'Facility', dbid=2784, latitude=19.003735711904, longitude=110.00468691401, }) end --cleans up data for all tests... CleanupTestData = function() Tool_EmulateNoConsole(true) unit = ScenEdit_GetUnit({guid='test-0001'}) Tool_EmulateNoConsole(false) if(unit ~= nil) then ScenEdit_RemoveSide({name = 'US'}) local eName = unit.guid..'_start_movement' CleanupScriptEvent(getEventName('test-0001')) CleanupScriptEvent(getEventName('test-0002')) CleanupScriptEvent(getEventName('test-0003')) CleanupScriptEvent(getEventName('test-0004')) CleanupScriptEvent('StepValueXXXtest-0001XXX1XXXStartStep') CleanupScriptEvent('StepValueXXXtest-0002XXX1XXXStartStep') CleanupScriptEvent('StepValueXXXtest-0003XXX1XXXStartStep') CleanupScriptEvent('StepValueXXXtest-0004XXX1XXXStartStep') end end ParseStepEventNameBase = function(stepEventNameBase) local guid = nil local step = nil if (stepEventNameBase ~= nil and type(stepEventNameBase) == 'string') then local separator = 'XXX' local data = splitString(stepEventNameBase,separator) if (#data > 2) then guid = data[2] step = tonumber(string.sub(data[3], 2)) end end return guid, step end Start = function(stepEventNameBase, doctrine, latDeg, lonDeg, daSpeed) --remove the current event local separator = 'XXX' local sName = stepEventNameBase..separator.."StartStep" --CleanupScriptEvent(sName) local event = ScenEdit_GetEvent(sName) --ScenEdit_SetEvent(event.guid, {mode = 'remove'}) local myguid, step = ParseStepEventNameBase(stepEventNameBase) local unit = ScenEdit_GetUnit({guid= myguid}) if(unit ~= nil) then --set the course for the unit. (actually starts it moving. ScenEdit_SetUnit({guid= myguid, course = {[1] = {TypeOf = 'ManualPlottedCourseWaypoint', latitude= latDeg, longitude = lonDeg}}, throttle = daSpeed}) end end --computes event start time based on delay value OR a time parameter (table of strings) StepStartTime = function(timeParams) local delaySecs = 0 if (timeParams ~= nil and type(timeParams) == 'table') then local startTime = timeParams.startTime local delay = timeParams.delay --set the default value to retuurn (assuming erroneous inputs) if (delay ~= nil and type(delay) == 'number') then --use the delay time (if given) delaySecs = delay*60 elseif (startTime ~= nil and type(startTime) == 'string') then --delay not specified... use startTime (if given) local sTimeData = splitString(startTime,":") if (sTimeData ~= nil) then if type(sTimeData) == 'table' and #sTimeData == 3 then local sHours = tonumber(sTimeData[1]) local sMinutes = tonumber(sTimeData[2]) local sSeconds = tonumber(sTimeData[3]) if (sHours and sMinutes and sSeconds) then local curTimeStr = ScenEdit_CurrentLocalTime() local curTimeData = splitString(curTimeStr,":") local curHours = tonumber(curTimeData[1]) local curMinutes = tonumber(curTimeData[2]) local curSeconds = tonumber(curTimeData[3]) local curTime = curHours * 3600 + curMinutes * 60 + curSeconds local sTime = sHours * 3600 + sMinutes * 60 + sSeconds if (curTime < sTime) then -- start time is later today... delaySecs = (sTime - curTime) elseif (curTime > sTime) then --start time is in 'past' meaning it is tomorrow. delaySecs = (sTime+24*3600 - curTime) end end end end end end return AddSecondsFromNow(delaySecs) end --the function to generate the event that start the unit moving. (calls start on the unit) StartStep = function(eventInfo) local sData = splitString(eventInfo.script,'<>') --create the first event for the step local separator = "XXX" local stepEventNameBase = "StepValue"..separator..eventInfo.guid..separator..eventInfo.step local eName = stepEventNameBase..separator..'StartStep' ScenEdit_SetEvent(eName, {mode='add', isRepeatable = false, isActive= active}) --create and set the trigger (time) local startTime = StepStartTime({startTime=eventInfo.startTime , delay=eventInfo.delay}) ScenEdit_SetTrigger({name = eName, Mode='add', type='Time', Time=startTime}) ScenEdit_SetEventTrigger(eName, {mode = 'add', name = eName}) --create and set the action (insert the actual event name base where '<>' appears in the script) local startscript = sData[1]..stepEventNameBase..sData[2] --original (complex) action (fails) ScenEdit_SetAction({name=eName, mode='add', type='LuaScript', ScriptText=startscript }) --simple action (works!) --unit = ScenEdit_GetUnit({guid=eventInfo.guid}) --local destLat = unit.latitude + 0.3 --local destLon = unit.longitude --local script = "ScenEdit_SetUnit({guid = '"..eventInfo.guid.."',".. -- "\r\n".." course = {[1] = {TypeOf = 'ManualPlottedCourseWaypoint', ".. -- "\r\n".." latitude = "..destLat..", ".. -- "\r\n".." longitude = "..destLon.."}},".. -- "\r\n".." throttle = 'Full'}) " --ScenEdit_SetAction({name=eName, mode='add', type='LuaScript', ScriptText=script }) -- ScenEdit_SetEventAction(eName, {mode = 'add', name = eName}) end generateBaseEventScript = function(myguid, to_lat, to_lon, to_rel, doctrineStr, speed, day) --attempt to get the unit local unit = ScenEdit_GetUnit({guid = myguid}) --make sure that the day has a value (1 if value not given) if (day == nil) then day = 1 end local latDeg=0 local lonDeg=0 if (to_rel) then --user indicates that the lat/lon is relative to teh current unit position. latDeg = unit.latitude + to_lat lonDeg = unit.longitude + to_lon else --user indicates that the lat/lon is fixed or real. latDeg = to_lat lonDeg = to_lon end --latitude and longidute values can not be exactly 0 in event script. if latDeg == 0 then latDeg = 0.000001 end if lonDeg == 0 then lonDeg = 0.000001 end --NOTE: this distance should be set relative to the speed of the unit...to prevent errors. --TODO: refactor to compute the appropriate ditance and remove testDist as a param to thsi function. local distToMark = testDist --get the name of the step value key local separator = 'XXX' local stepEventNameBase = "StepValue"..separator..myguid --note: place holeder '<>' is inserted here so that the StartStep function -- knows where to insert the final event name base value. retVal = "" retVal = retVal.."Start(\"<>\", \r\n" retVal = retVal.." "..doctrineStr..", \r\n" retVal = retVal.." "..latDeg..", \r\n" retVal = retVal.." "..lonDeg..", \r\n" retVal = retVal.." \""..speed.."\")" return retVal end generateEventScript = function(myguid, to_lat, to_lon, to_rel, day) local doctrineStr = '{ weapon_control_status_land = "0", engage_opportunity_targets = "1", automatic_evasion=false }' local speedStr = 'Creep' local retVal = generateBaseEventScript(myguid, to_lat, to_lon, to_rel, doctrineStr, speedStr, day) return retVal end --function abbreviated for simplicity (only using 'ATTACK' in test.) Order = function(GOParams) --interpret/parse the GOParams --NOTE: actual parmeter validaten delegated to teh generateGround???EventScript functions. local orderType = GOParams.orderType --the type of order to issue local myguid = GOParams.guid --the guid of the order to recieve the order local to_rel = GOParams.to_rel --flag (nil = false, or fixed not nil = true, or relative) --indicating whether destination is relative to current positin or fixed. local to_lat = GOParams.to_lat --the latitude of the destination (going to) either fixed, or relative to self. local to_lon = GOParams.to_lon --the longitude of the destination (going to) either fixed, or relative to self. local day = GOParams.day --the execution day of the unit's script. (allows for multi day scripting) local tgtGuid = GOParams.tgtGuid --the guid of the order being targeted local tgtRel = GOParams.tgtRel --flag (nil = false, or fixed not nil = true, or relative) -- indicating whether target lat/lon is relative to current positin or fixed. local tgtLat = GOParams.tgtLat --the latitude of the target either fixed, or relative to self. local tgtLon = GOParams.tgtLon --the longitude of the target either fixed, or relative to self. local tgtRadius = GOParams.tgtRadius --the radius of the area to shell (for area and search) local start = GOParams.startTime --the time to start/execute the order local theDelay = GOParams.delay --number of minutes to delay before startintg this step. local duration = GOParams.duration --the amount of time to execute the order --attmpet to generate the order script: myScript = "" if (orderType == 'ATTACK') then --move and engage targets along the way/ myScript = generateEventScript(myguid, to_lat, to_lon, to_rel, day) end if myScript ~= "" then --a script was successfully generated: create and return the eventInto element table return { guid = myguid, type = orderType, delay = theDelay, startTime = start, script=myScript, } else return nil end end --function abbreviated for simplicity (error happen on first item in script) Script = function(eventInfo) if (eventInfo ~= nil and type(eventInfo) == 'table') then --check to see if the StepKeyValueName has been defined for the unit... local i = 0 while i < #eventInfo do --for each itme in the array... i=i+1 --test to see if we have a valid table object if (eventInfo[i] ~= nil and type(eventInfo[i])=='table') then --check to see if the table object has a valid name field if (eventInfo[i].guid ~= nil and type(eventInfo[i].guid) == 'string') then --generate the base name of the script events (each event will append a fuirther identifier) --this must be unique on the system, accountin got days, steps, units, sides. local separator = "XXX" local stepKeyValueName = "StepValue"..separator..eventInfo[i].guid eventInfo[i].step=i if i == 1 then --this is the first eventInfo, so call StartStep immediately ScenEdit_SetKeyValue(stepKeyValueName, tostring(1), false) StartStep(eventInfo[i]) end end end end end end --simple event generation... (works) RunTest = function() DisplaceUnit('test-0001', 0.3, 0, '10:00:00') DisplaceUnit('test-0002', 0.3, 0, '10:00:00') DisplaceUnit('test-0003', 0.3, 0, '10:00:00') DisplaceUnit('test-0004', 0.3, 0, '10:00:00') end --Scripting like event generation... (fails) function Test() Script({[1] = Order({day=1, step=1, orderType='ATTACK', guid = 'test-0001', to_rel="yes", to_lat=0.35, to_lon=0, delay=0}),}) Script({[1] = Order({day=1, step=1, orderType='ATTACK', guid = 'test-0002', to_rel="yes", to_lat=0.35, to_lon=0, delay=0}),}) Script({[1] = Order({day=1, step=1, orderType='ATTACK', guid = 'test-0003', to_rel="yes", to_lat=0.35, to_lon=0, delay=0}),}) Script({[1] = Order({day=1, step=1, orderType='ATTACK', guid = 'test-0004', to_rel="yes", to_lat=0.35, to_lon=0, delay=0}),}) end CleanupTestData() SetupTestData() --RunTest() --the simple code (works) Test() --the complex code (works only if the delete event code is not executed.)