initial commit
This commit is contained in:
153
apis/service.lua
Normal file
153
apis/service.lua
Normal file
@@ -0,0 +1,153 @@
|
||||
--[[
|
||||
platform.service — Service lifecycle helpers for cc-platform-core
|
||||
|
||||
Provides a lightweight service context for Opus-based CC services.
|
||||
Handles modem discovery, channel management, config loading, and
|
||||
message sending using platform conventions.
|
||||
|
||||
Usage:
|
||||
local Service = require('platform.service')
|
||||
|
||||
local ctx = Service.create({
|
||||
name = 'inventory-manager',
|
||||
defaults = { scanInterval = 120 },
|
||||
})
|
||||
|
||||
Service.openChannels(ctx)
|
||||
|
||||
-- Send on a named channel
|
||||
Service.send(ctx, 'inventory.broadcast', 'state', { items = {...} })
|
||||
|
||||
-- Broadcast on both legacy and target channels during migration
|
||||
Service.broadcast(ctx, 'inventory.broadcast', 'state', { items = {...} })
|
||||
]]
|
||||
|
||||
local Config = require('opus.config')
|
||||
local Protocol = require('platform.protocol')
|
||||
local Channels = require('platform.channels')
|
||||
|
||||
local peripheral = _G.peripheral
|
||||
|
||||
local Service = {}
|
||||
|
||||
--- Create a new service context.
|
||||
-- Initializes the protocol identity, loads config, discovers modem.
|
||||
-- @param opts table Options:
|
||||
-- name string (required) Service identifier
|
||||
-- defaults table Default config values
|
||||
-- modem boolean Set to false to skip modem discovery
|
||||
-- channelPrefix string Override for channel name prefix (defaults to name)
|
||||
-- @return table Service context
|
||||
function Service.create(opts)
|
||||
assert(opts and opts.name, 'Service name required')
|
||||
|
||||
Protocol.setServiceId(opts.name)
|
||||
|
||||
local prefix = opts.channelPrefix or opts.name
|
||||
local ctx = {
|
||||
name = opts.name,
|
||||
config = Config.load(opts.name, opts.defaults or {}),
|
||||
prefix = prefix,
|
||||
channels = Channels.forService(prefix),
|
||||
modem = nil,
|
||||
}
|
||||
|
||||
-- Discover modem
|
||||
if opts.modem ~= false then
|
||||
-- Prefer Opus device registry
|
||||
if _G.device and _G.device.wireless_modem then
|
||||
ctx.modem = _G.device.wireless_modem
|
||||
else
|
||||
-- Fallback: scan peripherals (wired or wireless)
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.getType(name) == 'modem' then
|
||||
ctx.modem = peripheral.wrap(name)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ctx
|
||||
end
|
||||
|
||||
--- Open all registered channels for this service on its modem.
|
||||
-- Respects dual-mode: opens both current and target channels when appropriate.
|
||||
-- @param ctx table Service context from Service.create()
|
||||
function Service.openChannels(ctx)
|
||||
if not ctx.modem then return end
|
||||
|
||||
local channelList = Channels.openListForService(ctx.prefix)
|
||||
for _, ch in ipairs(channelList) do
|
||||
ctx.modem.open(ch)
|
||||
end
|
||||
end
|
||||
|
||||
--- Close all registered channels for this service.
|
||||
-- @param ctx table Service context
|
||||
function Service.closeChannels(ctx)
|
||||
if not ctx.modem then return end
|
||||
|
||||
local channelList = Channels.openListForService(ctx.prefix)
|
||||
for _, ch in ipairs(channelList) do
|
||||
ctx.modem.close(ch)
|
||||
end
|
||||
end
|
||||
|
||||
--- Send a message on a single named channel.
|
||||
-- Uses Protocol.message() to construct a well-formed envelope.
|
||||
-- @param ctx table Service context
|
||||
-- @param channelName string Logical channel name (e.g. 'inventory.broadcast')
|
||||
-- @param msgType string Message type discriminator
|
||||
-- @param data table|nil Payload fields
|
||||
-- @param opts table|nil Protocol.message options (id, src, noMeta)
|
||||
-- @return boolean true if sent successfully
|
||||
function Service.send(ctx, channelName, msgType, data, opts)
|
||||
if not ctx.modem then return false end
|
||||
local ch = Channels.get(channelName)
|
||||
local msg = Protocol.message(msgType, data, opts)
|
||||
ctx.modem.transmit(ch, ch, msg)
|
||||
return true
|
||||
end
|
||||
|
||||
--- Broadcast a message, sending on both channels during dual-mode migration.
|
||||
-- In current or target mode, behaves identically to send().
|
||||
-- @param ctx table Service context
|
||||
-- @param channelName string Logical channel name
|
||||
-- @param msgType string Message type discriminator
|
||||
-- @param data table|nil Payload fields
|
||||
-- @param opts table|nil Protocol.message options
|
||||
-- @return boolean true if sent on at least one channel
|
||||
function Service.broadcast(ctx, channelName, msgType, data, opts)
|
||||
if not ctx.modem then return false end
|
||||
|
||||
local msg = Protocol.message(msgType, data, opts)
|
||||
|
||||
if Channels.mode == 'dual' then
|
||||
local sent = false
|
||||
for _, ch in ipairs(Channels.getBoth(channelName)) do
|
||||
ctx.modem.transmit(ch, ch, msg)
|
||||
sent = true
|
||||
end
|
||||
return sent
|
||||
else
|
||||
local ch = Channels.get(channelName)
|
||||
ctx.modem.transmit(ch, ch, msg)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
--- Save the service config back to persistent storage.
|
||||
-- @param ctx table Service context
|
||||
function Service.saveConfig(ctx)
|
||||
Config.update(ctx.name, ctx.config)
|
||||
end
|
||||
|
||||
--- Reload the service config from persistent storage.
|
||||
-- @param ctx table Service context
|
||||
-- @param defaults table|nil Default values
|
||||
function Service.reloadConfig(ctx, defaults)
|
||||
ctx.config = Config.load(ctx.name, defaults or ctx.config)
|
||||
end
|
||||
|
||||
return Service
|
||||
Reference in New Issue
Block a user