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:
Joseph Doherty
2026-04-30 10:54:49 -04:00
parent 98bf2d0da4
commit ebc76e9315
46 changed files with 2554 additions and 122 deletions

View 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 216 — 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
View 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"
}
}
}

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"playwright": "^1.59.1",
"pptxgenjs": "^4.0.1",
"sharp": "^0.34.5"
}
}

View 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>

View 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 &quot;not possible before&quot; 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>

View 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 &amp; Interfaces, Equipment OPC UA.</p></div>
</body>
</html>

View 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>

View 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 &quot;not possible before&quot; 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>

View 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>

View 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>

View 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>

View 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>&rarr;</p></div>
<div class="node ot"><p>OtOpcUa</p><p class="type">OT · Layer 2</p></div>
<div class="arrow"><p>&rarr;</p></div>
<div class="node ot"><p>System Platform / Ignition</p><p class="type">OT · Layer 3</p></div>
<div class="arrow"><p>&rarr;</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>&rarr;</p></div>
<div class="node it"><p>SnowBridge</p><p class="type">IT · Ingest + Transform</p></div>
<div class="arrow"><p>&rarr;</p></div>
<div class="node it"><p>Snowflake</p><p class="type">IT · Curated Layer</p></div>
<div class="arrow"><p>&rarr;</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>

View 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 13).</li>
</ul>
</div>
</body>
</html>

View 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 &quot;not possible before&quot; AI/analytics use case</b> in production by end of plan — pillar 2 gate.</li>
</ul>
</div>
</body>
</html>

View 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>

View 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 &times; 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>

View 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>

View 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 &rarr; 0)</h1>
</div>
<div class="body">
<div class="item">
<p class="id">LEG-001</p>
<p class="title">Aveva Web API &rarr; 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 &larr; 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 &rarr; 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>

View 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>