mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-08-12 04:31:09 +00:00
MooseBridge
This commit is contained in:
@@ -434,7 +434,15 @@ function AIRWING:NewPayload(Unit, Npayloads, MissionTypes, Performance)
|
||||
capability.MissionType=AUFTRAG.Type.RELOCATECOHORT
|
||||
capability.Performance=50
|
||||
table.insert(payload.capabilities, capability)
|
||||
end
|
||||
end
|
||||
|
||||
-- Add OPSTRANSPORT for all.
|
||||
if not AUFTRAG.CheckMissionType(AUFTRAG.Type.OPSTRANSPORT, MissionTypes) then
|
||||
local capability={} --Ops.Auftrag#AUFTRAG.Capability
|
||||
capability.MissionType=AUFTRAG.Type.OPSTRANSPORT
|
||||
capability.Performance=50
|
||||
table.insert(payload.capabilities, capability)
|
||||
end
|
||||
|
||||
-- Info
|
||||
self:T(self.lid..string.format("Adding new payload from unit %s for aircraft type %s: ID=%d, N=%d (unlimited=%s), performance=%d, missions: %s",
|
||||
|
||||
@@ -62,6 +62,8 @@ function MOOSE_BRIDGE:New(host, port)
|
||||
self.Sequence = 0
|
||||
self.DebugOverlays = {}
|
||||
self.OutQueue = {}
|
||||
self.OutQueueOffset = 1
|
||||
self.ReadBuffer = ""
|
||||
self.CommandHandlers = {}
|
||||
self.RegisteredZones = {}
|
||||
self.RegisteredOpsZones = {}
|
||||
@@ -118,6 +120,8 @@ function MOOSE_BRIDGE:_Disconnect(reason)
|
||||
if reason then self:_Log("Disconnected: " .. safe_tostring(reason)) end
|
||||
if self.Socket then self.Socket:close(); self.Socket = nil end
|
||||
self.OutQueue = {}
|
||||
self.OutQueueOffset = 1
|
||||
self.ReadBuffer = ""
|
||||
self.Connected = false
|
||||
end
|
||||
|
||||
@@ -2010,6 +2014,61 @@ function MOOSE_BRIDGE:RegisterDefaultCommands()
|
||||
return {action="coordinates.convert_points", count=#points, points=points}
|
||||
end)
|
||||
|
||||
self:RegisterCommand("scenery.search", function(cmd)
|
||||
local p = cmd.params or {}
|
||||
if not world or not world.searchObjects or not world.VolumeType then
|
||||
error("DCS world.searchObjects is not available")
|
||||
end
|
||||
if not Object or not Object.Category or Object.Category.SCENERY == nil then
|
||||
error("DCS scenery object category is not available")
|
||||
end
|
||||
local center = self:_DebugMarkupPoint(p)
|
||||
local radius = tonumber(p.radius_m) or 500
|
||||
local max_results = math.floor(tonumber(p.max_results) or 250)
|
||||
if radius <= 0 or radius > 5000 then error("radius_m must be in range 0..5000") end
|
||||
if max_results <= 0 or max_results > 2000 then error("max_results must be in range 1..2000") end
|
||||
local objects = {}
|
||||
local truncated = false
|
||||
local volume = {id=world.VolumeType.SPHERE, params={point=center, radius=radius}}
|
||||
world.searchObjects(Object.Category.SCENERY, volume, function(object)
|
||||
if #objects >= max_results then
|
||||
truncated = true
|
||||
return false
|
||||
end
|
||||
local point = self:_DcsPoint(object)
|
||||
if point then
|
||||
local coordinates = self:_CoordinatesForPoint(point, "ll")
|
||||
local descriptor = self:_DcsCall(object, "getDesc")
|
||||
local name = self:_DcsCall(object, "getName")
|
||||
local type_name = self:_DcsCall(object, "getTypeName")
|
||||
objects[#objects + 1] = {
|
||||
object_id="SCENERY:" .. safe_tostring(name or (#objects + 1)),
|
||||
name=name and safe_tostring(name) or nil,
|
||||
type_name=type_name and safe_tostring(type_name) or nil,
|
||||
display_name=type(descriptor) == "table" and descriptor.displayName or nil,
|
||||
life=tonumber(self:_DcsCall(object, "getLife")),
|
||||
exists=self:_DcsCall(object, "isExist"),
|
||||
x=coordinates.x,
|
||||
y=coordinates.y,
|
||||
z=coordinates.z,
|
||||
latitude=coordinates.latitude,
|
||||
longitude=coordinates.longitude,
|
||||
}
|
||||
end
|
||||
return true
|
||||
end)
|
||||
local center_coordinates = self:_CoordinatesForPoint(center, "ll")
|
||||
return {
|
||||
action="scenery.search",
|
||||
radius_m=radius,
|
||||
max_results=max_results,
|
||||
count=#objects,
|
||||
truncated=truncated,
|
||||
center=center_coordinates,
|
||||
objects=objects,
|
||||
}
|
||||
end)
|
||||
|
||||
self:RegisterCommand("terrain.closest_road_points", function(cmd)
|
||||
local p = cmd.params or {}
|
||||
if not land or not land.getClosestPointOnRoads then error("DCS land.getClosestPointOnRoads is not available") end
|
||||
@@ -2045,6 +2104,85 @@ function MOOSE_BRIDGE:RegisterDefaultCommands()
|
||||
return {action="terrain.closest_road_points", road_type=road_type, count=#samples, samples=samples}
|
||||
end)
|
||||
|
||||
self:RegisterCommand("terrain.road_route", function(cmd)
|
||||
local total_cpu_started = os and os.clock and os.clock() or nil
|
||||
local p = cmd.params or {}
|
||||
if not land or not land.findPathOnRoads then error("DCS land.findPathOnRoads is not available") end
|
||||
local start_id = self:_OptionalString(p.start_object_id)
|
||||
local end_id = self:_OptionalString(p.end_object_id)
|
||||
if not start_id or not end_id then error("terrain.road_route requires start_object_id and end_object_id") end
|
||||
local road_type = string.lower(tostring(p.road_type or "roads"))
|
||||
if road_type ~= "roads" and road_type ~= "rails" then error("road_type must be roads or rails") end
|
||||
local sample_spacing = tonumber(p.sample_spacing_m) or 100
|
||||
local max_points = math.floor(tonumber(p.max_points) or 500)
|
||||
if sample_spacing < 0 or sample_spacing > 5000 then error("sample_spacing_m must be in range 0..5000") end
|
||||
if max_points < 2 or max_points > 2000 then error("max_points must be in range 2..2000") end
|
||||
|
||||
local start_point = self:_PointForObjectId(start_id)
|
||||
local end_point = self:_PointForObjectId(end_id)
|
||||
local pathfinding_cpu_started = os and os.clock and os.clock() or nil
|
||||
local raw_path = land.findPathOnRoads(
|
||||
road_type,
|
||||
start_point.x,
|
||||
start_point.z,
|
||||
end_point.x,
|
||||
end_point.z
|
||||
)
|
||||
local pathfinding_cpu_ms = pathfinding_cpu_started and (os.clock() - pathfinding_cpu_started) * 1000 or nil
|
||||
if type(raw_path) ~= "table" or #raw_path < 2 then error("DCS returned no connected road route") end
|
||||
|
||||
local distance = 0
|
||||
for index, point in ipairs(raw_path) do
|
||||
if type(point) ~= "table" or type(point.x) ~= "number" or type(point.y) ~= "number" then
|
||||
error("DCS returned an invalid road route point at index " .. safe_tostring(index))
|
||||
end
|
||||
if index > 1 then
|
||||
local previous = raw_path[index - 1]
|
||||
distance = distance + math.sqrt((point.x - previous.x) ^ 2 + (point.y - previous.y) ^ 2)
|
||||
end
|
||||
end
|
||||
|
||||
local effective_spacing = math.max(sample_spacing, distance / math.max(1, max_points - 1))
|
||||
local points = {}
|
||||
local distance_since_sample = 0
|
||||
local function append_route_point(point)
|
||||
local height = land.getHeight and land.getHeight({x=point.x, y=point.y}) or 0
|
||||
local converted = self:_CoordinatesForPoint({x=point.x, y=height or 0, z=point.y}, "ll")
|
||||
points[#points + 1] = {
|
||||
x=converted.x,
|
||||
y=converted.y,
|
||||
z=converted.z,
|
||||
latitude=converted.latitude,
|
||||
longitude=converted.longitude,
|
||||
}
|
||||
end
|
||||
append_route_point(raw_path[1])
|
||||
for index=2,#raw_path do
|
||||
local point = raw_path[index]
|
||||
local previous = raw_path[index - 1]
|
||||
distance_since_sample = distance_since_sample + math.sqrt((point.x - previous.x) ^ 2 + (point.y - previous.y) ^ 2)
|
||||
if distance_since_sample >= effective_spacing and index < #raw_path and #points < max_points - 1 then
|
||||
append_route_point(point)
|
||||
distance_since_sample = 0
|
||||
end
|
||||
end
|
||||
append_route_point(raw_path[#raw_path])
|
||||
local total_cpu_ms = total_cpu_started and (os.clock() - total_cpu_started) * 1000 or nil
|
||||
return {
|
||||
action="terrain.road_route",
|
||||
road_type=road_type,
|
||||
start_object_id=start_id,
|
||||
end_object_id=end_id,
|
||||
distance_m=distance,
|
||||
raw_point_count=#raw_path,
|
||||
sample_spacing_m=effective_spacing,
|
||||
count=#points,
|
||||
pathfinding_cpu_ms=pathfinding_cpu_ms,
|
||||
total_cpu_ms=total_cpu_ms,
|
||||
points=points,
|
||||
}
|
||||
end)
|
||||
|
||||
self:RegisterCommand("terrain.surface_types", function(cmd)
|
||||
local p = cmd.params or {}
|
||||
if not land or not land.getSurfaceType then error("DCS land.getSurfaceType is not available") end
|
||||
@@ -2240,9 +2378,15 @@ end
|
||||
function MOOSE_BRIDGE:_ReadLine()
|
||||
if not self.Socket then return nil, "no_socket" end
|
||||
local line, err, partial = self.Socket:receive("*l")
|
||||
if line then return line, nil end
|
||||
if line then
|
||||
line = (self.ReadBuffer or "") .. line
|
||||
self.ReadBuffer = ""
|
||||
return line, nil
|
||||
end
|
||||
if partial and #partial > 0 then
|
||||
self.ReadBuffer = (self.ReadBuffer or "") .. partial
|
||||
end
|
||||
if err == "timeout" then return nil, nil end
|
||||
if partial and #partial > 0 then return partial, nil end
|
||||
return nil, err
|
||||
end
|
||||
|
||||
@@ -2258,9 +2402,20 @@ end
|
||||
function MOOSE_BRIDGE:_FlushOutQueue()
|
||||
if not self.Socket or #self.OutQueue == 0 then return end
|
||||
while #self.OutQueue > 0 do
|
||||
local line = table.remove(self.OutQueue, 1)
|
||||
local ok, err = self.Socket:send(line .. "\n")
|
||||
if not ok then self:_Disconnect("send failed: " .. safe_tostring(err)); return end
|
||||
local payload = self.OutQueue[1] .. "\n"
|
||||
local offset = self.OutQueueOffset or 1
|
||||
local sent, err, last = self.Socket:send(payload, offset)
|
||||
if sent then
|
||||
table.remove(self.OutQueue, 1)
|
||||
self.OutQueueOffset = 1
|
||||
elseif err == "timeout" then
|
||||
local final_byte = tonumber(last) or (offset - 1)
|
||||
if final_byte >= offset then self.OutQueueOffset = final_byte + 1 end
|
||||
return
|
||||
else
|
||||
self:_Disconnect("send failed: " .. safe_tostring(err))
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user