diff --git a/.github/brand/README.md b/.github/brand/README.md index 874e12877..ffe2493fd 100644 --- a/.github/brand/README.md +++ b/.github/brand/README.md @@ -2,6 +2,9 @@ - `svg/` contains editable source artwork. Do not reference these files from Markdown. - `png/` contains rendered assets for README files and other published uses. -- Run `node .github/brand/gen-banners.mjs` from the repository root after changing the banner template, localized text, or social preview source. +- Run `node .github/brand/gen-banners.mjs` from the repository root after changing the banner template, localized text, + or social preview source. -The generator reads `svg/banner.template.svg`, refreshes the localized SVG sources, and renders their PNG counterparts into `png/`. It also renders `svg/social-preview.svg` to `png/social-preview.png`, keeping SVG files as editable sources and PNG files as the published assets. +The generator reads `svg/banner.template.svg`, refreshes the localized SVG sources, and renders their PNG counterparts +into `png/`. It also renders `svg/social-preview.svg` to `png/social-preview.png`, keeping SVG files as editable sources +and PNG files as the published assets. diff --git a/.github/brand/gen-banners.mjs b/.github/brand/gen-banners.mjs index 58689f742..3020cd8dd 100644 --- a/.github/brand/gen-banners.mjs +++ b/.github/brand/gen-banners.mjs @@ -18,109 +18,109 @@ const pngDir = join(here, 'png') // 各语言文案(与仓库根各 README 的 hero 主标语保持同一译法;SENSE–DECIDE–ACT 为品牌术语,各语言保留原文) const LANGS = { - zh: { - tagline: '多协议 · 云原生 · 开源工业物联网平台', - techtags: '28+ 协议驱动 · 边云协同 · SENSE–DECIDE–ACT' - }, - en: { - tagline: 'Multi-protocol · Cloud-native · Open-source Industrial IoT Platform', - techtags: '28+ PROTOCOL DRIVERS · EDGE-CLOUD · SENSE–DECIDE–ACT' - }, - ja: { - tagline: 'マルチプロトコル · クラウドネイティブ · オープンソース産業 IoT プラットフォーム', - techtags: '28+ プロトコルドライバー · エッジ・クラウド協働 · SENSE–DECIDE–ACT' - }, - ko: { - tagline: '멀티 프로토콜 · 클라우드 네이티브 · 오픈소스 산업용 IoT 플랫폼', - techtags: '28+ 프로토콜 드라이버 · 엣지-클라우드 · SENSE–DECIDE–ACT' - }, - es: { - tagline: 'Multiprotocolo · Nativa de la nube · Plataforma de IoT Industrial de Código Abierto', - techtags: '28+ CONTROLADORES · BORDE-NUBE · SENSE–DECIDE–ACT' - }, - ru: { - tagline: 'Мультипротокольная · Облачно-нативная · Открытая платформа промышленного IoT', - techtags: '28+ ДРАЙВЕРОВ · EDGE-CLOUD · SENSE–DECIDE–ACT' - }, - vi: { - tagline: 'Đa giao thức · Cloud-native · Nền tảng IoT Công nghiệp Mã nguồn Mở', - techtags: '28+ DRIVER GIAO THỨC · EDGE-CLOUD · SENSE–DECIDE–ACT' - } + zh: { + tagline: '多协议 · 云原生 · 开源工业物联网平台', + techtags: '28+ 协议驱动 · 边云协同 · SENSE–DECIDE–ACT' + }, + en: { + tagline: 'Multi-protocol · Cloud-native · Open-source Industrial IoT Platform', + techtags: '28+ PROTOCOL DRIVERS · EDGE-CLOUD · SENSE–DECIDE–ACT' + }, + ja: { + tagline: 'マルチプロトコル · クラウドネイティブ · オープンソース産業 IoT プラットフォーム', + techtags: '28+ プロトコルドライバー · エッジ・クラウド協働 · SENSE–DECIDE–ACT' + }, + ko: { + tagline: '멀티 프로토콜 · 클라우드 네이티브 · 오픈소스 산업용 IoT 플랫폼', + techtags: '28+ 프로토콜 드라이버 · 엣지-클라우드 · SENSE–DECIDE–ACT' + }, + es: { + tagline: 'Multiprotocolo · Nativa de la nube · Plataforma de IoT Industrial de Código Abierto', + techtags: '28+ CONTROLADORES · BORDE-NUBE · SENSE–DECIDE–ACT' + }, + ru: { + tagline: 'Мультипротокольная · Облачно-нативная · Открытая платформа промышленного IoT', + techtags: '28+ ДРАЙВЕРОВ · EDGE-CLOUD · SENSE–DECIDE–ACT' + }, + vi: { + tagline: 'Đa giao thức · Cloud-native · Nền tảng IoT Công nghiệp Mã nguồn Mở', + techtags: '28+ DRIVER GIAO THỨC · EDGE-CLOUD · SENSE–DECIDE–ACT' + } } function loadPlaywright() { - const candidates = [ - process.env.BANNER_PLAYWRIGHT_DIR, - join(here, '..', '..', '..', 'iot-dc3-online', 'node_modules') - ].filter(Boolean) - for (const root of candidates) { - // createRequire 基准要放在包内部(pnpm 符号链接结构下,从 node_modules 目录本身解析会失败) - const pkg = join(root, 'playwright', 'package.json') - if (!existsSync(pkg)) continue - try { - return createRequire(pkg)('playwright') - } catch { - // try next candidate + const candidates = [ + process.env.BANNER_PLAYWRIGHT_DIR, + join(here, '..', '..', '..', 'iot-dc3-online', 'node_modules') + ].filter(Boolean) + for (const root of candidates) { + // createRequire 基准要放在包内部(pnpm 符号链接结构下,从 node_modules 目录本身解析会失败) + const pkg = join(root, 'playwright', 'package.json') + if (!existsSync(pkg)) continue + try { + return createRequire(pkg)('playwright') + } catch { + // try next candidate + } } - } - throw new Error('playwright 不可用:请在 iot-dc3-online 中安装依赖,或设置 BANNER_PLAYWRIGHT_DIR 指向含 playwright 的 node_modules') + throw new Error('playwright 不可用:请在 iot-dc3-online 中安装依赖,或设置 BANNER_PLAYWRIGHT_DIR 指向含 playwright 的 node_modules') } const template = await async function () { - const {readFile} = await import('node:fs/promises') - return readFile(join(svgDir, 'banner.template.svg'), 'utf-8') + const {readFile} = await import('node:fs/promises') + return readFile(join(svgDir, 'banner.template.svg'), 'utf-8') }() for (const [lang, {tagline, techtags}] of Object.entries(LANGS)) { - const svg = template.replaceAll('{{TAGLINE}}', tagline).replaceAll('{{TECHTAGS}}', techtags) - await (await import('node:fs/promises')).writeFile(join(svgDir, `banner.${lang}.svg`), svg) + const svg = template.replaceAll('{{TAGLINE}}', tagline).replaceAll('{{TECHTAGS}}', techtags) + await (await import('node:fs/promises')).writeFile(join(svgDir, `banner.${lang}.svg`), svg) } const {chromium} = loadPlaywright() const browserExecutable = [ - process.env.BANNER_BROWSER_PATH, - 'C:/Program Files/Google/Chrome/Application/chrome.exe', - 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe', - '/usr/bin/google-chrome', - '/usr/bin/chromium', - '/usr/bin/chromium-browser' + process.env.BANNER_BROWSER_PATH, + 'C:/Program Files/Google/Chrome/Application/chrome.exe', + 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe', + '/usr/bin/google-chrome', + '/usr/bin/chromium', + '/usr/bin/chromium-browser' ].filter(Boolean).find(existsSync) const browser = await chromium.launch(browserExecutable ? {executablePath: browserExecutable} : {}) async function assertTextWithinSafeArea(page, label) { - const overflows = await page.$$eval('text[data-min-x], text[data-max-x]', nodes => nodes.flatMap(node => { - const box = node.getBoundingClientRect() - const left = box.left - const right = box.right - const minX = node.dataset.minX === undefined ? Number.NEGATIVE_INFINITY : Number(node.dataset.minX) - const maxX = node.dataset.maxX === undefined ? Number.POSITIVE_INFINITY : Number(node.dataset.maxX) - return left < minX - 0.5 || right > maxX + 0.5 - ? [{text: node.textContent, left, right, minX, maxX}] - : [] - })) - if (overflows.length > 0) { - throw new Error(`${label} text exceeds its safe area: ${JSON.stringify(overflows)}`) - } + const overflows = await page.$$eval('text[data-min-x], text[data-max-x]', nodes => nodes.flatMap(node => { + const box = node.getBoundingClientRect() + const left = box.left + const right = box.right + const minX = node.dataset.minX === undefined ? Number.NEGATIVE_INFINITY : Number(node.dataset.minX) + const maxX = node.dataset.maxX === undefined ? Number.POSITIVE_INFINITY : Number(node.dataset.maxX) + return left < minX - 0.5 || right > maxX + 0.5 + ? [{text: node.textContent, left, right, minX, maxX}] + : [] + })) + if (overflows.length > 0) { + throw new Error(`${label} text exceeds its safe area: ${JSON.stringify(overflows)}`) + } } for (const lang of Object.keys(LANGS)) { - const page = await browser.newPage({viewport: {width: 1600, height: 420}, deviceScaleFactor: 2}) - await page.goto('file://' + join(svgDir, `banner.${lang}.svg`)) - await assertTextWithinSafeArea(page, `banner.${lang}`) - await page.screenshot({ - path: join(pngDir, `banner.${lang}.png`), - clip: {x: 0, y: 0, width: 1600, height: 420} - }) - await page.close() - console.log(`generated svg/banner.${lang}.svg / png/banner.${lang}.png`) + const page = await browser.newPage({viewport: {width: 1600, height: 420}, deviceScaleFactor: 2}) + await page.goto('file://' + join(svgDir, `banner.${lang}.svg`)) + await assertTextWithinSafeArea(page, `banner.${lang}`) + await page.screenshot({ + path: join(pngDir, `banner.${lang}.png`), + clip: {x: 0, y: 0, width: 1600, height: 420} + }) + await page.close() + console.log(`generated svg/banner.${lang}.svg / png/banner.${lang}.png`) } const socialPage = await browser.newPage({viewport: {width: 1200, height: 600}}) await socialPage.goto('file://' + join(svgDir, 'social-preview.svg')) await assertTextWithinSafeArea(socialPage, 'social preview') await socialPage.screenshot({ - path: join(pngDir, 'social-preview.png'), - clip: {x: 0, y: 0, width: 1200, height: 600} + path: join(pngDir, 'social-preview.png'), + clip: {x: 0, y: 0, width: 1200, height: 600} }) await socialPage.close() console.log('generated svg/social-preview.svg / png/social-preview.png') diff --git a/.github/brand/svg/banner.en.svg b/.github/brand/svg/banner.en.svg index 81eb44a0e..22b6f977d 100644 --- a/.github/brand/svg/banner.en.svg +++ b/.github/brand/svg/banner.en.svg @@ -2,125 +2,163 @@ 修改设计只改本文件,然后运行 gen-banners.mjs 重新生成全部语言 svg+png。 占位符:Multi-protocol · Cloud-native · Open-source Industrial IoT Platform 定位语 / 28+ PROTOCOL DRIVERS · EDGE-CLOUD · SENSE–DECIDE–ACT 技术标签(各语言文案在 gen-banners.mjs 的 LANGS 表维护)。 品牌标题与 social-preview.svg 使用相同的 Segoe UI Variable Display 字体栈。 --> - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IoT DC3 - - Multi-protocol · Cloud-native · Open-source Industrial IoT Platform - 28+ PROTOCOL DRIVERS · EDGE-CLOUD · SENSE–DECIDE–ACT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IoT DC3 + + + Multi-protocol · Cloud-native · Open-source Industrial IoT Platform + + 28+ PROTOCOL DRIVERS · EDGE-CLOUD · SENSE–DECIDE–ACT + diff --git a/.github/brand/svg/banner.es.svg b/.github/brand/svg/banner.es.svg index 4cbf6cdd0..5fb5c1878 100644 --- a/.github/brand/svg/banner.es.svg +++ b/.github/brand/svg/banner.es.svg @@ -2,125 +2,164 @@ 修改设计只改本文件,然后运行 gen-banners.mjs 重新生成全部语言 svg+png。 占位符:Multiprotocolo · Nativa de la nube · Plataforma de IoT Industrial de Código Abierto 定位语 / 28+ CONTROLADORES · BORDE-NUBE · SENSE–DECIDE–ACT 技术标签(各语言文案在 gen-banners.mjs 的 LANGS 表维护)。 品牌标题与 social-preview.svg 使用相同的 Segoe UI Variable Display 字体栈。 --> - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IoT DC3 - - Multiprotocolo · Nativa de la nube · Plataforma de IoT Industrial de Código Abierto - 28+ CONTROLADORES · BORDE-NUBE · SENSE–DECIDE–ACT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IoT DC3 + + + Multiprotocolo · Nativa de la nube · Plataforma de IoT Industrial de + Código Abierto + + 28+ CONTROLADORES · BORDE-NUBE · SENSE–DECIDE–ACT + diff --git a/.github/brand/svg/banner.ja.svg b/.github/brand/svg/banner.ja.svg index 28cd5ecd8..102ae8a3d 100644 --- a/.github/brand/svg/banner.ja.svg +++ b/.github/brand/svg/banner.ja.svg @@ -2,125 +2,163 @@ 修改设计只改本文件,然后运行 gen-banners.mjs 重新生成全部语言 svg+png。 占位符:マルチプロトコル · クラウドネイティブ · オープンソース産業 IoT プラットフォーム 定位语 / 28+ プロトコルドライバー · エッジ・クラウド協働 · SENSE–DECIDE–ACT 技术标签(各语言文案在 gen-banners.mjs 的 LANGS 表维护)。 品牌标题与 social-preview.svg 使用相同的 Segoe UI Variable Display 字体栈。 --> - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IoT DC3 - - マルチプロトコル · クラウドネイティブ · オープンソース産業 IoT プラットフォーム - 28+ プロトコルドライバー · エッジ・クラウド協働 · SENSE–DECIDE–ACT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IoT DC3 + + + マルチプロトコル · クラウドネイティブ · オープンソース産業 IoT プラットフォーム + + 28+ プロトコルドライバー · エッジ・クラウド協働 · SENSE–DECIDE–ACT + diff --git a/.github/brand/svg/banner.ko.svg b/.github/brand/svg/banner.ko.svg index 37ac3a5da..d9b1db38e 100644 --- a/.github/brand/svg/banner.ko.svg +++ b/.github/brand/svg/banner.ko.svg @@ -2,125 +2,163 @@ 修改设计只改本文件,然后运行 gen-banners.mjs 重新生成全部语言 svg+png。 占位符:멀티 프로토콜 · 클라우드 네이티브 · 오픈소스 산업용 IoT 플랫폼 定位语 / 28+ 프로토콜 드라이버 · 엣지-클라우드 · SENSE–DECIDE–ACT 技术标签(各语言文案在 gen-banners.mjs 的 LANGS 表维护)。 品牌标题与 social-preview.svg 使用相同的 Segoe UI Variable Display 字体栈。 --> - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IoT DC3 - - 멀티 프로토콜 · 클라우드 네이티브 · 오픈소스 산업용 IoT 플랫폼 - 28+ 프로토콜 드라이버 · 엣지-클라우드 · SENSE–DECIDE–ACT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IoT DC3 + + + 멀티 프로토콜 · 클라우드 네이티브 · 오픈소스 산업용 IoT 플랫폼 + + 28+ 프로토콜 드라이버 · 엣지-클라우드 · SENSE–DECIDE–ACT + diff --git a/.github/brand/svg/banner.ru.svg b/.github/brand/svg/banner.ru.svg index 7449d3765..aa4e57802 100644 --- a/.github/brand/svg/banner.ru.svg +++ b/.github/brand/svg/banner.ru.svg @@ -2,125 +2,164 @@ 修改设计只改本文件,然后运行 gen-banners.mjs 重新生成全部语言 svg+png。 占位符:Мультипротокольная · Облачно-нативная · Открытая платформа промышленного IoT 定位语 / 28+ ДРАЙВЕРОВ · EDGE-CLOUD · SENSE–DECIDE–ACT 技术标签(各语言文案在 gen-banners.mjs 的 LANGS 表维护)。 品牌标题与 social-preview.svg 使用相同的 Segoe UI Variable Display 字体栈。 --> - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IoT DC3 - - Мультипротокольная · Облачно-нативная · Открытая платформа промышленного IoT - 28+ ДРАЙВЕРОВ · EDGE-CLOUD · SENSE–DECIDE–ACT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IoT DC3 + + + Мультипротокольная · Облачно-нативная · Открытая платформа промышленного + IoT + + 28+ ДРАЙВЕРОВ · EDGE-CLOUD · SENSE–DECIDE–ACT + diff --git a/.github/brand/svg/banner.template.svg b/.github/brand/svg/banner.template.svg index 0662e4195..31caf8728 100644 --- a/.github/brand/svg/banner.template.svg +++ b/.github/brand/svg/banner.template.svg @@ -2,125 +2,163 @@ 修改设计只改本文件,然后运行 gen-banners.mjs 重新生成全部语言 svg+png。 占位符:{{TAGLINE}} 定位语 / {{TECHTAGS}} 技术标签(各语言文案在 gen-banners.mjs 的 LANGS 表维护)。 品牌标题与 social-preview.svg 使用相同的 Segoe UI Variable Display 字体栈。 --> - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IoT DC3 - - {{TAGLINE}} - {{TECHTAGS}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IoT DC3 + + + {{TAGLINE}} + + {{TECHTAGS}} + diff --git a/.github/brand/svg/banner.vi.svg b/.github/brand/svg/banner.vi.svg index 9730036cc..bbb52642c 100644 --- a/.github/brand/svg/banner.vi.svg +++ b/.github/brand/svg/banner.vi.svg @@ -2,125 +2,163 @@ 修改设计只改本文件,然后运行 gen-banners.mjs 重新生成全部语言 svg+png。 占位符:Đa giao thức · Cloud-native · Nền tảng IoT Công nghiệp Mã nguồn Mở 定位语 / 28+ DRIVER GIAO THỨC · EDGE-CLOUD · SENSE–DECIDE–ACT 技术标签(各语言文案在 gen-banners.mjs 的 LANGS 表维护)。 品牌标题与 social-preview.svg 使用相同的 Segoe UI Variable Display 字体栈。 --> - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IoT DC3 - - Đa giao thức · Cloud-native · Nền tảng IoT Công nghiệp Mã nguồn Mở - 28+ DRIVER GIAO THỨC · EDGE-CLOUD · SENSE–DECIDE–ACT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IoT DC3 + + + Đa giao thức · Cloud-native · Nền tảng IoT Công nghiệp Mã nguồn Mở + + 28+ DRIVER GIAO THỨC · EDGE-CLOUD · SENSE–DECIDE–ACT + diff --git a/.github/brand/svg/banner.zh.svg b/.github/brand/svg/banner.zh.svg index 51c2ae7bc..be441522b 100644 --- a/.github/brand/svg/banner.zh.svg +++ b/.github/brand/svg/banner.zh.svg @@ -2,125 +2,163 @@ 修改设计只改本文件,然后运行 gen-banners.mjs 重新生成全部语言 svg+png。 占位符:多协议 · 云原生 · 开源工业物联网平台 定位语 / 28+ 协议驱动 · 边云协同 · SENSE–DECIDE–ACT 技术标签(各语言文案在 gen-banners.mjs 的 LANGS 表维护)。 品牌标题与 social-preview.svg 使用相同的 Segoe UI Variable Display 字体栈。 --> - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IoT DC3 - - 多协议 · 云原生 · 开源工业物联网平台 - 28+ 协议驱动 · 边云协同 · SENSE–DECIDE–ACT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IoT DC3 + + + 多协议 · 云原生 · 开源工业物联网平台 + + 28+ 协议驱动 · 边云协同 · SENSE–DECIDE–ACT + diff --git a/.github/brand/svg/social-preview.svg b/.github/brand/svg/social-preview.svg index 4169a83ef..0a33b13a2 100644 --- a/.github/brand/svg/social-preview.svg +++ b/.github/brand/svg/social-preview.svg @@ -1,142 +1,188 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IoT DC3 - Multi-protocol · Cloud-native · Open-source Industrial IoT Platform - 28+ PROTOCOL DRIVERS · EDGE-CLOUD · SENSE–DECIDE–ACT - - - - - dc3.site · OPEN SOURCE · AGPL-3.0 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IoT DC3 + + Multi-protocol · Cloud-native · Open-source + Industrial IoT Platform + + 28+ PROTOCOL DRIVERS · EDGE-CLOUD · SENSE–DECIDE–ACT + + + + + + dc3.site · OPEN SOURCE · AGPL-3.0 + + diff --git a/AGENTS.md b/AGENTS.md index 64ec7366a..09fc7f1c2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,22 +13,23 @@ Canonical engineering instructions for AI coding agents working in IoT DC3. Avoid copying volatile versions or generated state into documentation. Verify them at the source: -| Concern | Source of truth | -|---|---| -| Java, Spring, Maven plugins and reactor modules | root `pom.xml` and affected module POMs | -| Backend build commands | root `Makefile` | -| Frontend dependencies and scripts | `dc3-web/package.json` and `dc3-web/pnpm-lock.yaml` | -| Frontend build image/tool pins | `dc3-web/Dockerfile` | -| Containers and registries | root `Makefile`, `.env.example`, and `dc3/docker-compose*.yml` | -| CI behaviour | `.github/workflows/` | -| Release notes | `dc3/bin/changelog.py` and generated `dc3/doc/CHANGE.md` | +| Concern | Source of truth | +|-------------------------------------------------|----------------------------------------------------------------| +| Java, Spring, Maven plugins and reactor modules | root `pom.xml` and affected module POMs | +| Backend build commands | root `Makefile` | +| Frontend dependencies and scripts | `dc3-web/package.json` and `dc3-web/pnpm-lock.yaml` | +| Frontend build image/tool pins | `dc3-web/Dockerfile` | +| Containers and registries | root `Makefile`, `.env.example`, and `dc3/docker-compose*.yml` | +| CI behaviour | `.github/workflows/` | +| Release notes | `dc3/bin/changelog.py` and generated `dc3/doc/CHANGE.md` | -If this file disagrees with executable configuration, treat the executable configuration as current and update this -file as part of the same change when appropriate. +If this file disagrees with executable configuration, treat the executable configuration as current and update this file +as part of the same change when appropriate. ## Project overview -IoT DC3 is a multi-protocol, cloud-native, open-source industrial IoT platform evolving toward AI agents. Its main runtime areas are: +IoT DC3 is a multi-protocol, cloud-native, open-source industrial IoT platform evolving toward AI agents. Its main +runtime areas are: - Gateway: HTTP entrypoint through Spring Cloud Gateway. - Auth Center: tenant, token, user, role, resource, and API authorization. @@ -81,14 +82,14 @@ Controller (WebFlux) -> Service (BO) -> Manager (DO) -> Mapper (SQL) Common types: -| Type | Module | Role | -|---|---|---| -| `BaseService` | `dc3-common-public` | base CRUD service contract | -| `BaseController` | `dc3-common-web` | reactive controller helpers and user/tenant context | -| `R` | `dc3-common-public` | standard response envelope; use `R.ok(...)` and `R.fail(...)` | -| `BaseBO`, `BaseVO`, `BaseDTO` | `dc3-common-model` | shared business, web, and transfer fields | -| `BaseBuilder` | `dc3-common-model` | MapStruct conversion base | -| `TenantOwned` | `dc3-common-public` | marker for tenant-scoped entities | +| Type | Module | Role | +|-------------------------------|---------------------|---------------------------------------------------------------| +| `BaseService` | `dc3-common-public` | base CRUD service contract | +| `BaseController` | `dc3-common-web` | reactive controller helpers and user/tenant context | +| `R` | `dc3-common-public` | standard response envelope; use `R.ok(...)` and `R.fail(...)` | +| `BaseBO`, `BaseVO`, `BaseDTO` | `dc3-common-model` | shared business, web, and transfer fields | +| `BaseBuilder` | `dc3-common-model` | MapStruct conversion base | +| `TenantOwned` | `dc3-common-public` | marker for tenant-scoped entities | ### Shared code placement @@ -102,12 +103,12 @@ shared infrastructure. models may reference enums owned by `dc3-common-constant`. - Keep framework- or capability-specific public helpers in the narrowest owning module, such as gRPC conversion in `dc3-common-api`, WebFlux helpers in `dc3-common-web`, and RabbitMQ helpers in `dc3-common-rabbitmq`. -- Keep constants and nested enums used by only one module, protocol, configuration object, or implementation beside - that owner. Reserve top-level `*Constant` classes and top-level public enums for `dc3-common-constant`; use a +- Keep constants and nested enums used by only one module, protocol, configuration object, or implementation beside that + owner. Reserve top-level `*Constant` classes and top-level public enums for `dc3-common-constant`; use a concern-specific local name such as `*Limits`/`*Defaults`, a nested enum, or a private field until the concept becomes a stable cross-module contract. -- Do not duplicate cross-module wire names, header names, routing identifiers, cache-key fragments, or persistence codes. - Define one canonical symbol in `dc3-common-constant` and migrate callers together. +- Do not duplicate cross-module wire names, header names, routing identifiers, cache-key fragments, or persistence + codes. Define one canonical symbol in `dc3-common-constant` and migrate callers together. - Preserve the dependency floor: `dc3-common-constant` must not depend on other DC3 modules, and `dc3-common-public` must not depend on capability modules. @@ -169,13 +170,13 @@ Driver `application.yml` metadata is user-facing: CRUD-shaped names reflect result cardinality across Service, Controller, Facade, gRPC server, and proto RPCs: -| Action | Java | HTTP | gRPC | -|---|---|---|---| -| create one | `add(BO)` | `/add` | n/a | -| delete by ID | `delete(Long)` | `/delete` | n/a | -| update one | `update(BO)` | `/update` | n/a | -| return one | `getXxx(...)` | `/get_xxx` | `GetXxx` | -| return many | `listXxx(...)` | `/list_xxx` | `ListXxx` | +| Action | Java | HTTP | gRPC | +|--------------|----------------|-------------|-----------| +| create one | `add(BO)` | `/add` | n/a | +| delete by ID | `delete(Long)` | `/delete` | n/a | +| update one | `update(BO)` | `/update` | n/a | +| return one | `getXxx(...)` | `/get_xxx` | `GetXxx` | +| return many | `listXxx(...)` | `/list_xxx` | `ListXxx` | - Base CRUD comes from `BaseService`: `add`, `delete`, `update`, `getById`, and `list(Q)`. - Reserve `select*` for raw Mapper/Manager persistence operations. @@ -195,8 +196,8 @@ CRUD-shaped names reflect result cardinality across Service, Controller, Facade, - `*FlagEnum` is for boolean-like toggles, `*StatusEnum` for state machines, and `*TypeEnum` for classifications. - Enum constants use descriptive `UPPER_SNAKE_CASE`; enum `code` values use lowercase tokens. - Do not introduce magic flag constants such as `private static final Byte DEFAULT = 1`. -- Do not expose secrets in VOs. Exclude `apiKey`, `password`, `secret`, `token`, and credential fields from serialization - and Lombok `@ToString`. +- Do not expose secrets in VOs. Exclude `apiKey`, `password`, `secret`, `token`, and credential fields from + serialization and Lombok `@ToString`. ### Web API and OpenAPI @@ -238,7 +239,8 @@ Key rules: - `verbatimModuleSyntax` is enabled. Use `import type` for every type-only import; Vue components, functions, and icons remain normal value imports. - Use `Form` for create/update payloads and `Record` for read responses. -- Represent Java 64-bit IDs as strings. The backend emits identifiers as JSON strings on the HTTP contract, so standard JSON parsing (no JSONBigInt) is sufficient. +- Represent Java 64-bit IDs as strings. The backend emits identifiers as JSON strings on the HTTP contract, so standard + JSON parsing (no JSONBigInt) is sufficient. - API wrappers mirror backend cardinality: `getXxx` for one value, `listXxx` for collections/maps/pages, and `addXxx`/`updateXxx`/`deleteXxx` for mutations. - Reuse CRUD helpers from `src/api/common.ts` and API bases from `src/config/constant/api.ts`; keep API wrappers thin. @@ -296,8 +298,8 @@ mvn -s .mvn/settings.xml test -pl dc3-common/dc3-common-public \ When using `-am` together with `-Dtest`, add `-Dsurefire.failIfNoSpecifiedTests=false` so dependency modules without the selected test do not fail spuriously. -GitHub Actions should normally use public Maven repositories rather than the local mirror settings unless a workflow -is intentionally testing that mirror. +GitHub Actions should normally use public Maven repositories rather than the local mirror settings unless a workflow is +intentionally testing that mirror. ## Environment and Compose @@ -322,8 +324,8 @@ Compose change, validate every touched stack with its corresponding `make config ### Test types -- Unit tests (`*Test.java`, `*Tests.java`) run with Surefire, JUnit 5, Mockito, AssertJ, and Reactor `StepVerifier` where - appropriate. Do not start a Spring context for a test that can use direct construction. +- Unit tests (`*Test.java`, `*Tests.java`) run with Surefire, JUnit 5, Mockito, AssertJ, and Reactor `StepVerifier` + where appropriate. Do not start a Spring context for a test that can use direct construction. - Integration tests (`*IT.java`) run with Failsafe and may use `dc3-common-test` Testcontainers and harnesses. - E2E tests live in `dc3-e2e/` and are gated by the `DC3_E2E` environment variable. @@ -379,10 +381,10 @@ or create a GitHub Release directly. The `Docker Images` workflow owns release v GitHub Release creation. Keep its `release` environment protected with required reviewers and tag restrictions. If a version lands in `CHANGE.md` without a matching GitHub Release, backfill it with `make release-backfill` -(dry-run) or `make release-backfill-apply`. The tool maps date-formatted versions to same-day commits, assembles -the standard release body (TITLE.md + changelog block + RELEASE-FOOTER.md quick start that links docs.dc3.site), -and creates tags through the API - no image publishing, no `latest` pointer change. After TITLE.md or -RELEASE-FOOTER.md evolves, `make release-backfill-refresh` re-renders existing backfilled release bodies. +(dry-run) or `make release-backfill-apply`. The tool maps date-formatted versions to same-day commits, assembles the +standard release body (TITLE.md + changelog block + RELEASE-FOOTER.md quick start that links docs.dc3.site), and creates +tags through the API - no image publishing, no `latest` pointer change. After TITLE.md or RELEASE-FOOTER.md evolves, +`make release-backfill-refresh` re-renders existing backfilled release bodies. ## Commit rules diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2763b91b0..fb7b81508 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,18 +119,18 @@ Generated changelog-only release commits are skipped by default so rerunning the ### Backfilling Missing Releases -Every version recorded in `CHANGE.md` should end up with a GitHub Release. When a release window is skipped (a -version is committed to `CHANGE.md` but never tagged), close the gap without re-tagging history: +Every version recorded in `CHANGE.md` should end up with a GitHub Release. When a release window is skipped (a version +is committed to `CHANGE.md` but never tagged), close the gap without re-tagging history: ```bash make release-backfill # dry-run: list CHANGE.md versions that have no release make release-backfill-apply # create them (gh CLI, authenticated) ``` -The tool maps each date-formatted version to the last commit dated on or before that version day, assembles the -release body from `TITLE.md` + the version's changelog block + the `RELEASE-FOOTER.md` quick start (deep usage and -deployment content lives on docs.dc3.site), and creates the tag through the GitHub API — which does **not** trigger -the `Docker Images` workflow and never moves the `latest` pointer. When `TITLE.md` or `RELEASE-FOOTER.md` evolves, +The tool maps each date-formatted version to the last commit dated on or before that version day, assembles the release +body from `TITLE.md` + the version's changelog block + the `RELEASE-FOOTER.md` quick start (deep usage and deployment +content lives on docs.dc3.site), and creates the tag through the GitHub API — which does **not** trigger the +`Docker Images` workflow and never moves the `latest` pointer. When `TITLE.md` or `RELEASE-FOOTER.md` evolves, `make release-backfill-refresh` re-renders the bodies of already-backfilled releases. Run the dry-run periodically to catch drift between `CHANGE.md` and the release list. diff --git a/README.ai.md b/README.ai.md index 2fde2fdf9..d118bc0e1 100644 --- a/README.ai.md +++ b/README.ai.md @@ -9,8 +9,9 @@ If you are an AI assistant being asked to modify this repository, read [AGENTS.m ## What Is IoT DC3? -IoT DC3 is a multi-protocol, cloud-native, open-source industrial IoT platform evolving toward AI agents. It helps teams connect devices, collect telemetry, -manage device metadata, query data, dispatch commands, and run AI-assisted operations as a native platform capability. +IoT DC3 is a multi-protocol, cloud-native, open-source industrial IoT platform evolving toward AI agents. It helps teams +connect devices, collect telemetry, manage device metadata, query data, dispatch commands, and run AI-assisted +operations as a native platform capability. The project is designed for industrial IoT and operational technology scenarios where many device types, protocols, services, data flows, and intelligent workflows need to be coordinated. @@ -67,13 +68,13 @@ IoT DC3 helps teams build the core capabilities needed for industrial IoT system IoT DC3 includes 36 access driver modules: -| Category | Driver Modules | -|--------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Industrial protocols | Modbus TCP, Modbus RTU, OPC UA, OPC DA, Siemens S7, BACnet/IP, EtherNet/IP, Omron FINS, Mitsubishi MELSEC, IEC 60870-5-104, IEC 61850, DNP3, DLMS, DLT645, KNX, M-Bus, SL651 | -| IoT protocols | MQTT, CoAP, LwM2M, HTTP, BLE, Zigbee, LoRaWAN | -| Data bridging | MySQL, PostgreSQL, Oracle, SQL Server, Redis | -| Basic communication, messaging and network management | TCP/UDP, Serial, SNMP, CAN, Kafka | -| Simulation and debugging | Virtual, Listening Virtual | +| Category | Driver Modules | +|-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Industrial protocols | Modbus TCP, Modbus RTU, OPC UA, OPC DA, Siemens S7, BACnet/IP, EtherNet/IP, Omron FINS, Mitsubishi MELSEC, IEC 60870-5-104, IEC 61850, DNP3, DLMS, DLT645, KNX, M-Bus, SL651 | +| IoT protocols | MQTT, CoAP, LwM2M, HTTP, BLE, Zigbee, LoRaWAN | +| Data bridging | MySQL, PostgreSQL, Oracle, SQL Server, Redis | +| Basic communication, messaging and network management | TCP/UDP, Serial, SNMP, CAN, Kafka | +| Simulation and debugging | Virtual, Listening Virtual | ## Architecture in One Paragraph diff --git a/README.es.md b/README.es.md index 026d1ed54..932ea3ddd 100644 --- a/README.es.md +++ b/README.es.md @@ -102,13 +102,13 @@ IoT DC3 incluye **36 módulos de controladores de acceso** para automatización datos, comunicaciones básicas y escenarios de simulación/depuración, reduciendo el costo de conectar dispositivos y fuentes de datos comunes: -| Categoría | Módulos de controladores | -|------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Categoría | Módulos de controladores | +|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 🏭 **Protocolos industriales** | Modbus TCP · Modbus RTU · OPC UA · OPC DA · Siemens S7 · BACnet/IP · EtherNet/IP · Omron FINS · Mitsubishi MELSEC · IEC 60870-5-104 · IEC 61850 · DNP3 · DLMS · DLT645 · KNX · M-Bus · SL651 | -| 📡 **Protocolos IoT** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | -| 🗄️ **Puenteo de datos** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | -| 🔧 **Comunicaciones básicas y gestión de red** | TCP/UDP · Serial · SNMP · CAN · Kafka | -| 🧪 **Simulación y depuración** | Virtual · Listening Virtual | +| 📡 **Protocolos IoT** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | +| 🗄️ **Puenteo de datos** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | +| 🔧 **Comunicaciones básicas y gestión de red** | TCP/UDP · Serial · SNMP · CAN · Kafka | +| 🧪 **Simulación y depuración** | Virtual · Listening Virtual | El **Driver SDK** permite el desarrollo rápido de controladores de protocolo personalizados y su registro en la plataforma de ejecución. diff --git a/README.ja.md b/README.ja.md index 916884bca..e50b47590 100644 --- a/README.ja.md +++ b/README.ja.md @@ -98,13 +98,13 @@ AGE)永続層とオプションの可観測性スタック(ELK + Prometheus IoT DC3 は **36 個の接続ドライバーモジュール**を内蔵し、産業オートメーション、IoT 通信、データブリッジ、基本通信、シミュレーションとデバッグのシナリオをカバーします。一般的なデバイスやデータソースの接続コストを下げます。 -| 分類 | ドライバーモジュール | -|-----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 分類 | ドライバーモジュール | +|-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 🏭 **産業プロトコル** | Modbus TCP · Modbus RTU · OPC UA · OPC DA · Siemens S7 · BACnet/IP · EtherNet/IP · Omron FINS · Mitsubishi MELSEC · IEC 60870-5-104 · IEC 61850 · DNP3 · DLMS · DLT645 · KNX · M-Bus · SL651 | -| 📡 **IoT プロトコル** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | -| 🗄️ **データブリッジ** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | -| 🔧 **基本通信とネットワーク管理** | TCP/UDP · Serial · SNMP · CAN · Kafka | -| 🧪 **シミュレーションとデバッグ** | Virtual · Listening Virtual | +| 📡 **IoT プロトコル** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | +| 🗄️ **データブリッジ** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | +| 🔧 **基本通信とネットワーク管理** | TCP/UDP · Serial · SNMP · CAN · Kafka | +| 🧪 **シミュレーションとデバッグ** | Virtual · Listening Virtual | **Driver SDK** により、カスタムプロトコルドライバーをすばやく開発し、実行中のプラットフォームへ登録できます。 diff --git a/README.ko.md b/README.ko.md index f14bb21bb..b5e223718 100644 --- a/README.ko.md +++ b/README.ko.md @@ -96,13 +96,13 @@ API 경로 전체에 걸쳐 적용됩니다. 서비스와 팀 전반에 걸쳐 IoT DC3는 산업 자동화, IoT 통신, 데이터 브리징, 기본 통신, 시뮬레이션/디버깅 시나리오를 위한 **36개의 접근 드라이버 모듈**을 포함하여 일반적인 디바이스와 데이터 소스의 연결 비용을 줄입니다: -| 카테고리 | 드라이버 모듈 | -|-----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 카테고리 | 드라이버 모듈 | +|-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 🏭 **산업 프로토콜** | Modbus TCP · Modbus RTU · OPC UA · OPC DA · Siemens S7 · BACnet/IP · EtherNet/IP · Omron FINS · Mitsubishi MELSEC · IEC 60870-5-104 · IEC 61850 · DNP3 · DLMS · DLT645 · KNX · M-Bus · SL651 | -| 📡 **IoT 프로토콜** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | -| 🗄️ **데이터 브리징** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | -| 🔧 **기본 통신 및 네트워크 관리** | TCP/UDP · Serial · SNMP · CAN · Kafka | -| 🧪 **시뮬레이션 및 디버깅** | Virtual · Listening Virtual | +| 📡 **IoT 프로토콜** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | +| 🗄️ **데이터 브리징** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | +| 🔧 **기본 통신 및 네트워크 관리** | TCP/UDP · Serial · SNMP · CAN · Kafka | +| 🧪 **시뮬레이션 및 디버깅** | Virtual · Listening Virtual | **Driver SDK**를 통해 커스텀 프로토콜 드라이버를 빠르게 개발하고 실행 중인 플랫폼에 등록할 수 있습니다. diff --git a/README.md b/README.md index 4c606bafc..8818c5026 100644 --- a/README.md +++ b/README.md @@ -99,13 +99,13 @@ and API paths. Clear boundaries that scale across services and teams. IoT DC3 includes **36 access driver modules** for industrial automation, IoT communication, data bridging, basic communication, and simulation/debugging scenarios, reducing the cost of connecting common devices and data sources: -| Category | Driver Modules | -|-----------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Category | Driver Modules | +|-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 🏭 **Industrial protocols** | Modbus TCP · Modbus RTU · OPC UA · OPC DA · Siemens S7 · BACnet/IP · EtherNet/IP · Omron FINS · Mitsubishi MELSEC · IEC 60870-5-104 · IEC 61850 · DNP3 · DLMS · DLT645 · KNX · M-Bus · SL651 | -| 📡 **IoT protocols** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | -| 🗄️ **Data bridging** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | -| 🔧 **Basic communication, messaging and NMS** | TCP/UDP · Serial · SNMP · CAN · Kafka | -| 🧪 **Simulation and debugging** | Virtual · Listening Virtual | +| 📡 **IoT protocols** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | +| 🗄️ **Data bridging** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | +| 🔧 **Basic communication, messaging and NMS** | TCP/UDP · Serial · SNMP · CAN · Kafka | +| 🧪 **Simulation and debugging** | Virtual · Listening Virtual | The **Driver SDK** supports fast development of custom protocol drivers and registration into the runtime platform. @@ -133,7 +133,9 @@ Distributed microservice architecture based on **Spring Boot 4 + Spring Cloud 20 ### 📊 Real-Time Data Engine -- **Data collection** - Drivers collect device telemetry and send it asynchronously through the internal message broker — pluggable per deployment: RabbitMQ (default), Kafka, RocketMQ, Pulsar, ActiveMQ or any MQTT 5 broker ([broker guide](docs/mq-brokers.md)) +- **Data collection** - Drivers collect device telemetry and send it asynchronously through the internal message + broker — pluggable per deployment: RabbitMQ (default), Kafka, RocketMQ, Pulsar, ActiveMQ or any MQTT 5 broker + ([broker guide](docs/mq-brokers.md)) - **Time-series storage** - Efficient queries for real-time and historical data - **Rule engine** - Flexible alarm rules with multi-level alarms and notifications - **Event traceability** - Full command and event history @@ -150,9 +152,8 @@ Distributed microservice architecture based on **Spring Boot 4 + Spring Cloud 20 - **Driver SDK** - A complete driver development toolkit. See the [Driver Authoring Guide](https://docs.dc3.site/en/development/driver-authoring) - **Separated frontend and backend** - Vue 3 + TypeScript frontend, RESTful and gRPC APIs -- **Containerized deployment** - One-command startup with Podman / Docker Compose, plus compose - scaling, Docker Swarm, Kubernetes and Helm deployment configs. See - the [Deployment Guide](dc3/doc/DEPLOYMENT.md). +- **Containerized deployment** - One-command startup with Podman / Docker Compose, plus compose scaling, Docker Swarm, + Kubernetes and Helm deployment configs. See the [Deployment Guide](dc3/doc/DEPLOYMENT.md). - **Complete documentation** - Online docs, quickstart guide, and troubleshooting guide ## ⚡ Quick Start diff --git a/README.ru.md b/README.ru.md index 19deadc53..a7dd9aef2 100644 --- a/README.ru.md +++ b/README.ru.md @@ -101,13 +101,13 @@ IoT DC3 включает **36 модулей драйверов доступа** данных, базовых коммуникаций и сценариев моделирования/отладки, снижая стоимость подключения распространённых устройств и источников данных: -| Категория | Модули драйверов | -|------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Категория | Модули драйверов | +|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 🏭 **Промышленные протоколы** | Modbus TCP · Modbus RTU · OPC UA · OPC DA · Siemens S7 · BACnet/IP · EtherNet/IP · Omron FINS · Mitsubishi MELSEC · IEC 60870-5-104 · IEC 61850 · DNP3 · DLMS · DLT645 · KNX · M-Bus · SL651 | -| 📡 **IoT-протоколы** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | -| 🗄️ **Мостовая передача данных** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | -| 🔧 **Базовые коммуникации и управление** | TCP/UDP · Serial · SNMP · CAN · Kafka | -| 🧪 **Моделирование и отладка** | Virtual · Listening Virtual | +| 📡 **IoT-протоколы** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | +| 🗄️ **Мостовая передача данных** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | +| 🔧 **Базовые коммуникации и управление** | TCP/UDP · Serial · SNMP · CAN · Kafka | +| 🧪 **Моделирование и отладка** | Virtual · Listening Virtual | **Driver SDK** поддерживает быструю разработку пользовательских протокольных драйверов и их регистрацию в среде выполнения платформы. diff --git a/README.vi.md b/README.vi.md index cfc026163..632d3971a 100644 --- a/README.vi.md +++ b/README.vi.md @@ -98,13 +98,13 @@ mở rộng theo dịch vụ và đội nhóm. IoT DC3 tích hợp **36 module driver kết nối**, bao phủ tự động hóa công nghiệp, truyền thông IoT, cầu nối dữ liệu, truyền thông cơ bản, mô phỏng và gỡ lỗi, giúp giảm chi phí kết nối thiết bị và nguồn dữ liệu phổ biến: -| Nhóm | Module driver | -|---------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Nhóm | Module driver | +|---------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 🏭 **Giao thức công nghiệp** | Modbus TCP · Modbus RTU · OPC UA · OPC DA · Siemens S7 · BACnet/IP · EtherNet/IP · Omron FINS · Mitsubishi MELSEC · IEC 60870-5-104 · IEC 61850 · DNP3 · DLMS · DLT645 · KNX · M-Bus · SL651 | -| 📡 **Giao thức IoT** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | -| 🗄️ **Cầu nối dữ liệu** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | -| 🔧 **Truyền thông cơ bản và quản trị mạng** | TCP/UDP · Serial · SNMP · CAN · Kafka | -| 🧪 **Mô phỏng và gỡ lỗi** | Virtual · Listening Virtual | +| 📡 **Giao thức IoT** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | +| 🗄️ **Cầu nối dữ liệu** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | +| 🔧 **Truyền thông cơ bản và quản trị mạng** | TCP/UDP · Serial · SNMP · CAN · Kafka | +| 🧪 **Mô phỏng và gỡ lỗi** | Virtual · Listening Virtual | **Driver SDK** hỗ trợ phát triển nhanh driver giao thức tùy chỉnh và đăng ký vào nền tảng runtime. diff --git a/README.zh.md b/README.zh.md index 1a014a151..e93a60d18 100644 --- a/README.zh.md +++ b/README.zh.md @@ -95,13 +95,13 @@ API 全链路。边界清晰,易于规模化扩展与多团队协作。 内置 **36 个接入驱动模块**,覆盖工业自动化、物联网通信、数据桥接、基础通信与仿真调试场景,降低常见设备与数据源的接入成本: -| 分类 | 驱动模块 | -|-----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 分类 | 驱动模块 | +|-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 🏭 **工业协议** | Modbus TCP · Modbus RTU · OPC UA · OPC DA · Siemens S7 · BACnet/IP · EtherNet/IP · Omron FINS · Mitsubishi MELSEC · IEC 60870-5-104 · IEC 61850 · DNP3 · DLMS · DLT645 · KNX · M-Bus · SL651 | -| 📡 **物联网协议** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | -| 🗄️ **数据桥接** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | -| 🔧 **基础通信、消息与管理** | TCP/UDP · Serial · SNMP · CAN · Kafka | -| 🧪 **仿真与调试** | Virtual · Listening Virtual | +| 📡 **物联网协议** | MQTT · CoAP · LwM2M · HTTP · BLE · Zigbee · LoRaWAN | +| 🗄️ **数据桥接** | MySQL · PostgreSQL · Oracle · SQL Server · Redis | +| 🔧 **基础通信、消息与管理** | TCP/UDP · Serial · SNMP · CAN · Kafka | +| 🧪 **仿真与调试** | Virtual · Listening Virtual | 提供完整的 **Driver SDK**,支持快速开发自定义协议驱动,热插拔注册到运行平台。 diff --git a/dc3-api/README.md b/dc3-api/README.md index a1261a32f..62a848642 100644 --- a/dc3-api/README.md +++ b/dc3-api/README.md @@ -5,12 +5,12 @@ business logic belongs in `dc3-common-*` implementations. ## Modules -| Module | Contract surface | Primary consumers | -|---|---|---| -| `dc3-api-auth` | tenant, user, token, permission, resource registry, MCP runtime | gateway and center services | -| `dc3-api-data` | point values, command history, event history | manager, drivers, and data clients | -| `dc3-api-driver` | driver registration and driver-scoped device/point metadata | protocol drivers | -| `dc3-api-manager` | manager-scoped driver, device, profile, point, command, and event metadata | data and other centers | +| Module | Contract surface | Primary consumers | +|-------------------|----------------------------------------------------------------------------|------------------------------------| +| `dc3-api-auth` | tenant, user, token, permission, resource registry, MCP runtime | gateway and center services | +| `dc3-api-data` | point values, command history, event history | manager, drivers, and data clients | +| `dc3-api-driver` | driver registration and driver-scoped device/point metadata | protocol drivers | +| `dc3-api-manager` | manager-scoped driver, device, profile, point, command, and event metadata | data and other centers | Proto sources live under each module's `src/main/protobuf/` directory. Generated Java sources are build artifacts and must not be edited directly. diff --git a/dc3-api/dc3-api-auth/README.md b/dc3-api/dc3-api-auth/README.md index e132ba0b9..bc19e6df4 100644 --- a/dc3-api/dc3-api-auth/README.md +++ b/dc3-api/dc3-api-auth/README.md @@ -5,15 +5,15 @@ ## Services -| Service | RPCs | Purpose | -|---|---|---| -| `TenantApi` | `GetByCode` | resolve tenant metadata | -| `UserApi` | `GetById`, `GetByPrincipalId` | resolve user identity | -| `TokenApi` | `CheckValid` | validate login/token material | -| `LocalCredentialApi` | `GetByLoginName` | resolve local credentials | -| `PermissionApi` | `ListPermissionCodes` | resolve effective permission codes | -| `ResourceRegistryApi` | `Sync` | synchronize annotated API/menu resources | -| `McpRuntimeApi` | `Introspect`, `ListTools`, `ResolveTool`, `AuthorizeToolCall`, `Audit` | authorize and audit MCP tools | +| Service | RPCs | Purpose | +|-----------------------|------------------------------------------------------------------------|------------------------------------------| +| `TenantApi` | `GetByCode` | resolve tenant metadata | +| `UserApi` | `GetById`, `GetByPrincipalId` | resolve user identity | +| `TokenApi` | `CheckValid` | validate login/token material | +| `LocalCredentialApi` | `GetByLoginName` | resolve local credentials | +| `PermissionApi` | `ListPermissionCodes` | resolve effective permission codes | +| `ResourceRegistryApi` | `Sync` | synchronize annotated API/menu resources | +| `McpRuntimeApi` | `Introspect`, `ListTools`, `ResolveTool`, `AuthorizeToolCall`, `Audit` | authorize and audit MCP tools | Every response uses a contract-specific wrapper containing `GrpcR`. Callers must inspect the result envelope before reading response data. @@ -37,5 +37,5 @@ mvn -s .mvn/settings.xml -q -pl dc3-api/dc3-api-auth -am compile This module has no handwritten runtime code or module-specific tests. A successful compile verifies proto syntax and generated Java sources; server/facade behaviour is tested in the implementing modules. -When changing the contract, preserve field numbers, update implementations and clients together, and verify that -tenant and authorization context remain explicit. +When changing the contract, preserve field numbers, update implementations and clients together, and verify that tenant +and authorization context remain explicit. diff --git a/dc3-api/dc3-api-data/README.md b/dc3-api/dc3-api-data/README.md index 929a2e931..dc1235494 100644 --- a/dc3-api/dc3-api-data/README.md +++ b/dc3-api/dc3-api-data/README.md @@ -5,18 +5,18 @@ types use `io.github.pnoker.api.center.data`; proto sources live under `src/main ## Services -| Service | RPCs | Purpose | -|---|---|---| -| `PointValueApi` | `GetLastValue`, `ListHistoryValues`, `ListSeriesVolumes` | query point values and value-volume series | -| `PointValueApi` | `ReadCommand`, `WriteCommand` | submit point read/write commands | -| `CommandHistoryApi` | `CallCommand`, `GetByRecordId`, `ListByPage` | dispatch and query command history | -| `EventHistoryApi` | `ReportEvent`, `GetByRecordId`, `ListByPage` | report and query event history | -| `StatusHealthApi` | `DeviceStatusesByIds`, `DeviceStatusesByProfileId` | query device status snapshots | -| `StatusHealthApi` | `DriverStatusesByIds`, `DriverDeviceStatusSummary` | query driver status snapshots and device summaries | -| `StatusHealthApi` | `SystemHealth` | query the platform health snapshot | +| Service | RPCs | Purpose | +|---------------------|----------------------------------------------------------|----------------------------------------------------| +| `PointValueApi` | `GetLastValue`, `ListHistoryValues`, `ListSeriesVolumes` | query point values and value-volume series | +| `PointValueApi` | `ReadCommand`, `WriteCommand` | submit point read/write commands | +| `CommandHistoryApi` | `CallCommand`, `GetByRecordId`, `ListByPage` | dispatch and query command history | +| `EventHistoryApi` | `ReportEvent`, `GetByRecordId`, `ListByPage` | report and query event history | +| `StatusHealthApi` | `DeviceStatusesByIds`, `DeviceStatusesByProfileId` | query device status snapshots | +| `StatusHealthApi` | `DriverStatusesByIds`, `DriverDeviceStatusSummary` | query driver status snapshots and device summaries | +| `StatusHealthApi` | `SystemHealth` | query the platform health snapshot | -Single-result RPCs use `GetXxx`; collection/page results use `ListXxx` or an explicitly named status aggregation. Do -not reintroduce legacy `SelectXxx` names. +Single-result RPCs use `GetXxx`; collection/page results use `ListXxx` or an explicitly named status aggregation. Do not +reintroduce legacy `SelectXxx` names. ## Consumers and implementation diff --git a/dc3-api/dc3-api-driver/README.md b/dc3-api/dc3-api-driver/README.md index 61f7d4e68..bdcf4b285 100644 --- a/dc3-api/dc3-api-driver/README.md +++ b/dc3-api/dc3-api-driver/README.md @@ -5,15 +5,15 @@ driver-scoped device and point configuration. Generated Java types use `io.githu ## Services -| Service | RPC | Response | Purpose | -|---|---|---|---| -| `DriverApi` | `DriverRegister` | `GrpcRDriverRegisterDTO` | register metadata and receive assigned configuration | -| `DriverApi` | `GetById` | `GrpcRDriverRegisterDTO` | reload registered driver metadata | -| `DriverApi` | `RenewLease` | stream of `GrpcRDriverLeaseDTO` | renew one runtime lease and stream its owned-device snapshot | -| `DeviceApi` | `ListByPage` | `GrpcRPageDeviceDTO` | page through driver-owned devices | -| `DeviceApi` | `GetById` | `GrpcRDeviceDTO` | get one device with attached attribute configuration | -| `PointApi` | `ListByPage` | `GrpcRPagePointDTO` | page through driver-visible points | -| `PointApi` | `GetById` | `GrpcRPointDTO` | get one point with attached configuration | +| Service | RPC | Response | Purpose | +|-------------|------------------|---------------------------------|--------------------------------------------------------------| +| `DriverApi` | `DriverRegister` | `GrpcRDriverRegisterDTO` | register metadata and receive assigned configuration | +| `DriverApi` | `GetById` | `GrpcRDriverRegisterDTO` | reload registered driver metadata | +| `DriverApi` | `RenewLease` | stream of `GrpcRDriverLeaseDTO` | renew one runtime lease and stream its owned-device snapshot | +| `DeviceApi` | `ListByPage` | `GrpcRPageDeviceDTO` | page through driver-owned devices | +| `DeviceApi` | `GetById` | `GrpcRDeviceDTO` | get one device with attached attribute configuration | +| `PointApi` | `ListByPage` | `GrpcRPagePointDTO` | page through driver-visible points | +| `PointApi` | `GetById` | `GrpcRPointDTO` | get one point with attached configuration | Proto sources live under `src/main/protobuf/api/common/driver/`. The `.proto` files are authoritative for fields and wrapper shapes. diff --git a/dc3-api/dc3-api-manager/README.md b/dc3-api/dc3-api-manager/README.md index 1ca4494e4..db86b15bb 100644 --- a/dc3-api/dc3-api-manager/README.md +++ b/dc3-api/dc3-api-manager/README.md @@ -5,14 +5,14 @@ services. Generated Java types use `io.github.pnoker.api.center.manager`. ## Services -| Service | Single-result RPCs | Collection/page RPCs | -|---|---|---| -| `DriverApi` | `GetByDriverId`, `GetByDeviceId` | `ListByPage`, `ListByDriverIds` | -| `DeviceApi` | `GetByDeviceId`, `GetActiveOwner` | `ListByPage`, `ListByProfileId`, `ListByDriverId`, `ListByDeviceIds` | -| `PointApi` | `GetById` | `ListByPage`, `ListByIds` | -| `ProfileApi` | `GetByProfileId` | `ListByPage`, `ListByProfileIds`, `ListByDeviceId` | -| `CommandApi` | `GetById` | `ListByPage`, `ListByIds` | -| `EventApi` | `GetById` | `ListByPage`, `ListByIds` | +| Service | Single-result RPCs | Collection/page RPCs | +|--------------|-----------------------------------|----------------------------------------------------------------------| +| `DriverApi` | `GetByDriverId`, `GetByDeviceId` | `ListByPage`, `ListByDriverIds` | +| `DeviceApi` | `GetByDeviceId`, `GetActiveOwner` | `ListByPage`, `ListByProfileId`, `ListByDriverId`, `ListByDeviceIds` | +| `PointApi` | `GetById` | `ListByPage`, `ListByIds` | +| `ProfileApi` | `GetByProfileId` | `ListByPage`, `ListByProfileIds`, `ListByDeviceId` | +| `CommandApi` | `GetById` | `ListByPage`, `ListByIds` | +| `EventApi` | `GetById` | `ListByPage`, `ListByIds` | Proto sources live under `src/main/protobuf/api/common/manager/`. Shared query and page messages are defined in `manager_query.proto` and `manager_query_page.proto`. diff --git a/dc3-center/README.md b/dc3-center/README.md index 95e251f0f..98c25a48e 100644 --- a/dc3-center/README.md +++ b/dc3-center/README.md @@ -5,13 +5,13 @@ modules; center modules assemble dependencies, configuration, and process bounda ## Applications -| Module | HTTP | gRPC | Base path | Purpose | -|---|---:|---:|---|---| -| `dc3-center-single` | 8100 | 9100 | `/single` | auth, manager, and data in one JVM | -| `dc3-center-auth` | 8300 | 9300 | `/auth` | identity, authorization, OAuth2, and MCP authorization | -| `dc3-center-manager` | 8400 | 9400 | `/manager` | device and metadata management | -| `dc3-center-data` | 8500 | 9500 | `/data` | values, commands, events, and status | -| `dc3-center-agentic` | 8600 | n/a | `/agentic` | AI-assisted operations | +| Module | HTTP | gRPC | Base path | Purpose | +|----------------------|-----:|-----:|------------|--------------------------------------------------------| +| `dc3-center-single` | 8100 | 9100 | `/single` | auth, manager, and data in one JVM | +| `dc3-center-auth` | 8300 | 9300 | `/auth` | identity, authorization, OAuth2, and MCP authorization | +| `dc3-center-manager` | 8400 | 9400 | `/manager` | device and metadata management | +| `dc3-center-data` | 8500 | 9500 | `/data` | values, commands, events, and status | +| `dc3-center-agentic` | 8600 | n/a | `/agentic` | AI-assisted operations | Ports are defaults; environment variables in each `application.yml` are authoritative. The distributed applications use static, environment-overridable gRPC addresses. They do not depend on Nacos service discovery. diff --git a/dc3-center/dc3-center-data/README.md b/dc3-center/dc3-center-data/README.md index 2ea71c4d2..09f8e8593 100644 --- a/dc3-center/dc3-center-data/README.md +++ b/dc3-center/dc3-center-data/README.md @@ -2,9 +2,9 @@ ## Overview -`dc3-center-data` is the Data Center of the IoT DC3 platform. It consumes device point values from drivers over -RabbitMQ (AMQP), stores them through the pluggable time-series port (`dc3-tsdb`, TimescaleDB by default), and exposes -data query and command APIs. +`dc3-center-data` is the Data Center of the IoT DC3 platform. It consumes device point values from drivers over RabbitMQ +(AMQP), stores them through the pluggable time-series port (`dc3-tsdb`, TimescaleDB by default), and exposes data query +and command APIs. ## Module Information @@ -36,13 +36,13 @@ Accessible through the gateway at `/api/v3/data/**` (authentication required). ## Messaging Topics -| Exchange | Direction | Purpose | -|-----------------------|-----------|-----------------------------------------| -| `dc3.e.value` | Inbound | Receive point values from drivers | -| `dc3.e.point_command` | Outbound | Dispatch point read/write commands | -| `dc3.e.command` | Outbound | Dispatch custom device commands | -| `dc3.e.state` | Inbound | Receive driver/device state events | -| `dc3.e.event` | Inbound | Receive reported domain events | +| Exchange | Direction | Purpose | +|-----------------------|-----------|------------------------------------| +| `dc3.e.value` | Inbound | Receive point values from drivers | +| `dc3.e.point_command` | Outbound | Dispatch point read/write commands | +| `dc3.e.command` | Outbound | Dispatch custom device commands | +| `dc3.e.state` | Inbound | Receive driver/device state events | +| `dc3.e.event` | Inbound | Receive reported domain events | ## Dependencies diff --git a/dc3-center/dc3-center-manager/README.md b/dc3-center/dc3-center-manager/README.md index 18b7c98d7..e4e737d34 100644 --- a/dc3-center/dc3-center-manager/README.md +++ b/dc3-center/dc3-center-manager/README.md @@ -14,8 +14,8 @@ management, and command interfaces. ## Service Ports -| Protocol | Port | Configuration variable | -|-----------|--------|------------------------| +| Protocol | Port | Configuration variable | +|-----------|--------|-------------------------| | HTTP REST | `8400` | `DC3_MANAGER_PORT` | | gRPC | `9400` | `DC3_MANAGER_GRPC_PORT` | @@ -53,12 +53,12 @@ The complete prefix set (labels, dictionaries, attribute configs, dashboards, ba ## gRPC Services (consumed by drivers and data service) -| Service | Used by | -|----------------------------|-------------------------------------------------| -| `DriverApi.DriverRegister` | Drivers registering on startup | -| `DeviceApi.GetById` | Drivers fetching device configuration | -| `PointApi.GetById` | Drivers fetching point configuration | -| `DriverApi.GetByDeviceId` | Distributed facades resolving command routing | +| Service | Used by | +|----------------------------|-----------------------------------------------| +| `DriverApi.DriverRegister` | Drivers registering on startup | +| `DeviceApi.GetById` | Drivers fetching device configuration | +| `PointApi.GetById` | Drivers fetching point configuration | +| `DriverApi.GetByDeviceId` | Distributed facades resolving command routing | ## Dependencies diff --git a/dc3-center/dc3-center-single/README.md b/dc3-center/dc3-center-single/README.md index 5ff4f2ff7..7cdd74095 100644 --- a/dc3-center/dc3-center-single/README.md +++ b/dc3-center/dc3-center-single/README.md @@ -15,8 +15,8 @@ management services into a single deployable module for simplified single-node o | Protocol | Port | Configuration variable | |-----------|--------|------------------------| -| HTTP REST | `8100` | `DC3_SINGLE_PORT` | -| gRPC | `9100` | `DC3_SINGLE_GRPC_PORT` | +| HTTP REST | `8100` | `DC3_SINGLE_PORT` | +| gRPC | `9100` | `DC3_SINGLE_GRPC_PORT` | ## Key Responsibilities diff --git a/dc3-common/README.md b/dc3-common/README.md index 99af4c300..619a83afc 100644 --- a/dc3-common/README.md +++ b/dc3-common/README.md @@ -5,39 +5,39 @@ center and driver applications. ## Domain modules -| Module | Responsibility | -|---|---| -| `dc3-common-auth` | authentication, authorization, identity, OAuth2, and auth gRPC servers | -| `dc3-common-manager` | driver/device/profile/point metadata and manager APIs | -| `dc3-common-data` | values, commands, events, status, and data APIs | -| `dc3-common-agentic` | AI models, sessions, tools, and assisted operations | -| `dc3-common-driver` | driver SDK, registration, scheduling, metadata, command, and value runtime | -| `dc3-common-gateway` | gateway routes, authentication filter, and ingress support | +| Module | Responsibility | +|----------------------|----------------------------------------------------------------------------| +| `dc3-common-auth` | authentication, authorization, identity, OAuth2, and auth gRPC servers | +| `dc3-common-manager` | driver/device/profile/point metadata and manager APIs | +| `dc3-common-data` | values, commands, events, status, and data APIs | +| `dc3-common-agentic` | AI models, sessions, tools, and assisted operations | +| `dc3-common-driver` | driver SDK, registration, scheduling, metadata, command, and value runtime | +| `dc3-common-gateway` | gateway routes, authentication filter, and ingress support | ## Contracts and models -| Module | Responsibility | -|---|---| -| `dc3-common-model` | shared BO/VO/DTO bases, builders, extension models, validation groups, and transport models | -| `dc3-common-public` | response envelope, `BaseService`, shared entities/utilities, and tenant markers | -| `dc3-common-api` | shared gRPC conversion helpers | -| `dc3-common-facade` | cross-service facade contracts and implementations | -| `dc3-common-constant` | stable platform-wide constants and shared domain/wire/persistence enums | -| `dc3-common-exception` | shared exception hierarchy | +| Module | Responsibility | +|------------------------|---------------------------------------------------------------------------------------------| +| `dc3-common-model` | shared BO/VO/DTO bases, builders, extension models, validation groups, and transport models | +| `dc3-common-public` | response envelope, `BaseService`, shared entities/utilities, and tenant markers | +| `dc3-common-api` | shared gRPC conversion helpers | +| `dc3-common-facade` | cross-service facade contracts and implementations | +| `dc3-common-constant` | stable platform-wide constants and shared domain/wire/persistence enums | +| `dc3-common-exception` | shared exception hierarchy | ## Infrastructure modules -| Module | Responsibility | -|---|---| -| `dc3-common-dal` | shared label/group persistence | -| `dc3-common-mqtt` | MQTT client configuration | -| `dc3-common-quartz` | scheduling infrastructure | -| `dc3-common-thread` | managed executors | -| `dc3-common-web` | WebFlux, springdoc, security, and controller support | -| `dc3-common-log` | logging defaults | -| `dc3-common-sql` | SQL utilities | +| Module | Responsibility | +|---------------------------------|-------------------------------------------------------| +| `dc3-common-dal` | shared label/group persistence | +| `dc3-common-mqtt` | MQTT client configuration | +| `dc3-common-quartz` | scheduling infrastructure | +| `dc3-common-thread` | managed executors | +| `dc3-common-web` | WebFlux, springdoc, security, and controller support | +| `dc3-common-log` | logging defaults | +| `dc3-common-sql` | SQL utilities | | `dc3-common-resource-registrar` | API/resource annotation discovery and synchronization | -| `dc3-common-test` | shared tests, harnesses, and Testcontainers | +| `dc3-common-test` | shared tests, harnesses, and Testcontainers | Datasource, messaging, and time-series storage were split into dedicated top-level families; business modules depend on them directly: diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/config/ChatClientConfig.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/config/ChatClientConfig.java index 0021ba07c..32301a99f 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/config/ChatClientConfig.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/config/ChatClientConfig.java @@ -74,7 +74,7 @@ public class ChatClientConfig { * Create and configure the application-managed agentic chat memory repository. * * @param messageService message service - * @param properties properties + * @param properties properties * @return agentic chat memory repository result */ @Bean @@ -88,7 +88,7 @@ public class ChatClientConfig { * Create and configure the application-managed agentic chat memory. * * @param chatMemoryRepository chat memory repository - * @param properties properties + * @param properties properties * @return agentic chat memory result */ @Bean @@ -129,15 +129,15 @@ public class ChatClientConfig { /** * Create and configure the application-managed agentic tool callback provider. * - * @param tenantTool tenant tool - * @param userTool user tool - * @param deviceTool device tool - * @param driverTool driver tool - * @param profileTool profile tool - * @param pointTool point tool + * @param tenantTool tenant tool + * @param userTool user tool + * @param deviceTool device tool + * @param driverTool driver tool + * @param profileTool profile tool + * @param pointTool point tool * @param pointValueTool point value tool - * @param systemTool system tool - * @param objectMapper object mapper + * @param systemTool system tool + * @param objectMapper object mapper * @return agentic tool callback provider result */ @Bean @@ -157,7 +157,7 @@ public class ChatClientConfig { /** * Create and configure the application-managed agentic chat client builder. * - * @param chatModel chat model + * @param chatModel chat model * @param memoryAdvisor memory advisor * @return agentic chat client builder result */ diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/builder/ModelProviderBuilder.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/builder/ModelProviderBuilder.java index 51e70af6a..de3e16fc1 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/builder/ModelProviderBuilder.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/builder/ModelProviderBuilder.java @@ -65,7 +65,7 @@ public interface ModelProviderBuilder { * After process. * * @param entityRequest entity request - * @param entityBO business object + * @param entityBO business object */ @Mapping(target = "providerType", ignore = true) @Mapping(target = "tenantId", ignore = true) diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticMessageContent.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticMessageContent.java index 75fa08a7c..c6e0e254b 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticMessageContent.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticMessageContent.java @@ -74,7 +74,9 @@ public class AgenticMessageContent implements Serializable { return content; } - /** Tool-call trace entry (type, title, detail) for the run timeline. */ + /** + * Tool-call trace entry (type, title, detail) for the run timeline. + */ @Getter @Setter @ToString @@ -103,10 +105,10 @@ public class AgenticMessageContent implements Serializable { /** * Of. * - * @param type type - * @param title title - * @param detail detail - * @param name name + * @param type type + * @param title title + * @param detail detail + * @param name name * @param created created * @return of result */ @@ -117,14 +119,14 @@ public class AgenticMessageContent implements Serializable { /** * Of. * - * @param type type - * @param title title - * @param detail detail - * @param name name + * @param type type + * @param title title + * @param detail detail + * @param name name * @param created created - * @param phase phase - * @param status status - * @param code code + * @param phase phase + * @param status status + * @param code code * @return of result */ public static Trace of(String type, String title, String detail, String name, Long created, String phase, @@ -143,7 +145,9 @@ public class AgenticMessageContent implements Serializable { } - /** One context block (type + content) fed to the model. */ + /** + * One context block (type + content) fed to the model. + */ @Getter @Setter @ToString @@ -160,7 +164,7 @@ public class AgenticMessageContent implements Serializable { /** * Of. * - * @param type type + * @param type type * @param content content * @return of result */ @@ -173,7 +177,9 @@ public class AgenticMessageContent implements Serializable { } - /** Token accounting: input/output/text/context counts of one message. */ + /** + * Token accounting: input/output/text/context counts of one message. + */ @Getter @Setter @ToString @@ -198,12 +204,12 @@ public class AgenticMessageContent implements Serializable { /** * Of. * - * @param input input - * @param output output - * @param text text + * @param input input + * @param output output + * @param text text * @param context context - * @param system system - * @param memory memory + * @param system system + * @param memory memory * @return of result */ public static Tokens of(Integer input, Integer output, Integer text, Integer context, Integer system, diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticRunEvent.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticRunEvent.java index f55eee5de..77ac15c66 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticRunEvent.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticRunEvent.java @@ -45,8 +45,8 @@ public record AgenticRunEvent(String type, String name, String title, String det * Tool start. * * @param toolName tool name - * @param domain domain - * @param title title + * @param domain domain + * @param title title * @return tool start result */ public static AgenticRunEvent toolStart(String toolName, String domain, String title) { @@ -58,9 +58,9 @@ public record AgenticRunEvent(String type, String name, String title, String det * Tool result. * * @param toolName tool name - * @param success success - * @param code code - * @param message message + * @param success success + * @param code code + * @param message message * @return tool result result */ public static AgenticRunEvent toolResult(String toolName, boolean success, String code, String message) { @@ -79,7 +79,7 @@ public record AgenticRunEvent(String type, String name, String title, String det * Tool error. * * @param toolName tool name - * @param message message + * @param message message * @return tool error result */ public static AgenticRunEvent toolError(String toolName, String message) { diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticToolResult.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticToolResult.java index 997bb8440..e8c49b7cc 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticToolResult.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticToolResult.java @@ -39,9 +39,9 @@ public record AgenticToolResult(boolean success, String code, String message, /** * Ok. * - * @param generic type parameter + * @param generic type parameter * @param message message - * @param data data + * @param data data * @return ok result */ public static AgenticToolResult ok(String message, T data) { @@ -51,9 +51,9 @@ public record AgenticToolResult(boolean success, String code, String message, /** * Ok. * - * @param generic type parameter - * @param message message - * @param data data + * @param generic type parameter + * @param message message + * @param data data * @param visualizations visualizations * @return ok result */ @@ -65,9 +65,9 @@ public record AgenticToolResult(boolean success, String code, String message, /** * Empty. * - * @param generic type parameter + * @param generic type parameter * @param message message - * @param data data + * @param data data * @return empty result */ public static AgenticToolResult empty(String message, T data) { @@ -77,7 +77,7 @@ public record AgenticToolResult(boolean success, String code, String message, /** * Invalid. * - * @param generic type parameter + * @param generic type parameter * @param message message * @return invalid result */ @@ -89,7 +89,7 @@ public record AgenticToolResult(boolean success, String code, String message, /** * Not found. * - * @param generic type parameter + * @param generic type parameter * @param message message * @return not found result */ @@ -100,7 +100,7 @@ public record AgenticToolResult(boolean success, String code, String message, /** * Unavailable. * - * @param generic type parameter + * @param generic type parameter * @param message message * @return unavailable result */ @@ -111,7 +111,7 @@ public record AgenticToolResult(boolean success, String code, String message, /** * Error. * - * @param generic type parameter + * @param generic type parameter * @param message message * @return error result */ diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticVisualizationSpec.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticVisualizationSpec.java index 76308861a..e49f3e53e 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticVisualizationSpec.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/model/AgenticVisualizationSpec.java @@ -62,7 +62,9 @@ public class AgenticVisualizationSpec implements Serializable { private List annotations; - /** Visual-channel bindings: which field maps to x/y/color/size. */ + /** + * Visual-channel bindings: which field maps to x/y/color/size. + */ @Getter @Setter @ToString @@ -99,7 +101,9 @@ public class AgenticVisualizationSpec implements Serializable { } - /** One chart annotation (type, value, label). */ + /** + * One chart annotation (type, value, label). + */ @Getter @Setter @ToString diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/AgenticVisualizationVO.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/AgenticVisualizationVO.java index 46c9c39a7..7246070ec 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/AgenticVisualizationVO.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/AgenticVisualizationVO.java @@ -48,7 +48,7 @@ public class AgenticVisualizationVO { * Of. * * @param visualization visualization - * @param created created + * @param created created * @return of result */ public static AgenticVisualizationVO of(AgenticVisualizationSpec visualization, long created) { diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/ChatCompletionChunkVO.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/ChatCompletionChunkVO.java index 009f54df5..69b092b85 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/ChatCompletionChunkVO.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/ChatCompletionChunkVO.java @@ -60,7 +60,9 @@ public class ChatCompletionChunkVO { @Schema(description = "List of streaming choices included in this chunk; typically contains exactly one element for non-branching completions.") private List choices; - /** One streaming choice: index + incremental delta. */ + /** + * One streaming choice: index + incremental delta. + */ @Getter @Setter @NoArgsConstructor @@ -83,7 +85,9 @@ public class ChatCompletionChunkVO { } - /** Incremental content/role fragment of one chunk. */ + /** + * Incremental content/role fragment of one chunk. + */ @Getter @Setter @NoArgsConstructor diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/ChatCompletionResponseVO.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/ChatCompletionResponseVO.java index 585184c3a..89ffa795c 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/ChatCompletionResponseVO.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/entity/vo/ChatCompletionResponseVO.java @@ -64,7 +64,9 @@ public class ChatCompletionResponseVO { @Schema(description = "Token usage statistics for the request and response.") private Usage usage; - /** One completion choice: index + assistant message. */ + /** + * One completion choice: index + assistant message. + */ @Getter @Setter @NoArgsConstructor @@ -86,7 +88,9 @@ public class ChatCompletionResponseVO { } - /** Generated assistant message (role + content). */ + /** + * Generated assistant message (role + content). + */ @Getter @Setter @NoArgsConstructor @@ -109,7 +113,9 @@ public class ChatCompletionResponseVO { } - /** Token usage of the completion (prompt/completion/total). */ + /** + * Token usage of the completion (prompt/completion/total). + */ @Getter @Setter @NoArgsConstructor diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/service/chat/AgenticChatResponseCodec.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/service/chat/AgenticChatResponseCodec.java index 74de77142..7a1a87ee3 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/service/chat/AgenticChatResponseCodec.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/service/chat/AgenticChatResponseCodec.java @@ -97,9 +97,9 @@ public class AgenticChatResponseCodec { /** * Format final chunk. * - * @param id id - * @param created created - * @param model model + * @param id id + * @param created created + * @param model model * @param finishReason finish reason * @return format final chunk result */ diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/service/chat/AgenticMessageRecorder.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/service/chat/AgenticMessageRecorder.java index 04d2cb9cc..7de547768 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/service/chat/AgenticMessageRecorder.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/service/chat/AgenticMessageRecorder.java @@ -44,7 +44,7 @@ public class AgenticMessageRecorder { /** * Persist user message. * - * @param prepared prepared + * @param prepared prepared * @param userHeader user header */ public void persistUserMessage(AgenticPreparedChatBO prepared, RequestHeader.PrincipalHeader userHeader) { @@ -55,8 +55,8 @@ public class AgenticMessageRecorder { /** * Persist assistant message. * - * @param prepared prepared - * @param content content + * @param prepared prepared + * @param content content * @param userHeader user header */ public void persistAssistantMessage(AgenticPreparedChatBO prepared, String content, diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/CommandTool.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/CommandTool.java index 9975a1280..35b856d03 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/CommandTool.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/CommandTool.java @@ -50,7 +50,7 @@ public class CommandTool { /** * Return command by identifier. * - * @param commandId command identifier + * @param commandId command identifier * @param toolContext tool context * @return lookup command by identifier result */ @@ -71,7 +71,7 @@ public class CommandTool { /** * Return commands by identifiers. * - * @param commandIds command identifiers + * @param commandIds command identifiers * @param toolContext tool context * @return lookup commands by identifiers result */ @@ -97,9 +97,9 @@ public class CommandTool { * Return the matching commands. * * @param commandName command name - * @param profileId profile identifier - * @param page page - * @param size size + * @param profileId profile identifier + * @param page page + * @param size size * @param toolContext tool context * @return search commands result */ @@ -131,9 +131,9 @@ public class CommandTool { /** * Return the matching commands by device identifier. * - * @param deviceId device identifier - * @param page page - * @param size size + * @param deviceId device identifier + * @param page page + * @param size size * @param toolContext tool context * @return list commands by device identifier result */ @@ -163,9 +163,9 @@ public class CommandTool { /** * Return the matching commands by profile identifier. * - * @param profileId profile identifier - * @param page page - * @param size size + * @param profileId profile identifier + * @param page page + * @param size size * @param toolContext tool context * @return list commands by profile identifier result */ diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/DeviceTool.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/DeviceTool.java index a465e1a53..b07699d28 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/DeviceTool.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/DeviceTool.java @@ -66,7 +66,7 @@ public class DeviceTool { /** * Return device by identifier. * - * @param deviceId device identifier + * @param deviceId device identifier * @param toolContext tool context * @return lookup device by identifier result */ @@ -87,7 +87,7 @@ public class DeviceTool { /** * Return devices by identifiers. * - * @param deviceIds device identifiers + * @param deviceIds device identifiers * @param toolContext tool context * @return lookup devices by identifiers result */ @@ -112,11 +112,11 @@ public class DeviceTool { /** * Return the matching devices. * - * @param deviceName device name - * @param deviceCode device code - * @param driverId driver identifier - * @param page page - * @param size size + * @param deviceName device name + * @param deviceCode device code + * @param driverId driver identifier + * @param page page + * @param size size * @param toolContext tool context * @return search devices result */ @@ -151,7 +151,7 @@ public class DeviceTool { /** * Return the matching devices by driver identifier. * - * @param driverId driver identifier + * @param driverId driver identifier * @param toolContext tool context * @return list devices by driver identifier result */ @@ -173,7 +173,7 @@ public class DeviceTool { /** * Return the matching devices by profile identifier. * - * @param profileId profile identifier + * @param profileId profile identifier * @param toolContext tool context * @return list devices by profile identifier result */ @@ -195,8 +195,8 @@ public class DeviceTool { /** * Return device latest point values. * - * @param deviceId device identifier - * @param limit limit + * @param deviceId device identifier + * @param limit limit * @param toolContext tool context * @return get device latest point values result */ @@ -243,7 +243,7 @@ public class DeviceTool { /** * Return device statuses by identifiers. * - * @param deviceIds device identifiers + * @param deviceIds device identifiers * @param toolContext tool context * @return get device statuses by identifiers result */ @@ -272,7 +272,7 @@ public class DeviceTool { /** * Return device statuses by profile identifier. * - * @param profileId profile identifier + * @param profileId profile identifier * @param toolContext tool context * @return get device statuses by profile identifier result */ diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/DriverTool.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/DriverTool.java index eeeb659b1..14e427a48 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/DriverTool.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/DriverTool.java @@ -57,7 +57,7 @@ public class DriverTool { /** * Return driver by identifier. * - * @param driverId driver identifier + * @param driverId driver identifier * @param toolContext tool context * @return lookup driver by identifier result */ @@ -78,7 +78,7 @@ public class DriverTool { /** * Return drivers by identifiers. * - * @param driverIds driver identifiers + * @param driverIds driver identifiers * @param toolContext tool context * @return lookup drivers by identifiers result */ @@ -103,7 +103,7 @@ public class DriverTool { /** * Return driver by device identifier. * - * @param deviceId device identifier + * @param deviceId device identifier * @param toolContext tool context * @return lookup driver by device identifier result */ @@ -125,9 +125,9 @@ public class DriverTool { /** * Return the matching drivers. * - * @param driverName driver name - * @param page page - * @param size size + * @param driverName driver name + * @param page page + * @param size size * @param toolContext tool context * @return search drivers result */ @@ -157,7 +157,7 @@ public class DriverTool { /** * Return driver statuses by identifiers. * - * @param driverIds driver identifiers + * @param driverIds driver identifiers * @param toolContext tool context * @return get driver statuses by identifiers result */ @@ -186,7 +186,7 @@ public class DriverTool { /** * Return driver device status summary. * - * @param driverId driver identifier + * @param driverId driver identifier * @param toolContext tool context * @return get driver device status summary result */ diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/EventTool.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/EventTool.java index be9e0495b..9054d90d2 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/EventTool.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/EventTool.java @@ -50,7 +50,7 @@ public class EventTool { /** * Return event by identifier. * - * @param eventId event identifier + * @param eventId event identifier * @param toolContext tool context * @return lookup event by identifier result */ @@ -71,7 +71,7 @@ public class EventTool { /** * Return events by identifiers. * - * @param eventIds event identifiers + * @param eventIds event identifiers * @param toolContext tool context * @return lookup events by identifiers result */ @@ -96,10 +96,10 @@ public class EventTool { /** * Return the matching events. * - * @param eventName event name - * @param profileId profile identifier - * @param page page - * @param size size + * @param eventName event name + * @param profileId profile identifier + * @param page page + * @param size size * @param toolContext tool context * @return search events result */ @@ -131,9 +131,9 @@ public class EventTool { /** * Return the matching events by device identifier. * - * @param deviceId device identifier - * @param page page - * @param size size + * @param deviceId device identifier + * @param page page + * @param size size * @param toolContext tool context * @return list events by device identifier result */ @@ -163,9 +163,9 @@ public class EventTool { /** * Return the matching events by profile identifier. * - * @param profileId profile identifier - * @param page page - * @param size size + * @param profileId profile identifier + * @param page page + * @param size size * @param toolContext tool context * @return list events by profile identifier result */ diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/PointTool.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/PointTool.java index 18e847fc6..1ce41dac0 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/PointTool.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/PointTool.java @@ -50,7 +50,7 @@ public class PointTool { /** * Return point by identifier. * - * @param pointId point identifier + * @param pointId point identifier * @param toolContext tool context * @return lookup point by identifier result */ @@ -71,7 +71,7 @@ public class PointTool { /** * Return points by identifiers. * - * @param pointIds point identifiers + * @param pointIds point identifiers * @param toolContext tool context * @return lookup points by identifiers result */ @@ -96,10 +96,10 @@ public class PointTool { /** * Return the matching points. * - * @param pointName point name - * @param profileId profile identifier - * @param page page - * @param size size + * @param pointName point name + * @param profileId profile identifier + * @param page page + * @param size size * @param toolContext tool context * @return search points result */ @@ -131,9 +131,9 @@ public class PointTool { /** * Return the matching points by device identifier. * - * @param deviceId device identifier - * @param page page - * @param size size + * @param deviceId device identifier + * @param page page + * @param size size * @param toolContext tool context * @return list points by device identifier result */ @@ -163,9 +163,9 @@ public class PointTool { /** * Return the matching points by profile identifier. * - * @param profileId profile identifier - * @param page page - * @param size size + * @param profileId profile identifier + * @param page page + * @param size size * @param toolContext tool context * @return list points by profile identifier result */ diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/PointValueTool.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/PointValueTool.java index f4e64e8a8..33b955a8e 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/PointValueTool.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/PointValueTool.java @@ -62,8 +62,8 @@ public class PointValueTool { /** * Return latest point value. * - * @param deviceId device identifier - * @param pointId point identifier + * @param deviceId device identifier + * @param pointId point identifier * @param toolContext tool context * @return get latest point value result */ @@ -93,9 +93,9 @@ public class PointValueTool { /** * Return point value history. * - * @param deviceId device identifier - * @param pointId point identifier - * @param count count + * @param deviceId device identifier + * @param pointId point identifier + * @param count count * @param toolContext tool context * @return get point value history result */ @@ -134,8 +134,8 @@ public class PointValueTool { /** * Read point value. * - * @param deviceId device identifier - * @param pointId point identifier + * @param deviceId device identifier + * @param pointId point identifier * @param toolContext tool context * @return read point value result */ @@ -165,9 +165,9 @@ public class PointValueTool { /** * Write point value. * - * @param deviceId device identifier - * @param pointId point identifier - * @param value value + * @param deviceId device identifier + * @param pointId point identifier + * @param value value * @param toolContext tool context * @return write point value result */ diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/ProfileTool.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/ProfileTool.java index 7b8602d49..8bc7bdc18 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/ProfileTool.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/ProfileTool.java @@ -54,7 +54,7 @@ public class ProfileTool { /** * Return profile by identifier. * - * @param profileId profile identifier + * @param profileId profile identifier * @param toolContext tool context * @return lookup profile by identifier result */ @@ -80,7 +80,7 @@ public class ProfileTool { /** * Return profiles by identifiers. * - * @param profileIds profile identifiers + * @param profileIds profile identifiers * @param toolContext tool context * @return lookup profiles by identifiers result */ @@ -112,8 +112,8 @@ public class ProfileTool { * @param profileName profile name * @param profileCode profile code * @param profileType profile type - * @param page page - * @param size size + * @param page page + * @param size size * @param toolContext tool context * @return search profiles result */ @@ -151,7 +151,7 @@ public class ProfileTool { /** * Return the matching profiles by device identifier. * - * @param deviceId device identifier + * @param deviceId device identifier * @param toolContext tool context * @return list profiles by device identifier result */ diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/TenantTool.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/TenantTool.java index 84cf632e1..6c0ca13a7 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/TenantTool.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/TenantTool.java @@ -48,7 +48,9 @@ public class TenantTool { return AgenticToolResult.ok("Current tenant context loaded", new CurrentTenantContext(tenantId)); } - /** The tenant injected from the authenticated ToolContext (never agent-supplied). */ + /** + * The tenant injected from the authenticated ToolContext (never agent-supplied). + */ public record CurrentTenantContext(Long tenantId) { } diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/UserTool.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/UserTool.java index ade3233f4..e9f1ebe57 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/UserTool.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/tools/UserTool.java @@ -51,7 +51,9 @@ public class UserTool { new CurrentUserProfile(userId, header.getUserName(), header.getNickName())); } - /** The acting user resolved from the ToolContext identity. */ + /** + * The acting user resolved from the ToolContext identity. + */ public record CurrentUserProfile(Long userId, String username, String nickname) { } diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticToolContextUtil.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticToolContextUtil.java index c2ff00223..296cf90b9 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticToolContextUtil.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticToolContextUtil.java @@ -112,8 +112,8 @@ public class AgenticToolContextUtil { * Record tool invocation. * * @param toolContext tool context - * @param toolName tool name - * @param domain domain + * @param toolName tool name + * @param domain domain * @param description description */ public static void recordToolInvocation(ToolContext toolContext, String toolName, String domain, @@ -125,10 +125,10 @@ public class AgenticToolContextUtil { * Record tool result. * * @param toolContext tool context - * @param toolName tool name - * @param success success - * @param code code - * @param message message + * @param toolName tool name + * @param success success + * @param code code + * @param message message */ public static void recordToolResult(ToolContext toolContext, String toolName, boolean success, String code, String message) { @@ -139,8 +139,8 @@ public class AgenticToolContextUtil { * Record tool error. * * @param toolContext tool context - * @param toolName tool name - * @param message message + * @param toolName tool name + * @param message message */ public static void recordToolError(ToolContext toolContext, String toolName, String message) { recordRunEvent(toolContext, AgenticRunEvent.toolError(toolName, message)); @@ -149,7 +149,7 @@ public class AgenticToolContextUtil { /** * Record visualizations. * - * @param toolContext tool context + * @param toolContext tool context * @param visualizations visualizations */ @SuppressWarnings("unchecked") @@ -168,7 +168,7 @@ public class AgenticToolContextUtil { * Record run event. * * @param toolContext tool context - * @param event event + * @param event event */ @SuppressWarnings("unchecked") public static void recordRunEvent(ToolContext toolContext, AgenticRunEvent event) { diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticToolUtil.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticToolUtil.java index db6713b5b..46b4cf60c 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticToolUtil.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticToolUtil.java @@ -59,7 +59,7 @@ public class AgenticToolUtil { * Page. * * @param current current - * @param size size + * @param size size * @return page result */ public static Pages page(int current, int size) { @@ -73,8 +73,8 @@ public class AgenticToolUtil { * Clamp. * * @param value value - * @param min min - * @param max max + * @param min min + * @param max max * @return clamp result */ public static int clamp(int value, int min, int max) { diff --git a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticVisualizationUtil.java b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticVisualizationUtil.java index 160a8de55..a014fb921 100644 --- a/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticVisualizationUtil.java +++ b/dc3-common/dc3-common-agentic/src/main/java/io/github/pnoker/common/agentic/utils/AgenticVisualizationUtil.java @@ -74,12 +74,12 @@ public class AgenticVisualizationUtil { /** * Line. * - * @param id id - * @param title title + * @param id id + * @param title title * @param description description - * @param dataset dataset - * @param encode encode - * @param meta meta + * @param dataset dataset + * @param encode encode + * @param meta meta * @param annotations annotations * @return line result */ @@ -107,11 +107,11 @@ public class AgenticVisualizationUtil { /** * Stat. * - * @param id id - * @param title title + * @param id id + * @param title title * @param description description - * @param row row - * @param meta meta + * @param row row + * @param meta meta * @return stat result */ public static AgenticVisualizationSpec stat(String id, String title, String description, @@ -145,8 +145,8 @@ public class AgenticVisualizationUtil { /** * Point history meta. * - * @param deviceId device identifier - * @param pointId point identifier + * @param deviceId device identifier + * @param pointId point identifier * @param valueSource value source * @return point history meta result */ diff --git a/dc3-common/dc3-common-auth/README.md b/dc3-common/dc3-common-auth/README.md index c897d9446..5ae82deff 100644 --- a/dc3-common/dc3-common-auth/README.md +++ b/dc3-common/dc3-common-auth/README.md @@ -17,21 +17,21 @@ functionality. It is wired directly into `dc3-center-auth`. |--------------|----------------------------------------------------------------------------| | Controllers | REST controllers for user, tenant, token, dictionary endpoints | | Services | `TokenService`, `UserService`, `TenantService`, `DictionaryForAuthService` | -| gRPC Servers | Spring `@Service` beans extending generated `*ImplBase` server classes | +| gRPC Servers | Spring `@Service` beans extending generated `*ImplBase` server classes | | DAL | MyBatis-Plus mappers and DAL managers for auth tables | | Init | `AuthInitRunner` for startup checks | ## gRPC Services Exposed -| Service | Purpose | -|---|---| -| `TokenApi` | Validate login and token material for gateway authentication | -| `TenantApi` | Resolve tenant metadata by code | -| `UserApi` | Resolve users by ID or principal ID | -| `LocalCredentialApi` | Resolve local credentials by login name | -| `PermissionApi` | List effective permission codes | -| `ResourceRegistryApi` | Synchronize discovered API and menu resources | -| `McpRuntimeApi` | Introspect, authorize, resolve, and audit MCP tool calls | +| Service | Purpose | +|-----------------------|--------------------------------------------------------------| +| `TokenApi` | Validate login and token material for gateway authentication | +| `TenantApi` | Resolve tenant metadata by code | +| `UserApi` | Resolve users by ID or principal ID | +| `LocalCredentialApi` | Resolve local credentials by login name | +| `PermissionApi` | List effective permission codes | +| `ResourceRegistryApi` | Synchronize discovered API and menu resources | +| `McpRuntimeApi` | Introspect, authorize, resolve, and audit MCP tool calls | Distributed callers use the corresponding facade interfaces; they should not construct gRPC channels in business code. diff --git a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/controller/TokenController.java b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/controller/TokenController.java index e166cf934..ca1264c29 100644 --- a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/controller/TokenController.java +++ b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/controller/TokenController.java @@ -69,6 +69,7 @@ public class TokenController implements BaseController { */ // Public endpoint: invoked before login, so no @PreAuthorize. Path is also // permitted in WebFluxSecurityConfig (POST /token/salt). + /** * Handle the generate salt request. * @@ -102,6 +103,7 @@ public class TokenController implements BaseController { */ // Public endpoint: invoked during login (before a token exists), so no // @PreAuthorize. Path is also permitted in WebFluxSecurityConfig (POST /token/generate). + /** * Handle the generate token request. * @@ -145,6 +147,7 @@ public class TokenController implements BaseController { */ // Public endpoint: invoked during login when no token can be issued yet, so no // @PreAuthorize. Path is also permitted in WebFluxSecurityConfig (POST /token/change_password). + /** * Handle the change password request. * diff --git a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/dal/IdentityAuditLogManager.java b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/dal/IdentityAuditLogManager.java index 36f704520..0a34839b1 100644 --- a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/dal/IdentityAuditLogManager.java +++ b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/dal/IdentityAuditLogManager.java @@ -33,13 +33,13 @@ public interface IdentityAuditLogManager extends IService { /** * Return the matching identity audit. * - * @param tenantId tenant identifier - * @param principalId principal identifier - * @param action action to execute + * @param tenantId tenant identifier + * @param principalId principal identifier + * @param action action to execute * @param resourceType resource type - * @param resourceId resource identifier - * @param status status - * @param limit limit + * @param resourceId resource identifier + * @param status status + * @param limit limit * @return list identifierentity audit result */ List listIdentityAudit(Long tenantId, Long principalId, String action, diff --git a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpAuditBuilder.java b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpAuditBuilder.java index 0a4809a8c..efae53ff4 100644 --- a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpAuditBuilder.java +++ b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpAuditBuilder.java @@ -54,7 +54,7 @@ public interface McpAuditBuilder { * After process. * * @param entityRecord entity record - * @param entityVO view object + * @param entityVO view object */ @AfterMapping default void afterProcess(McpAuditCommand entityRecord, @MappingTarget McpAuditVO entityVO) { diff --git a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpConnectionBuilder.java b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpConnectionBuilder.java index 60909a049..b6578f49b 100644 --- a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpConnectionBuilder.java +++ b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpConnectionBuilder.java @@ -54,7 +54,7 @@ public interface McpConnectionBuilder { * After process. * * @param entityRecord entity record - * @param entityVO view object + * @param entityVO view object */ @AfterMapping default void afterProcess(McpConnectionRecord entityRecord, @MappingTarget McpConnectionVO entityVO) { diff --git a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpToolBuilder.java b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpToolBuilder.java index 08dd1b62d..b726c76f5 100644 --- a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpToolBuilder.java +++ b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/McpToolBuilder.java @@ -50,7 +50,7 @@ public interface McpToolBuilder { * After process. * * @param entityRecord entity record - * @param entityVO view object + * @param entityVO view object */ @AfterMapping default void afterProcess(McpToolRecord entityRecord, @MappingTarget McpToolVO entityVO) { diff --git a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/OAuthClientBuilder.java b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/OAuthClientBuilder.java index e955766d1..00b42ecfe 100644 --- a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/OAuthClientBuilder.java +++ b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/entity/builder/OAuthClientBuilder.java @@ -55,7 +55,7 @@ public interface OAuthClientBuilder { * After process. * * @param entityRecord entity record - * @param entityVO view object + * @param entityVO view object */ @AfterMapping default void afterProcess(OAuthRegisteredClientRecord entityRecord, @MappingTarget OAuthClientVO entityVO) { diff --git a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcLocalCredentialBuilder.java b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcLocalCredentialBuilder.java index a05aaf118..e6e77bfa6 100644 --- a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcLocalCredentialBuilder.java +++ b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcLocalCredentialBuilder.java @@ -60,7 +60,7 @@ public interface GrpcLocalCredentialBuilder { /** * After process. * - * @param entityBO business object + * @param entityBO business object * @param entityGrpc entity grpc */ @AfterMapping diff --git a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcTenantBuilder.java b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcTenantBuilder.java index 786b8d497..a3846cdff 100644 --- a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcTenantBuilder.java +++ b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcTenantBuilder.java @@ -61,7 +61,7 @@ public interface GrpcTenantBuilder { /** * After process. * - * @param entityBO business object + * @param entityBO business object * @param entityGrpc entity grpc */ @AfterMapping diff --git a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcUserBuilder.java b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcUserBuilder.java index c4891ac72..370d7dfb9 100644 --- a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcUserBuilder.java +++ b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/grpc/builder/GrpcUserBuilder.java @@ -67,7 +67,7 @@ public interface GrpcUserBuilder { /** * After process. * - * @param entityBO business object + * @param entityBO business object * @param entityGrpc entity grpc */ @AfterMapping diff --git a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/mapper/IdentityAuditLogMapper.java b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/mapper/IdentityAuditLogMapper.java index ec37f0bd5..0146d27f9 100644 --- a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/mapper/IdentityAuditLogMapper.java +++ b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/mapper/IdentityAuditLogMapper.java @@ -34,13 +34,13 @@ public interface IdentityAuditLogMapper extends BaseMapper { /** * Return the matching identity audit. * - * @param tenantId tenant identifier - * @param principalId principal identifier - * @param action action to execute + * @param tenantId tenant identifier + * @param principalId principal identifier + * @param action action to execute * @param resourceType resource type - * @param resourceId resource identifier - * @param status status - * @param limit limit + * @param resourceId resource identifier + * @param status status + * @param limit limit * @return list identifierentity audit result */ List listIdentityAudit(@Param("tenantId") Long tenantId, diff --git a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/service/LocalCredentialService.java b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/service/LocalCredentialService.java index f79f4f07d..14424adf2 100644 --- a/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/service/LocalCredentialService.java +++ b/dc3-common/dc3-common-auth/src/main/java/io/github/pnoker/common/auth/service/LocalCredentialService.java @@ -49,7 +49,7 @@ public interface LocalCredentialService extends BaseService SELECT id, - client_id, - client_name, - client_type, - owner_principal_id, - service_account_principal_id, - tenant_id, - client_secret_hash, - client_secret_expires_at, - client_auth_methods, - authorization_grant_types, - redirect_uris, - scopes, - require_pkce, - require_consent, - enable_flag + client_id, + client_name, + client_type, + owner_principal_id, + service_account_principal_id, + tenant_id, + client_secret_hash, + client_secret_expires_at, + client_auth_methods, + authorization_grant_types, + redirect_uris, + scopes, + require_pkce, + require_consent, + enable_flag FROM dc3_oauth_registered_client WHERE client_id = #{clientId} - AND deleted = 0 + AND deleted = 0 LIMIT 1 @@ -46,38 +46,38 @@ parameterType="io.github.pnoker.common.auth.entity.oauth.OAuthRegisteredClientRecord"> INSERT INTO dc3_oauth_registered_client (id, client_id, client_name, client_type, owner_principal_id, service_account_principal_id, tenant_id, - client_secret_hash, client_secret_expires_at, client_auth_methods, authorization_grant_types, - redirect_uris, scopes, require_pkce, require_consent, enable_flag, client_settings, token_settings, - creator_id, creator_name, operator_id, operator_name) + client_secret_hash, client_secret_expires_at, client_auth_methods, authorization_grant_types, + redirect_uris, scopes, require_pkce, require_consent, enable_flag, client_settings, token_settings, + creator_id, creator_name, operator_id, operator_name) VALUES (#{id}, #{clientId}, #{clientName}, #{clientType}, #{ownerPrincipalId}, #{serviceAccountPrincipalId}, - #{tenantId}, #{clientSecretHash}, #{clientSecretExpiresAt}, #{clientAuthMethods}, - #{authorizationGrantTypes}, #{redirectUris}, #{scopes}, #{requirePkce}, #{requireConsent}, - #{enableFlag}, '{}', '{}', #{ownerPrincipalId}, #{clientName}, #{ownerPrincipalId}, - #{clientName}) + #{tenantId}, #{clientSecretHash}, #{clientSecretExpiresAt}, #{clientAuthMethods}, + #{authorizationGrantTypes}, #{redirectUris}, #{scopes}, #{requirePkce}, #{requireConsent}, + #{enableFlag}, '{}', '{}', #{ownerPrincipalId}, #{clientName}, #{ownerPrincipalId}, + #{clientName}) @@ -85,118 +85,118 @@ @@ -204,91 +204,91 @@ parameterType="io.github.pnoker.common.auth.entity.oauth.OAuthAuthorizationRecord"> INSERT INTO dc3_oauth_authorization (id, registered_client_id, client_id, principal_id, principal_type, tenant_id, mcp_connection_id, - authorization_grant_type, authorized_scopes, state_hash, authorization_code_hash, - authorization_code_issued, authorization_code_expires, token_claims, token_metadata) + authorization_grant_type, authorized_scopes, state_hash, authorization_code_hash, + authorization_code_issued, authorization_code_expires, token_claims, token_metadata) VALUES (#{id}, #{registeredClientId}, #{clientId}, #{principalId}, #{principalType}, #{tenantId}, - #{mcpConnectionId}, #{authorizationGrantType}, #{authorizedScopes}, #{stateHash}, - #{authorizationCodeHash}, #{authorizationCodeIssued}, #{authorizationCodeExpires}, '{}', - CAST(#{tokenMetadata} AS JSON)) + #{mcpConnectionId}, #{authorizationGrantType}, #{authorizedScopes}, #{stateHash}, + #{authorizationCodeHash}, #{authorizationCodeIssued}, #{authorizationCodeExpires}, '{}', + CAST(#{tokenMetadata} AS JSON)) UPDATE dc3_oauth_authorization - SET authorization_code_hash = #{codeHash}, - access_token_jti = #{accessTokenJti}, - access_token_issued = #{accessIssued}, - access_token_expires = #{accessExpires}, - refresh_token_hash = #{refreshHash}, - previous_refresh_token_hash = #{previousRefreshHash}, - refresh_token_issued = #{refreshIssued}, - refresh_token_expires = #{refreshExpires}, - token_claims = CAST(#{tokenClaims} AS JSON), - operate_time = CURRENT_TIMESTAMP + SET authorization_code_hash = #{codeHash}, + access_token_jti = #{accessTokenJti}, + access_token_issued = #{accessIssued}, + access_token_expires = #{accessExpires}, + refresh_token_hash = #{refreshHash}, + previous_refresh_token_hash = #{previousRefreshHash}, + refresh_token_issued = #{refreshIssued}, + refresh_token_expires = #{refreshExpires}, + token_claims = CAST(#{tokenClaims} AS JSON), + operate_time = CURRENT_TIMESTAMP WHERE id = #{id} - AND deleted = 0 + AND deleted = 0 UPDATE dc3_oauth_authorization - SET revoked_time = #{revokedTime}, - revoke_reason = #{reason}, - operate_time = CURRENT_TIMESTAMP + SET revoked_time = #{revokedTime}, + revoke_reason = #{reason}, + operate_time = CURRENT_TIMESTAMP WHERE access_token_jti = #{jti} - AND deleted = 0 + AND deleted = 0 UPDATE dc3_oauth_authorization - SET revoked_time = #{revokedTime}, - revoke_reason = #{reason}, - operate_time = CURRENT_TIMESTAMP + SET revoked_time = #{revokedTime}, + revoke_reason = #{reason}, + operate_time = CURRENT_TIMESTAMP WHERE refresh_token_hash = #{refreshHash} - AND deleted = 0 + AND deleted = 0 @@ -296,22 +296,22 @@ @@ -320,59 +320,59 @@ parameterType="io.github.pnoker.common.auth.entity.oauth.McpConnectionRecord"> INSERT INTO dc3_mcp_connection (id, connection_name, client_id, principal_id, principal_type, tenant_id, grant_type, enable_flag, - expire_time, connection_ext, remark, creator_id, creator_name, operator_id, operator_name) + expire_time, connection_ext, remark, creator_id, creator_name, operator_id, operator_name) VALUES (#{id}, #{connectionName}, #{clientId}, #{principalId}, #{principalType}, #{tenantId}, #{grantType}, - #{enableFlag}, #{expireTime}, '{}', #{remark}, #{creatorId}, #{creatorName}, #{creatorId}, - #{creatorName}) + #{enableFlag}, #{expireTime}, '{}', #{remark}, #{creatorId}, #{creatorName}, #{creatorId}, + #{creatorName}) UPDATE dc3_mcp_connection - SET revoke_time = #{revokeTime}, - operate_time = CURRENT_TIMESTAMP + SET revoke_time = #{revokeTime}, + operate_time = CURRENT_TIMESTAMP WHERE id = #{id} - AND tenant_id = #{tenantId} - AND creator_id = #{principalId} - AND deleted = 0 + AND tenant_id = #{tenantId} + AND creator_id = #{principalId} + AND deleted = 0 @@ -386,81 +386,81 @@ - @@ -495,53 +495,53 @@ parameterType="io.github.pnoker.common.auth.entity.oauth.McpToolRecord"> INSERT INTO dc3_mcp_tool_catalog (id, tool_id, tool_name, tool_title, tool_category, service_name, api_code, permission_code, - http_method, api_path, schema_hash, risk_level, read_only_hint, destructive_hint, idempotent_hint, - open_world_hint, enable_flag, tool_ext, remark, creator_id, creator_name, operator_id, operator_name) + http_method, api_path, schema_hash, risk_level, read_only_hint, destructive_hint, idempotent_hint, + open_world_hint, enable_flag, tool_ext, remark, creator_id, creator_name, operator_id, operator_name) VALUES (#{id}, #{toolId}, #{toolName}, #{toolTitle}, #{toolCategory}, #{serviceName}, #{apiCode}, - #{permissionCode}, #{httpMethod}, #{apiPath}, #{schemaHash}, #{riskLevel}, #{readOnlyHint}, - #{destructiveHint}, #{idempotentHint}, #{openWorldHint}, #{enableFlag}, - COALESCE(NULLIF(#{toolExt}, '')::json, '{}'::json), #{remark}, - 0, 'system', 0, 'system') + #{permissionCode}, #{httpMethod}, #{apiPath}, #{schemaHash}, #{riskLevel}, #{readOnlyHint}, + #{destructiveHint}, #{idempotentHint}, #{openWorldHint}, #{enableFlag}, + COALESCE(NULLIF(#{toolExt}, '')::json, '{}'::json), #{remark}, + 0, 'system', 0, 'system') UPDATE dc3_mcp_tool_catalog - SET tool_name = #{toolName}, - tool_title = #{toolTitle}, - tool_category = #{toolCategory}, - service_name = #{serviceName}, - api_code = #{apiCode}, - permission_code = #{permissionCode}, - http_method = #{httpMethod}, - api_path = #{apiPath}, - schema_hash = #{schemaHash}, - risk_level = #{riskLevel}, - read_only_hint = #{readOnlyHint}, - destructive_hint = #{destructiveHint}, - idempotent_hint = #{idempotentHint}, - open_world_hint = #{openWorldHint}, - enable_flag = #{enableFlag}, - tool_ext = COALESCE(NULLIF(#{toolExt}, '')::json, '{}'::json), - remark = #{remark}, - operate_time = CURRENT_TIMESTAMP + SET tool_name = #{toolName}, + tool_title = #{toolTitle}, + tool_category = #{toolCategory}, + service_name = #{serviceName}, + api_code = #{apiCode}, + permission_code = #{permissionCode}, + http_method = #{httpMethod}, + api_path = #{apiPath}, + schema_hash = #{schemaHash}, + risk_level = #{riskLevel}, + read_only_hint = #{readOnlyHint}, + destructive_hint = #{destructiveHint}, + idempotent_hint = #{idempotentHint}, + open_world_hint = #{openWorldHint}, + enable_flag = #{enableFlag}, + tool_ext = COALESCE(NULLIF(#{toolExt}, '')::json, '{}'::json), + remark = #{remark}, + operate_time = CURRENT_TIMESTAMP WHERE id = #{id} - AND deleted = 0 + AND deleted = 0 SELECT DISTINCT resource.resource_code FROM dc3_role_principal_bind role_principal - JOIN dc3_role_resource_bind role_resource - ON role_resource.role_id = role_principal.role_id - AND role_resource.deleted = 0 - JOIN dc3_resource resource - ON resource.id = role_resource.resource_id - AND resource.deleted = 0 - AND resource.enable_flag = 0 + JOIN dc3_role_resource_bind role_resource + ON role_resource.role_id = role_principal.role_id + AND role_resource.deleted = 0 + JOIN dc3_resource resource + ON resource.id = role_resource.resource_id + AND resource.deleted = 0 + AND resource.enable_flag = 0 WHERE role_principal.tenant_id = #{tenantId} - AND role_principal.principal_id = #{principalId} - AND role_principal.deleted = 0 + AND role_principal.principal_id = #{principalId} + AND role_principal.deleted = 0

* Batch size, prefetch and the retry policy bind from configuration * ({@code dc3.data.point.batch.*}), not annotation literals. * @@ -77,7 +77,7 @@ public @interface Dc3Listener { /** * @return consumer group / per-instance queue suffix, empty for the platform-shared - * destination (drivers set their client id programmatically) + * destination (drivers set their client id programmatically) */ String group() default ""; diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/config/MqAutoConfiguration.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/config/MqAutoConfiguration.java index 635fa93f0..a80430c88 100644 --- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/config/MqAutoConfiguration.java +++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/config/MqAutoConfiguration.java @@ -44,7 +44,9 @@ import java.util.Objects; @EnableConfigurationProperties(BatchConsumerProperties.class) public class MqAutoConfiguration { - /** Publishing facade over the active adapter. */ + /** + * Publishing facade over the active adapter. + */ @Bean @ConditionalOnMissingBean(MessageSender.class) public MessageSender messageSender(ObjectProvider adapterProvider) { @@ -64,7 +66,9 @@ public class MqAutoConfiguration { return new MessageSenderImpl(adapter); } - /** Registers beans carrying @Dc3Listener methods with the active adapter. */ + /** + * Registers beans carrying @Dc3Listener methods with the active adapter. + */ @Bean @ConditionalOnMissingBean public Dc3ListenerProcessor dc3ListenerProcessor(ObjectProvider adapterProvider) { diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/Dc3ListenerProcessor.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/Dc3ListenerProcessor.java index 6e42f757e..1a244a06a 100644 --- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/Dc3ListenerProcessor.java +++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/Dc3ListenerProcessor.java @@ -98,7 +98,7 @@ public class Dc3ListenerProcessor implements SmartInitializingSingleton, Applica return listenerMethodCache.computeIfAbsent(targetClass, clazz -> { List methods = new ArrayList<>(); for (Class current = clazz; Objects.nonNull(current) && current != Object.class; - current = current.getSuperclass()) { + current = current.getSuperclass()) { for (Method method : current.getDeclaredMethods()) { if (method.isAnnotationPresent(Dc3Listener.class) && !Modifier.isStatic(method.getModifiers())) { method.setAccessible(true); diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/EnvelopeCodec.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/EnvelopeCodec.java index 98f1d9494..b877e92b6 100644 --- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/EnvelopeCodec.java +++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/EnvelopeCodec.java @@ -77,9 +77,9 @@ public final class EnvelopeCodec { * header is informational; the subscription's declared type wins, which also keeps * pre-migration messages (carrying only the legacy type header) consumable. * - * @param delivery the raw delivery - * @param payloadType the declared payload type - * @param payload type + * @param delivery the raw delivery + * @param payloadType the declared payload type + * @param payload type * @return the deserialized payload */ public static T deserialize(WireMqDelivery delivery, Class payloadType) { diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/message/MqMessage.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/message/MqMessage.java index 97cef0a66..6eea66bab 100644 --- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/message/MqMessage.java +++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/message/MqMessage.java @@ -63,7 +63,9 @@ public class MqMessage { @Builder.Default private final Duration delay = Duration.ZERO; - /** Build a message for a topic with an explicit partition key (ordered streams). */ + /** + * Build a message for a topic with an explicit partition key (ordered streams). + */ public static MqMessage of(MqTopic topic, String partitionKey, Object payload) { return MqMessage.builder() .topic(topic) diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/RetryPolicy.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/RetryPolicy.java index 447ca0ec5..b6201708c 100644 --- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/RetryPolicy.java +++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/RetryPolicy.java @@ -21,10 +21,10 @@ package io.github.pnoker.common.mq.subscription; * Bounded redelivery with exponential backoff; exhaustion routes to the dead-letter * instead of dropping. Defaults mirror the point-value batch consumer configuration. * - * @param maxAttempts maximum delivery attempts before dead-lettering - * @param initialBackoffMillis first retry delay - * @param multiplier backoff multiplier - * @param maxBackoffMillis backoff ceiling + * @param maxAttempts maximum delivery attempts before dead-lettering + * @param initialBackoffMillis first retry delay + * @param multiplier backoff multiplier + * @param maxBackoffMillis backoff ceiling * @author pnoker * @since 2026.8.19 */ diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/SubscriptionSpec.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/SubscriptionSpec.java index 0a97b6fbd..50b7ac203 100644 --- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/SubscriptionSpec.java +++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/SubscriptionSpec.java @@ -28,16 +28,16 @@ import java.time.Duration; * Subscription declaration — replaces {@code @RabbitListener} plus the container-factory * choice. Physical destinations are derived by the adapter from topic + mode + keyPattern. * - * @param topic logical destination - * @param mode load-balanced or broadcast - * @param profile latency/throughput tuning preset - * @param delivery single or batch delivery - * @param keyPattern subscription key filter relative to the topic (empty = topic - * default), e.g. {@code "driver.*"} on STATE vs {@code "device.*"} - * @param group consumer group / per-instance queue suffix (drivers use their - * client id); empty = platform-shared destination - * @param instanceTtl per-instance queue/subscription expiry, null = never expire - * @param payloadType type the listener expects + * @param topic logical destination + * @param mode load-balanced or broadcast + * @param profile latency/throughput tuning preset + * @param delivery single or batch delivery + * @param keyPattern subscription key filter relative to the topic (empty = topic + * default), e.g. {@code "driver.*"} on STATE vs {@code "device.*"} + * @param group consumer group / per-instance queue suffix (drivers use their + * client id); empty = platform-shared destination + * @param instanceTtl per-instance queue/subscription expiry, null = never expire + * @param payloadType type the listener expects * @param deadLetterEnabled whether rejects route to the topic's dead-letter * @author pnoker * @since 2026.8.19 diff --git a/dc3-mq/dc3-mq-kafka/README.md b/dc3-mq/dc3-mq-kafka/README.md index 1c6ff4853..65f10cd43 100644 --- a/dc3-mq/dc3-mq-kafka/README.md +++ b/dc3-mq/dc3-mq-kafka/README.md @@ -10,8 +10,8 @@ Active when `dc3.mq.type=kafka`. ## Configuration -| Key | Default | Meaning | -|---|---|---| +| Key | Default | Meaning | +|----------------------------------|----------------------------------------------------------------------------------------|-------------| | `dc3.mq.kafka.bootstrap-servers` | `DC3_MQ_KAFKA_BOOTSTRAP`, then `spring.kafka.bootstrap-servers`, then `localhost:9092` | broker list | ## Dependencies diff --git a/dc3-mq/dc3-mq-kafka/src/main/java/io/github/pnoker/common/mq/kafka/config/KafkaMqAdapterConfiguration.java b/dc3-mq/dc3-mq-kafka/src/main/java/io/github/pnoker/common/mq/kafka/config/KafkaMqAdapterConfiguration.java index a749f2b16..934615f70 100644 --- a/dc3-mq/dc3-mq-kafka/src/main/java/io/github/pnoker/common/mq/kafka/config/KafkaMqAdapterConfiguration.java +++ b/dc3-mq/dc3-mq-kafka/src/main/java/io/github/pnoker/common/mq/kafka/config/KafkaMqAdapterConfiguration.java @@ -40,7 +40,9 @@ import java.util.Map; @ConditionalOnProperty(prefix = "dc3.mq", name = "type", havingValue = "kafka") public class KafkaMqAdapterConfiguration { - /** Producer template on the adapter bootstrap servers; overridable by a user bean. */ + /** + * Producer template on the adapter bootstrap servers; overridable by a user bean. + */ @Bean @ConditionalOnMissingBean(KafkaTemplate.class) public KafkaTemplate kafkaMqTemplate( @@ -48,7 +50,9 @@ public class KafkaMqAdapterConfiguration { return KafkaMqAdapter.template(bootstrapServers); } - /** The port adapter bound to the Kafka template. */ + /** + * The port adapter bound to the Kafka template. + */ @Bean public KafkaMqAdapter kafkaMqAdapter(KafkaTemplate kafkaTemplate, @Value("${dc3.mq.kafka.bootstrap-servers:${DC3_MQ_KAFKA_BOOTSTRAP:${spring.kafka.bootstrap-servers:localhost:9092}}}") diff --git a/dc3-mq/dc3-mq-mqtt/README.md b/dc3-mq/dc3-mq-mqtt/README.md index 0328c6120..d6fe8c04d 100644 --- a/dc3-mq/dc3-mq-mqtt/README.md +++ b/dc3-mq/dc3-mq-mqtt/README.md @@ -1,7 +1,7 @@ # DC3 MQ MQTT -`dc3-mq-mqtt` adapts the broker-neutral port to MQTT 5 (`hivemq-mqtt-client`), compatible with EMQX, HiveMQ, -NanoMQ, and other MQTT 5 brokers. +`dc3-mq-mqtt` adapts the broker-neutral port to MQTT 5 (`hivemq-mqtt-client`), compatible with EMQX, HiveMQ, NanoMQ, and +other MQTT 5 brokers. ## Activation @@ -9,10 +9,10 @@ Active when `dc3.mq.type=mqtt`. ## Configuration -| Key | Default | Meaning | -|---|---|---| +| Key | Default | Meaning | +|--------------------|-------------|-------------| | `dc3.mq.mqtt.host` | `localhost` | broker host | -| `dc3.mq.mqtt.port` | `1883` | broker port | +| `dc3.mq.mqtt.port` | `1883` | broker port | ## Dependencies @@ -26,8 +26,7 @@ mvn -s .mvn/settings.xml -pl dc3-mq/dc3-mq-mqtt -am package ## Testing -No module-specific tests; behaviour is verified by `MqttContractTest` in `dc3-mq-tck` (disposable HiveMQ CE -container). +No module-specific tests; behaviour is verified by `MqttContractTest` in `dc3-mq-tck` (disposable HiveMQ CE container). ## Related Modules diff --git a/dc3-mq/dc3-mq-mqtt/pom.xml b/dc3-mq/dc3-mq-mqtt/pom.xml index 22d3d2af5..c290fa7d8 100644 --- a/dc3-mq/dc3-mq-mqtt/pom.xml +++ b/dc3-mq/dc3-mq-mqtt/pom.xml @@ -31,7 +31,8 @@ 2026.5.22 jar - IoT DC3 MQTT 5 adapter (EMQX / HiveMQ / NanoMQ / ...) for the broker-neutral messaging port + IoT DC3 MQTT 5 adapter (EMQX / HiveMQ / NanoMQ / ...) for the broker-neutral messaging port + diff --git a/dc3-mq/dc3-mq-mqtt/src/main/java/io/github/pnoker/common/mq/mqtt/config/MqttMqAdapterConfiguration.java b/dc3-mq/dc3-mq-mqtt/src/main/java/io/github/pnoker/common/mq/mqtt/config/MqttMqAdapterConfiguration.java index 815d74f8b..40748e3fd 100644 --- a/dc3-mq/dc3-mq-mqtt/src/main/java/io/github/pnoker/common/mq/mqtt/config/MqttMqAdapterConfiguration.java +++ b/dc3-mq/dc3-mq-mqtt/src/main/java/io/github/pnoker/common/mq/mqtt/config/MqttMqAdapterConfiguration.java @@ -36,7 +36,9 @@ import org.springframework.context.annotation.Bean; @ConditionalOnProperty(prefix = "dc3.mq", name = "type", havingValue = "mqtt") public class MqttMqAdapterConfiguration { - /** The port adapter over the HiveMQ MQTT5 client. */ + /** + * The port adapter over the HiveMQ MQTT5 client. + */ @Bean public MqttMqAdapter mqttMqAdapter(@Value("${dc3.mq.mqtt.host:localhost}") String host, @Value("${dc3.mq.mqtt.port:1883}") int port, diff --git a/dc3-mq/dc3-mq-pulsar/README.md b/dc3-mq/dc3-mq-pulsar/README.md index 6dc467867..cf7f2caef 100644 --- a/dc3-mq/dc3-mq-pulsar/README.md +++ b/dc3-mq/dc3-mq-pulsar/README.md @@ -1,7 +1,7 @@ # DC3 MQ Pulsar -`dc3-mq-pulsar` adapts the broker-neutral port to Apache Pulsar (`pulsar-client`). Logical topics map to Pulsar -topics; publish and subscription use the standard client APIs with the port's confirmation model. +`dc3-mq-pulsar` adapts the broker-neutral port to Apache Pulsar (`pulsar-client`). Logical topics map to Pulsar topics; +publish and subscription use the standard client APIs with the port's confirmation model. ## Activation @@ -9,8 +9,8 @@ Active when `dc3.mq.type=pulsar`. ## Configuration -| Key | Default | Meaning | -|---|---|---| +| Key | Default | Meaning | +|-----------------------------|---------------------------|--------------------| | `dc3.mq.pulsar.service-url` | `pulsar://localhost:6650` | broker service URL | ## Dependencies diff --git a/dc3-mq/dc3-mq-pulsar/src/main/java/io/github/pnoker/common/mq/pulsar/config/PulsarMqAdapterConfiguration.java b/dc3-mq/dc3-mq-pulsar/src/main/java/io/github/pnoker/common/mq/pulsar/config/PulsarMqAdapterConfiguration.java index 4839e2ea0..a5b7afe60 100644 --- a/dc3-mq/dc3-mq-pulsar/src/main/java/io/github/pnoker/common/mq/pulsar/config/PulsarMqAdapterConfiguration.java +++ b/dc3-mq/dc3-mq-pulsar/src/main/java/io/github/pnoker/common/mq/pulsar/config/PulsarMqAdapterConfiguration.java @@ -38,7 +38,9 @@ import org.springframework.context.annotation.Bean; @ConditionalOnProperty(prefix = "dc3.mq", name = "type", havingValue = "pulsar") public class PulsarMqAdapterConfiguration { - /** Shared Pulsar client on the service url. */ + /** + * Shared Pulsar client on the service url. + */ @Bean(destroyMethod = "close") @ConditionalOnMissingBean(PulsarClient.class) public PulsarClient pulsarClient( @@ -47,7 +49,9 @@ public class PulsarMqAdapterConfiguration { return PulsarClient.builder().serviceUrl(serviceUrl).build(); } - /** The port adapter bound to the shared client. */ + /** + * The port adapter bound to the shared client. + */ @Bean public PulsarMqAdapter pulsarMqAdapter(PulsarClient pulsarClient, BatchConsumerProperties batchProperties) { return new PulsarMqAdapter(pulsarClient, batchProperties); diff --git a/dc3-mq/dc3-mq-rabbitmq/README.md b/dc3-mq/dc3-mq-rabbitmq/README.md index a3f332fb9..3381ba350 100644 --- a/dc3-mq/dc3-mq-rabbitmq/README.md +++ b/dc3-mq/dc3-mq-rabbitmq/README.md @@ -10,12 +10,12 @@ Active when `dc3.mq.type=rabbitmq` — the default (`matchIfMissing = true`). ## Key types -| Type | Role | -|---|---| -| `RabbitMqAdapter` | `BrokerAdapter` implementation (exchanges, queues, listeners, confirms) | -| `RabbitNames` / `RabbitTopology` | canonical exchange/queue/routing names and bindings | -| `RabbitAcknowledgment` | publisher-confirm handling | -| `ActiveRabbitProfileConfig` / `RabbitEnvironmentConfig` | profile wiring and environment defaults | +| Type | Role | +|---------------------------------------------------------|-------------------------------------------------------------------------| +| `RabbitMqAdapter` | `BrokerAdapter` implementation (exchanges, queues, listeners, confirms) | +| `RabbitNames` / `RabbitTopology` | canonical exchange/queue/routing names and bindings | +| `RabbitAcknowledgment` | publisher-confirm handling | +| `ActiveRabbitProfileConfig` / `RabbitEnvironmentConfig` | profile wiring and environment defaults | ## Configuration diff --git a/dc3-mq/dc3-mq-rabbitmq/pom.xml b/dc3-mq/dc3-mq-rabbitmq/pom.xml index 17e475ca4..7c26d5915 100644 --- a/dc3-mq/dc3-mq-rabbitmq/pom.xml +++ b/dc3-mq/dc3-mq-rabbitmq/pom.xml @@ -31,7 +31,9 @@ 2026.5.22 jar - IoT DC3 RabbitMQ adapter for the broker-neutral messaging port; physical topology is byte-for-byte identical to the pre-port layout + IoT DC3 RabbitMQ adapter for the broker-neutral messaging port; physical topology is byte-for-byte + identical to the pre-port layout + diff --git a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitAcknowledgment.java b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitAcknowledgment.java index 7512244cb..5a3c975a7 100644 --- a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitAcknowledgment.java +++ b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitAcknowledgment.java @@ -43,12 +43,16 @@ public final class RabbitAcknowledgment implements Acknowledgment { this.multiple = multiple; } - /** Ack exactly one delivery ({@code multiple=false}). */ + /** + * Ack exactly one delivery ({@code multiple=false}). + */ public static RabbitAcknowledgment single(Channel channel, long deliveryTag) { return new RabbitAcknowledgment(channel, deliveryTag, false); } - /** Ack everything up to the tag ({@code multiple=true}) — the broker-batch commit path. */ + /** + * Ack everything up to the tag ({@code multiple=true}) — the broker-batch commit path. + */ public static RabbitAcknowledgment batch(Channel channel, long lastDeliveryTag) { return new RabbitAcknowledgment(channel, lastDeliveryTag, true); } diff --git a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitTopology.java b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitTopology.java index b68905a4b..9678b7d41 100644 --- a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitTopology.java +++ b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitTopology.java @@ -55,12 +55,12 @@ public final class RabbitTopology { /** * Descriptor of a platform-shared queue. * - * @param queueName queue name - * @param exchangeName source exchange - * @param routingKey binding routing key (pattern) - * @param ttlMillis per-queue message TTL, 0 = none - * @param deadExchange dead-letter exchange, null = none - * @param deadRouting dead-letter routing key + * @param queueName queue name + * @param exchangeName source exchange + * @param routingKey binding routing key (pattern) + * @param ttlMillis per-queue message TTL, 0 = none + * @param deadExchange dead-letter exchange, null = none + * @param deadRouting dead-letter routing key * @param bindingArgument whether the binding carries the x-auto-delete argument */ private record SharedQueue(String queueName, String exchangeName, String routingKey, int ttlMillis, @@ -147,8 +147,8 @@ public final class RabbitTopology { /** * Declare the driver-side metadata broadcast queue (auto-delete, 30 s TTL). * - * @param admin rabbit admin - * @param client driver client id + * @param admin rabbit admin + * @param client driver client id * @param routingKey exact routing key (service name) */ public static void declareMetadataQueue(RabbitAdmin admin, String client, String routingKey) { diff --git a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/config/RabbitMqAdapterConfiguration.java b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/config/RabbitMqAdapterConfiguration.java index bef62b3d1..6f842d2b3 100644 --- a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/config/RabbitMqAdapterConfiguration.java +++ b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/config/RabbitMqAdapterConfiguration.java @@ -47,14 +47,18 @@ import org.springframework.context.annotation.Bean; @ConditionalOnProperty(prefix = "dc3.mq", name = "type", havingValue = "rabbitmq", matchIfMissing = true) public class RabbitMqAdapterConfiguration { - /** JSON converter with typed envelope headers. */ + /** + * JSON converter with typed envelope headers. + */ @Bean @ConditionalOnMissingBean public MessageConverter messageConverter() { return new JacksonJsonMessageConverter(JsonUtil.getJsonMapper()); } - /** Publisher-confirms template with mandatory returns. */ + /** + * Publisher-confirms template with mandatory returns. + */ @Bean(name = "rabbitTemplate") @ConditionalOnMissingBean(RabbitTemplate.class) public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) { @@ -81,14 +85,18 @@ public class RabbitMqAdapterConfiguration { return rabbitTemplate; } - /** Declares queues/exchanges/bindings at startup. */ + /** + * Declares queues/exchanges/bindings at startup. + */ @Bean @ConditionalOnMissingBean public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) { return new RabbitAdmin(connectionFactory); } - /** The port adapter bound to the template and admin. */ + /** + * The port adapter bound to the template and admin. + */ @Bean public RabbitMqAdapter rabbitMqAdapter(RabbitTemplate rabbitTemplate, RabbitAdmin rabbitAdmin, ConnectionFactory connectionFactory, BatchConsumerProperties batchProperties, diff --git a/dc3-mq/dc3-mq-rabbitmq/src/main/resources/META-INF/spring.factories b/dc3-mq/dc3-mq-rabbitmq/src/main/resources/META-INF/spring.factories index 1f1315989..41d168881 100644 --- a/dc3-mq/dc3-mq-rabbitmq/src/main/resources/META-INF/spring.factories +++ b/dc3-mq/dc3-mq-rabbitmq/src/main/resources/META-INF/spring.factories @@ -14,7 +14,6 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - org.springframework.boot.env.EnvironmentPostProcessor=\ io.github.pnoker.common.mq.rabbit.config.ActiveRabbitProfileConfig,\ io.github.pnoker.common.mq.rabbit.config.RabbitEnvironmentConfig diff --git a/dc3-mq/dc3-mq-rocketmq/README.md b/dc3-mq/dc3-mq-rocketmq/README.md index 3a62ed5eb..15cb96721 100644 --- a/dc3-mq/dc3-mq-rocketmq/README.md +++ b/dc3-mq/dc3-mq-rocketmq/README.md @@ -1,7 +1,7 @@ # DC3 MQ RocketMQ -`dc3-mq-rocketmq` adapts the broker-neutral port to Apache RocketMQ (`rocketmq-client`). Logical topics map to -RocketMQ topics; publish and subscription use the standard producer/consumer APIs with the port's confirmation model. +`dc3-mq-rocketmq` adapts the broker-neutral port to Apache RocketMQ (`rocketmq-client`). Logical topics map to RocketMQ +topics; publish and subscription use the standard producer/consumer APIs with the port's confirmation model. ## Activation @@ -9,8 +9,8 @@ Active when `dc3.mq.type=rocketmq`. ## Configuration -| Key | Default | Meaning | -|---|---|---| +| Key | Default | Meaning | +|---------------------------------------|------------------|--------------------| | `dc3.mq.rocketmq.name-server-address` | `localhost:9876` | NameServer address | ## Dependencies diff --git a/dc3-mq/dc3-mq-rocketmq/src/main/java/io/github/pnoker/common/mq/rocketmq/config/RocketMqAdapterConfiguration.java b/dc3-mq/dc3-mq-rocketmq/src/main/java/io/github/pnoker/common/mq/rocketmq/config/RocketMqAdapterConfiguration.java index f720a72f7..30ee10502 100644 --- a/dc3-mq/dc3-mq-rocketmq/src/main/java/io/github/pnoker/common/mq/rocketmq/config/RocketMqAdapterConfiguration.java +++ b/dc3-mq/dc3-mq-rocketmq/src/main/java/io/github/pnoker/common/mq/rocketmq/config/RocketMqAdapterConfiguration.java @@ -35,7 +35,9 @@ import org.springframework.context.annotation.Bean; @ConditionalOnProperty(prefix = "dc3.mq", name = "type", havingValue = "rocketmq") public class RocketMqAdapterConfiguration { - /** The port adapter bound to the RocketMQ producer/consumer. */ + /** + * The port adapter bound to the RocketMQ producer/consumer. + */ @Bean public RocketMqAdapter rocketMqAdapter( @Value("${dc3.mq.rocketmq.name-server-address:localhost:9876}") String namesrvAddr, diff --git a/dc3-mq/dc3-mq-rocketmq/src/test/java/io/github/pnoker/common/mq/rocketmq/RocketMqFreshGroupProbe.java b/dc3-mq/dc3-mq-rocketmq/src/test/java/io/github/pnoker/common/mq/rocketmq/RocketMqFreshGroupProbe.java index aae538c55..20ee775b4 100644 --- a/dc3-mq/dc3-mq-rocketmq/src/test/java/io/github/pnoker/common/mq/rocketmq/RocketMqFreshGroupProbe.java +++ b/dc3-mq/dc3-mq-rocketmq/src/test/java/io/github/pnoker/common/mq/rocketmq/RocketMqFreshGroupProbe.java @@ -72,7 +72,7 @@ class RocketMqFreshGroupProbe { private List subscribe(String group) { List received = new CopyOnWriteArrayList<>(); adapter.subscribe(new SubscriptionSpec(MqTopic.EVENT, SubscriptionMode.LOAD_BALANCE, - ConsumptionProfile.LATENCY, DeliveryMode.SINGLE, "", group, null, String.class, true), + ConsumptionProfile.LATENCY, DeliveryMode.SINGLE, "", group, null, String.class, true), delivery -> { received.add(new String(delivery.body(), java.nio.charset.StandardCharsets.UTF_8)); delivery.acknowledgment().ack(); diff --git a/dc3-mq/dc3-mq-tck/README.md b/dc3-mq/dc3-mq-tck/README.md index 2178cf59b..07650f267 100644 --- a/dc3-mq/dc3-mq-tck/README.md +++ b/dc3-mq/dc3-mq-tck/README.md @@ -7,14 +7,14 @@ Testcontainers container. ## Contract tests -| Test | Broker | -|---|---| +| Test | Broker | +|------------------------|----------------------------------------------| | `RabbitMqContractTest` | RabbitMQ (`rabbitmq:3.13-management-alpine`) | -| `KafkaContractTest` | Apache Kafka (`apache/kafka:3.9.0`) | -| `RocketMqContractTest` | RocketMQ | -| `PulsarContractTest` | Pulsar | -| `ActiveMqContractTest` | ActiveMQ | -| `MqttContractTest` | HiveMQ CE | +| `KafkaContractTest` | Apache Kafka (`apache/kafka:3.9.0`) | +| `RocketMqContractTest` | RocketMQ | +| `PulsarContractTest` | Pulsar | +| `ActiveMqContractTest` | ActiveMQ | +| `MqttContractTest` | HiveMQ CE | ## Running diff --git a/dc3-tsdb/README.md b/dc3-tsdb/README.md index a6e5d5ddd..5a05ff9c5 100644 --- a/dc3-tsdb/README.md +++ b/dc3-tsdb/README.md @@ -1,19 +1,19 @@ # DC3 TSDB -dc3-tsdb is the pluggable time-series storage layer of IoT DC3. It defines a store-neutral port — the TsdbStore SPI -with a sample model and capability set — plus one adapter per supported time-series database. The Data Center writes -point values through the port and never through store-specific classes. +dc3-tsdb is the pluggable time-series storage layer of IoT DC3. It defines a store-neutral port — the TsdbStore SPI with +a sample model and capability set — plus one adapter per supported time-series database. The Data Center writes point +values through the port and never through store-specific classes. ## Modules -| Module | Role | -|---|---| -| dc3-tsdb-core | store-neutral port: TsdbStore SPI, TsdbModel sample model, capabilities; zero store dependencies | -| dc3-tsdb-timescale | TimescaleDB adapter (default; embedded or standalone PostgreSQL) | -| dc3-tsdb-tdengine | TDengine adapter — supertable + per-series subtables over the REST/WS JDBC driver | -| dc3-tsdb-influxdb | InfluxDB 3 adapter — tags/fields over the documented v3 HTTP SQL and line-protocol APIs | -| dc3-tsdb-iotdb | Apache IoTDB adapter — tree paths root.dc3.* over the session API | -| dc3-tsdb-tck | store-neutral contract suite: an adapter that passes these tests is compliant | +| Module | Role | +|--------------------|--------------------------------------------------------------------------------------------------| +| dc3-tsdb-core | store-neutral port: TsdbStore SPI, TsdbModel sample model, capabilities; zero store dependencies | +| dc3-tsdb-timescale | TimescaleDB adapter (default; embedded or standalone PostgreSQL) | +| dc3-tsdb-tdengine | TDengine adapter — supertable + per-series subtables over the REST/WS JDBC driver | +| dc3-tsdb-influxdb | InfluxDB 3 adapter — tags/fields over the documented v3 HTTP SQL and line-protocol APIs | +| dc3-tsdb-iotdb | Apache IoTDB adapter — tree paths root.dc3.* over the session API | +| dc3-tsdb-tck | store-neutral contract suite: an adapter that passes these tests is compliant | ## Selection diff --git a/dc3-tsdb/dc3-tsdb-core/README.md b/dc3-tsdb/dc3-tsdb-core/README.md index 9f8d53f57..b128a535a 100644 --- a/dc3-tsdb/dc3-tsdb-core/README.md +++ b/dc3-tsdb/dc3-tsdb-core/README.md @@ -6,10 +6,10 @@ store-specific classes. The module has zero store dependencies. ## Key types -| Type | Role | -|---|---| +| Type | Role | +|-------------|-----------------------------------------------------------------------------------| | `TsdbStore` | SPI implemented by every time-series adapter (write, query, schema, capabilities) | -| `TsdbModel` | sample model shared across adapters | +| `TsdbModel` | sample model shared across adapters | ## Build Instructions diff --git a/dc3-tsdb/dc3-tsdb-core/pom.xml b/dc3-tsdb/dc3-tsdb-core/pom.xml index 9445b05b5..d41df91a2 100644 --- a/dc3-tsdb/dc3-tsdb-core/pom.xml +++ b/dc3-tsdb/dc3-tsdb-core/pom.xml @@ -31,6 +31,8 @@ 2026.5.22 jar - IoT DC3 store-neutral time-series port: sample model, TsdbStore SPI, capabilities. Zero store dependencies + IoT DC3 store-neutral time-series port: sample model, TsdbStore SPI, capabilities. Zero store + dependencies + diff --git a/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/model/TsdbModel.java b/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/model/TsdbModel.java index 814d4bd2e..c1f064002 100644 --- a/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/model/TsdbModel.java +++ b/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/model/TsdbModel.java @@ -155,21 +155,37 @@ public final class TsdbModel { * form the M4 rendering quadruple with MIN/MAX; PERCENTILE is capability-gated. */ public enum AggregateFunction { - /** Arithmetic mean over the window. */ + /** + * Arithmetic mean over the window. + */ AVG, - /** Minimum value in the window. */ + /** + * Minimum value in the window. + */ MIN, - /** Maximum value in the window. */ + /** + * Maximum value in the window. + */ MAX, - /** Sum over the window. */ + /** + * Sum over the window. + */ SUM, - /** Row count over the window. */ + /** + * Row count over the window. + */ COUNT, - /** First sample in the window. */ + /** + * First sample in the window. + */ FIRST, - /** Last sample in the window. */ + /** + * Last sample in the window. + */ LAST, - /** Percentile, p supplied per call and capability-gated. */ + /** + * Percentile, p supplied per call and capability-gated. + */ PERCENTILE } @@ -180,7 +196,9 @@ public final class TsdbModel { * @param toExclusive exclusive end */ public record TimeWindow(Instant from, Instant toExclusive) { - /** Rejects empty or reversed windows. */ + /** + * Rejects empty or reversed windows. + */ public TimeWindow { if (!from.isBefore(toExclusive)) { throw new IllegalArgumentException("window from must be before toExclusive"); @@ -253,11 +271,17 @@ public final class TsdbModel { * S13-② grouping dimensions (the dashboard's whitelisted set). */ public enum GroupDimension { - /** Group by device. */ + /** + * Group by device. + */ DEVICE, - /** Group by point. */ + /** + * Group by point. + */ POINT, - /** Group by driver. */ + /** + * Group by driver. + */ DRIVER } @@ -283,7 +307,7 @@ public final class TsdbModel { /** * S19 aligned-bucket Pearson correlation. * - * @param pearson correlation coefficient in [-1,1] + * @param pearson correlation coefficient in [-1,1] * @param alignedBuckets buckets used after alignment */ public record CorrelationResult(double pearson, long alignedBuckets) { @@ -307,7 +331,9 @@ public final class TsdbModel { } } - /** S18/S6 read timeout signal — the port's runaway-scan guard. */ + /** + * S18/S6 read timeout signal — the port's runaway-scan guard. + */ public static final class TsdbQueryTimeout extends RuntimeException { /** diff --git a/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/spi/TsdbStore.java b/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/spi/TsdbStore.java index 81ad3bb9f..0323ad308 100644 --- a/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/spi/TsdbStore.java +++ b/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/spi/TsdbStore.java @@ -249,18 +249,18 @@ public interface TsdbStore { * Adapter capability declaration (§8 of the design). The startup negotiation log * prints this row, mirroring the MQ port. * - * @param gapFill zero-fill empty buckets - * @param tenantWideScan series-empty history/aggregate/count/last - * @param tenantWideAnalytics S13 facet - * @param latencyHistogram S13-④ store-side - * @param percentile S15 PERCENTILE - * @param rollupSupport S16 tiered-rollup mode - * @param maxAppendBatch S18 chunking threshold - * @param deleteRange S10 - * @param ordering NONE | PER_SERIES - * @param precision native timestamp precision - * @param backfill out-of-order/late writes accepted - * @param correlation S19 store-side correlation + * @param gapFill zero-fill empty buckets + * @param tenantWideScan series-empty history/aggregate/count/last + * @param tenantWideAnalytics S13 facet + * @param latencyHistogram S13-④ store-side + * @param percentile S15 PERCENTILE + * @param rollupSupport S16 tiered-rollup mode + * @param maxAppendBatch S18 chunking threshold + * @param deleteRange S10 + * @param ordering NONE | PER_SERIES + * @param precision native timestamp precision + * @param backfill out-of-order/late writes accepted + * @param correlation S19 store-side correlation */ record TsdbCapabilities( boolean gapFill, @@ -277,31 +277,53 @@ public interface TsdbStore { boolean correlation) { } - /** S16 tiered-rollup support levels. */ + /** + * S16 tiered-rollup support levels. + */ enum RollupSupport { - /** Store-side rollup tiers. */ + /** + * Store-side rollup tiers. + */ NATIVE, - /** Rollup maintained by the platform on top of the store. */ + /** + * Rollup maintained by the platform on top of the store. + */ MANUAL, - /** No rollup support. */ + /** + * No rollup support. + */ NONE } - /** S2/S8 result ordering guarantees. */ + /** + * S2/S8 result ordering guarantees. + */ enum OrderingGuarantee { - /** No ordering guarantee. */ + /** + * No ordering guarantee. + */ NONE, - /** Samples ordered within each series. */ + /** + * Samples ordered within each series. + */ PER_SERIES } - /** Native timestamp precision of the store. */ + /** + * Native timestamp precision of the store. + */ enum Precision { - /** Microsecond precision. */ + /** + * Microsecond precision. + */ MICRO, - /** Millisecond precision. */ + /** + * Millisecond precision. + */ MILLI, - /** Nanosecond precision. */ + /** + * Nanosecond precision. + */ NANO } } diff --git a/dc3-tsdb/dc3-tsdb-influxdb/README.md b/dc3-tsdb/dc3-tsdb-influxdb/README.md index 94121af26..9064ac669 100644 --- a/dc3-tsdb/dc3-tsdb-influxdb/README.md +++ b/dc3-tsdb/dc3-tsdb-influxdb/README.md @@ -1,7 +1,7 @@ # DC3 TSDB InfluxDB -`dc3-tsdb-influxdb` adapts the store-neutral port to InfluxDB 3 over the documented v3 HTTP SQL and line-protocol -APIs. Points map to tag/field columns; no InfluxDB-specific JDBC or client SDK is required. +`dc3-tsdb-influxdb` adapts the store-neutral port to InfluxDB 3 over the documented v3 HTTP SQL and line-protocol APIs. +Points map to tag/field columns; no InfluxDB-specific JDBC or client SDK is required. ## Activation @@ -11,11 +11,11 @@ Active when `dc3.tsdb.type=influxdb`. `InfluxdbTsdbProperties` binds the `dc3.tsdb.influxdb` prefix: -| Key | Default | Meaning | -|---|---|---| -| `dc3.tsdb.influxdb.url` | `http://localhost:8181` | InfluxDB 3 HTTP endpoint | -| `dc3.tsdb.influxdb.token` | *(empty)* | auth token | -| `dc3.tsdb.influxdb.database` | `dc3` | target database | +| Key | Default | Meaning | +|------------------------------|-------------------------|--------------------------| +| `dc3.tsdb.influxdb.url` | `http://localhost:8181` | InfluxDB 3 HTTP endpoint | +| `dc3.tsdb.influxdb.token` | *(empty)* | auth token | +| `dc3.tsdb.influxdb.database` | `dc3` | target database | ## Dependencies diff --git a/dc3-tsdb/dc3-tsdb-influxdb/pom.xml b/dc3-tsdb/dc3-tsdb-influxdb/pom.xml index 77ce20fa5..858dcf206 100644 --- a/dc3-tsdb/dc3-tsdb-influxdb/pom.xml +++ b/dc3-tsdb/dc3-tsdb-influxdb/pom.xml @@ -29,7 +29,9 @@ dc3-tsdb-influxdb ${project.artifactId} - InfluxDB 3 adapter of the TSDB port — tags/fields over the documented v3 HTTP SQL and line-protocol APIs, zero client dependencies + InfluxDB 3 adapter of the TSDB port — tags/fields over the documented v3 HTTP SQL and line-protocol + APIs, zero client dependencies + diff --git a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/InfluxdbTsdbStore.java b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/InfluxdbTsdbStore.java index 83228bb66..5d900e1c6 100644 --- a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/InfluxdbTsdbStore.java +++ b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/InfluxdbTsdbStore.java @@ -437,7 +437,9 @@ public final class InfluxdbTsdbStore implements TsdbStore { return "date_bin(%s, time, TIMESTAMP '1970-01-01T00:00:00Z')".formatted(intervalLiteral(bucketWidth)); } - /** Largest exactly-dividing unit; DataFusion intervals lack a generic millisecond form. */ + /** + * Largest exactly-dividing unit; DataFusion intervals lack a generic millisecond form. + */ private static String intervalLiteral(Duration width) { long nanos = width.toNanos(); if (nanos % 3_600_000_000_000L == 0) { @@ -452,7 +454,9 @@ public final class InfluxdbTsdbStore implements TsdbStore { return "INTERVAL '" + (nanos / 1_000_000L) + " millisecond'"; } - /** RFC3339 literal — InfluxDB 3 compares timestamps against string literals. */ + /** + * RFC3339 literal — InfluxDB 3 compares timestamps against string literals. + */ private static String literal(Instant instant) { return "TIMESTAMP '" + instant + "'"; } @@ -506,7 +510,9 @@ public final class InfluxdbTsdbStore implements TsdbStore { } } - /** CSV response: header row then data rows, RFC-4180 quoting. */ + /** + * CSV response: header row then data rows, RFC-4180 quoting. + */ private List query(String sql, TsdbDeadline deadline) { String body = "{\"db\":\"" + database + "\",\"q\":" + com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.textNode(sql).toString() @@ -537,7 +543,9 @@ public final class InfluxdbTsdbStore implements TsdbStore { return CsvRows.parse(response); } - /** Minimal CSV row with typed accessors; values arrive as text and parse on demand. */ + /** + * Minimal CSV row with typed accessors; values arrive as text and parse on demand. + */ static final class CsvRow { private final Map values; diff --git a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbAutoConfiguration.java b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbAutoConfiguration.java index bbc292a9b..70aa3242f 100644 --- a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbAutoConfiguration.java +++ b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbAutoConfiguration.java @@ -39,7 +39,9 @@ import org.springframework.context.annotation.Bean; @ConditionalOnProperty(prefix = "dc3.tsdb", name = "type", havingValue = "influxdb") public class InfluxdbTsdbAutoConfiguration { - /** The InfluxDB 3 adapter over the v3 HTTP APIs. */ + /** + * The InfluxDB 3 adapter over the v3 HTTP APIs. + */ @Bean @ConditionalOnMissingBean(TsdbStore.class) public TsdbStore tsdbStore(InfluxdbTsdbProperties properties) { diff --git a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbProperties.java b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbProperties.java index ae28e9f2e..579897c31 100644 --- a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbProperties.java +++ b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbProperties.java @@ -33,12 +33,18 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "dc3.tsdb.influxdb") public class InfluxdbTsdbProperties { - /** Base url of the InfluxDB 3 node, e.g. {@code http://dc3-influxdb:8181}. */ + /** + * Base url of the InfluxDB 3 node, e.g. {@code http://dc3-influxdb:8181}. + */ private String url = "http://localhost:8181"; - /** Bearer token with write+query permissions. */ + /** + * Bearer token with write+query permissions. + */ private String token = ""; - /** Database (auto-created on first write). */ + /** + * Database (auto-created on first write). + */ private String database = "dc3"; } diff --git a/dc3-tsdb/dc3-tsdb-iotdb/README.md b/dc3-tsdb/dc3-tsdb-iotdb/README.md index 4c9600656..b51f4b37a 100644 --- a/dc3-tsdb/dc3-tsdb-iotdb/README.md +++ b/dc3-tsdb/dc3-tsdb-iotdb/README.md @@ -1,7 +1,7 @@ # DC3 TSDB IoTDB -`dc3-tsdb-iotdb` adapts the store-neutral port to Apache IoTDB over the session API (`iotdb-session`). Series are -stored as tree paths under `root.dc3.*`. +`dc3-tsdb-iotdb` adapts the store-neutral port to Apache IoTDB over the session API (`iotdb-session`). Series are stored +as tree paths under `root.dc3.*`. ## Activation @@ -11,12 +11,12 @@ Active when `dc3.tsdb.type=iotdb`. `IotdbTsdbProperties` binds the `dc3.tsdb.iotdb` prefix: -| Key | Default | Meaning | -|---|---|---| -| `dc3.tsdb.iotdb.host` | `localhost` | IoTDB host | -| `dc3.tsdb.iotdb.port` | `6667` | session port | -| `dc3.tsdb.iotdb.username` | `root` | login name | -| `dc3.tsdb.iotdb.password` | `root` | login password | +| Key | Default | Meaning | +|---------------------------|-------------|----------------| +| `dc3.tsdb.iotdb.host` | `localhost` | IoTDB host | +| `dc3.tsdb.iotdb.port` | `6667` | session port | +| `dc3.tsdb.iotdb.username` | `root` | login name | +| `dc3.tsdb.iotdb.password` | `root` | login password | ## Dependencies diff --git a/dc3-tsdb/dc3-tsdb-iotdb/pom.xml b/dc3-tsdb/dc3-tsdb-iotdb/pom.xml index 16cee039d..8f7db823c 100644 --- a/dc3-tsdb/dc3-tsdb-iotdb/pom.xml +++ b/dc3-tsdb/dc3-tsdb-iotdb/pom.xml @@ -29,7 +29,9 @@ dc3-tsdb-iotdb ${project.artifactId} - Apache IoTDB adapter of the TSDB port — tree paths root.dc3.* over the session API; requires timestamp_precision=us + Apache IoTDB adapter of the TSDB port — tree paths root.dc3.* over the session API; requires + timestamp_precision=us + diff --git a/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/IotdbTsdbStore.java b/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/IotdbTsdbStore.java index 741eb352f..1c3db1e00 100644 --- a/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/IotdbTsdbStore.java +++ b/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/IotdbTsdbStore.java @@ -561,12 +561,16 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable { return rows; } - /** One result row: timestamp plus typed accessors over the fixed sample layout - * or name-derived series/value views for aggregate shapes. */ + /** + * One result row: timestamp plus typed accessors over the fixed sample layout + * or name-derived series/value views for aggregate shapes. + */ static final class Row { private final long timestamp; - /** Result column names; IoTDB prefixes a "Time" entry the field list omits. */ + /** + * Result column names; IoTDB prefixes a "Time" entry the field list omits. + */ private final List columns; private final List fieldColumns; private final RowRecord record; @@ -602,8 +606,10 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable { return Objects.isNull(value) ? null : value.doubleValue(); } - /** Aggregate columns may come back typed differently per function; the - * typed getters on Field throw when the backing slot is unset. */ + /** + * Aggregate columns may come back typed differently per function; the + * typed getters on Field throw when the backing slot is unset. + */ private Object valueAt(int index) { org.apache.tsfile.read.common.Field field = fieldAt(index); if (Objects.isNull(field)) { @@ -624,7 +630,9 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable { return Objects.isNull(field) || field.getDataType() == TSDataType.UNKNOWN ? null : field; } - /** Measurement-name keyed view of a SELECT * row (last path segment). */ + /** + * Measurement-name keyed view of a SELECT * row (last path segment). + */ Map measurements() { Map out = new LinkedHashMap<>(); for (int i = 0; i < fieldColumns.size() && i < record.getFields().size(); i++) { @@ -675,7 +683,9 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable { return null; } - /** Series of the first field column, for aggregate result shapes. */ + /** + * Series of the first field column, for aggregate result shapes. + */ SeriesKey series() { return fieldColumns.isEmpty() ? null : seriesOfPath(fieldColumns.getFirst()); } @@ -684,7 +694,9 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable { return nullableDoubleAt(fieldIndex); } - /** Second column of the (value, count) aggregate pair, when selected. */ + /** + * Second column of the (value, count) aggregate pair, when selected. + */ long count() { return columns.size() > 1 ? longAt(1) : 1L; } @@ -692,8 +704,10 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable { record Cell(Double value, long count, boolean countColumn) { } - /** Series-keyed cells for aggregate shapes; a series may carry one value - * column and one count column. */ + /** + * Series-keyed cells for aggregate shapes; a series may carry one value + * column and one count column. + */ Map cells() { Map cells = new LinkedHashMap<>(); for (int i = 0; i < fieldColumns.size() && i < record.getFields().size(); i++) { diff --git a/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/config/IotdbTsdbAutoConfiguration.java b/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/config/IotdbTsdbAutoConfiguration.java index 416020053..3ab6e5559 100644 --- a/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/config/IotdbTsdbAutoConfiguration.java +++ b/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/config/IotdbTsdbAutoConfiguration.java @@ -39,7 +39,9 @@ import org.springframework.context.annotation.Bean; @ConditionalOnProperty(prefix = "dc3.tsdb", name = "type", havingValue = "iotdb") public class IotdbTsdbAutoConfiguration { - /** The IoTDB adapter over the session API; closed with the context. */ + /** + * The IoTDB adapter over the session API; closed with the context. + */ @Bean(destroyMethod = "close") @ConditionalOnMissingBean(TsdbStore.class) public IotdbTsdbStore tsdbStore(IotdbTsdbProperties properties) { diff --git a/dc3-tsdb/dc3-tsdb-tck/README.md b/dc3-tsdb/dc3-tsdb-tck/README.md index ae80e618b..252748ebf 100644 --- a/dc3-tsdb/dc3-tsdb-tck/README.md +++ b/dc3-tsdb/dc3-tsdb-tck/README.md @@ -1,18 +1,18 @@ # DC3 TSDB TCK `dc3-tsdb-tck` is the store-neutral contract suite of the `dc3-tsdb` family: an adapter that passes these tests is -compliant with the time-series port. `AbstractTsdbContractTest` defines the shared contract (write, latest-value -read, history read, and schema behaviour); one concrete test per store boots the engine in a disposable Testcontainers +compliant with the time-series port. `AbstractTsdbContractTest` defines the shared contract (write, latest-value read, +history read, and schema behaviour); one concrete test per store boots the engine in a disposable Testcontainers container. ## Contract tests -| Test | Store | -|---|---| -| `TimescaleContractTest` | TimescaleDB | -| `TdengineContractTest` | TDengine | -| `InfluxdbContractTest` | InfluxDB 3 | -| `IotdbContractTest` | Apache IoTDB | +| Test | Store | +|-------------------------|--------------| +| `TimescaleContractTest` | TimescaleDB | +| `TdengineContractTest` | TDengine | +| `InfluxdbContractTest` | InfluxDB 3 | +| `IotdbContractTest` | Apache IoTDB | ## Running diff --git a/dc3-tsdb/dc3-tsdb-tck/pom.xml b/dc3-tsdb/dc3-tsdb-tck/pom.xml index 9e7f7b5dc..35d0be151 100644 --- a/dc3-tsdb/dc3-tsdb-tck/pom.xml +++ b/dc3-tsdb/dc3-tsdb-tck/pom.xml @@ -31,7 +31,8 @@ 2026.5.22 jar - IoT DC3 store-neutral time-series contract suite: an adapter that passes these tests is compliant + IoT DC3 store-neutral time-series contract suite: an adapter that passes these tests is compliant + diff --git a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/AbstractTsdbContractTest.java b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/AbstractTsdbContractTest.java index cca9f6b62..2d4b21554 100644 --- a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/AbstractTsdbContractTest.java +++ b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/AbstractTsdbContractTest.java @@ -355,7 +355,7 @@ public abstract class AbstractTsdbContractTest { // COUNT via (possibly tiered) minute buckets must equal the raw count. long tierCount = store().bucketedAggregate(SeriesFilter.of(key), AggregateFunction.COUNT, - window, Duration.ofMinutes(1), null, DEADLINE) + window, Duration.ofMinutes(1), null, DEADLINE) .getOrDefault(key, List.of()).stream().mapToLong(BucketAggregate::sampleCount).sum(); assertThat(tierCount).isEqualTo(store().count(SeriesFilter.of(key), window, DEADLINE)); @@ -384,7 +384,7 @@ public abstract class AbstractTsdbContractTest { // percentiles on the raw path without supertable-style failures. if (store().capabilities().percentile()) { List p50 = store().bucketedAggregate(SeriesFilter.of(key), - AggregateFunction.PERCENTILE, window, Duration.ofMinutes(1), 0.5, DEADLINE) + AggregateFunction.PERCENTILE, window, Duration.ofMinutes(1), 0.5, DEADLINE) .getOrDefault(key, List.of()); assertThat(p50).hasSize(5); assertThat(p50).allSatisfy(bucket -> assertThat(bucket.value()).isNotNull()); diff --git a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/InfluxdbContractTest.java b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/InfluxdbContractTest.java index c57d73f9a..bebd30156 100644 --- a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/InfluxdbContractTest.java +++ b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/InfluxdbContractTest.java @@ -64,7 +64,9 @@ class InfluxdbContractTest extends AbstractTsdbContractTest { INFLUX.stop(); } - /** Any HTTP answer (even 401) means the node is serving; then mint a token. */ + /** + * Any HTTP answer (even 401) means the node is serving; then mint a token. + */ private static void awaitHttpUp() { String url = "http://" + INFLUX.getHost() + ":" + INFLUX.getMappedPort(8181) + "/health"; long deadline = System.currentTimeMillis() + 5 * 60 * 1000; diff --git a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/IotdbContractTest.java b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/IotdbContractTest.java index 65e6593bb..ae857596e 100644 --- a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/IotdbContractTest.java +++ b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/IotdbContractTest.java @@ -43,7 +43,9 @@ import java.util.Objects; @EnabledIfEnvironmentVariable(named = "DC3_TSDB_TCK", matches = "(?i)true|1|yes|on") class IotdbContractTest extends AbstractTsdbContractTest { - /** The minimal properties override: Java properties merge over code defaults. */ + /** + * The minimal properties override: Java properties merge over code defaults. + */ private static final GenericContainer IOTDB = new GenericContainer<>( DockerImageName.parse("apache/iotdb:2.0.10-standalone")) .withExposedPorts(6667) diff --git a/dc3-tsdb/dc3-tsdb-tdengine/README.md b/dc3-tsdb/dc3-tsdb-tdengine/README.md index a8055223c..3b41a5b2e 100644 --- a/dc3-tsdb/dc3-tsdb-tdengine/README.md +++ b/dc3-tsdb/dc3-tsdb-tdengine/README.md @@ -1,7 +1,7 @@ # DC3 TSDB TDengine -`dc3-tsdb-tdengine` adapts the store-neutral port to TDengine over its REST/WS JDBC driver -(`taos-jdbcdriver`). Points are written to a supertable with per-series subtables. +`dc3-tsdb-tdengine` adapts the store-neutral port to TDengine over its REST/WS JDBC driver (`taos-jdbcdriver`). Points +are written to a supertable with per-series subtables. ## Activation @@ -11,13 +11,13 @@ Active when `dc3.tsdb.type=tdengine`. `TdengineTsdbProperties` binds the `dc3.tsdb.tdengine` prefix: -| Key | Default | Meaning | -|---|---|---| -| `dc3.tsdb.tdengine.url` | `jdbc:TAOS-RS://localhost:6041/` | JDBC URL | -| `dc3.tsdb.tdengine.username` | `root` | login name | -| `dc3.tsdb.tdengine.password` | `taosdata` | login password | -| `dc3.tsdb.tdengine.database` | `dc3` | target database | -| `dc3.tsdb.tdengine.maximum-pool-size` | `8` | connection pool size | +| Key | Default | Meaning | +|---------------------------------------|----------------------------------|----------------------| +| `dc3.tsdb.tdengine.url` | `jdbc:TAOS-RS://localhost:6041/` | JDBC URL | +| `dc3.tsdb.tdengine.username` | `root` | login name | +| `dc3.tsdb.tdengine.password` | `taosdata` | login password | +| `dc3.tsdb.tdengine.database` | `dc3` | target database | +| `dc3.tsdb.tdengine.maximum-pool-size` | `8` | connection pool size | ## Dependencies diff --git a/dc3-tsdb/dc3-tsdb-tdengine/pom.xml b/dc3-tsdb/dc3-tsdb-tdengine/pom.xml index f23376103..ffc9ab813 100644 --- a/dc3-tsdb/dc3-tsdb-tdengine/pom.xml +++ b/dc3-tsdb/dc3-tsdb-tdengine/pom.xml @@ -29,7 +29,8 @@ dc3-tsdb-tdengine ${project.artifactId} - TDengine adapter of the TSDB port — supertable + per-series subtables over the REST/WS JDBC driver + TDengine adapter of the TSDB port — supertable + per-series subtables over the REST/WS JDBC driver + diff --git a/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbAutoConfiguration.java b/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbAutoConfiguration.java index 18ce03c5f..db3310c54 100644 --- a/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbAutoConfiguration.java +++ b/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbAutoConfiguration.java @@ -41,7 +41,9 @@ import org.springframework.context.annotation.Bean; @ConditionalOnProperty(prefix = "dc3.tsdb", name = "type", havingValue = "tdengine") public class TdengineTsdbAutoConfiguration { - /** The TDengine adapter over the REST JDBC driver. */ + /** + * The TDengine adapter over the REST JDBC driver. + */ @Bean(destroyMethod = "close") @ConditionalOnMissingBean(TsdbStore.class) public TsdbStore tsdbStore(TdengineTsdbProperties properties) { diff --git a/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbProperties.java b/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbProperties.java index ec55ad31e..cb1bb65a7 100644 --- a/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbProperties.java +++ b/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbProperties.java @@ -33,14 +33,18 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "dc3.tsdb.tdengine") public class TdengineTsdbProperties { - /** REST/WS JDBC url without a database segment, e.g. {@code jdbc:TAOS-RS://dc3-tdengine:6041/}. */ + /** + * REST/WS JDBC url without a database segment, e.g. {@code jdbc:TAOS-RS://dc3-tdengine:6041/}. + */ private String url = "jdbc:TAOS-RS://localhost:6041/"; private String username = "root"; private String password = "taosdata"; - /** Database the adapter creates and owns (PRECISION 'us'). */ + /** + * Database the adapter creates and owns (PRECISION 'us'). + */ private String database = "dc3"; private int maximumPoolSize = 8; diff --git a/dc3-tsdb/dc3-tsdb-timescale/README.md b/dc3-tsdb/dc3-tsdb-timescale/README.md index f3b987ac4..c1aa39995 100644 --- a/dc3-tsdb/dc3-tsdb-timescale/README.md +++ b/dc3-tsdb/dc3-tsdb-timescale/README.md @@ -9,16 +9,16 @@ Active when `dc3.tsdb.type=timescale` — the default (`matchIfMissing = true`). ## Key types -| Type | Role | -|---|---| -| `TimescaleTsdbStore` | `TsdbStore` implementation (hypertable writes, latest-value upserts, history queries) | -| `TsdbTimescaleAutoConfiguration` | adapter wiring and rollup retention | +| Type | Role | +|----------------------------------|---------------------------------------------------------------------------------------| +| `TimescaleTsdbStore` | `TsdbStore` implementation (hypertable writes, latest-value upserts, history queries) | +| `TsdbTimescaleAutoConfiguration` | adapter wiring and rollup retention | ## Configuration -| Key | Default | Meaning | -|---|---|---| -| `dc3.tsdb.timescale.rollup.minute-keep-days` | `365` | minute-tier retention used by the rollup job | +| Key | Default | Meaning | +|----------------------------------------------|---------|----------------------------------------------| +| `dc3.tsdb.timescale.rollup.minute-keep-days` | `365` | minute-tier retention used by the rollup job | ## Dependencies diff --git a/dc3-tsdb/dc3-tsdb-timescale/pom.xml b/dc3-tsdb/dc3-tsdb-timescale/pom.xml index 64d2ecffc..936eb9eb8 100644 --- a/dc3-tsdb/dc3-tsdb-timescale/pom.xml +++ b/dc3-tsdb/dc3-tsdb-timescale/pom.xml @@ -31,7 +31,9 @@ 2026.5.22 jar - IoT DC3 TimescaleDB adapter for the store-neutral time-series port (embedded or standalone PostgreSQL) + IoT DC3 TimescaleDB adapter for the store-neutral time-series port (embedded or standalone + PostgreSQL) + diff --git a/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/TimescaleTsdbStore.java b/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/TimescaleTsdbStore.java index a76a2ed09..916653511 100644 --- a/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/TimescaleTsdbStore.java +++ b/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/TimescaleTsdbStore.java @@ -535,7 +535,7 @@ public final class TimescaleTsdbStore implements TsdbStore { OffsetDateTime.ofInstant(window.toExclusive(), ZoneOffset.UTC), limit}; return timed(deadline, () -> jdbc.query(sql, (rs, i) -> new DimensionCount(dimension, - rs.getLong(1), rs.getLong(2)), args)); + rs.getLong(1), rs.getLong(2)), args)); } @Override @@ -561,7 +561,7 @@ public final class TimescaleTsdbStore implements TsdbStore { OffsetDateTime.ofInstant(window.from(), ZoneOffset.UTC), OffsetDateTime.ofInstant(window.toExclusive(), ZoneOffset.UTC)}; return timed(deadline, () -> jdbc.query(sql, (rs, i) -> new SeriesLastSeen( - new SeriesKey(rs.getLong(1), rs.getLong(2), rs.getLong(3)), toInstant(rs, 4)), args)); + new SeriesKey(rs.getLong(1), rs.getLong(2), rs.getLong(3)), toInstant(rs, 4)), args)); } @Override @@ -724,7 +724,9 @@ public final class TimescaleTsdbStore implements TsdbStore { return result; } - /** Exactly composable re-aggregation over the shared observability tiers. */ + /** + * Exactly composable re-aggregation over the shared observability tiers. + */ private static String tierExpression(AggregateFunction fn) { return switch (fn) { case AVG -> "SUM(num_sum) / NULLIF(SUM(num_count), 0)"; diff --git a/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/config/TsdbTimescaleAutoConfiguration.java b/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/config/TsdbTimescaleAutoConfiguration.java index a85512330..605af9e84 100644 --- a/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/config/TsdbTimescaleAutoConfiguration.java +++ b/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/config/TsdbTimescaleAutoConfiguration.java @@ -50,7 +50,9 @@ import javax.sql.DataSource; @ConditionalOnProperty(prefix = "dc3.tsdb", name = "type", havingValue = "timescale", matchIfMissing = true) public class TsdbTimescaleAutoConfiguration { - /** The TimescaleDB adapter on the application-provided tsdbDataSource. */ + /** + * The TimescaleDB adapter on the application-provided tsdbDataSource. + */ @Bean @ConditionalOnMissingBean(TsdbStore.class) public TsdbStore tsdbStore(@Qualifier("tsdbDataSource") DataSource tsdbDataSource, diff --git a/dc3-web/FRONTEND_OPTIMIZATION_PLAN.md b/dc3-web/FRONTEND_OPTIMIZATION_PLAN.md index 2a5636523..5702e4e96 100644 --- a/dc3-web/FRONTEND_OPTIMIZATION_PLAN.md +++ b/dc3-web/FRONTEND_OPTIMIZATION_PLAN.md @@ -6,18 +6,18 @@ ## 优先级矩阵 -| 级别 | 编号 | 标题 | 工作量 | 类型 | -|---|---|---|---|---| -| P0 | P0-1 | 统一 done 回调契约:失败保留弹窗、消除双提示/误报成功 | S | 正确性 | -| P0 | P0-2 | operationTime 误绑 createTime(4 处同源) | S | 正确性 | -| P0 | P0-3 | DeviceEdit 切换 driver/profile 加未保存守卫 + i18n 离开确认 | S | 正确性 | -| P0 | P0-4 | PointValue 分页 reset + DeviceImport 空文件永久 loading | S | 正确性 | -| P1 | P1-1 | 系统化「错误/加载/空」三态 + CardListShell | M | 体验 | -| P1 | P1-2 | 令牌化收敛:裸 hex → CSS 变量 + 圆角令牌 + 共享 footer 类 | M | 美化 | -| P1 | P1-3 | 抽 useRemoteDictionary + Device/Profile 共享字段组件 | M | 重构 | -| P1 | P1-4 | StatCard 补加载态 + 业务卡实体色身份 + 修 DriverCard 死 .active | M | 美化 | -| P1 | P1-5 | PointValueCard 数值按质量着色 + 稳定字号 | M | 美化 | -| P2 | — | a11y / 暗色模式 / 死代码清理 / DeviceEdit 抽 ConfigMatrix | S–L | 打磨 | +| 级别 | 编号 | 标题 | 工作量 | 类型 | +|------|------|-----------------------------------------------------------------|--------|--------| +| P0 | P0-1 | 统一 done 回调契约:失败保留弹窗、消除双提示/误报成功 | S | 正确性 | +| P0 | P0-2 | operationTime 误绑 createTime(4 处同源) | S | 正确性 | +| P0 | P0-3 | DeviceEdit 切换 driver/profile 加未保存守卫 + i18n 离开确认 | S | 正确性 | +| P0 | P0-4 | PointValue 分页 reset + DeviceImport 空文件永久 loading | S | 正确性 | +| P1 | P1-1 | 系统化「错误/加载/空」三态 + CardListShell | M | 体验 | +| P1 | P1-2 | 令牌化收敛:裸 hex → CSS 变量 + 圆角令牌 + 共享 footer 类 | M | 美化 | +| P1 | P1-3 | 抽 useRemoteDictionary + Device/Profile 共享字段组件 | M | 重构 | +| P1 | P1-4 | StatCard 补加载态 + 业务卡实体色身份 + 修 DriverCard 死 .active | M | 美化 | +| P1 | P1-5 | PointValueCard 数值按质量着色 + 稳定字号 | M | 美化 | +| P2 | — | a11y / 暗色模式 / 死代码清理 / DeviceEdit 抽 ConfigMatrix | S–L | 打磨 | --- @@ -81,19 +81,18 @@ const onAdd = (form, done) => { `submitting`,提示与重置交给父页 `.then`)。本方案与其对齐。 - `ProfileAddForm.vue:111`(事件名 `add-thing`)同样修改;`Profile.vue:122` `addThing` 父页同样改。 - `DeviceImportForm.vue:258` 的 `importTemplate` done 回调同源,一并按此契约修。 -- 顺带给 `DeviceAddForm` / `ProfileAddForm` 的确认按钮补 `:loading="submitting"`(引入 `submitting` ref, - 提交前置 true、`try/finally` 复位),防重复提交——对齐 `PointEditForm.vue:108`。 +- 顺带给 `DeviceAddForm` / `ProfileAddForm` 的确认按钮补 `:loading="submitting"`(引入 `submitting` ref, 提交前置 true、 + `try/finally` 复位),防重复提交——对齐 `PointEditForm.vue:108`。 -**验证**:`pnpm test`(补充用例:mock reject 时弹窗不关、不弹成功);手测新建失败/成功两种路径。 -**风险**:低。纯交互契约调整,不改数据流。 +**验证**:`pnpm test`(补充用例:mock reject 时弹窗不关、不弹成功);手测新建失败/成功两种路径。 **风险**:低。纯交互契约调整,不改数据流。 --- ### P0-2 operationTime 误绑 createTime(4 处同源 bug) -**问题**:详情页「操作时间」与「创建时间」都绑定 `createTime`,渲染完全相同的时刻。卡片层 -(`PointCard.vue:60`、`ProfileCard.vue:36`)与 settings 族(`CommandList.vue:93`、`EventList.vue:90`) -都正确用了 `operateTime`,证明这是 detail 页的复制粘贴回归。 +**问题**:详情页「操作时间」与「创建时间」都绑定 `createTime`,渲染完全相同的时刻。卡片层 (`PointCard.vue:60`、 +`ProfileCard.vue:36`)与 settings 族(`CommandList.vue:93`、`EventList.vue:90`) 都正确用了 `operateTime`,证明这是 detail +页的复制粘贴回归。 **Before**(`views/device/detail/DeviceDetail.vue:40`) @@ -124,8 +123,8 @@ const onAdd = (form, done) => { {{ $t('pointValue.card.saveTime') }}: {{ displayTime(data.createTime) }} ``` -参照 `PointValue.vue:156-158` 的 `interval = operateTime - createTime`(用于 `delay` 计算), -语义为 `createTime`=采集/产生时刻、`operateTime`=保存/入库时刻,建议: +参照 `PointValue.vue:156-158` 的 `interval = operateTime - createTime`(用于 `delay` 计算), 语义为 `createTime`=采集/产生时刻、 +`operateTime`=保存/入库时刻,建议: ```html {{ $t('pointValue.card.collectTime') }}: {{ displayTime(data.createTime) }} @@ -134,8 +133,7 @@ const onAdd = (form, done) => { > ⚠️ 若后端 `PointValue` 字段语义相反,则 collectTime/saveTime 对调——实施前与后端字段定义核对一次。 -**验证**:构造 `operateTime ≠ createTime` 的 mock 数据,确认两个描述项显示不同时刻。 -**风险**:极低。纯展示字段替换。 +**验证**:构造 `operateTime ≠ createTime` 的 mock 数据,确认两个描述项显示不同时刻。 **风险**:极低。纯展示字段替换。 --- @@ -144,10 +142,10 @@ const onAdd = (form, done) => { **问题**: 1. `device/edit/index.ts:625` `changeAttribute` 与 `:1423` `changeProfile` 切换时直接重建 - `driverFormData`/`pointInfoData`/`commandInfoData`/`eventInfoData` 四类矩阵,**不查 `totalDirtyCount`**, + `driverFormData`/`pointInfoData`/`commandInfoData`/`eventInfoData` 四类矩阵, **不查 `totalDirtyCount`**, 几十个未保存脏单元格被无声清空。`onBeforeRouteLeave`(:1478)只拦路由离开,拦不住本页内下拉切换。 -2. `:1480` `window.confirm('You have unsaved changes...')` 是全流程唯一硬编码英文文案, - 可复用已有的 `common.discardConfirm`(`config/i18n/locales/en.ts`、`zh.ts`)。 +2. `:1480` `window.confirm('You have unsaved changes...')` 是全流程唯一硬编码英文文案, 可复用已有的 + `common.discardConfirm`(`config/i18n/locales/en.ts`、`zh.ts`)。 **Before**(`views/device/edit/index.ts:1423`) @@ -188,20 +186,20 @@ const changeProfile = () => { const leave = window.confirm(t('common.discardConfirm')); ``` -> ⚠️ 注意点:`changeAttribute` 由 driver 下拉的 `@change` 触发,若用户取消需把下拉值**回退**到切换前 +> ⚠️ 注意点:`changeAttribute` 由 driver 下拉的 `@change` 触发,若用户取消需把下拉值 **回退**到切换前 > 的 `oldDriverFormData` 对应 driverId(否则 UI 已变但数据未重建,状态不一致)。建议在 select 上用 > `:value` + 手动 commit 模式,或在守卫取消时 `reactiveData.deviceFormData.driverId = prevDriverId`。 > 进一步可用 `ElMessageBox.confirm` 取代原生 `window.confirm` 以保持视觉统一。 -**验证**:先在矩阵里改几个单元格不保存,再切换 driver/profile,应弹确认;取消则数据保留。 -**风险**:中。需处理「取消后回退下拉值」的边界,建议配组件测试。 +**验证**:先在矩阵里改几个单元格不保存,再切换 driver/profile,应弹确认;取消则数据保留。 **风险** +:中。需处理「取消后回退下拉值」的边界,建议配组件测试。 --- ### P0-4 PointValue 分页 reset + DeviceImport 空文件永久 loading **问题 A**:`views/point/value/PointValue.vue:110` 的 `list()` 使用 `reactiveData.page`,但 -`search()` / `sizeChange()` 只更新 `query`/`size` 就调用 `list()`,**未把 `page.current` 重置为 1** +`search()` / `sizeChange()` 只更新 `query`/`size` 就调用 `list()`, **未把 `page.current` 重置为 1** (`usePagedList` 会重置,而这里是手写分页)→ 在第 3 页搜索会落到空页。 **After**(最小修复:在 `search()` / `sizeChange()` 开头重置) @@ -221,7 +219,7 @@ const sizeChange = () => { > `listPointValue` / `getPointValueLatest`,顺带补齐错误态(见 P1-1)。 **问题 B**:`views/device/import/DeviceImportForm.vue:267` `importThing` 在 `form.validate()` 后立即 -`submit()` 并置 `formLoading = true`;但 `el-upload`(`auto-upload=false`)在**文件列表为空**时 +`submit()` 并置 `formLoading = true`;但 `el-upload`(`auto-upload=false`)在 **文件列表为空**时 `submit()` 不触发 `http-request` → 确认按钮永久 loading、无任何提示。 **Before** @@ -259,8 +257,7 @@ const importThing = async () => { > 同时确认 `http-request` handler 在其 `finally` 里复位 `formLoading = false`,避免上传完成后按钮卡住。 -**验证**:PointValue 翻到第 3 页再搜索,应回到第 1 页;导入弹窗不选文件点确认,应提示而非卡 loading。 -**风险**:低。 +**验证**:PointValue 翻到第 3 页再搜索,应回到第 1 页;导入弹窗不选文件点确认,应提示而非卡 loading。 **风险**:低。 --- @@ -269,19 +266,18 @@ const importThing = async () => { ### P1-1 系统化「错误/加载/空」三态:停止吞错 + CardListShell 补 error/retry **问题**:`composables/usePagedList.ts:87` 的 `catch { // handled globally }` 与 -`PointValue.vue:126/140` 的 `.catch(() => {})` 静默吞错,`listData` 残留空数组,UI 退化为 `el-empty`, -用户**无法区分「无数据」与「加载失败」**;全仓 grep `retry` 零命中,无重试入口。 +`PointValue.vue:126/140` 的 `.catch(() => {})` 静默吞错,`listData` 残留空数组,UI 退化为 `el-empty`, 用户 +**无法区分「无数据」与「加载失败」**;全仓 grep `retry` 零命中,无重试入口。 **方案**: 1. `usePagedList` 增加 `error` 态:`catch` 里捕获并存 `error` 标识,`load` 暴露 `retry`; 2. 抽取 `components/card/list/CardListShell.vue`(复用 `usePagedList`),封装 loading(12 个 `SkeletonCard`)+ empty(`el-empty`)+ **error+retry** 三态 + 卡片栅格; -3. `Device.vue` / `Profile.vue` / `Point.vue` / `PointValue.vue` 仅传 card 组件与 query,删除三处近乎逐行 - 重复的列表模板。 +3. `Device.vue` / `Profile.vue` / `Point.vue` / `PointValue.vue` 仅传 card 组件与 query,删除三处近乎逐行 重复的列表模板。 -**收益**:一次修复覆盖 device/profile/point/command/event/pointValue 全族列表页。 -**验证**:mock `listXxx` reject,应显示错误态 + 重试按钮,而非空态。 +**收益**:一次修复覆盖 device/profile/point/command/event/pointValue 全族列表页。 **验证**:mock `listXxx` +reject,应显示错误态 + 重试按钮,而非空态。 --- @@ -289,14 +285,14 @@ const importThing = async () => { **问题**:大量样式绕过 Element Plus CSS 变量与 palette 令牌,阻断统一调参与暗色模式。 -| 位置 | 当前 | 建议 | -|---|---|---| -| `components/card/actions/ThingsCardActions.vue:24/37/50` | `icon-color: #e6a23c/#67c23a/#f56c6c` | `var(--el-color-warning/success/danger)` | -| `components/card/base/CardShell.vue:117` | `background: #f6f7f9` | `var(--el-fill-color-light)` | -| `views/driver/card/DriverCard.vue:134` | `border-top: 1px solid #dcdfe6` | `var(--el-border-color)` | -| `components/card/stat/StatCard.vue:125`(TS) | `purple: '#9059f6'` | 引用 `DASHBOARD_PALETTE.driver`(`config/constant/palette.ts`),消除 TS/SCSS 双写 | -| `components/card/stat/StatCard.vue:168`(SCSS) | `--stat-card-accent: #9059f6` | `@use '@/styles/palette'; → $dashboard-driver` | -| 全仓 30+ 处(含 `ThingsCardHeader.vue:53`、`SkeletonCard.vue`) | `border-radius: 4px` | 新增 `$radius-card` 令牌或用 `var(--el-border-radius-base)` | +| 位置 | 当前 | 建议 | +|-----------------------------------------------------------------|---------------------------------------|------------------------------------------------------------------------------------| +| `components/card/actions/ThingsCardActions.vue:24/37/50` | `icon-color: #e6a23c/#67c23a/#f56c6c` | `var(--el-color-warning/success/danger)` | +| `components/card/base/CardShell.vue:117` | `background: #f6f7f9` | `var(--el-fill-color-light)` | +| `views/driver/card/DriverCard.vue:134` | `border-top: 1px solid #dcdfe6` | `var(--el-border-color)` | +| `components/card/stat/StatCard.vue:125`(TS) | `purple: '#9059f6'` | 引用 `DASHBOARD_PALETTE.driver`(`config/constant/palette.ts`),消除 TS/SCSS 双写 | +| `components/card/stat/StatCard.vue:168`(SCSS) | `--stat-card-accent: #9059f6` | `@use '@/styles/palette'; → $dashboard-driver` | +| 全仓 30+ 处(含 `ThingsCardHeader.vue:53`、`SkeletonCard.vue`) | `border-radius: 4px` | 新增 `$radius-card` 令牌或用 `var(--el-border-radius-base)` | **圆角令牌**:`config/plugins/element/element-variables.scss` 当前仅有 `$form-width-*` 令牌(且被 Vite `additionalData` 全局注入,符合约定)。在其中新增: @@ -329,18 +325,16 @@ $radius-control: var(--el-border-radius-base); // 控件 三处卡片删除各自的 scoped footer 外壳样式,仅保留按钮。 -**验证**:`pnpm build`;切暗色(若已支持)确认无残留亮色硬编码。 -**风险**:低,但面广——建议分文件小步提交,每步 `pnpm build`。 +**验证**:`pnpm build`;切暗色(若已支持)确认无残留亮色硬编码。 **风险**:低,但面广——建议分文件小步提交,每步 `pnpm build`。 --- ### P1-3 抽 useRemoteDictionary 组合式 + Device/Profile 共享字段组件 -**问题**:`driverDictionary` / `profileDictionary` 的「loading + `listXxxDictionary({page,label})` + -visible-change 触发 + catch 吞错」模式在 5+ 处逐字重复(`DeviceAddForm.vue:164`、 -`DeviceImportForm.vue:177`、`device/edit/index.ts:551`、`DeviceTool.vue:118`、`PointTool.vue:139`, -且 `PointTool.vue:160` 在 setup 顶层无条件预拉是浪费请求)。同时 device 的 add 与 edit 字段/校验各写 -一份(仅 `PointEditForm` 做到 add/edit 单组件复用)。 +**问题**:`driverDictionary` / `profileDictionary` 的「loading + `listXxxDictionary({page,label})` + visible-change 触发 + +catch 吞错」模式在 5+ 处逐字重复(`DeviceAddForm.vue:164`、 +`DeviceImportForm.vue:177`、`device/edit/index.ts:551`、`DeviceTool.vue:118`、`PointTool.vue:139`, 且 `PointTool.vue:160` 在 +setup 顶层无条件预拉是浪费请求)。同时 device 的 add 与 edit 字段/校验各写 一份(仅 `PointEditForm` 做到 add/edit 单组件复用)。 **方案**: @@ -353,32 +347,31 @@ visible-change 触发 + catch 吞错」模式在 5+ 处逐字重复(`DeviceAdd } ``` -2. 抽 `views/device/components/DeviceFormFields.vue`(含 `deviceName/driverId/profileId/remark` 字段 + - 校验 + 字典加载),`DeviceAddForm` 弹窗与 `DeviceEdit` 的 `InfoCard` `#fields` 插槽共用; +2. 抽 `views/device/components/DeviceFormFields.vue`(含 `deviceName/driverId/profileId/remark` 字段 + 校验 + 字典加载), + `DeviceAddForm` 弹窗与 `DeviceEdit` 的 `InfoCard` `#fields` 插槽共用; `ProfileFormFields.vue` 同理。 -**验证**:`pnpm test`;手测下拉远程搜索、visible 懒加载。 -**风险**:中。涉及多文件,保持 props/事件契约不变即可低风险替换。 +**验证**:`pnpm test`;手测下拉远程搜索、visible 懒加载。 **风险**:中。涉及多文件,保持 props/事件契约不变即可低风险替换。 --- ### P1-4 StatCard 补加载态 + 业务卡实体色身份 + 修 DriverCard 死 .active -**问题 A**:`StatCard.vue:72-87` 的 props 无 `loading`,`Home.vue` 首屏 `value` 默认 `0`、sparkline 空 -→ 显示**虚假数据**(而 `DashboardCard` 有完整 loading、列表卡有 `SkeletonCard`,三套不一致)。 +**问题 A**:`StatCard.vue:72-87` 的 props 无 `loading`,`Home.vue` 首屏 `value` 默认 `0`、sparkline 空 → 显示 **虚假数据** +(而 `DashboardCard` 有完整 loading、列表卡有 `SkeletonCard`,三套不一致)。 **方案**:给 `StatCard` 加 `loading` prop,数值区与 sparkline 区用 `el-skeleton` 占位,命名与 `DashboardCard.loading` 对齐;可选地给 `SkeletonCard` 增 `variant: 'list' | 'stat' | 'dashboard'`。 -**问题 B**:`palette.scss:23-26` 已定义 driver 紫 / device 蓝 / profile 橙 / point 绿 四色令牌,但仅用于 -dashboard,5 张业务卡都用通用 PNG 图标 + `el-color-primary`,**翻卡时无法一眼分辨类型**。 +**问题 B**:`palette.scss:23-26` 已定义 driver 紫 / device 蓝 / profile 橙 / point 绿 四色令牌,但仅用于 dashboard,5 +张业务卡都用通用 PNG 图标 + `el-color-primary`, **翻卡时无法一眼分辨类型**。 -**方案**:在 `ThingsCardHeader.vue:49-60` 的图标容器(当前仅 `border-radius:4px` 无底色)加一层 8% -透明度的实体色底(通过新增 `tone` prop 传入 `$dashboard-*` 令牌),各业务卡传自己的实体色。 +**方案**:在 `ThingsCardHeader.vue:49-60` 的图标容器(当前仅 `border-radius:4px` 无底色)加一层 8% 透明度的实体色底(通过新增 +`tone` prop 传入 `$dashboard-*` 令牌),各业务卡传自己的实体色。 **问题 C**:`views/driver/card/style.scss:18-20` 的 `.active { border-left: 5px solid #409eff }` 是死代码, -`DriverCard.vue:93` emit 的 `select-change` 未被 `Driver.vue` 消费 → DriverCard 是**唯一没有选中反馈的 -列表卡**(对比 `PointInfoCard.vue:20` 用 `shadow='always'` 表达选中)。 +`DriverCard.vue:93` emit 的 `select-change` 未被 `Driver.vue` 消费 → DriverCard 是 **唯一没有选中反馈的 列表卡**(对比 +`PointInfoCard.vue:20` 用 `shadow='always'` 表达选中)。 **方案(二选一)**: @@ -387,16 +380,15 @@ dashboard,5 张业务卡都用通用 PNG 图标 + `el-color-primary`,**翻 `$dashboard-driver`(紫); - **B. 删除**:删 `style.scss` 整个文件 + `DriverCard.vue:126` 的 `@use` + 未消费的 `select-change` emit。 -**验证**:`pnpm build`;首页首屏应显示骨架而非 0;driver 列表点选应有高亮反馈。 -**风险**:低(A)/ 极低(B)。 +**验证**:`pnpm build`;首页首屏应显示骨架而非 0;driver 列表点选应有高亮反馈。 **风险**:低(A)/ 极低(B)。 --- ### P1-5 PointValueCard 数值按质量着色 + 稳定字号 -**问题**:`views/point/value/card/PointValueCard.vue:321-326` 实时值用 `font-size: xx-large`(相对尺寸, -跨屏不稳定)+ 装饰性 `hue` 动画(`:324`、`:364-374`,primary→light-9→primary),颜色不承载信息; -唯一着色是 `value-missing` 灰。卡片已算出 `delayOk` / `delaySlow`(`:180-185`)却只用来染 header 下边框。 +**问题**:`views/point/value/card/PointValueCard.vue:321-326` 实时值用 `font-size: xx-large`(相对尺寸, 跨屏不稳定)+ 装饰性 +`hue` 动画(`:324`、`:364-374`,primary→light-9→primary),颜色不承载信息; 唯一着色是 `value-missing` 灰。卡片已算出 +`delayOk` / `delaySlow`(`:180-185`)却只用来染 header 下边框。 **Before** @@ -428,23 +420,22 @@ dashboard,5 张业务卡都用通用 PNG 图标 + `el-color-primary`,**翻 模板按质量位绑定 `:class`:`delayOk` → `value--fresh`、`delaySlow` → `value--stale`,让颜色传达「新鲜度」。 -**验证**:`pnpm build`;构造不同 delay 的位号值,确认数值随新鲜度变色、字号稳定。 -**风险**:低。 +**验证**:`pnpm build`;构造不同 delay 的位号值,确认数值随新鲜度变色、字号稳定。 **风险**:低。 --- ## P2 打磨与基础设施(迭代) - **a11y 专项(最大盲区)**:全仓仅 9 处 `aria`/`role`。优先给纯图标按钮(`el-button` 仅 `:icon`: - edit/delete/refresh/disable/import 等)批量补 `aria-label`;自定义交互(`PointValueCard`、卡片选中态) - 加 `role` + keyboard handler;`components/layout/Layout.vue` 落地 skip-link 与弹窗/抽屉焦点管理。 + edit/delete/refresh/disable/import 等)批量补 `aria-label`;自定义交互(`PointValueCard`、卡片选中态) 加 `role` + keyboard + handler;`components/layout/Layout.vue` 落地 skip-link 与弹窗/抽屉焦点管理。 - **错误通知 i18n 化**:`config/axios/index.ts:130-150` 全局错误通知硬编码英文(`'Server Error'` / `'Network Error'`),影响面大于 `device/edit` 单点,统一收敛为 i18n key;并做 `en.ts(1334)` vs `zh.ts(1332)` 的 key parity 审计。 - **暗色模式**(依赖 P1-2 令牌化前置完成):引入 Element Plus dark CSS + `useDark`,settings 增主题切换。 - **死代码清理**:`components/card/title/TitleCard.vue`(零消费者)、`PointInfoCard.vue` 未用字段、 - `profile/detail/index.ts` 未用字段、`Profile.vue` 的 `deviceId` 死参 + `listProfileByDeviceId`; - 删除三处无效的 `enableFlag` 空校验规则(`PointEditForm.vue:172`、`device/edit/index.ts:376`、 + `profile/detail/index.ts` 未用字段、`Profile.vue` 的 `deviceId` 死参 + `listProfileByDeviceId`; 删除三处无效的 + `enableFlag` 空校验规则(`PointEditForm.vue:172`、`device/edit/index.ts:376`、 `profile/edit/index.ts:58`)。 - **DeviceEdit 现代化**:由 `defineComponent` 迁移到 `