Seed UNS hierarchy across 6 sites; rebrand outputs to SCADA IT/OT with ZB template
Lands per-site UNS subtree files (Warsaw West/North, Shannon, Galway, TMT, Ponce) seeded from OpenText facility docs — Warsaw split confirmed as numbered = legacy Zimmer = West, lettered = legacy Biomet = North. Renames project framing from "Shopfloor IT/OT" to "SCADA IT/OT" for accuracy. Extracts a ZB-branded PowerPoint template from example_powerpoint.pptx and wires it into the outputs pipeline. Trims deck from 18 to 16 slides (BOBJ->Power BI transferred to another team, Non-Goals and Asks dropped); goal-state BOBJ analysis pruned to a stub.
This commit is contained in:
155
outputs/workspace/generate.js
Normal file
155
outputs/workspace/generate.js
Normal file
@@ -0,0 +1,155 @@
|
||||
const pptxgen = require('pptxgenjs');
|
||||
const html2pptx = require('/Users/dohertj2/.claude/plugins/cache/anthropic-agent-skills/document-skills/unknown/skills/pptx/scripts/html2pptx.js');
|
||||
const path = require('path');
|
||||
|
||||
const SLIDES_DIR = path.join(__dirname, 'slides');
|
||||
const OUTPUT = '/Users/dohertj2/Desktop/plan/outputs/generated/plan-presentation.pptx';
|
||||
|
||||
// ZB template assets and design constants (see /Users/dohertj2/Desktop/plan/templates/README.md)
|
||||
const TEMPLATE_ASSETS = '/Users/dohertj2/Desktop/plan/templates/assets';
|
||||
const COVER_BG = path.join(TEMPLATE_ASSETS, 'cover-background.jpeg');
|
||||
const BOTTOM_BORDER = path.join(TEMPLATE_ASSETS, 'bottom-border.jpg');
|
||||
|
||||
const ZB_BLUE = '0066B3';
|
||||
const ZB_NAVY = '2B2A80';
|
||||
const ZB_TITLE_WHITE = 'F4F4F4';
|
||||
const CONFIDENTIAL_GRAY = '404040';
|
||||
const CONFIDENTIAL_TEXT = 'CONFIDENTIAL – FOR ZIMMER BIOMET INTERNAL USE ONLY';
|
||||
|
||||
// Roadmap-table colors (slide 13)
|
||||
const NAVY = '1C2833';
|
||||
const SLATE = '2E4053';
|
||||
const SILVER = 'AAB7B8';
|
||||
const WHITE = 'FFFFFF';
|
||||
const ROW_BG = 'FFFFFF';
|
||||
const ROW_BG_ALT = 'EEF1F3';
|
||||
|
||||
async function main() {
|
||||
const pres = new pptxgen();
|
||||
pres.layout = 'LAYOUT_16x9'; // 10.0 × 5.625 in — matches the HTML slide CSS
|
||||
pres.author = 'IT/OT Transformation Team';
|
||||
pres.subject = 'SCADA IT/OT Transformation — 3-Year Plan';
|
||||
pres.title = 'SCADA IT/OT Transformation';
|
||||
|
||||
// Slide 1 — ZB cover (replaces slide01.html; cover background + title overlay)
|
||||
console.log('Processing slide 1 (ZB cover)...');
|
||||
addCoverSlide(pres);
|
||||
|
||||
// Slides 2–16 — html2pptx-rendered content + ZB brand chrome (bottom border, confidentiality, page number)
|
||||
for (let i = 2; i <= 16; i++) {
|
||||
const num = String(i).padStart(2, '0');
|
||||
const file = path.join(SLIDES_DIR, `slide${num}.html`);
|
||||
console.log(`Processing slide ${i}...`);
|
||||
|
||||
const { slide } = await html2pptx(file, pres);
|
||||
addBrandChrome(slide, i);
|
||||
if (i === 13) {
|
||||
addRoadmapTable(pres, slide);
|
||||
}
|
||||
}
|
||||
|
||||
await pres.writeFile({ fileName: OUTPUT });
|
||||
console.log(`Saved: ${OUTPUT}`);
|
||||
}
|
||||
|
||||
// Coordinates below are scaled 0.75x from the 13.33×7.5 ZB template (templates/README.md)
|
||||
// to fit pptxgen's LAYOUT_16x9 (10×5.625 in). The HTML CSS uses 720×405pt = 10×5.625 in.
|
||||
|
||||
function addCoverSlide(pres) {
|
||||
const slide = pres.addSlide();
|
||||
slide.background = { color: 'FFFFFF' };
|
||||
slide.addImage({ path: COVER_BG, x: 0, y: 0, w: 10.0, h: 5.625 });
|
||||
slide.addText('SCADA IT/OT Transformation', {
|
||||
x: 0.69, y: 1.04, w: 4.5, h: 1.13,
|
||||
fontFace: 'Arial', fontSize: 24, bold: true,
|
||||
color: ZB_TITLE_WHITE, valign: 'top',
|
||||
});
|
||||
slide.addText('3-Year Plan', {
|
||||
x: 0.69, y: 2.21, w: 4.5, h: 0.38,
|
||||
fontFace: 'Arial', fontSize: 14,
|
||||
color: ZB_TITLE_WHITE,
|
||||
});
|
||||
slide.addText('AS OF 2026-04-30', {
|
||||
x: 0.69, y: 2.59, w: 4.5, h: 0.30,
|
||||
fontFace: 'Arial', fontSize: 9,
|
||||
color: ZB_TITLE_WHITE, charSpacing: 1,
|
||||
});
|
||||
return slide;
|
||||
}
|
||||
|
||||
function addBrandChrome(slide, pageNumber) {
|
||||
slide.addImage({
|
||||
path: BOTTOM_BORDER,
|
||||
x: 0.01, y: 5.07, w: 9.99, h: 0.555,
|
||||
});
|
||||
slide.addText(CONFIDENTIAL_TEXT, {
|
||||
x: 0.22, y: 4.91, w: 6.0, h: 0.15,
|
||||
fontFace: 'Arial', fontSize: 6, color: CONFIDENTIAL_GRAY,
|
||||
});
|
||||
slide.addText(String(pageNumber), {
|
||||
x: 9.55, y: 4.91, w: 0.35, h: 0.15,
|
||||
fontFace: 'Arial', fontSize: 8, color: ZB_NAVY, align: 'right',
|
||||
});
|
||||
}
|
||||
|
||||
function addRoadmapTable(pres, slide) {
|
||||
const headerOpts = { bold: true, color: WHITE, fill: { color: NAVY }, fontSize: 10, fontFace: 'Arial', align: 'center', valign: 'middle' };
|
||||
const wsOpts = { bold: true, fontSize: 9, fontFace: 'Arial', color: NAVY, fill: { color: ROW_BG_ALT }, valign: 'middle' };
|
||||
const cellOpts = { fontSize: 8, fontFace: 'Arial', color: SLATE, fill: { color: ROW_BG }, valign: 'middle' };
|
||||
|
||||
const rows = [
|
||||
[
|
||||
{ text: 'Workstream', options: headerOpts },
|
||||
{ text: 'Year 1 — Foundation', options: headerOpts },
|
||||
{ text: 'Year 2 — Scale', options: headerOpts },
|
||||
{ text: 'Year 3 — Completion', options: headerOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'OtOpcUa', options: wsOpts },
|
||||
{ text: 'Evolve LmxOpcUa; deploy to every site; tier-1 cutover begins; UNS hierarchy walk', options: cellOpts },
|
||||
{ text: 'Complete tier 1; begin tier 2 (Ignition); long-tail drivers on demand', options: cellOpts },
|
||||
{ text: 'Complete tier 2; execute tier 3 (System Platform IO) with compliance validation', options: cellOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'Redpanda EventHub', options: wsOpts },
|
||||
{ text: 'Central cluster in SB; schema registry; canonical model v1 published (FANUC CNC pilot)', options: cellOpts },
|
||||
{ text: 'Expand topic coverage; WAN-outage replay drill; iterate canonical model', options: cellOpts },
|
||||
{ text: 'Steady state; canonical model mature; alerting + runbooks hardened', options: cellOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'SnowBridge', options: wsOpts },
|
||||
{ text: 'Custom build in .NET; first adapter (Historian SQL); first curated tables aligned to canonical model', options: cellOpts },
|
||||
{ text: 'Add Redpanda adapter; operator UI + approval workflow; canonical-state OEE model; first "not possible before" use case in dev', options: cellOpts },
|
||||
{ text: 'All source adapters live; hardening. "Not possible before" use case in production — pillar 2 passes', options: cellOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'ScadaBridge Extensions', options: wsOpts },
|
||||
{ text: 'Deadband / exception-based publishing; EventHub producer with store-and-forward', options: cellOpts },
|
||||
{ text: 'Roll to all integrated sites; tune deadband from observed Snowflake cost', options: cellOpts },
|
||||
{ text: 'Steady state; residual cleanup for onboarding / retirement tails', options: cellOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'Site Onboarding', options: wsOpts },
|
||||
{ text: 'No new sites; define lightweight onboarding pattern for smaller sites', options: cellOpts },
|
||||
{ text: 'Pilot one smaller site; scale to additional smaller sites', options: cellOpts },
|
||||
{ text: 'Complete onboarding of all remaining smaller sites — pillar 1 passes', options: cellOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'Legacy Retirement', options: wsOpts },
|
||||
{ text: 'Inventory populated (3 rows); retire first integration as pattern-proving exercise', options: cellOpts },
|
||||
{ text: 'Bulk migration against inventory; quarterly burn-down tracking', options: cellOpts },
|
||||
{ text: 'Inventory reaches zero — pillar 3 passes', options: cellOpts },
|
||||
],
|
||||
];
|
||||
|
||||
slide.addTable(rows, {
|
||||
x: 0.42,
|
||||
y: 1.2,
|
||||
w: 9.2,
|
||||
colW: [1.5, 2.566, 2.567, 2.567],
|
||||
rowH: 0.55,
|
||||
border: { pt: 0.5, color: SILVER },
|
||||
});
|
||||
}
|
||||
|
||||
main().catch((err) => { console.error(err); process.exit(1); });
|
||||
761
outputs/workspace/package-lock.json
generated
Normal file
761
outputs/workspace/package-lock.json
generated
Normal file
@@ -0,0 +1,761 @@
|
||||
{
|
||||
"name": "workspace",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"playwright": "^1.59.1",
|
||||
"pptxgenjs": "^4.0.1",
|
||||
"sharp": "^0.34.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/colour": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
|
||||
"integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
|
||||
"integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
|
||||
"integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-riscv64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
|
||||
"integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
|
||||
"integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
|
||||
"integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-ppc64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
|
||||
"integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-riscv64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
|
||||
"integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
|
||||
"integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
|
||||
"integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
|
||||
"integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.19.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
|
||||
"integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/https": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
|
||||
"integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/image-size": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz",
|
||||
"integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"queue": "6.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"image-size": "bin/image-size.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.x"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"license": "(MIT OR GPL-3.0-or-later)",
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
|
||||
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.59.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
|
||||
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/pptxgenjs": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pptxgenjs/-/pptxgenjs-4.0.1.tgz",
|
||||
"integrity": "sha512-TeJISr8wouAuXw4C1F/mC33xbZs/FuEG6nH9FG1Zj+nuPcGMP5YRHl6X+j3HSUnS1f3at6k75ZZXPMZlA5Lj9A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "^22.8.1",
|
||||
"https": "^1.0.0",
|
||||
"image-size": "^1.2.1",
|
||||
"jszip": "^3.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/queue": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
|
||||
"integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "~2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
||||
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
"semver": "^7.7.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.34.5",
|
||||
"@img/sharp-darwin-x64": "0.34.5",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4",
|
||||
"@img/sharp-linux-arm": "0.34.5",
|
||||
"@img/sharp-linux-arm64": "0.34.5",
|
||||
"@img/sharp-linux-ppc64": "0.34.5",
|
||||
"@img/sharp-linux-riscv64": "0.34.5",
|
||||
"@img/sharp-linux-s390x": "0.34.5",
|
||||
"@img/sharp-linux-x64": "0.34.5",
|
||||
"@img/sharp-linuxmusl-arm64": "0.34.5",
|
||||
"@img/sharp-linuxmusl-x64": "0.34.5",
|
||||
"@img/sharp-wasm32": "0.34.5",
|
||||
"@img/sharp-win32-arm64": "0.34.5",
|
||||
"@img/sharp-win32-ia32": "0.34.5",
|
||||
"@img/sharp-win32-x64": "0.34.5"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
7
outputs/workspace/package.json
Normal file
7
outputs/workspace/package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"playwright": "^1.59.1",
|
||||
"pptxgenjs": "^4.0.1",
|
||||
"sharp": "^0.34.5"
|
||||
}
|
||||
}
|
||||
26
outputs/workspace/slides/slide01.html
Normal file
26
outputs/workspace/slides/slide01.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #1C2833; font-family: Arial, sans-serif;
|
||||
display: flex; color: #F4F6F6;
|
||||
}
|
||||
.wrap { width: 720pt; padding: 140pt 60pt 0 60pt; text-align: center; }
|
||||
.accent-bar { width: 100pt; height: 4pt; background: #D4843D; margin: 0 auto 22pt auto; }
|
||||
h1 { font-size: 32pt; font-weight: bold; margin: 0 0 12pt 0; letter-spacing: -0.5pt; text-align: center; }
|
||||
.subtitle { font-size: 20pt; color: #AAB7B8; margin: 0 0 44pt 0; font-weight: normal; text-align: center; }
|
||||
.date { font-size: 11pt; color: #AAB7B8; margin: 0; letter-spacing: 1pt; text-align: center; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="accent-bar"></div>
|
||||
<h1>SCADA IT/OT Transformation</h1>
|
||||
<p class="subtitle">3-Year Plan</p>
|
||||
<p class="date">AS OF 2026-04-24</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
33
outputs/workspace/slides/slide02.html
Normal file
33
outputs/workspace/slides/slide02.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
.vision-wrap { border-left: 3pt solid #0066B3; padding-left: 12pt; margin: 0 0 14pt 0; }
|
||||
.vision { color: #2B2A80; font-size: 14pt; font-style: italic; margin: 0; }
|
||||
ul { margin: 6pt 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 8pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Executive Summary</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="vision-wrap"><p class="vision">Vision: stable, single point of integration between shopfloor OT and enterprise IT.</p></div>
|
||||
<ul>
|
||||
<li><b>Pillar 1 — Unification:</b> 100% of sites on the standardized stack at end of Year 3.</li>
|
||||
<li><b>Pillar 2 — Analytics/AI enablement:</b> ≤15-minute analytics SLO; one "not possible before" use case in production.</li>
|
||||
<li><b>Pillar 3 — Legacy retirement:</b> bespoke IT↔OT integration inventory driven to zero.</li>
|
||||
<li><b>Out of scope:</b> operator UX modernization, licensing strategy, VM-level DR, physical network segmentation.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
34
outputs/workspace/slides/slide03.html
Normal file
34
outputs/workspace/slides/slide03.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 24pt 40pt; flex: 1; color: #333333; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 13pt; margin-bottom: 11pt; line-height: 1.35; }
|
||||
.footer { padding: 6pt 40pt 10pt 40pt; color: #AAB7B8; font-size: 9pt; font-style: italic; }
|
||||
.footer p { margin: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Today's Reality</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<ul>
|
||||
<li><b>Split SCADA stack</b> — Aveva System Platform for validated data; Ignition for KPI monitoring.</li>
|
||||
<li><b>Central Ignition</b> in South Bend holds direct OPC UA sessions to site equipment over the WAN.</li>
|
||||
<li><b>Multiple concurrent OPC UA sessions</b> to the same equipment from different consumers.</li>
|
||||
<li><b>Three legacy IT↔OT integrations</b> bypass ScadaBridge central (Delmia DNC, Camstar MES, custom email service).</li>
|
||||
<li><b>Fragmented data access</b> — consumers reading the same tag can see different values depending on sampling/deadband settings.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer"><p>Full detail: current-state.md → Systems & Interfaces, Equipment OPC UA.</p></div>
|
||||
</body>
|
||||
</html>
|
||||
32
outputs/workspace/slides/slide04.html
Normal file
32
outputs/workspace/slides/slide04.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 40pt 60pt; flex: 1; color: #333333; display: flex; flex-direction: column; justify-content: center; }
|
||||
.quote-wrap { border-left: 5pt solid #0066B3; padding-left: 18pt; margin: 0 0 28pt 0; }
|
||||
.quote { font-size: 22pt; color: #2B2A80; font-weight: bold; line-height: 1.3; margin: 0; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 13pt; margin-bottom: 9pt; line-height: 1.35; color: #333333; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Vision</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="quote-wrap"><p class="quote">A stable, single point of integration between shopfloor OT and enterprise IT.</p></div>
|
||||
<ul>
|
||||
<li>Every cross-domain path flows through ScadaBridge central — the sole sanctioned IT↔OT crossing point.</li>
|
||||
<li>Equipment data is site-local, canonically modeled, and shared through a single session per device.</li>
|
||||
<li>Tiebreaker for ambiguous decisions: preserve this stable-single-point-of-integration posture.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
45
outputs/workspace/slides/slide05.html
Normal file
45
outputs/workspace/slides/slide05.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 26pt 36pt; flex: 1; color: #333333; display: flex; flex-direction: row; gap: 18pt; }
|
||||
.pillar { flex: 1; background: #ffffff; border-top: 4pt solid #0066B3; padding: 18pt 16pt; border-radius: 4pt; }
|
||||
.pillar h2 { margin: 0 0 6pt 0; font-size: 14pt; color: #2B2A80; }
|
||||
.pillar .tag { font-size: 9pt; color: #AAB7B8; margin: 0 0 12pt 0; letter-spacing: 1pt; }
|
||||
.pillar .criterion { font-size: 12pt; font-weight: bold; color: #2B2A80; margin: 0 0 10pt 0; }
|
||||
.pillar .context { font-size: 11pt; margin: 0; color: #333333; line-height: 1.4; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Three Pillars</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="pillar">
|
||||
<p class="tag">PILLAR 1</p>
|
||||
<h2>Unification</h2>
|
||||
<p class="criterion">100% of sites on the standardized stack.</p>
|
||||
<p class="context">Every site runs OtOpcUa + ScadaBridge + Redpanda + SnowBridge + Snowflake. No bespoke site-specific integration.</p>
|
||||
</div>
|
||||
<div class="pillar">
|
||||
<p class="tag">PILLAR 2</p>
|
||||
<h2>Analytics / AI Enablement</h2>
|
||||
<p class="criterion">≤15-minute SLO; one "not possible before" use case in production.</p>
|
||||
<p class="context">Canonical model + curated layer unlock cross-domain analytics that fragmented today's estate can't deliver.</p>
|
||||
</div>
|
||||
<div class="pillar">
|
||||
<p class="tag">PILLAR 3</p>
|
||||
<h2>Legacy Retirement</h2>
|
||||
<p class="criterion">Inventory driven to zero.</p>
|
||||
<p class="context">Three bespoke IT↔OT integrations today; each migrates to ScadaBridge-central or is decommissioned.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
44
outputs/workspace/slides/slide06.html
Normal file
44
outputs/workspace/slides/slide06.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
.group { margin-bottom: 13pt; }
|
||||
.group-label { font-size: 10pt; color: #0066B3; letter-spacing: 1pt; font-weight: bold; margin: 0 0 4pt 0; }
|
||||
.group-content { font-size: 13pt; margin: 0; line-height: 1.35; color: #2B2A80; }
|
||||
.footer { padding: 6pt 40pt 10pt 40pt; color: #AAB7B8; font-size: 9pt; font-style: italic; }
|
||||
.footer p { margin: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Enterprise Layout</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="group">
|
||||
<p class="group-label">PRIMARY DATA CENTER</p>
|
||||
<p class="group-content">South Bend — central hosting for Aveva System Platform primary cluster, Ignition, Historian, Snowflake.</p>
|
||||
</div>
|
||||
<div class="group">
|
||||
<p class="group-label">LARGEST SITES</p>
|
||||
<p class="group-content">Warsaw West (bldg-5, bldg-19) · Warsaw North — one cluster per production building.</p>
|
||||
</div>
|
||||
<div class="group">
|
||||
<p class="group-label">OTHER INTEGRATED SITES</p>
|
||||
<p class="group-content">Shannon · Galway · TMT · Ponce — single cluster per site.</p>
|
||||
</div>
|
||||
<div class="group">
|
||||
<p class="group-label">NOT YET INTEGRATED</p>
|
||||
<p class="group-content">Berlin · Winterthur · Jacksonville · others — lighter-weight onboarding pattern Year 2+.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer"><p>Smaller-site list is volatile; rely on named examples as illustrative, not authoritative.</p></div>
|
||||
</body>
|
||||
</html>
|
||||
32
outputs/workspace/slides/slide07.html
Normal file
32
outputs/workspace/slides/slide07.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 9pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Today's Systems</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<ul>
|
||||
<li><b>Aveva System Platform</b> — validated data collection; primary cluster central + site clusters.</li>
|
||||
<li><b>Ignition SCADA</b> — KPI monitoring; central in South Bend, WAN-dependent direct OPC UA to sites.</li>
|
||||
<li><b>ScadaBridge</b> — in-house Akka.NET integration layer; already deployed; the IT↔OT strategic layer.</li>
|
||||
<li><b>LmxOpcUa</b> — in-house OPC UA server exposing System Platform objects; per-node today, evolves into OtOpcUa.</li>
|
||||
<li><b>Camstar MES</b> — sole enterprise MES; today integrates via single Aveva Web API call.</li>
|
||||
<li><b>Aveva Historian</b> — sole historian; central-only in South Bend; permanent retention; BOBJ → Power BI migration in flight.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
54
outputs/workspace/slides/slide08.html
Normal file
54
outputs/workspace/slides/slide08.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; display: flex; flex-direction: column; }
|
||||
.layer-stack { display: flex; flex-direction: column; gap: 6pt; }
|
||||
.layer { background: #ffffff; padding: 10pt 14pt; border-left: 6pt solid #0066B3; border-radius: 3pt; }
|
||||
.layer .tag { font-size: 9pt; color: #0066B3; letter-spacing: 1pt; font-weight: bold; margin: 0 0 2pt 0; }
|
||||
.layer .name { font-size: 12pt; font-weight: bold; margin: 0 0 2pt 0; color: #2B2A80; }
|
||||
.layer .desc { font-size: 10pt; margin: 0; color: #333333; }
|
||||
.callout { background: #0066B3; color: #FFFFFF; padding: 10pt 14pt; border-radius: 3pt; margin-top: 10pt; }
|
||||
.callout p { margin: 0; font-size: 11pt; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Goal State — Layered Architecture</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="layer-stack">
|
||||
<div class="layer">
|
||||
<p class="tag">LAYER 1 — EQUIPMENT</p>
|
||||
<p class="name">PLCs, CNCs, OPC UA-native devices</p>
|
||||
<p class="desc">One OPC UA session per device; OtOpcUa holds it.</p>
|
||||
</div>
|
||||
<div class="layer">
|
||||
<p class="tag">LAYER 2 — OtOpcUa</p>
|
||||
<p class="name">Equipment namespace + System Platform namespace (absorbs LmxOpcUa)</p>
|
||||
<p class="desc">Clustered, site-local. Single sanctioned OPC UA access point per site.</p>
|
||||
</div>
|
||||
<div class="layer">
|
||||
<p class="tag">LAYER 3 — SCADA</p>
|
||||
<p class="name">Aveva System Platform (validated) · Ignition (KPI)</p>
|
||||
<p class="desc">Derives canonical machine state from raw signals.</p>
|
||||
</div>
|
||||
<div class="layer">
|
||||
<p class="tag">LAYER 4 — ScadaBridge</p>
|
||||
<p class="name">IT/OT bridge — central integration layer</p>
|
||||
<p class="desc">Deadband, store-and-forward, producer to Redpanda.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="callout">
|
||||
<p><b>ScadaBridge central is the sole IT↔OT crossing point.</b></p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
50
outputs/workspace/slides/slide09.html
Normal file
50
outputs/workspace/slides/slide09.html
Normal file
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 30pt; flex: 1; color: #333333; display: flex; flex-direction: column; justify-content: center; }
|
||||
.flow-row { display: flex; justify-content: space-between; align-items: center; gap: 4pt; margin-bottom: 16pt; }
|
||||
.node { background: #ffffff; border: 1pt solid #AAB7B8; padding: 10pt 6pt; border-radius: 3pt; flex: 1; text-align: center; }
|
||||
.node p { margin: 0; font-size: 9pt; font-weight: bold; color: #2B2A80; }
|
||||
.node .type { font-size: 7pt; color: #AAB7B8; margin-top: 2pt; letter-spacing: 0.5pt; }
|
||||
.arrow { font-size: 12pt; color: #0066B3; font-weight: bold; }
|
||||
.arrow p { margin: 0; }
|
||||
.ot { background: #E8EDF0; }
|
||||
.it { background: #FCEFE3; }
|
||||
.caption { font-size: 11pt; color: #333333; text-align: center; line-height: 1.4; margin: 12pt 20pt 0 20pt; font-style: italic; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>End-to-End Data Flow</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="flow-row">
|
||||
<div class="node ot"><p>Equipment</p><p class="type">OT</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node ot"><p>OtOpcUa</p><p class="type">OT · Layer 2</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node ot"><p>System Platform / Ignition</p><p class="type">OT · Layer 3</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node ot"><p>ScadaBridge</p><p class="type">OT · Layer 4</p></div>
|
||||
</div>
|
||||
<div class="flow-row">
|
||||
<div class="node it"><p>Redpanda</p><p class="type">IT · EventHub</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node it"><p>SnowBridge</p><p class="type">IT · Ingest + Transform</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node it"><p>Snowflake</p><p class="type">IT · Curated Layer</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node it"><p>Power BI</p><p class="type">IT · Consumer</p></div>
|
||||
</div>
|
||||
<p class="caption">A tag read from a machine in Warsaw West flows across the stack into enterprise analytics, without a second IT↔OT crossing.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
35
outputs/workspace/slides/slide10.html
Normal file
35
outputs/workspace/slides/slide10.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
.tagline-wrap { border-left: 3pt solid #0066B3; padding-left: 10pt; margin: 0 0 12pt 0; }
|
||||
.tagline { font-size: 12pt; color: #2B2A80; margin: 0; font-style: italic; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 9pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>OtOpcUa — the Unification Layer</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="tagline-wrap"><p class="tagline">Single sanctioned OPC UA access point per site — one session per piece of equipment.</p></div>
|
||||
<ul>
|
||||
<li><b>Two namespaces:</b> equipment (raw) + System Platform (processed) — absorbs LmxOpcUa.</li>
|
||||
<li><b>Clustered,</b> co-located on existing System Platform nodes — no new hardware footprint.</li>
|
||||
<li><b>Hybrid driver strategy:</b> proactive core library (8 drivers) + on-demand long-tail.</li>
|
||||
<li><b>OPC UA-native auth:</b> UserName tokens + standard security modes (inherited from LmxOpcUa).</li>
|
||||
<li><b>Data-path ACL model:</b> scope hierarchy + per-node permissions, committed in v2.</li>
|
||||
<li><b>Tiered cutover:</b> ScadaBridge first, Ignition second, System Platform IO last (across Years 1–3).</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
35
outputs/workspace/slides/slide11.html
Normal file
35
outputs/workspace/slides/slide11.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
.tagline-wrap { border-left: 3pt solid #0066B3; padding-left: 10pt; margin: 0 0 12pt 0; }
|
||||
.tagline { font-size: 12pt; color: #2B2A80; margin: 0; font-style: italic; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 8pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Analytics Stack — SnowBridge + Snowflake</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="tagline-wrap"><p class="tagline">SnowBridge owns the full path: ingest + in-process transform + curated-table write.</p></div>
|
||||
<ul>
|
||||
<li><b>Custom-built .NET service</b> that reads from Aveva Historian SQL (Year 1) and Redpanda/ScadaBridge (Year 2).</li>
|
||||
<li><b>In-process transforms,</b> canonical-model-aligned — aggregation, state enrichment, dim_equipment resolution.</li>
|
||||
<li><b>No dbt, no external orchestrator, no Snowflake landing tier</b> — transforms live in the service.</li>
|
||||
<li><b>Governed selection</b> of topics/tags via operator web UI with blast-radius-based approval workflow.</li>
|
||||
<li><b>≤15-minute analytics SLO</b> end-to-end (ScadaBridge emit → curated table in Snowflake).</li>
|
||||
<li><b>One "not possible before" AI/analytics use case</b> in production by end of plan — pillar 2 gate.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
35
outputs/workspace/slides/slide12.html
Normal file
35
outputs/workspace/slides/slide12.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
.tagline-wrap { border-left: 3pt solid #0066B3; padding-left: 10pt; margin: 0 0 12pt 0; }
|
||||
.tagline { font-size: 12pt; color: #2B2A80; margin: 0; font-style: italic; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 8pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Redpanda EventHub — the Async Backbone</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="tagline-wrap"><p class="tagline">Self-hosted Kafka-compatible backbone. Single-cluster HA, central in South Bend.</p></div>
|
||||
<ul>
|
||||
<li><b>Per-topic tiered retention</b> — operational 7d · analytics 30d · compliance 90d.</li>
|
||||
<li><b>Bundled Schema Registry,</b> Protobuf schemas in central <b>schemas</b> repo with <b>buf</b> CI.</li>
|
||||
<li><b>BACKWARD_TRANSITIVE compatibility</b> — consumers can upgrade independently of producers.</li>
|
||||
<li><b>Topic naming:</b> <b>{domain}.{entity}.{event-type}</b>; site identity lives in the message, not the topic.</li>
|
||||
<li><b>Auth:</b> SASL/OAUTHBEARER via enterprise IdP + prefix ACLs per producer/consumer principal.</li>
|
||||
<li><b>Store-and-forward at ScadaBridge</b> handles WAN outages; analytics-tier retention doubles as a replay surface.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
26
outputs/workspace/slides/slide13.html
Normal file
26
outputs/workspace/slides/slide13.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 14pt 30pt; flex: 1; color: #333333; }
|
||||
.sub { font-size: 11pt; color: #333333; margin: 0 0 8pt 0; font-style: italic; }
|
||||
.placeholder { width: 640pt; height: 300pt; background: transparent; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>3-Year Roadmap — Workstreams × Years</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="sub">Six workstreams across three years. Each cell shows the single most load-bearing commitment.</p>
|
||||
<div id="roadmap-table" class="placeholder"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
32
outputs/workspace/slides/slide14.html
Normal file
32
outputs/workspace/slides/slide14.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 9pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Year 1 Focus</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<ul>
|
||||
<li><b>OtOpcUa</b> — evolve LmxOpcUa, deploy to every site, begin ScadaBridge-first cutover, UNS hierarchy walk.</li>
|
||||
<li><b>Redpanda</b> — stand up central cluster, schema registry, initial topics, publish canonical model v1.</li>
|
||||
<li><b>SnowBridge</b> — design + build; first source adapter (Historian SQL); first curated tables aligned to canonical model.</li>
|
||||
<li><b>ScadaBridge Extensions</b> — deadband / exception-based publishing + EventHub producer with store-and-forward.</li>
|
||||
<li><b>Site Onboarding</b> — no new sites; define the lightweight onboarding pattern for smaller sites.</li>
|
||||
<li><b>Legacy Retirement</b> — inventory populated (3 rows); retire first integration as pattern-proving exercise.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
44
outputs/workspace/slides/slide15.html
Normal file
44
outputs/workspace/slides/slide15.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; display: flex; flex-direction: column; }
|
||||
.item { background: #ffffff; border-left: 5pt solid #0066B3; padding: 10pt 14pt; margin-bottom: 10pt; }
|
||||
.item .id { font-size: 9pt; color: #0066B3; letter-spacing: 1pt; font-weight: bold; margin: 0; }
|
||||
.item .title { font-size: 12pt; font-weight: bold; color: #2B2A80; margin: 2pt 0; }
|
||||
.item .desc { font-size: 10pt; margin: 0; color: #333333; line-height: 1.35; }
|
||||
.callout { background: #0066B3; color: #FFFFFF; padding: 8pt 14pt; border-radius: 3pt; margin-top: auto; }
|
||||
.callout p { margin: 0; font-size: 10pt; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Pillar 3 — Legacy Retirement (3 → 0)</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="item">
|
||||
<p class="id">LEG-001</p>
|
||||
<p class="title">Aveva Web API → Delmia DNC</p>
|
||||
<p class="desc">Bidirectional orchestrated handshake. Harder retirement — requires ScadaBridge scripts to re-implement System Platform parse logic.</p>
|
||||
</div>
|
||||
<div class="item">
|
||||
<p class="id">LEG-002</p>
|
||||
<p class="title">Aveva Web API ← Camstar MES</p>
|
||||
<p class="desc">Camstar-initiated. Easier retirement — ScadaBridge already has a native Camstar path; requires Camstar-side reconfiguration.</p>
|
||||
</div>
|
||||
<div class="item">
|
||||
<p class="id">LEG-003</p>
|
||||
<p class="title">System Platform → custom email notification service</p>
|
||||
<p class="desc">Easier retirement — ScadaBridge native notifications already exist.</p>
|
||||
</div>
|
||||
<div class="callout"><p><b>Carve-out:</b> Historian MSSQL reporting surface (BOBJ / Power BI) is explicitly not legacy — see legacy-integrations.md → Deliberately not tracked.</p></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
47
outputs/workspace/slides/slide16.html
Normal file
47
outputs/workspace/slides/slide16.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }
|
||||
.body { padding: 24pt 40pt; flex: 1; color: #333333; }
|
||||
.lede { font-size: 12pt; color: #2B2A80; margin: 0 0 14pt 0; line-height: 1.4; }
|
||||
.lede b { color: #0066B3; }
|
||||
.patterns { display: flex; gap: 18pt; margin: 0 0 12pt 0; }
|
||||
.pattern { flex: 1; background: #F5F8FB; border-top: 4pt solid #0066B3; padding: 12pt 14pt; border-radius: 3pt; }
|
||||
.pattern .tag { font-size: 8pt; color: #0066B3; letter-spacing: 1pt; margin: 0 0 4pt 0; font-weight: bold; }
|
||||
.pattern h2 { font-size: 12pt; color: #2B2A80; margin: 0 0 6pt 0; }
|
||||
.pattern p { font-size: 10pt; margin: 0; line-height: 1.4; }
|
||||
.footnote { font-size: 9pt; color: #666666; margin: 6pt 0 0 0; line-height: 1.35; font-style: italic; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Digital Twin Scope</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="lede">Scope is <b>two access-control patterns</b> — not a new component, not a new workstream. Both are delivered by architecture already committed (OtOpcUa ACL model + canonical model + single-connection-per-equipment).</p>
|
||||
|
||||
<div class="patterns">
|
||||
<div class="pattern">
|
||||
<p class="tag">PATTERN 1</p>
|
||||
<h2>Environment-lifecycle promotion</h2>
|
||||
<p>Promote between dev / staging / prod by flipping write-authority ACLs against stable equipment UUIDs — no client reconfiguration.</p>
|
||||
</div>
|
||||
<div class="pattern">
|
||||
<p class="tag">PATTERN 2</p>
|
||||
<h2>Safe read-only consumption</h2>
|
||||
<p>KPI / monitoring consumers get read-only grants with a structural zero-write-path guarantee — no equipment-side session for them to misuse.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="footnote">Out of scope for this plan: physics simulation, FAT, commissioning emulation. Those would be separate funded initiatives — adjacent to the plan, not part of it.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user