mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-08-18 13:16:03 +00:00
405 lines
16 KiB
Lua
405 lines
16 KiB
Lua
-- Optional INTEL snapshot and event extension for MOOSE Bridge.
|
|
--
|
|
-- MOOSE remains the owner of INTEL tactical logic. This extension only mirrors
|
|
-- registered INTEL objects and forwards their FSM events to Python.
|
|
|
|
local function bridge_intel_safe_tostring(value)
|
|
if value == nil then return nil end
|
|
return tostring(value)
|
|
end
|
|
|
|
local function bridge_intel_object_name(object)
|
|
if not object then return nil end
|
|
if MOOSE_BRIDGE and MOOSE_BRIDGE._ObjectName then
|
|
local ok, value = pcall(function() return MOOSE_BRIDGE:_ObjectName(object) end)
|
|
if ok and value then return value end
|
|
end
|
|
if object.alias then return tostring(object.alias) end
|
|
if object.name then return tostring(object.name) end
|
|
if object.Name then return tostring(object.Name) end
|
|
if object.groupname then return tostring(object.groupname) end
|
|
return nil
|
|
end
|
|
|
|
local function bridge_intel_auftrag_id(bridge, auftrag)
|
|
if not auftrag then return nil end
|
|
if bridge and bridge._AuftragObjectId then
|
|
local ok, value = pcall(function() return bridge:_AuftragObjectId(auftrag) end)
|
|
if ok and value then return value end
|
|
end
|
|
if auftrag.auftragsnummer then return "AUFTRAG:" .. tostring(auftrag.auftragsnummer) end
|
|
if auftrag.name then return "AUFTRAG:" .. tostring(auftrag.name) end
|
|
return nil
|
|
end
|
|
|
|
local function bridge_intel_point(bridge, value)
|
|
if not value then return nil end
|
|
if bridge and bridge._PointFromMooseObject then
|
|
local ok, point = pcall(function() return bridge:_PointFromMooseObject(value) end)
|
|
if ok and point then return point end
|
|
end
|
|
if type(value) == "table" and type(value.x) == "number" and type(value.z) == "number" then
|
|
return value
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function bridge_intel_velocity(value)
|
|
if type(value) ~= "table" then return nil end
|
|
return {
|
|
x=type(value.x) == "number" and value.x or nil,
|
|
y=type(value.y) == "number" and value.y or nil,
|
|
z=type(value.z) == "number" and value.z or nil,
|
|
}
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_EnsureIntelRegistry()
|
|
self.RegisteredIntels = self.RegisteredIntels or {}
|
|
return self.RegisteredIntels
|
|
end
|
|
|
|
function MOOSE_BRIDGE:RegisterIntel(intel, name)
|
|
if not intel then return self end
|
|
local intel_name = name or bridge_intel_object_name(intel) or intel.alias
|
|
if not intel_name then return self end
|
|
intel_name = tostring(intel_name)
|
|
self:_EnsureIntelRegistry()[intel_name] = intel
|
|
self:_AttachIntelEventForwarders(intel, intel_name)
|
|
if type(intel.SetAgentAuto) ~= "function" then error("INTEL:SetAgentAuto is not available") end
|
|
intel:SetAgentAuto()
|
|
return self
|
|
end
|
|
|
|
function MOOSE_BRIDGE:RegisterIntels(intels)
|
|
if type(intels) ~= "table" then return self end
|
|
for name, intel in pairs(intels) do self:RegisterIntel(intel, name) end
|
|
return self
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_IntelObjectId(intel_name)
|
|
return "INTEL:" .. tostring(intel_name)
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_IntelContactId(intel_name, contact)
|
|
local name = contact and contact.groupname or nil
|
|
if not name then return nil end
|
|
return "INTELCONTACT:" .. tostring(intel_name) .. ":" .. tostring(name)
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_IntelClusterId(intel_name, cluster)
|
|
local index = cluster and cluster.index or nil
|
|
if not index then return nil end
|
|
return "INTELCLUSTER:" .. tostring(intel_name) .. ":" .. tostring(index)
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_IntelContactTargetObjectId(contact)
|
|
if not contact then return nil end
|
|
local name = contact.groupname
|
|
if not name then return nil end
|
|
if contact.isStatic then return "STATIC:" .. tostring(name) end
|
|
return "GROUP:" .. tostring(name)
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_IntelAgentCounts(intel)
|
|
local detectionset = intel and intel.detectionset or nil
|
|
if not detectionset then return 0, 0 end
|
|
return detectionset:Count(), detectionset:CountAlive()
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_IntelAgentSnapshot(intel)
|
|
local result = {}
|
|
local detectionset = intel and intel.detectionset or nil
|
|
if not detectionset then return result, 0, 0 end
|
|
|
|
for name, _ in pairs(detectionset.Set or {}) do
|
|
result[#result + 1] = "GROUP:" .. tostring(name)
|
|
end
|
|
|
|
local agent_count, alive_agent_count = self:_IntelAgentCounts(intel)
|
|
return result, agent_count, alive_agent_count
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_ResolveRegisteredIntel(intel_id)
|
|
local name = type(intel_id) == "string" and string.match(intel_id, "^INTEL:(.+)$") or nil
|
|
if not name then return nil, nil, "Invalid INTEL object id: " .. tostring(intel_id) end
|
|
local intel = self:_EnsureIntelRegistry()[name]
|
|
if not intel then return nil, nil, "INTEL not registered: " .. name end
|
|
return intel, name, nil
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_ResolveIntelAgent(agent_id)
|
|
local prefix, name
|
|
if type(agent_id) == "string" then prefix, name = string.match(agent_id, "^([^:]+):(.+)$") end
|
|
if not prefix or not name then return nil, "Invalid agent object id: " .. tostring(agent_id) end
|
|
|
|
if prefix == "GROUP" then
|
|
local group = GROUP and GROUP.FindByName and GROUP:FindByName(name) or nil
|
|
if not group then return nil, "GROUP not found: " .. name end
|
|
return group, nil
|
|
end
|
|
|
|
if prefix == "OPSGROUP" then
|
|
if not self._ResolveOpsGroupById then
|
|
return nil, "OPSGROUP agents require MooseBridgeAuftragExecutionExtension.lua"
|
|
end
|
|
return self:_ResolveOpsGroupById(agent_id)
|
|
end
|
|
|
|
return nil, "Agent must be GROUP:<name> or OPSGROUP:<name>"
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_BuildIntelContactSnapshotItem(intel_name, contact, source)
|
|
if type(contact) ~= "table" then return nil end
|
|
local point = bridge_intel_point(self, contact.position)
|
|
local recce_name = bridge_intel_safe_tostring(contact.recce)
|
|
local recce_unit = recce_name and UNIT and UNIT.FindByName and UNIT:FindByName(recce_name) or nil
|
|
local recce_group = recce_unit and self:_SafeCall(recce_unit, "GetGroup") or nil
|
|
local recce_group_name = recce_group and self:_SafeCall(recce_group, "GetName") or nil
|
|
local item = {
|
|
object_id=self:_IntelContactId(intel_name, contact),
|
|
dcs_name=bridge_intel_safe_tostring(contact.groupname),
|
|
object_type="INTELCONTACT",
|
|
category=bridge_intel_safe_tostring(contact.ctype or contact.categoryname),
|
|
source=source,
|
|
intel_id=self:_IntelObjectId(intel_name),
|
|
target_object_id=self:_IntelContactTargetObjectId(contact),
|
|
typename=bridge_intel_safe_tostring(contact.typename),
|
|
attribute=bridge_intel_safe_tostring(contact.attribute),
|
|
category_id=contact.category,
|
|
category_name=bridge_intel_safe_tostring(contact.categoryname),
|
|
threat_level=contact.threatlevel,
|
|
detected_time=contact.Tdetected,
|
|
recce=recce_name,
|
|
recce_unit_id=recce_name and "UNIT:" .. recce_name or nil,
|
|
recce_group_id=recce_group_name and "GROUP:" .. tostring(recce_group_name) or nil,
|
|
contact_type=bridge_intel_safe_tostring(contact.ctype),
|
|
speed_mps=contact.speed,
|
|
velocity=bridge_intel_velocity(contact.velocity),
|
|
is_ground=contact.isground and true or false,
|
|
is_ship=contact.isship and true or false,
|
|
is_static=contact.isStatic and true or false,
|
|
platform=bridge_intel_safe_tostring(contact.platform),
|
|
heading=contact.heading,
|
|
maneuvering=contact.maneuvering and true or false,
|
|
altitude_m=contact.altitude,
|
|
rcs=contact.rcs,
|
|
mission_id=bridge_intel_auftrag_id(self, contact.mission),
|
|
}
|
|
if point then self:_AddPointFields(item, point) end
|
|
return item
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_BuildIntelClusterSnapshotItem(intel_name, cluster, source)
|
|
if type(cluster) ~= "table" then return nil end
|
|
local point = bridge_intel_point(self, cluster.coordinate)
|
|
local contact_ids = {}
|
|
if type(cluster.Contacts) == "table" then
|
|
for _, contact in pairs(cluster.Contacts) do
|
|
local contact_id = self:_IntelContactId(intel_name, contact)
|
|
if contact_id then contact_ids[#contact_ids + 1] = contact_id end
|
|
end
|
|
end
|
|
local item = {
|
|
object_id=self:_IntelClusterId(intel_name, cluster),
|
|
dcs_name="Cluster " .. tostring(cluster.index or "?"),
|
|
object_type="INTELCLUSTER",
|
|
category=bridge_intel_safe_tostring(cluster.ctype),
|
|
source=source,
|
|
intel_id=self:_IntelObjectId(intel_name),
|
|
index=cluster.index,
|
|
size=cluster.size,
|
|
contact_ids=contact_ids,
|
|
threat_level_max=cluster.threatlevelMax,
|
|
threat_level_sum=cluster.threatlevelSum,
|
|
threat_level_avg=cluster.threatlevelAve,
|
|
contact_type=bridge_intel_safe_tostring(cluster.ctype),
|
|
altitude_m=cluster.altitude,
|
|
mission_id=bridge_intel_auftrag_id(self, cluster.mission),
|
|
}
|
|
if point then self:_AddPointFields(item, point) end
|
|
return item
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_BuildIntelSnapshotItem(intel_name, intel, source)
|
|
local contacts = self:_SafeCall(intel, "GetContactTable") or intel.Contacts or {}
|
|
local clusters = self:_SafeCall(intel, "GetClusterTable") or intel.Clusters or {}
|
|
local agent_ids, agent_count, alive_agent_count = self:_IntelAgentSnapshot(intel)
|
|
return {
|
|
object_id=self:_IntelObjectId(intel_name),
|
|
dcs_name=tostring(intel_name),
|
|
object_type="INTEL",
|
|
category="INTEL",
|
|
source=source,
|
|
alias=bridge_intel_safe_tostring(intel.alias),
|
|
coalition=self:_CoalitionToName(intel.coalition),
|
|
state=self:_SafeCallArg(intel, "GetState") or nil,
|
|
is_running=self:_SafeCallArg(intel, "Is", "Running") and true or false,
|
|
cluster_analysis=intel.clusteranalysis and true or false,
|
|
cluster_markers=intel.clustermarkers and true or false,
|
|
cluster_arrows=intel.clusterarrows and true or false,
|
|
cluster_radius_m=intel.clusterradius,
|
|
detect_statics=intel.detectStatics and true or false,
|
|
detect_accoustic=intel.DetectAccoustic and true or false,
|
|
detect_accoustic_radius_m=intel.DetectAccousticRadius,
|
|
doppler_radar=intel.DopplerRadar and true or false,
|
|
contact_count=type(contacts) == "table" and #contacts or 0,
|
|
cluster_count=type(clusters) == "table" and #clusters or 0,
|
|
agent_count=agent_count,
|
|
alive_agent_count=alive_agent_count,
|
|
agent_ids=agent_ids,
|
|
}
|
|
end
|
|
|
|
function MOOSE_BRIDGE:BuildIntelSnapshot()
|
|
local result = {}
|
|
for name, intel in pairs(self:_EnsureIntelRegistry()) do
|
|
local ok, item = pcall(function() return self:_BuildIntelSnapshotItem(name, intel, "registered") end)
|
|
if ok and item then result[#result + 1] = item end
|
|
end
|
|
return result
|
|
end
|
|
|
|
function MOOSE_BRIDGE:BuildIntelContactSnapshot()
|
|
local result = {}
|
|
for name, intel in pairs(self:_EnsureIntelRegistry()) do
|
|
local contacts = self:_SafeCall(intel, "GetContactTable") or intel.Contacts or {}
|
|
if type(contacts) == "table" then
|
|
for _, contact in pairs(contacts) do
|
|
local ok, item = pcall(function() return self:_BuildIntelContactSnapshotItem(name, contact, "registered") end)
|
|
if ok and item and item.object_id then result[#result + 1] = item end
|
|
end
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
function MOOSE_BRIDGE:BuildIntelClusterSnapshot()
|
|
local result = {}
|
|
for name, intel in pairs(self:_EnsureIntelRegistry()) do
|
|
local clusters = self:_SafeCall(intel, "GetClusterTable") or intel.Clusters or {}
|
|
if type(clusters) == "table" then
|
|
for _, cluster in pairs(clusters) do
|
|
local ok, item = pcall(function() return self:_BuildIntelClusterSnapshotItem(name, cluster, "registered") end)
|
|
if ok and item and item.object_id then result[#result + 1] = item end
|
|
end
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_SendIntelEvent(event_name, intel_name, fsm_event, from_state, to_state, item_kind, item)
|
|
local payload = {
|
|
event=event_name,
|
|
intel_id=self:_IntelObjectId(intel_name),
|
|
fsm_event=fsm_event,
|
|
from_state=from_state,
|
|
to_state=to_state,
|
|
}
|
|
if item_kind == "contact" then
|
|
payload.contact = item
|
|
payload.contact_id = item and item.object_id or nil
|
|
payload.target_object_id = item and item.target_object_id or nil
|
|
elseif item_kind == "cluster" then
|
|
payload.cluster = item
|
|
payload.cluster_id = item and item.object_id or nil
|
|
end
|
|
self:SendEvent(event_name, payload)
|
|
end
|
|
|
|
function MOOSE_BRIDGE:_AttachIntelEventForwarders(intel, intel_name)
|
|
if type(intel) ~= "table" or intel.MooseBridgeIntelEventsRegistered then return self end
|
|
intel.MooseBridgeIntelEventsRegistered = true
|
|
local bridge = self
|
|
local previous_new_contact = intel.OnAfterNewContact
|
|
local previous_lost_contact = intel.OnAfterLostContact
|
|
local previous_new_cluster = intel.OnAfterNewCluster
|
|
local previous_lost_cluster = intel.OnAfterLostCluster
|
|
|
|
intel.OnAfterNewContact = function(intel_self, From, Event, To, Contact)
|
|
if type(previous_new_contact) == "function" then pcall(previous_new_contact, intel_self, From, Event, To, Contact) end
|
|
local item = bridge:_BuildIntelContactSnapshotItem(intel_name, Contact, "event")
|
|
bridge:_SendIntelEvent("intel.new_contact", intel_name, Event, From, To, "contact", item)
|
|
end
|
|
|
|
intel.OnAfterLostContact = function(intel_self, From, Event, To, Contact)
|
|
if type(previous_lost_contact) == "function" then pcall(previous_lost_contact, intel_self, From, Event, To, Contact) end
|
|
local item = bridge:_BuildIntelContactSnapshotItem(intel_name, Contact, "event")
|
|
bridge:_SendIntelEvent("intel.lost_contact", intel_name, Event, From, To, "contact", item)
|
|
end
|
|
|
|
intel.OnAfterNewCluster = function(intel_self, From, Event, To, Cluster)
|
|
if type(previous_new_cluster) == "function" then pcall(previous_new_cluster, intel_self, From, Event, To, Cluster) end
|
|
local item = bridge:_BuildIntelClusterSnapshotItem(intel_name, Cluster, "event")
|
|
bridge:_SendIntelEvent("intel.new_cluster", intel_name, Event, From, To, "cluster", item)
|
|
end
|
|
|
|
intel.OnAfterLostCluster = function(intel_self, From, Event, To, Cluster, Mission)
|
|
if type(previous_lost_cluster) == "function" then pcall(previous_lost_cluster, intel_self, From, Event, To, Cluster, Mission) end
|
|
local item = bridge:_BuildIntelClusterSnapshotItem(intel_name, Cluster, "event")
|
|
if item and not item.mission_id then item.mission_id = bridge_intel_auftrag_id(bridge, Mission) end
|
|
bridge:_SendIntelEvent("intel.lost_cluster", intel_name, Event, From, To, "cluster", item)
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
local _moose_bridge_base_register_default_commands_for_intel = MOOSE_BRIDGE.RegisterDefaultCommands
|
|
|
|
function MOOSE_BRIDGE:RegisterDefaultCommands()
|
|
_moose_bridge_base_register_default_commands_for_intel(self)
|
|
|
|
local previous_snapshot_all = self.CommandHandlers["snapshot.all"]
|
|
|
|
self:RegisterCommand("snapshot.intels", function(cmd)
|
|
local intels = self:BuildIntelSnapshot()
|
|
self:SendSnapshot("intels", {intels=intels})
|
|
return {kind="intels", count=#intels}
|
|
end)
|
|
|
|
self:RegisterCommand("intel.add_agent", function(cmd)
|
|
local params = self:_CommandParams(cmd)
|
|
local intel, intel_name, intel_err = self:_ResolveRegisteredIntel(params.intel_id)
|
|
if not intel then error(intel_err) end
|
|
local agent, agent_err = self:_ResolveIntelAgent(params.agent_id)
|
|
if not agent then error(agent_err) end
|
|
|
|
intel:AddAgent(agent)
|
|
local agent_count, alive_agent_count = self:_IntelAgentCounts(intel)
|
|
return {
|
|
action="intel.add_agent",
|
|
intel_id=self:_IntelObjectId(intel_name),
|
|
agent_id=params.agent_id,
|
|
agent_count=agent_count,
|
|
alive_agent_count=alive_agent_count,
|
|
}
|
|
end)
|
|
|
|
self:RegisterCommand("snapshot.intel_contacts", function(cmd)
|
|
local contacts = self:BuildIntelContactSnapshot()
|
|
self:SendSnapshot("intel_contacts", {intel_contacts=contacts})
|
|
return {kind="intel_contacts", count=#contacts}
|
|
end)
|
|
|
|
self:RegisterCommand("snapshot.intel_clusters", function(cmd)
|
|
local clusters = self:BuildIntelClusterSnapshot()
|
|
self:SendSnapshot("intel_clusters", {intel_clusters=clusters})
|
|
return {kind="intel_clusters", count=#clusters}
|
|
end)
|
|
|
|
if previous_snapshot_all then
|
|
self:RegisterCommand("snapshot.all", function(cmd)
|
|
local result = previous_snapshot_all(cmd) or {}
|
|
local intels = self:BuildIntelSnapshot()
|
|
local contacts = self:BuildIntelContactSnapshot()
|
|
local clusters = self:BuildIntelClusterSnapshot()
|
|
self:SendSnapshot("intels", {intels=intels})
|
|
self:SendSnapshot("intel_contacts", {intel_contacts=contacts})
|
|
self:SendSnapshot("intel_clusters", {intel_clusters=clusters})
|
|
result.intels = #intels
|
|
result.intel_contacts = #contacts
|
|
result.intel_clusters = #clusters
|
|
return result
|
|
end)
|
|
end
|
|
end
|