サービス (services)¶
このページは、signage-aws-nodejs の サービス層(HTTP ルート/Socket 層の橋渡し)を 1 ページで俯瞰します。
対象:services/* 配下(Command / DeviceSettings / Playlist / File Download / Socket Helper)
Note
ここでは 設計骨子と I/F 仕様に集中します。実装詳細は各ソースを参照。
共通方針・前提¶
ソケット取得とデバイス識別¶
- 端末ごとの接続は
deviceSockets: Map<deviceId, socketId>で管理(定義元は Socket 層)。 - Socket.IO サーバは
getIO()から取得。io.sockets.sockets.get(socketId)で実体へ。 - デバイス未接続/Socket 不明は 404 を返す(もしくは
statusを付与して throw)。
ACK 付きイベント送受(相関 ID)¶
emitWithAck(socketId, event, payload, ackEvent, timeoutMs)を使用。- 相関 ID は
payload.requestIdと ACK 側res.requestIdの一致で判断。 - タイムアウトは用途別(例:設定 1s、プレイリスト 5s)。
エラー/タイムアウトの扱い¶
- HTTP レイヤから呼ばれるサービスは、
statusを持つ Error を上位で適切に変換。 - タイムアウトは原則 504 相当(
error.status = 504を付与)。
依存(抜粋)
getIO(),deviceSocketsは Socket 層の責務。サービスでは存在前提で使用。- 外部 HTTP 取得(
downloadService)はネットワーク障害に依存するため再試行方針は上位で検討。
1) Command(emitCommand / validators)¶
emitCommand(services/command/emitCommand.js)¶
目的:デバイスへ単発イベントを ACK なしで送信。
I/F
- 入力:
deviceId,eventName,payload,res(Express Response) - 出力:
200 { ok: true, message } - 失敗:
400…deviceId未指定404… デバイス未接続 / Socket 実体なし
フロー
1) deviceId → socketId 解決(deviceSockets)
2) getIO() → sock 取得
3) sock.emit(eventName, payload)
4) 送信結果を JSON で返却
Note
- UI 側が ACK を返さない制御系に向く。到達性保証が必要な場合は
emitWithAckを検討。
validators(services/command/validators.js)¶
目的:コマンド用クエリの入力検証(Joi)
スキーマ(抜粋)
deviceId: string(required)command: enum('playVideo','showImage','switchView','switchViewYT')(required)fileName: string(optional, allow(''))isSingle: boolean(optional)(truthy('true')/falsy('false')サポート)
方針
- ルート層での事前検証により、サービス層を単純化。
2) DeviceSettings Service(
services/deviceSettingsService.js)¶
目的:端末側の自動再生設定など 設定取得/更新 を ACK 付きイベントで行う。
定数
ACK_GET = 'configResponse'ACK_UPDATE = 'configUpdated'TIMEOUT_MS = 1000(1 秒)
内部
ensureSocket(deviceId):接続検証。未初期化 / 未接続 / 実体なしはstatus付きで throw。
API
get(deviceId) -> { autoPlaylist: boolean }
送信:event='getConfig',payload={ deviceId, requestId },ack='configResponse'
タイムアウト時はstatus=504を付与して再 throw。update(deviceId, updates) -> { autoPlaylist: boolean }
送信:event='updateConfig',payload={ deviceId, ...updates, requestId },ack='configUpdated'
タイムアウト時は同上。
戻り値(正規化)
autoPlaylistは 二値化(!!)して返却。
3) Playlist Service(
services/playlistService.js)¶
目的:プレイリストの取得・更新・削除・サムネ取得など。
共通
DEFAULT_TIMEOUT = 5000(5 秒)ensureSocket(deviceId):deviceSocketsのみで最小限検証(未接続は Error)
API
1) fetchPlaylist(deviceId) -> playlist
送信: event='updatePlaylist', payload={ requestId, action:'list' }
ACK: 'playlistUpdateResponse'
2) updateItem(deviceId, payload) -> updatedPlaylist
送信: event='updatePlaylist', payload={ requestId, ...payload }
ACK: 'playlistUpdateResponse'
3) modifyItem(deviceId, payload) -> updatedPlaylist
機能同上(汎用更新ラッパ)
4) removeItem(deviceId, uuid)
modifyItem(deviceId, { action:'remove', uuid })
5) fetchThumbnail(deviceId, eventName, thumbFile) -> buffer
送信: event=eventName, payload={ requestId, fileName: thumbFile }
ACK: 'thumbnailResponse'
6) clearFile(deviceId, playlistName) -> result
送信: event='clearPlaylistFile', payload={ requestId, playlistName }
ACK: 'playlistUpdateResponse'
Note
- ACK 応答に含まれるフィールド名(
playlist,updatedPlaylist,buffer,result)は UI 実装と厳密一致が必要。
4) File Download Service(
services/file/downloadService.js)¶
目的:外部 URL のファイルを Buffer 取得。
API
fetchFileBuffer(fileUrl) -> Buffer実装:axios.get(fileUrl, { responseType:'arraybuffer' })→Buffer.from(data)
Note
- ネットワーク障害・サイズ制限は上位(呼び出し元)で対処。
- セキュリティ(許可ドメイン/拡張子/サイズ上限)は事前バリデーション推奨。
5) Socket Helper(
services/socket/emitWithAck.js)¶
目的:ACK 付きイベント送信の共通ユーティリティ。
I/F
emitWithAck(socketId, event, payload, ackEvent = event, timeoutMs = 5000) -> Promise<ackRes>
挙動
1) getIO() → sock 取得(無ければ Error('No socket id=...'))
2) ackEvent に対して 一時的なリスナを登録
3) setTimeout で timeoutMs 経過時に リスナ解除→reject
4) 受信 res の requestId が payload.requestId と一致 → リスナ解除→resolve
5) 常に sock.emit(event, payload) を最終行で送出
設計意図
- 相関 ID により、同種イベントの同時多発でも誤配を防止。
- リスナの確実な解除でメモリリーク・多重ハンドラを回避。
生成/変更されるもの¶
- 本ページで記載のサービスは 恒常的なファイル生成や OS 設定変更を行いません。
- 生成物(サムネイル等)は 端末側の責務です。
注意(運用上のポイント)¶
ACK 使い分け
到達性・同期待ちが不要な UI 操作は emitCommand(ACK 無し)、
一貫性が重要な設定・同期は emitWithAck(ACK あり) を利用。
タイムアウトと再試行
タイムアウト(504)時に無闇な自動再試行は、UI 側の重複処理を招く可能性。
クリティカルな操作は 冪等性を確保し、再送戦略を設計してください。
依存の明示
getIO() と deviceSockets の実装は Socket 層。本ページでは前提として扱います。