182 lines
6.2 KiB
Lua
182 lines
6.2 KiB
Lua
--[[
|
|
platform.channels — Channel registry for cc-platform-core
|
|
|
|
Centralizes modem channel allocation across all platform services.
|
|
Supports controlled migration between legacy and target channels
|
|
via three modes: "current", "target", and "dual".
|
|
|
|
Usage:
|
|
local Channels = require('platform.channels')
|
|
|
|
-- Get the active channel for a service endpoint
|
|
local ch = Channels.get('inventory.broadcast') -- => 4200
|
|
|
|
-- During migration, listen on both old and new channels
|
|
Channels.setMode('dual')
|
|
local both = Channels.getBoth('remoteturtle.command') -- => {100, 4210}
|
|
|
|
-- Get all channels for a service
|
|
local chs = Channels.forService('inventory')
|
|
|
|
-- Register a custom channel
|
|
Channels.register('myservice.data', 5000)
|
|
]]
|
|
|
|
local Config = require('opus.config')
|
|
|
|
local Channels = {}
|
|
|
|
--- Default channel allocations.
|
|
-- Each entry maps a logical channel name to current and target channel numbers.
|
|
-- During migration, 'current' is the legacy channel and 'target' is the new one.
|
|
-- When current == target, no migration is needed for that channel.
|
|
Channels.registry = {
|
|
-- RemoteTurtle service (legacy: 100-103, target: 4210-4213)
|
|
['remoteturtle.command'] = { current = 100, target = 4210 },
|
|
['remoteturtle.response'] = { current = 101, target = 4211 },
|
|
['remoteturtle.status'] = { current = 102, target = 4212 },
|
|
['remoteturtle.pocket'] = { current = 103, target = 4213 },
|
|
|
|
-- Inventory Manager service (no migration needed — channels stay the same)
|
|
['inventory.broadcast'] = { current = 4200, target = 4200 },
|
|
['inventory.order'] = { current = 4201, target = 4201 },
|
|
['inventory.client'] = { current = 4202, target = 4202 },
|
|
['inventory.craft_tx'] = { current = 4203, target = 4203 },
|
|
['inventory.craft_rx'] = { current = 4204, target = 4204 },
|
|
['inventory.system'] = { current = 4205, target = 4205 },
|
|
['inventory.bridge'] = { current = 4206, target = 4206 },
|
|
|
|
-- Platform-level shared channels (reserved range: 4250-4259)
|
|
['platform.discovery'] = { current = 4250, target = 4250 },
|
|
['platform.heartbeat'] = { current = 4251, target = 4251 },
|
|
}
|
|
|
|
--- Channel mode controls which channel number is returned by get().
|
|
-- "current" — use legacy channel numbers (default, safe)
|
|
-- "target" — use new channel numbers (post-migration)
|
|
-- "dual" — listeners open both; senders use target
|
|
Channels.mode = 'current'
|
|
|
|
--- Load channel mode from persistent config if available.
|
|
-- Called automatically on require; can be called again to reload.
|
|
function Channels.loadConfig()
|
|
local cfg = Config.load('platform', { channelMode = 'current' })
|
|
if cfg.channelMode then
|
|
Channels.mode = cfg.channelMode
|
|
end
|
|
-- Allow per-channel overrides from config
|
|
if cfg.channels then
|
|
for name, entry in pairs(cfg.channels) do
|
|
if type(entry) == 'number' then
|
|
Channels.registry[name] = { current = entry, target = entry }
|
|
elseif type(entry) == 'table' then
|
|
Channels.registry[name] = entry
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Get the active channel number for a logical channel name.
|
|
-- In "current" mode, returns the legacy channel.
|
|
-- In "target" mode, returns the new channel.
|
|
-- In "dual" mode, returns the target channel (for sending).
|
|
-- @param name string Logical channel name (e.g. 'inventory.broadcast')
|
|
-- @return number Channel number
|
|
function Channels.get(name)
|
|
local entry = Channels.registry[name]
|
|
if not entry then
|
|
error('Unknown channel: ' .. tostring(name), 2)
|
|
end
|
|
if Channels.mode == 'current' then
|
|
return entry.current
|
|
end
|
|
return entry.target
|
|
end
|
|
|
|
--- Get both current and target channels for a logical name.
|
|
-- Returns a deduplicated list. Used for opening channels in dual mode.
|
|
-- @param name string Logical channel name
|
|
-- @return table Array of channel numbers (1 or 2 entries)
|
|
function Channels.getBoth(name)
|
|
local entry = Channels.registry[name]
|
|
if not entry then
|
|
error('Unknown channel: ' .. tostring(name), 2)
|
|
end
|
|
if entry.current == entry.target then
|
|
return { entry.current }
|
|
end
|
|
return { entry.current, entry.target }
|
|
end
|
|
|
|
--- Set the channel migration mode.
|
|
-- @param mode string One of: "current", "target", "dual"
|
|
function Channels.setMode(mode)
|
|
assert(mode == 'current' or mode == 'target' or mode == 'dual',
|
|
'Invalid channel mode: ' .. tostring(mode))
|
|
Channels.mode = mode
|
|
end
|
|
|
|
--- Get all channels for a service prefix.
|
|
-- @param prefix string Service prefix (e.g. 'inventory', 'remoteturtle')
|
|
-- @return table Map of channel name → active channel number
|
|
function Channels.forService(prefix)
|
|
local result = {}
|
|
local pattern = '^' .. prefix:gsub('%-', '%%-') .. '%.'
|
|
for name, _ in pairs(Channels.registry) do
|
|
if name:find(pattern) then
|
|
result[name] = Channels.get(name)
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
--- Get all channels that should be opened for a service (respects dual mode).
|
|
-- @param prefix string Service prefix
|
|
-- @return table Array of unique channel numbers
|
|
function Channels.openListForService(prefix)
|
|
local seen = {}
|
|
local result = {}
|
|
local pattern = '^' .. prefix:gsub('%-', '%%-') .. '%.'
|
|
for name, _ in pairs(Channels.registry) do
|
|
if name:find(pattern) then
|
|
for _, ch in ipairs(Channels.getBoth(name)) do
|
|
if not seen[ch] then
|
|
seen[ch] = true
|
|
result[#result + 1] = ch
|
|
end
|
|
end
|
|
end
|
|
end
|
|
table.sort(result)
|
|
return result
|
|
end
|
|
|
|
--- Register a custom channel (for extensions or new services).
|
|
-- @param name string Logical channel name
|
|
-- @param channel number|table Channel number or {current=N, target=N}
|
|
function Channels.register(name, channel)
|
|
if type(channel) == 'number' then
|
|
channel = { current = channel, target = channel }
|
|
end
|
|
assert(type(channel) == 'table' and channel.current and channel.target,
|
|
'Channel must be a number or {current=N, target=N}')
|
|
Channels.registry[name] = channel
|
|
end
|
|
|
|
--- Check whether a modem channel belongs to a known service.
|
|
-- @param ch number Channel number
|
|
-- @return string|nil Channel name, or nil if unknown
|
|
function Channels.identify(ch)
|
|
for name, entry in pairs(Channels.registry) do
|
|
if entry.current == ch or entry.target == ch then
|
|
return name
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- Auto-load config on require
|
|
pcall(Channels.loadConfig)
|
|
|
|
return Channels
|