--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. -- --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. -- --[[ KH Notes: added some helperfunctions added sample eventremover. added some code to add regular timer event for that purpose. changed code such that TC and E's get remove during self execution but A's get queued. some other minor things like print statements just so you can follow it in the lua-log. Just make sure to let it run at least 2 full seconds before stopping and looking at the log and for the events to be cleared fully via gui. 2 seconds should ensure that any queued deletes are dealt with. --]] gKH={}; gKH.Events={}; gKH.EventCleaner={}; gKH.EventCleaner.CleanupArray={}; gKH.EventCleaner.dbglevel = 2; function gKH.EventCleaner:ProcessCycleTest() local fn = "gKH.EventCleaner:ProcessCycleTest(): " if self.dbglevel > 2 then print(fn .. ' called, current contents:'); print(self.CleanupArray); end for k,v in pairs(self.CleanupArray) do if self.dbglevel > 0 then print(string.format("%s Removing Action with name: %s guid: %s ",fn,tostring(v),tostring(k))); end local retval,a = pcall(ScenEdit_SetAction,{MODE="remove",DESCRIPTION=k}); if (retval ==true) and a ~=nil then if self.dbglevel > 1 then print(fn .. "success removing " .. tostring(v) .. " || " .. tostring(k)) end; self.CleanupArray[k]=nil; else print(fn .. " Error removing action " ..tostring(v) .." || ".. tostring(k) .." errmsg: " .. tostring(a)); end end end function gKH.Events.GetNameFromAction(a) local aName; if (a ~=nil) and type(a) =='table' then if (a.LuaScript ~= nil) then aName = a.LuaScript.Description; elseif (a.ChangeMissionStatus ~= nil) then aName = a.ChangeMissionStatus.Description; elseif (a.NewStatus ~= nil) then aName = a.NewStatus.Description; elseif (a.EndScenario ~= nil) then aName = a.EndScenario.Description; elseif (a.Message ~= nil) then aName = a.Message.Description; elseif (a.Text ~= nil) then aName = a.Text.Description; elseif (a.Points ~= nil) then aName = a.Points.Description; elseif (a.SideID ~= nil) then aName = a.SideID.Description; elseif (a.TeleportInArea ~= nil) then aName = a.TeleportInArea.Description; else aName = nil end else print("GetNameFromAction(a): action table parameter is nil! aboring and returning nil.") end return aName; end function gKH.Events.GetIDFromAction(a) local aID =''; if (a ~=nil) and type(a) =='table' then if (a.LuaScript ~= nil) then aID = a.LuaScript.ID; elseif (a.ChangeMissionStatus ~= nil) then aID = a.ChangeMissionStatus.ID; elseif (a.NewStatus ~= nil) then aID = a.NewStatus.ID; elseif (a.EndScenario ~= nil) then aID = a.EndScenario.ID; elseif (a.Message ~= nil) then aID = a.Message.ID; elseif (a.Text ~= nil) then aID = a.Text.ID; elseif (a.Points ~= nil) then aID = a.Points.ID; elseif (a.SideID ~= nil) then aID = a.SideID.ID; elseif (a.TeleportInArea ~= nil) then aID = a.TeleportInArea.ID; else aID = nil end else print("GetIDFromAction(a): action table parameter is nil! aboring and returning nil.") end return aID; end function gKH.Events.GetNameFromTrigger(t) local tName; if (t ~=nil) and type(t) =='table' then if (t.Points ~= nil) then tName = t.Points.Description elseif (t.RandomTime) then tName = t.RandomTime.Description elseif (t.RegularTime) then tName = t.RegularTime.Description elseif (t.ScenEnded) then tName = t.ScenEnded.Description elseif (t.ScenLoaded) then tName = t.ScenLoaded.Description elseif (t.Time) then tName = t.Time.Description elseif (t.UnitDamaged) then tName = t.UnitDamaged.Description elseif (t.UnitDestroyed) then tName = t.UnitDestroyed.Description elseif (t.UnitDetected) then tName = t.UnitDetected.Description elseif (t.UnitEmissions) then tName = t.UnitEmissions.Description elseif (t.UnitEntersArea) then tName = t.UnitEntersArea.Description elseif (t.UnitRemainsInArea) then tName = t.UnitRemainsInArea.Description elseif (t.UnitBaseStatus) then tName = t.UnitBaseStatus.Description elseif (t.UnitCargoMoved) then tName = t.UnitCargoMoved.Description else tName = nil end else print("GetNameFromTrigger(t): trigger table parameter is nil! aboring and returning nil.") end return tName; end function gKH.Events.GetIDFromTrigger(t) local tID; if (t ~=nil) and type(t) =='table' then if (t.Points ~= nil) then tName = t.Points.ID elseif (t.RandomTime) then tName = t.RandomTime.ID elseif (t.RegularTime) then tName = t.RegularTime.ID elseif (t.ScenEnded) then tName = t.ScenEnded.ID elseif (t.ScenLoaded) then tName = t.ScenLoaded.ID elseif (t.Time) then tName = t.Time.ID elseif (t.UnitDamaged) then tName = t.UnitDamaged.ID elseif (t.UnitDestroyed) then tName = t.UnitDestroyed.ID elseif (t.UnitDetected) then tName = t.UnitDetected.ID elseif (t.UnitEmissions) then tName = t.UnitEmissions.ID elseif (t.UnitEntersArea) then tName = t.UnitEntersArea.ID elseif (t.UnitRemainsInArea) then tName = t.UnitRemainsInArea.ID elseif (t.UnitBaseStatus) then tName = t.UnitBaseStatus.ID elseif (t.UnitCargoMoved) then tName = t.UnitCargoMoved.ID else tID = nil end else print("GetIDFromTrigger(t): trigger table parameter is nil! aboring and returning nil.") end return tID; end function gKH.Events.GetNameFromCondition(c) local cName; if (c ~=nil) and type(c) =='table' then if (c.LuaScript~= nil) then cName = c.LuaScript.Description elseif (c.ScenHasStarted) then cName = c.ScenHasStarted.Description elseif (c.SidePosture) then cName = c.SidePosture.Description else cName = nil end else print("GetNameFromCondition(c): condition table parameter is nil! aboring and returning nil.") end return cName; end CleanupScriptEvent = function( eventName, bForceDelete ) local fn= "CleanupScriptEvent(sEventName,bForce): "; if bForceDelete ==nil then bForceDelete =false; end if (eventName==nil) or type(eventName) ~= 'string' then print(fn.. " eventname is not a string or is nil. aborting."); return; end local e,p = pcall(ScenEdit_GetEvent,eventName); --print(fn.. 'Processing event: ' ..eventName); if(e == true and p ~=nil) then --remove all the actions connected to this event! local aName; local aGuid; for k,v in pairs(p.actions) do aName = gKH.Events.GetNameFromAction(v) aGuid = gKH.Events.GetIDFromAction(v) if (aName ~= nil and aGuid ~=nil ) then if bForceDelete == true then print(fn.. 'Bypassing fix for action ' .. aName); print(fn .. 'Removing action: ' ..aName.. ' from event: '..eventName) ScenEdit_SetEventAction(eventName,{mode='remove',name=aName}); print(fn .. 'Deleting action: ' ..aName.. ' from event: '..eventName) ScenEdit_SetAction({mode='remove',name=aName}); elseif (bForceDelete == false and gKH.EventCleaner.CleanupArray[aGuid] == nil) then print(fn.. 'Queuing the deleting of action: ' .. aName .. ' guid:' .. aGuid) gKH.EventCleaner.CleanupArray[aGuid]=aName; else print(fn .. '** This should not happen **' .. eventName) end else print('name or guid was nil'); end end --remove all the triggers connected to this event! for k,v in pairs(p.triggers) do local tName = gKH.Events.GetNameFromTrigger(v) if (tName ~= nil) then print(fn .. 'Disconnecting tigger: '..tName..'from event: '..eventName) ScenEdit_SetEventTrigger(eventName,{mode='remove',name=tName}) print(fn .. '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 = gKH.Events.GetNameFromCondition(v) if (cName ~= nil) then print(fn .. 'Disconnecting condition: '..cName..'from event: '..eventName) ScenEdit_SetEventCondition(eventName,{mode='remove',name=cName}) print(fn .. 'Destroying condition: '..cName) ScenEdit_SetCondition({mode='remove',name=cName}) end end --remove the event print(fn .. 'Removing event: ' .. tostring(eventName)); ScenEdit_SetEvent(eventName, {mode='remove'}) else print(fn ..'Nothing to do, could not obtain event with that name: ' ..eventName); 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= true}) 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, }); ScenEdit_SetAction({MODE="add",Description="EventCleaner_ProcessCycle",Type="LuaScript",ScriptText="gKH.EventCleaner:ProcessCycleTest();"}); ScenEdit_SetTrigger({MODE="add",Description="EventCleaner_EveryOneSecond", Type="RegularTime",Interval=0}); ScenEdit_SetEvent("EventCleaner",{Description="EventCleaner",IsActive=true,IsRepeatable=true,IsShown=false,Mode="add"}); ScenEdit_SetEventTrigger("EventCleaner",{MODE="add",Description="EventCleaner_EveryOneSecond"}); ScenEdit_SetEventAction("EventCleaner",{MODE="add",Description="EventCleaner_ProcessCycle"}); print("TestSetupCompleted.") 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',true) CleanupScriptEvent('StepValueXXXtest-0002XXX1XXXStartStep',true) CleanupScriptEvent('StepValueXXXtest-0003XXX1XXXStartStep',true) CleanupScriptEvent('StepValueXXXtest-0004XXX1XXXStartStep',true) end CleanupScriptEvent('EventCleaner',true) gKH.EventCleaner:ProcessCycleTest(); print("CleanUpTestData Completed.") 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= true}) --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 print('\r\n**** Begin Test Run ****'); CleanupTestData() SetupTestData() --RunTest() --the simple code (works) Test() --the complex code (works only if the delete event code is not executed.)