@litfamily/lithermes 1.0.28 → 1.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -9
- package/README_Ko-KR.md +12 -9
- package/assets/lithermes-plugin/README.md +15 -2
- package/assets/lithermes-plugin/__init__.py +49 -10
- package/assets/lithermes-plugin/adhd_stack.py +228 -0
- package/assets/lithermes-plugin/core.py +30 -4
- package/assets/lithermes-plugin/core_commands.py +99 -0
- package/assets/lithermes-plugin/core_contexts.py +54 -4
- package/assets/lithermes-plugin/core_contract.py +2 -2
- package/assets/lithermes-plugin/core_routing.py +126 -0
- package/assets/lithermes-plugin/diagnostics.py +3 -2
- package/assets/lithermes-plugin/lit_mark.py +75 -0
- package/assets/lithermes-plugin/payload-version.json +472 -60
- package/assets/lithermes-plugin/plugin.yaml +1 -1
- package/assets/lithermes-plugin/scientific_visualization.py +23 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/SKILL.md +7 -7
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/components.css +414 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/fonts.json +42 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/layouts/landing.html +164 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/layouts/settings.html +114 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/layouts/workspace.html +115 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/computational-brutalism.json +227 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/field-notes.json +205 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/graphite.json +200 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/meadow.json +199 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/neo-brutalism.json +222 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/ui.js +226 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/README.md +2 -4
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/complete-contract.md +4 -4
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/craft-floor.md +10 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/design-lock.mjs +400 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe-rules.json +6 -1
- package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe.mjs +76 -4
- package/assets/lithermes-plugin/skills/lit-adhd/SKILL.md +194 -0
- package/assets/lithermes-plugin/skills/lit-adhd/references/brief-contract.md +55 -0
- package/assets/lithermes-plugin/skills/lit-adhd/references/card-examples.md +86 -0
- package/assets/lithermes-plugin/skills/lit-adhd/references/status-taxonomy.md +40 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/adhd.mjs +11 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/collect.mjs +554 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/gauge.mjs +89 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/hermes-sessions.mjs +219 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/next-step.mjs +101 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/plan-task-rows.mjs +32 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/redact.mjs +42 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/secret-shapes.mjs +59 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/SKILL.md +7 -4
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/style-guide.md +20 -11
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-architecture.md +3 -3
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-dependency.md +1 -1
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-tree.md +3 -3
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-wardley.md +1 -1
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/verifier-guide.md +5 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_quality.py +4 -4
- package/assets/lithermes-plugin/skills/lit-docx/SKILL.md +1 -1
- package/assets/lithermes-plugin/skills/lit-hijack/SKILL.md +239 -0
- package/assets/lithermes-plugin/skills/lit-hijack/references/capture-and-provenance.md +123 -0
- package/assets/lithermes-plugin/skills/lit-hijack/references/clean-room-build.md +128 -0
- package/assets/lithermes-plugin/skills/lit-hijack/references/distil-and-licence.md +128 -0
- package/assets/lithermes-plugin/skills/lit-hijack/references/gap-and-port-design.md +152 -0
- package/assets/lithermes-plugin/skills/lit-hijack/references/parity-and-report.md +131 -0
- package/assets/lithermes-plugin/skills/lit-hijack/scripts/hijack.mjs +995 -0
- package/assets/lithermes-plugin/skills/lit-plan/SKILL.md +1 -0
- package/assets/lithermes-plugin/skills/lit-pptx/SKILL.md +11 -6
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Bold.ttf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Regular.ttf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/jetbrains-mono/OFL.txt +93 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/provenance.json +1 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/anti-slop-checklist.md +3 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/authoring-guide.md +1 -1
- package/assets/lithermes-plugin/skills/lit-pptx/references/consensus-protocol.md +1 -1
- package/assets/lithermes-plugin/skills/lit-pptx/references/direction-step.md +8 -2
- package/assets/lithermes-plugin/skills/lit-pptx/references/examples/13-engineering-review-computational-brutalism-ko.md +175 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/examples/14-launch-pitch-neo-brutalism-ko.md +156 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/examples/15-design-lecture-bauhaus-ko.md +141 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/examples/16-community-programme-riso-ko.md +160 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/examples/17-public-data-briefing-newsroom-ko.md +183 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/layout-families.md +1 -1
- package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/bauhaus.md +105 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/computational-brutalism.md +111 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/neo-brutalism.md +114 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/newsroom.md +106 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/riso.md +101 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/craft_extras.py +49 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/deck_output.py +4 -2
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/render-pack.js +96 -10
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/template-registry.js +50 -6
- package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/bauhaus/pack.yaml +103 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/computational-brutalism/pack.yaml +99 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/neo-brutalism/pack.yaml +107 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/newsroom/pack.yaml +99 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/riso/pack.yaml +99 -0
- package/assets/lithermes-plugin/skills/lit-recap/SKILL.md +1 -1
- package/assets/lithermes-plugin/skills/lit-schematic/SKILL.md +144 -0
- package/assets/lithermes-plugin/skills/lit-schematic/examples/iso-heat-exchanger/build.mjs +38 -0
- package/assets/lithermes-plugin/skills/lit-schematic/examples/iso-heat-exchanger/scene.mjs +133 -0
- package/assets/lithermes-plugin/skills/lit-schematic/examples/journal-adsorption-rig/build.mjs +87 -0
- package/assets/lithermes-plugin/skills/lit-schematic/examples/pid-dosing-skid/build.mjs +85 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/cutaway-outputs.md +70 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/isometric-craft.md +101 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/journal-figures.md +72 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/kit-api.md +134 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/pfd-and-layout.md +53 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/pid-drafting.md +103 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/pid-symbols.md +163 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/verification.md +100 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/doctor.mjs +57 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/figure.mjs +81 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/fonts.mjs +27 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/geom.mjs +113 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/interactive.mjs +292 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/isa.mjs +115 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/iso.mjs +719 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/legend-sheet.mjs +162 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/pid.mjs +937 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/png.mjs +132 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/qa-core.mjs +658 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/route.mjs +218 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/sheet.mjs +890 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/svg.mjs +82 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/theme.mjs +115 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/motion.mjs +129 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/qa.mjs +92 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/render.mjs +233 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/scaffold.mjs +60 -0
- package/assets/lithermes-plugin/skills/lit-scientific-visualization/SKILL.md +19 -1
- package/assets/lithermes-plugin/skills/lit-typographic-motion/SKILL.md +12 -11
- package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/motion.mjs +56 -2
- package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/runtime.mjs +129 -4
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/THIRD_PARTY_NOTICES +9 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/blender/runner.py +218 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/blender.mjs +154 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/board.mjs +132 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/completion.mjs +48 -5
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/gate.mjs +13 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/house.mjs +44 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/scaffold.mjs +238 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/score.mjs +643 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/sound-dsp.mjs +21 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/sound.mjs +171 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-qa.mjs +23 -3
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage.mjs +124 -17
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/styles.mjs +240 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/treatment.mjs +152 -10
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/LICENSE +21 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/addons/environments/RoomEnvironment.js +144 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/addons/geometries/RoundedBoxGeometry.js +155 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/three.module.js +54571 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/hero-3d/expected.json +67 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/hero-3d/index.html +139 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/depth.md +74 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/studio.md +87 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/style-bibles.md +9 -2
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/treatment.md +24 -8
- package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/paper.json +41 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/swiss-signal.json +41 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/terminalcore.json +42 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/tidal.json +41 -0
- package/assets/lithermes-plugin/skills/litresearch/SKILL.md +4 -0
- package/assets/lithermes-plugin/skills/readme-studio/SKILL.md +1 -1
- package/assets/lithermes-plugin/skills/readme-studio/templates/hyperframes/package-lock.json +3 -3
- package/assets/lithermes-plugin/skills/readme-studio/templates/remotion/package-lock.json +3 -3
- package/assets/lithermes-plugin/skills/review-work/SKILL.md +2 -0
- package/assets/lithermes-plugin/skills/start-work/SKILL.md +2 -1
- package/assets/lithermes-plugin/skills/visual-qa/references/complete-contract.md +3 -3
- package/package.json +1 -1
- package/readme-assets/badge-version.svg +1 -1
- package/src/lib/skillPayload.js +1 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Drafting a P&ID sheet
|
|
2
|
+
|
|
3
|
+
A P&ID is an operating document: an operator, a reviewer in a HAZOP and a commissioning engineer each read it for a different question, and each must find the answer without asking the author. Everything here serves that reading.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
1. Before drawing
|
|
7
|
+
2. The sheet
|
|
8
|
+
3. Placing equipment
|
|
9
|
+
4. Lines: routing, crossings, branches
|
|
10
|
+
5. Line numbers and flow arrows
|
|
11
|
+
6. Valves and isolation
|
|
12
|
+
7. Instruments and control loops
|
|
13
|
+
8. Relief and venting
|
|
14
|
+
9. Legend, header, notes and title block
|
|
15
|
+
10. Faults that reviewers catch
|
|
16
|
+
|
|
17
|
+
## 1. Before drawing
|
|
18
|
+
|
|
19
|
+
Write four lists in the build script before any coordinate:
|
|
20
|
+
|
|
21
|
+
- **Equipment**: tag, name, design pressure and temperature, size or duty. These go to the header band (`sheet.equipmentInfo(sym, ["10 MPa / -20…80 °C", "1.0 L, SS316"])`).
|
|
22
|
+
- **Lines**: from, to, nominal size, fluid code, sequence, piping class → the line number `1/4"-CH4-101-HP1`. State the format once in the notes.
|
|
23
|
+
- **Instruments**: tag, what it measures, where it reads (field, panel, DCS), what it acts on.
|
|
24
|
+
- **Safety devices**: each PSV and rupture disk, its set pressure, the equipment it protects, and where it discharges.
|
|
25
|
+
|
|
26
|
+
Invent nothing silently. When the brief gives no set point, choose a plausible one, keep it in the build as a named constant, and list it in the reply as an assumption.
|
|
27
|
+
|
|
28
|
+
## 2. The sheet
|
|
29
|
+
|
|
30
|
+
`createSheet({ size: "A3", … })` draws an ISO A3 landscape sheet (420 × 297 mm): a frame 20 mm from the left edge (binding) and 10 mm elsewhere, zone numbers along the top and bottom and zone letters along the sides so a reviewer can say "the PSV at C6", the equipment header band across the top, the legend column on the right, the notes block bottom left and the title block bottom right. A1 and A2 work the same way for dense units; A4 suits a small skid.
|
|
31
|
+
|
|
32
|
+
Text at 1:1 on A3: tags 2.5 mm, equipment tags 3.0 mm bold, line numbers and notes 2.2 mm, title 4 mm. Nothing under 2.0 mm, because sheets are photocopied and reduced; the QA floor for P&ID sheets is 5.6 pt.
|
|
33
|
+
|
|
34
|
+
Line weights carry the hierarchy on the ISO 128 series, and every pipe class has its own: main process 0.7 mm, minor process 0.5 mm, utility 0.35 mm, instrument supply or process connection 0.18 mm; equipment outlines 0.5 mm, symbols 0.35 mm, signals 0.25 mm with the mark of their medium, tangent lines and details 0.18 mm. Pick the class by what the line carries, not by how it looks: a 2-inch cooling-water header is a utility line however big it is.
|
|
35
|
+
|
|
36
|
+
## 3. Placing equipment
|
|
37
|
+
|
|
38
|
+
- **Flow runs left to right.** Feed and utilities enter on the left, products and vents leave on the right or top. The main process route reads as one horizontal story.
|
|
39
|
+
- **Gravity is real.** Pumps sit below and near their suction vessel; drains go down; vents and relief discharge go up. A tank's outlet nozzle is at its bottom.
|
|
40
|
+
- **Equal elevations for related items.** Put duty and standby pumps one above the other with the same spacing; line up instruments of one loop.
|
|
41
|
+
- **Room for lines.** Keep 15–25 mm between symbols that exchange several lines, and leave free horizontal lanes for headers and a lane below the equipment for signal runs to the DCS.
|
|
42
|
+
- **Instruments sit next to what they measure,** offset 10–15 mm from the line or nozzle, with a short connection. Controllers and DCS bubbles go between the measurement and the final element, toward the top or the bottom lane, so signal lines do not cross the process.
|
|
43
|
+
- **Symbols never overlap.** The canvas warns, and the router cannot pass between symbols closer than twice the clearance (2.2 mm each side on a sheet).
|
|
44
|
+
|
|
45
|
+
## 4. Lines: routing, crossings, branches
|
|
46
|
+
|
|
47
|
+
The router follows the drafting rules a checker applies to a hand-drawn sheet:
|
|
48
|
+
|
|
49
|
+
1. **Leave a port on its normal.** A line leaves a nozzle straight out (up from a top nozzle, sideways from a side nozzle) before it turns, so the reader sees where it starts.
|
|
50
|
+
2. **Never through a symbol.** A route enters a symbol only at its own port. When there is no way around, the build throws: move a symbol or give `via` waypoints. Never draw the line by hand through it; `LINE.THROUGH_SYMBOL` fails.
|
|
51
|
+
3. **Few bends.** Each bend costs as much as about 9 mm of straight line, so routes prefer one or two clean corners to a staircase.
|
|
52
|
+
4. **Cross at right angles, on straights.** A later process line hops over an earlier one (a small semicircle); signal lines cross process lines without a hop. A line never turns on top of another line (`LINE.BEND_ON_LINE`): a corner on another line reads as a junction.
|
|
53
|
+
5. **Never run along another line** (`LINE.OVERLAP`): two lines on one track cannot be told apart.
|
|
54
|
+
6. **Branches leave a straight run.** `line.tap()` refuses a point within 3 mm of a bend and draws a junction dot. A branch leaves perpendicular to the main line.
|
|
55
|
+
7. **Every line ends on something:** a port, another line (a junction), or a terminal (vent, drain, off-page, utility). `LINE.DANGLING` fails anything else.
|
|
56
|
+
|
|
57
|
+
Draw the main process lines first, then minor lines, then utilities, then instrument connections and signals last, so the thin lines route around the heavy ones (the same order an illustrator uses for ducts before cables).
|
|
58
|
+
|
|
59
|
+
## 5. Line numbers and flow arrows
|
|
60
|
+
|
|
61
|
+
A line number labels each line between two pieces of equipment, placed parallel to its longest clear straight, beside it, never on it. The format sits in the notes: size – fluid code – sequence – piping class (`2"-PG-101-A1A`). Fluid codes are short and listed in the legend notes when they are not obvious (`CH4`, `CO2`, `CWS`/`CWR`, `N2`, `IA`, `PW`).
|
|
62
|
+
|
|
63
|
+
Flow arrows: one on each line's longest clear straight by default, kept clear of junctions and symbols; add arrows (`arrows: [0.2, 0.8]`) where the direction is not obvious, such as at a recycle or after a three-way valve. Never arrow a signal line.
|
|
64
|
+
|
|
65
|
+
## 6. Valves and isolation
|
|
66
|
+
|
|
67
|
+
- **Every instrument, pump, control valve and relief device can be isolated.** Put a block valve on each side of a control valve, with a bypass globe or needle valve when the process must run while the control valve is out.
|
|
68
|
+
- **Check valves** sit on pump discharges and on any line that must never flow back (gas supply into a reactor).
|
|
69
|
+
- **Drains and vents** at low and high points of a liquid loop, each with a valve.
|
|
70
|
+
- **Double block and bleed** on high-pressure gas: two block valves with a bleed valve between, where the gas supply meets the reactor.
|
|
71
|
+
- **Valve on a straight run**: `inline` snaps a valve to the nearest straight run that can hold it, and throws when none can.
|
|
72
|
+
|
|
73
|
+
## 7. Instruments and control loops
|
|
74
|
+
|
|
75
|
+
A loop on the sheet: element or transmitter at the process (`PT-101` connected by a thin solid line to a nozzle or a tap), signal to the controller (`PIC-101` in the DCS, dashed for electric), signal to the final element (`PV-101` diaphragm valve, or a pump's `signal` port, or a chiller's setpoint input). Draw the whole loop on the sheet, or end it at an off-page connector naming the sheet that continues it.
|
|
76
|
+
|
|
77
|
+
Alarms and trips: put `PAH`, `PAHH`, `TAH` on the controller bubble's loop number; a trip (`PSHH-101` → interlock `I-1` → `XV-101` closes) is drawn as its own loop through an interlock diamond. Safety instrumented functions are labelled as such in the notes.
|
|
78
|
+
|
|
79
|
+
## 8. Relief and venting
|
|
80
|
+
|
|
81
|
+
Every vessel that can be over-pressured (by a gas supply, a booster, heat input, fire, or a blocked outlet) has a relief device set at or below its design pressure. Draw the PSV on the vessel or its inlet line with its set pressure beside it (`setPressure: "SET 12 MPa"`), and draw where it discharges: a vent header, a flare off-page connector or a safe vent with `ATM`. A rupture disk upstream of a PSV protects the valve from a corrosive or hydrate-forming gas; draw a pressure gauge between them to show the disk is intact. A relief path that ends in the air is the first defect a HAZOP finds.
|
|
82
|
+
|
|
83
|
+
## 9. Legend, header, notes and title block
|
|
84
|
+
|
|
85
|
+
- **Legend**: built from what the sheet draws: every line kind, every symbol kind (composed kinds such as a globe valve with a diaphragm actuator, or a DCS function in the control room, get their own row and sample), and the instrument identification letters used with their meaning. Its column is fixed; when it would overflow, the identification letters move into the right half of the notes block, and if it still overflows the sheet warns: move to a larger sheet.
|
|
86
|
+
- **Legend sheet**: a drawing set opens with a "P&ID symbols and abbreviations" sheet. `createLegendSheet({ number, rev, date })` builds it from the library (every symbol, line class, the ISA shape-and-location matrix, the letters, fail positions, the line-number form, the equipment prefixes); give it the set's first drawing number.
|
|
87
|
+
- **Equipment header**: tag, name and two or three spec lines above each major item.
|
|
88
|
+
- **Notes**: numbered; the line number format, the meaning of duty/standby, set-point references, hold points. Keep them to facts the drawing cannot show.
|
|
89
|
+
- **Title block** (ISO 7200 fields): title, drawing number, revision, sheet, project, owner, drawn/checked/approved, date, scale (`NTS` for P&IDs). `TITLEBLOCK.FIELDS` fails an empty title, number, revision, date or scale.
|
|
90
|
+
|
|
91
|
+
## 10. Faults that reviewers catch
|
|
92
|
+
|
|
93
|
+
| You see | Fix |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| A line passes behind a vessel or through a valve body | Re-route with `via`, or move the symbol; the router refuses the pass-through. |
|
|
96
|
+
| A branch leaves at a corner | Tap a straight run at least 3 mm from the bend. |
|
|
97
|
+
| A controller with no final element | Draw the output signal to the valve, pump or heater it moves, or remove the `C`. |
|
|
98
|
+
| An instrument floating next to a vessel | Connect it to a nozzle (`vessel.port("right", 0.4)`) or a tap. |
|
|
99
|
+
| PSV discharge stops in the air | Run it to a vent, header or off-page connector. |
|
|
100
|
+
| Two lines labelled with the same number | Number every line between two items once. |
|
|
101
|
+
| Tags like `TCI` or `PCIC` | Readout letters come before output letters, no repeats: `TIC`, `PIC`. |
|
|
102
|
+
| A dense corner where labels overlap | Spread the equipment; never shrink text under 2.0 mm. |
|
|
103
|
+
| The legend shows symbols not on the sheet | The kit builds the legend from the drawing; a hand-written legend drifts. |
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# P&ID symbols and identification
|
|
2
|
+
|
|
3
|
+
The kit draws a vocabulary close to ISO 10628-2 (graphical symbols for process diagrams) for equipment and fittings, and ANSI/ISA-5.1 for instruments, their identification letters and signal lines. Conventions vary between owners and countries, so **the legend on the sheet governs**: the sheet's legend is built from the symbols and line kinds actually drawn, and the QA gate fails a sheet whose legend misses one. When a project has its own symbol standard, follow it and keep the legend true.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
1. Identification letters (ISA-5.1)
|
|
7
|
+
2. Instrument bubbles: what the function is and where it lives
|
|
8
|
+
3. Pipe classes and signal lines
|
|
9
|
+
4. Equipment symbols and tags
|
|
10
|
+
5. Valves, actuators and fail positions
|
|
11
|
+
6. Piping items, flow elements and terminals
|
|
12
|
+
7. Common tags for a high-pressure gas-hydrate loop
|
|
13
|
+
8. Standards basis, symbol by symbol
|
|
14
|
+
|
|
15
|
+
## 1. Identification letters (ISA-5.1)
|
|
16
|
+
|
|
17
|
+
A tag is **letters + loop number**: `PIC-101` is loop 101, pressure (P), indicating (I), controller (C). The first letter is the measured or initiating variable; an optional modifier follows it; then the functions, readout or passive ones before output ones; `H`, `L`, `M` modify the alarm or switch letter before them.
|
|
18
|
+
|
|
19
|
+
| Letter | First (measured variable) | Modifier after the first letter | Succeeding: readout / passive | Succeeding: output | Function modifier |
|
|
20
|
+
| --- | --- | --- | --- | --- | --- |
|
|
21
|
+
| A | Analysis (composition, pH, O₂) | | Alarm | | |
|
|
22
|
+
| B | Burner, combustion | | User's choice | | |
|
|
23
|
+
| C | User's choice (often conductivity) | | | Control | |
|
|
24
|
+
| D | User's choice (often density) | Differential | | | |
|
|
25
|
+
| E | Voltage | | Primary element (sensor) | | |
|
|
26
|
+
| F | Flow | Ratio | | | |
|
|
27
|
+
| G | User's choice (gauging) | | Glass, gauge, viewing | | |
|
|
28
|
+
| H | Hand | | | | High |
|
|
29
|
+
| I | Current | | Indicate | | |
|
|
30
|
+
| J | Power | Scan | | | |
|
|
31
|
+
| K | Time, schedule | Time rate of change | | Control station | |
|
|
32
|
+
| L | Level | | Light | | Low |
|
|
33
|
+
| M | User's choice (moisture) | Momentary | | | Middle |
|
|
34
|
+
| O | User's choice | | Orifice, restriction | | |
|
|
35
|
+
| P | Pressure, vacuum | | Point (test connection) | | |
|
|
36
|
+
| Q | Quantity | Integrate, totalise | | | |
|
|
37
|
+
| R | Radiation | | Record | | |
|
|
38
|
+
| S | Speed, frequency | Safety | | Switch | |
|
|
39
|
+
| T | Temperature | | | Transmit | |
|
|
40
|
+
| U | Multivariable | | | Multifunction | |
|
|
41
|
+
| V | Vibration | | | Valve, damper | |
|
|
42
|
+
| W | Weight, force | | Well, probe | | |
|
|
43
|
+
| X | Unclassified | x-axis | Unclassified | | |
|
|
44
|
+
| Y | Event, state | y-axis | | Relay, compute, convert | |
|
|
45
|
+
| Z | Position, dimension | z-axis | | Driver, actuator | |
|
|
46
|
+
|
|
47
|
+
Readable examples: `PT` pressure transmitter · `PI` local pressure gauge reading · `PIC` pressure indicating controller · `PCV` self-actuated pressure regulator · `PV` pressure control valve (final element of a PIC loop) · `PSV` pressure safety valve · `PSE` rupture disk · `PAH`/`PAHH` high and high-high pressure alarm · `PSHH` high-high pressure switch (trip) · `PDT` differential pressure transmitter · `TE` temperature element (thermocouple, RTD) · `TW` thermowell · `TT`, `TIC`, `TV` · `FT`, `FE` (orifice), `FIC`, `FV`, `FQI` totaliser · `LT`, `LG` level gauge, `LIC`, `LSL` · `AT` analyser transmitter (GC, MS, O₂) · `SC` speed controller · `HS` hand switch · `XV` on-off valve · `ZSO`/`ZSC` position switch open/closed.
|
|
48
|
+
|
|
49
|
+
`parseInstrumentTag` enforces: the first letter exists; a variable modifier only directly after it; function letters from the table, readout `I`/`R` before outputs, no repeats; `H`/`L`/`M` only after `A`, `S` or another `H`/`L`/`M`. `TAG.GRAMMAR` fails a symbol tag that breaks these, `TAG.DUPLICATE` fails two items with one tag.
|
|
50
|
+
|
|
51
|
+
**Loop numbering.** One loop number per measured point and everything that acts on it: `PT-101 → PIC-101 → PV-101`, and its alarms `PAH-101`. Number loops by unit (100s for the reactor section, 200s for the chiller) so a reader finds them.
|
|
52
|
+
|
|
53
|
+
## 2. Instrument bubbles: what the function is and where it lives
|
|
54
|
+
|
|
55
|
+
ISA-5.1 Table 5.1.1 separates two things a bubble says. Its **shape** names the kind of device or function; the **lines across it** name where it lives.
|
|
56
|
+
|
|
57
|
+
| Shape | Function | Kit `fn` |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| Circle | Discrete instrument: a transmitter, gauge, switch or single-loop controller | `"discrete"` (default) |
|
|
60
|
+
| Circle in a square | Shared display and control: a function of the DCS or operator screen | `"shared"` |
|
|
61
|
+
| Hexagon | Computer function: an optimiser, a calculation outside the DCS | `"computer"` |
|
|
62
|
+
| Diamond in a square | PLC logic, or a safety instrumented system function | `"plc"` |
|
|
63
|
+
|
|
64
|
+
| Lines across the shape | Location | Kit `location` |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| none | In the field, at the equipment | `"field"` (default) |
|
|
67
|
+
| one solid | Main control room, operator accessible (on the console or panel front) | `"main"` |
|
|
68
|
+
| one dashed | Main control room, not normally accessible (behind the panel, in a cabinet) | `"main-rear"` |
|
|
69
|
+
| two solid | Auxiliary or local panel near the unit, operator accessible | `"aux"` |
|
|
70
|
+
| two dashed | Auxiliary or local panel, not normally accessible | `"aux-rear"` |
|
|
71
|
+
|
|
72
|
+
So a DCS controller on the operator console is `instrument({ tag: "PIC-101", fn: "shared", location: "main" })`: a circle in a square with one solid line. A local pressure gauge is a plain circle. The kind is `instrument.<fn>-<location>` and the legend names both halves. The round-1 names still work: `location: "dcs"` means shared/main, `"plc"` plc/main, `"panel"` discrete/main, `"panel-rear"` discrete/main-rear, `"local-panel"` discrete/aux.
|
|
73
|
+
|
|
74
|
+
The letters sit in the upper half and the loop number in the lower half, clear of the location lines. Keep bubbles 10 mm across on an A3 sheet (`r = 5`); a 4-letter tag drops to 2.1 mm text automatically. An interlock or logic block is a diamond with its number (`interlock()`).
|
|
75
|
+
|
|
76
|
+
## 3. Pipe classes and signal lines
|
|
77
|
+
|
|
78
|
+
Pipe classes differ by **weight**, on the ISO 128 series, so they never look alike on the sheet or in the legend: main process 0.7 mm, minor process 0.5 mm, utility 0.35 mm, instrument supply or process connection 0.18 mm. Instrument signals are 0.25 mm and carry the mark of ISA-5.1 Table 5.3.2 for their medium.
|
|
79
|
+
|
|
80
|
+
| Line | Drawing | Kit |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| Main process line | heavy solid, 0.7 mm | `pipe({ kind: "process-major" })` |
|
|
83
|
+
| Minor process line, small bore | solid, 0.5 mm | `"process-minor"` |
|
|
84
|
+
| Utility (cooling water, nitrogen, instrument air, steam) | solid, 0.35 mm | `"utility"` |
|
|
85
|
+
| Instrument supply or process connection, impulse line (Table 5.3.1) | thin solid, 0.18 mm | `connect()` |
|
|
86
|
+
| Electric or electronic signal (4–20 mA, discrete) | dashed | `signal({ type: "electric" })` |
|
|
87
|
+
| Pneumatic signal | double slashes every 6 mm | `"pneumatic"` |
|
|
88
|
+
| Hydraulic signal | L marks | `"hydraulic"` |
|
|
89
|
+
| Filled-system capillary tube | crosses | `"capillary"` |
|
|
90
|
+
| Guided electromagnetic or sonic signal (fibre, guided wave) | sine wave | `"guided"` |
|
|
91
|
+
| Unguided electromagnetic signal (radio, wireless transmitter) | broken sine wave | `"unguided"` |
|
|
92
|
+
| Data link within a control system (DCS bus, fieldbus segment) | small open circles | `"data"` (`"software"` still works) |
|
|
93
|
+
| Mechanical link (a lever, a shaft between two functions) | small filled dots | `"mechanical"` |
|
|
94
|
+
|
|
95
|
+
A signal runs from the transmitter to the controller to the final element. Every loop on the sheet ends at something that acts (a control valve, a pump's speed or stroke input, a heater, a trip), or it is an indication-only loop and has no controller letter. An actuated valve has an `actuator` port at the top of its actuator: run the controller's signal to `valve.port("actuator")`, never to the valve body.
|
|
96
|
+
|
|
97
|
+
## 4. Equipment symbols and tags
|
|
98
|
+
|
|
99
|
+
Equipment tags are a class prefix and a number, with an optional train letter: `R-101` reactor, `V-101` vessel, `TK-101` tank, `D-101` drum, `C-101` column, `E-101` exchanger, `P-101A/B` duty and standby pumps, `K-101` compressor, `BR-101` gas booster, `CH-101` chiller, `F-101` filter, `GC-101` gas cylinder, `M-101` mixer or agitator. The kit draws:
|
|
100
|
+
|
|
101
|
+
- **Vessels** (`vessel`): shell with 2:1 ellipsoidal heads (or flat or conical), tangent lines, optional jacket (`jacket-in` at the bottom, `jacket-out` at the top, so coolant flows upward), agitator with motor (turbine, anchor or propeller), internal coil, packing (a box with its diagonals, ISO 10628-2 item 2.4), trays, demister, legs or skirt, liquid level mark. Trays run across the whole shell as on a tray column (item 2.2); `trayStyle: "staggered"` draws segmental baffles that stop short of the far wall (item 2.5).
|
|
102
|
+
- **Machines** (`pump`, `compressor`): a centrifugal pump is a circle with a tangential discharge on a base; a rotary positive-displacement pump shows two rotors; a metering pump adds the diagonal adjustment arrow. A centrifugal compressor is a circle with converging sides; a reciprocating compressor or gas booster is a circle with a cylinder and rod.
|
|
103
|
+
- **Heat transfer**: a general exchanger or condenser (circle crossed by a zigzag, item 3.1); shell-and-tube with fixed tube sheets (`shellTube()`, item 3.3: shell, a tube sheet at each end, straight tubes), floating head (`type: "floating"`, item 3.4) or U-tubes (`type: "utube"`, item 3.5); plate exchanger (`plateExchanger()`, item 3.6: the pack with both diagonals and a centre line); air-cooled exchanger (`airCooler()`: bundle with its tube run, fan and motor below).
|
|
104
|
+
- **Packaged units**: anything with its own skid (chiller, circulator, analyser, DAQ, booster package) is a box with its caption and declared ports (`unit({ glyph: "coil" | "wave" })`).
|
|
105
|
+
|
|
106
|
+
## 5. Valves, actuators and fail positions
|
|
107
|
+
|
|
108
|
+
Valves are inline symbols on a straight run; the line is gapped under them. Bodies (ISA-5.1 Table 5.4.1, ISO 10628-2 groups 21 and 22): gate (plain bowtie), globe (bowtie with a filled dot), ball (bowtie with an open circle), plug (bowtie with a small square), butterfly (two end bars, the disc on the diagonal, a hub dot), needle (bowtie with a solid point above, for fine metering), diaphragm (bowtie under a bonnet arc), check (bowtie with the solid half pointing along the allowed flow), three-way (a third body on the branch, port `c`). An angle valve (`angleValve({ inlet, outlet })`) sits where the line turns: one body toward each port.
|
|
109
|
+
|
|
110
|
+
Actuators (Table 5.4.2) stand perpendicular to the line and name themselves in the kind, `valve.<body>.<actuator>`: `diaphragm` (spring-opposed pneumatic, the usual control valve), `piston` (cylinder), `motor` (electric, `M`), `solenoid` (on-off, `S`), `hand` (handwheel; `manual` is the same). Fail positions (Table 5.4.4) go beside the actuator as letters: `FO` fail open, `FC` fail closed, `FL` fail locked in its last position, `FI` fail indeterminate; anything else throws. Self-acting devices (Table 5.4.3): a pressure-reducing regulator (`regulator`) senses downstream through its impulse line, a back-pressure regulator (`backPressure: true`) senses upstream; a pressure safety or relief valve (`safetyValve`) has its inlet from below, outlet to the side and spring on top; a rupture disk (`ruptureDisk`) is two bars with the domed disk between them.
|
|
111
|
+
|
|
112
|
+
## 6. Piping items, flow elements and terminals
|
|
113
|
+
|
|
114
|
+
Piping items: concentric reducer (`reducer`) or eccentric reducer (`reducer({ eccentric: true, flat: "bottom" | "top" })`, flat side down on pump suctions), flange pair (`flange`), spectacle blind (`blind`, figure-8: the half in the line is solid when the line is blinded, `state: "open"` turns it to a ring), inline filter or strainer (`filter`), sight glass. Flow elements (ISA-5.1 Table 5.2.3) through `flowElement({ type })`: `orifice` (plate between flanges), `venturi` (converging-diverging throat), and notation boxes for meters without a drawn form of their own, labelled with the Table 5.2.2 notation: `magnetic` MAG, `coriolis` COR, `vortex` VOR, `turbine` TUR, `ultrasonic` US, `thermal` TMF. A variable-area meter (`rotameter`) and a mass flow controller (`mfc`, with a `signal` port) complete the set.
|
|
115
|
+
|
|
116
|
+
Terminals end a line on purpose: vent to atmosphere (`vent`, labelled `ATM` or the vent header), drain, sample point, off-page connector with the destination and drawing number, utility connection (`N2`, `IA`, `CWS`, `CWR`). A line that ends anywhere else is dangling (`LINE.DANGLING`).
|
|
117
|
+
|
|
118
|
+
**Line numbers** read `size-fluid-sequence-class[-insulation]`: `2"-P-1001-A1A`, `1/4"-CH4-101-HP1`, `DN50-CW-205-B1-IH`. On a P&ID the QA gate fails a line number that breaks this form (`LINE.NUMBER`); `parseLineNumber` reads one apart.
|
|
119
|
+
|
|
120
|
+
## 7. Common tags for a high-pressure gas-hydrate loop
|
|
121
|
+
|
|
122
|
+
A hydrate formation rig is a compact high-pressure gas loop; its P&ID usually carries these items, which the kit covers as listed:
|
|
123
|
+
|
|
124
|
+
| Item | Tag | Kit |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| Gas cylinders (CH₄, CO₂, mixtures) with cylinder regulator | `GC-101`, `PCV-101` | `gasCylinder`, `regulator` |
|
|
127
|
+
| Gas booster raising cylinder gas to test pressure | `BR-101` | `compressor({ type: "reciprocating" })` or `unit` |
|
|
128
|
+
| Buffer or supply vessel | `V-101` | `vessel` |
|
|
129
|
+
| Jacketed stirred high-pressure reactor (sapphire windows optional) | `R-101` | `reactor` |
|
|
130
|
+
| Chiller or circulator on the jacket | `CH-101` | `unit({ glyph: "coil" })` |
|
|
131
|
+
| Reactor pressure and temperature | `PT-101`, `PI-101`, `TE-101`/`TT-101`, `TW-101` | `instrument` |
|
|
132
|
+
| Pressure control (supply regulator or BPR on vent) | `PIC-101`, `PV-101` | `valve({ actuator: "diaphragm" })` |
|
|
133
|
+
| Temperature control on the chiller | `TIC-201` → chiller setpoint | `signal` to the chiller unit |
|
|
134
|
+
| Relief: PSV set below the reactor's design pressure, rupture disk in series or parallel | `PSV-101`, `PSE-101` | `safetyValve`, `ruptureDisk` |
|
|
135
|
+
| Gas flow into the reactor | `FT-101`, `FQI-101` (consumed gas) | `instrument`, `mfc` |
|
|
136
|
+
| Sampling to GC | `SP-101`, `AT-101` | `samplePoint`, `instrument` |
|
|
137
|
+
| Vent to a safe location | `ATM` or `TO VENT HEADER` | `vent`, `offPage` |
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
## 8. Standards basis, symbol by symbol
|
|
141
|
+
|
|
142
|
+
Where each drawn form comes from, so a reviewer can check it against the project's own licensed copies. ISO 10628-2:2012 numbers its symbols by subject group (Table 1) and item (Table 2: group.item with an ISO 14617 registration number); ANSI/ISA-5.1-2009 groups instrument symbols in tables 4.1 and 5.1.1 to 5.4.4. Where the kit follows common US practice (PIP PIC001) rather than a numbered entry, the row says so.
|
|
143
|
+
|
|
144
|
+
| Kit kind | Basis |
|
|
145
|
+
| --- | --- |
|
|
146
|
+
| `vessel.*`, `vessel.horizontal`, `vessel.tank`, `gas.cylinder` | ISO 10628-2 group 1 (vessels and tanks): items 1.5 dished ends, 1.3 cone roof tank, 1.13 drum, 1.14 gas cylinder, 1.27 jacketed vessel |
|
|
147
|
+
| `vessel.column` | ISO 10628-2 group 2: 2.2 tray column (full-width trays, default), 2.4 fixed bed (packing), 2.5 staggered baffles |
|
|
148
|
+
| `exchanger.general`, `.shelltube`, `.floating`, `.utube`, `.plate` | ISO 10628-2 group 3: 3.1, 3.3, 3.4, 3.5, 3.6 |
|
|
149
|
+
| `exchanger.aircooler` | ISO 10628-2 group 3 (air cooler); drawn as bundle, fan and motor after common practice |
|
|
150
|
+
| `pump.*` | ISO 10628-2 group 15 (liquid pumps); centrifugal form with tangential discharge after PIP practice |
|
|
151
|
+
| `compressor.*` | ISO 10628-2 group 16 (compressors) |
|
|
152
|
+
| `valve.<body>` | ISA-5.1 Table 5.4.1; ISO 10628-2 group 21 (valves), group 22 (check valves) |
|
|
153
|
+
| `valve.<body>.<actuator>`, fail letters | ISA-5.1 Table 5.4.2 (actuators), Table 5.4.4 (failure and de-energised positions) |
|
|
154
|
+
| `valve.regulator`, `.backpressure`, `.safety`, `relief.rupture` | ISA-5.1 Table 5.4.3 (self-actuated final control elements); ISO 10628-2 group 23 (safety function) |
|
|
155
|
+
| `line.reducer*`, `line.flange`, `line.blind*`, `line.filter`, `line.sightglass` | ISO 10628-2 groups 24 (fittings) and 25 (piping); spectacle blind form after PIP practice |
|
|
156
|
+
| `flow.orifice`, `flow.venturi`, `flow.<meter>` | ISA-5.1 Table 5.2.3 (primary elements) and Table 5.2.2 (measurement notations MAG, COR, VOR, TUR, US, TMF) |
|
|
157
|
+
| `instrument.<fn>-<location>` | ISA-5.1 Table 5.1.1 (device and function symbols by location) |
|
|
158
|
+
| `logic.interlock` | ISA-5.1 binary logic (Table 5.7) as a diamond with its number |
|
|
159
|
+
| Pipe classes and widths | ISO 128 line-width series; ISO 10628-1 (main lines heavier than others) |
|
|
160
|
+
| Signal lines | ISA-5.1 Table 5.3.1 (instrument to process) and Table 5.3.2 (instrument to instrument) |
|
|
161
|
+
| Identification letters, tags | ISA-5.1 Table 4.1 |
|
|
162
|
+
|
|
163
|
+
The standalone legend sheet (`createLegendSheet`, `scripts/lib/legend-sheet.mjs`) prints every row of this vocabulary from the library itself, so the sheet a drawing set opens with never drifts from what the kit draws.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Verification
|
|
2
|
+
|
|
3
|
+
Look at the drawing the way its harshest reader will, before anyone else does. Rendering is cheap; a reviewer finding a line through a vessel costs a revision.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
1. The loop
|
|
7
|
+
2. The QA gate: every code and its fix
|
|
8
|
+
3. Render and look
|
|
9
|
+
4. The close-up sweep
|
|
10
|
+
5. Review checklist
|
|
11
|
+
6. Exports
|
|
12
|
+
|
|
13
|
+
## 1. The loop
|
|
14
|
+
|
|
15
|
+
1. Build → `node "$SKILL_ROOT/scripts/qa.mjs" drawing.svg`.
|
|
16
|
+
2. Fix every FAIL **in the build script**, rebuild, re-run QA until `QA PASS`.
|
|
17
|
+
3. Render (`render.mjs --png --dpi 600`) and open the PNG. Read it in the order of section 3. QA reads the SVG, never the PNG, so the renderer checks the PNG itself: it compares the ink in 36 regions against a small reference render of the same page, and a region that holds ink there but came out blank means the browser stopped painting (it happens above roughly 60 to 80 million pixels, an A2 sheet at 600 dpi). The renderer then steps the dpi down until the page arrives whole and prints `reduced from 600`; if it cannot, it stops with `RENDER_INCOMPLETE`. A reduced dpi on a large sheet is fine for reading; the PDF stays vector, and `--crop` gives any region at full resolution.
|
|
18
|
+
4. For anything denser than a dozen symbols or parts, render the close-up sweep (`--sheet sweep.png --grid 3x2`, or `4x3` for an A1 sheet) and read every cell.
|
|
19
|
+
5. Fix what the eye found, rebuild, re-run QA, re-render. Stop when QA passes and the last render shows nothing to fix.
|
|
20
|
+
|
|
21
|
+
QA is necessary, never sufficient: it reads geometry and markers, not appearance. It cannot see a leader dot on a face hidden behind another part, a nozzle standing on a cut-away shell, a misleading symbol choice, or a crowded corner that is technically clear. The look is the gate that catches those.
|
|
22
|
+
|
|
23
|
+
## 2. The QA gate: every code and its fix
|
|
24
|
+
|
|
25
|
+
`qa.mjs` parses the SVG itself (no browser), applies every transform and reads the markers the kit writes (`data-sym`, `data-box`, `data-ports`, `data-line`, `data-part`, `data-hull`, `data-leader`, `data-panel`, `data-legend-item`, `data-within`). Unmarked geometry still counts for strokes and text, so a hand-edited SVG cannot slip past by dropping markers. `--json` gives the same result as JSON; `--codes` lists the codes.
|
|
26
|
+
|
|
27
|
+
| Code | Meaning | Fix in the build |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `XML.WELLFORMED` | The SVG does not parse. | Rebuild; never hand-edit the output. |
|
|
30
|
+
| `XML.EXTERNAL` | It loads an outside file or URL. | Inline it or drop it. |
|
|
31
|
+
| `SVG.ACTIVE` | Script or event handlers. | Remove them; a schematic is static. |
|
|
32
|
+
| `SVG.SIZE` | No physical width/height or viewBox. | Use `createSheet` / `createFigure`, which write mm sizes. |
|
|
33
|
+
| `TEXT.MIN_SIZE` | Text under the floor at print size (5.6 pt on sheets, 6 pt on figures). | Spread the layout or shorten the text; never shrink it. |
|
|
34
|
+
| `TEXT.OVERLAP` | Two labels overlap. | Move a symbol; the placer then finds room. |
|
|
35
|
+
| `TEXT.ON_SYMBOL` | A label sits on a symbol it does not belong to. | Move the label's owner or the symbol. |
|
|
36
|
+
| `TEXT.ON_ART` | A label sits on the isometric art. | Use `callout`, which places labels beside the art. |
|
|
37
|
+
| `TEXT.ON_LINE` | A line runs through a label. | Re-route (`via`) or move the label's owner. |
|
|
38
|
+
| `TEXT.CLIPPED` | A label runs past the drawing edge. | Widen the panel or shorten the label. |
|
|
39
|
+
| `TEXT.OUT_OF_CELL` | Text overflows its title-block cell, legend or notes block. | Shorten it, or move to a larger sheet. |
|
|
40
|
+
| `LINE.THROUGH_SYMBOL` | A line passes through a symbol. | Let the router route it, add `via`, or move the symbol. |
|
|
41
|
+
| `LINE.DANGLING` | A line end touches no port, line or terminal. | End it on a port, a `tap`, or a vent/drain/off-page terminal. |
|
|
42
|
+
| `LINE.BEND_ON_LINE` | A line turns on another line. | Move the waypoint; corners on lines read as junctions. |
|
|
43
|
+
| `LINE.OVERLAP` | Two lines share a track. | Give one a different lane. |
|
|
44
|
+
| `LINE.CROSSING` (warn) | Process lines cross without a hop. | Hand-drawn lines: route through the canvas so later lines hop. |
|
|
45
|
+
| `LINE.ARROW_IN_SYMBOL` | A flow arrow is drawn inside a valve, instrument or other body, where it reads as part of the symbol. | Let the canvas place arrows, or give `arrows` fractions that fall on open pipe; the canvas already slides an asked-for arrow off a body and warns when it cannot. |
|
|
46
|
+
| `LEADER.LOOSE` | A leader does not run from its label to a dot on its named part. | Use `callout({ part })` and anchor on that part. |
|
|
47
|
+
| `LEADER.CROSSING` | Two leaders cross. | Reorder anchors or move one callout to the other side. |
|
|
48
|
+
| `LEADER.SPACING` | A leader runs within 1.2 mm (print size) of another leader or of another leader's dot, so two read as one. | Move one dot to a clearer spot on its part (another face, further along the body), or send that label to the other side. The placer already reorders labels and opens rows to separate them; what it cannot clear it lists in `fig.warnings()`. |
|
|
49
|
+
| `STROKE.MIN` | A stroke under 0.25 pt. | Thicken it; hairlines vanish in print. |
|
|
50
|
+
| `SYMBOL.NO_BOX` | A symbol group without its box. | Draw symbols with the kit factories. |
|
|
51
|
+
| `TAG.GRAMMAR` | A tag breaks ISA-5.1 or the equipment pattern. | See `pid-symbols.md` section 1. |
|
|
52
|
+
| `LINE.NUMBER` | A P&ID line number is not size-fluid-sequence-class[-insulation]. | Write it as `2"-P-1001-A1A`; see `pid-symbols.md` section 6. |
|
|
53
|
+
| `TAG.DUPLICATE` | Two items share a tag. | Number loops and trains uniquely. |
|
|
54
|
+
| `LEGEND.COVERAGE` | A sheet draws a kind the legend lacks; a figure draws a stream role its key lacks. | Let the sheet build its legend; add the role to `fig.key`. |
|
|
55
|
+
| `TITLEBLOCK.FIELDS` | Title, number, revision, date or scale empty. | Fill them in `createSheet`. |
|
|
56
|
+
| `PALETTE.CVD` | Two stream colours are too close for some reader. | Use the `STREAM` roles; never invent colours for categories. |
|
|
57
|
+
| `PANEL.LABELS` | Panel labels missing, out of order, unequal or misplaced. | Use `fig.panel("a", …)` in reading order. |
|
|
58
|
+
| `FONT.FAMILIES` (warn) | More than two families. | Arial for Latin, Pretendard for Hangul. |
|
|
59
|
+
|
|
60
|
+
## 3. Render and look
|
|
61
|
+
|
|
62
|
+
Open the PNG at a size where the whole drawing fits, then read it in this order:
|
|
63
|
+
|
|
64
|
+
1. **Lines.** Every line runs from a port to a port, a junction or a terminal; nothing passes through a body; crossings are hops at right angles on straights; no line hugs another.
|
|
65
|
+
2. **Labels.** Nothing on a line, a symbol or the art; every label next to its owner; tags readable.
|
|
66
|
+
3. **Topology.** Trace each stream from source to destination; trace each control loop from element to final element; trace each relief path to its discharge.
|
|
67
|
+
4. **Isometric order.** Nothing behind shows through anything in front; the ground is under everything; tubes, baffles and pipes interleave correctly.
|
|
68
|
+
5. **Cutaways.** Nozzles and brackets stand on closed shell; internals stay inside the wall; the cut faces read as wall thickness.
|
|
69
|
+
6. **Leaders.** Each dot is on the face of the part its label names and on a face the viewer can see.
|
|
70
|
+
7. **Detail.** At this size the drawing reads as one machine or one process; no region is bare where the real thing has parts.
|
|
71
|
+
|
|
72
|
+
## 4. The close-up sweep
|
|
73
|
+
|
|
74
|
+
`render.mjs drawing.svg --sheet sweep.png --grid 3x2` renders every region of the drawing enlarged in one image, each cell captioned with its viewBox box. Read every cell for: line ends a hair short of a port, hops that clip a junction, arrowheads on dots, text touching strokes, bolt circles that stop in mid-face, seams that miss the outline, tone steps on a curved body. To inspect one region at print resolution: `render.mjs drawing.svg --crop x,y,w,h --png close.png --dpi 1200`.
|
|
75
|
+
|
|
76
|
+
## 5. Review checklist
|
|
77
|
+
|
|
78
|
+
**Content**
|
|
79
|
+
- [ ] Every number on the drawing comes from the brief, or is listed in the reply as an assumption.
|
|
80
|
+
- [ ] Every vessel that can be over-pressured has a relief device with a discharge destination.
|
|
81
|
+
- [ ] Every controller has a measurement and a final element; every instrument has a process connection.
|
|
82
|
+
- [ ] Isolation valves on both sides of control valves and instruments where the process requires them.
|
|
83
|
+
|
|
84
|
+
**Drawing**
|
|
85
|
+
- [ ] `QA PASS` on the final SVG.
|
|
86
|
+
- [ ] Flow reads left to right; gravity is respected.
|
|
87
|
+
- [ ] The legend and title block are complete (sheets); the key covers every stream role (figures).
|
|
88
|
+
- [ ] Line weights follow the hierarchy; text sizes meet the floor at print size.
|
|
89
|
+
|
|
90
|
+
**Isometric**
|
|
91
|
+
- [ ] It reads as the real equipment; 40+ parts; each part in its real material colour (grey only on request), at most one accent; halo under the base.
|
|
92
|
+
- [ ] Painter's order correct everywhere; interleaving parts split.
|
|
93
|
+
- [ ] Leaders end on visible faces of their parts; none cross.
|
|
94
|
+
|
|
95
|
+
**Exports**
|
|
96
|
+
- [ ] PNG at 600 dpi or more, pHYs written; PDF page equals the drawing size; both opened.
|
|
97
|
+
|
|
98
|
+
## 6. Exports
|
|
99
|
+
|
|
100
|
+
`qa.mjs drawing.svg --png drawing.png --pdf drawing.pdf [--dpi 600]` adds `PNG.CLOSED` (truncated file or bad checksum), `PNG.DPI` (declared resolution under the requirement, or pixel width that does not match the drawing width at that resolution), `PDF.CLOSED` (no header or end marker) and `PDF.SIZE` (page size differs from the drawing by more than 1 mm). A sheet larger than about 16 000 px at the requested resolution is rendered at the highest resolution Chrome can capture and the render line says so; render crops for a larger print.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Reports what lit-schematic can do on this machine. Drawing and QA need only Node; PNG and
|
|
3
|
+
// PDF export need Chrome or Chromium; Korean labels render in Pretendard when it is found; a
|
|
4
|
+
// cutaway's motion loop needs ffmpeg (MP4, GIF) and img2webp (WebP), and is the only part that does.
|
|
5
|
+
// It never installs anything and always exits 0 unless --strict is given and export is blocked.
|
|
6
|
+
//
|
|
7
|
+
// node doctor.mjs [--out <dir>] [--json] [--strict]
|
|
8
|
+
|
|
9
|
+
import { accessSync, constants, existsSync } from "node:fs";
|
|
10
|
+
import { resolve } from "node:path";
|
|
11
|
+
import { pathToFileURL } from "node:url";
|
|
12
|
+
import { findPretendard } from "./lib/fonts.mjs";
|
|
13
|
+
import { findTool } from "./motion.mjs";
|
|
14
|
+
import { findChrome } from "./render.mjs";
|
|
15
|
+
|
|
16
|
+
export function diagnose({ out = process.cwd(), env = process.env } = {}) {
|
|
17
|
+
const major = Number(process.versions.node.split(".")[0]);
|
|
18
|
+
const chrome = findChrome(env);
|
|
19
|
+
const font = findPretendard();
|
|
20
|
+
let writable = false;
|
|
21
|
+
try {
|
|
22
|
+
accessSync(existsSync(out) ? out : resolve(out, ".."), constants.W_OK);
|
|
23
|
+
writable = true;
|
|
24
|
+
} catch {
|
|
25
|
+
writable = false;
|
|
26
|
+
}
|
|
27
|
+
const checks = [
|
|
28
|
+
{ id: "node", ok: major >= 20, detail: `Node ${process.versions.node}`, needed: "drawing, QA" },
|
|
29
|
+
{ id: "renderer", ok: Boolean(chrome), detail: chrome ?? "no Chrome or Chromium found (set CHROME_PATH)", needed: "PNG, PDF, close-up sheets" },
|
|
30
|
+
{ id: "font.korean", ok: Boolean(font), detail: font ? `${font.file} (${font.source})` : "Pretendard not found; Hangul falls back to the system font", needed: "Korean labels" },
|
|
31
|
+
{ id: "output", ok: writable, detail: resolve(out), needed: "writing files" },
|
|
32
|
+
{ id: "motion.ffmpeg", ok: Boolean(findTool("ffmpeg", env)), detail: findTool("ffmpeg", env) ?? "ffmpeg not found", needed: "motion loop (MP4, GIF)" },
|
|
33
|
+
{ id: "motion.webp", ok: Boolean(findTool("img2webp", env)), detail: findTool("img2webp", env) ?? "img2webp not found", needed: "motion loop (WebP)" },
|
|
34
|
+
];
|
|
35
|
+
const status = !checks[0].ok || !writable ? "BLOCKED" : chrome ? "PASS" : "DEGRADED";
|
|
36
|
+
return {
|
|
37
|
+
status,
|
|
38
|
+
checks,
|
|
39
|
+
setup: [
|
|
40
|
+
...(chrome ? [] : ["Install Google Chrome (or Chromium), or point CHROME_PATH at an existing binary. lit-schematic never installs it for you."]),
|
|
41
|
+
...(findTool("ffmpeg", env) && findTool("img2webp", env) ? [] : ["Only for motion loops: install ffmpeg and the WebP tools (img2webp) with your package manager. lit-schematic never installs them."]),
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
|
47
|
+
const args = process.argv.slice(2);
|
|
48
|
+
const i = args.indexOf("--out");
|
|
49
|
+
const result = diagnose({ out: i >= 0 ? args[i + 1] : process.cwd() });
|
|
50
|
+
if (args.includes("--json")) console.log(JSON.stringify(result, null, 2));
|
|
51
|
+
else {
|
|
52
|
+
for (const c of result.checks) console.log(`${c.ok ? "ok " : "MISS"} ${c.id.padEnd(12)} ${c.detail} [${c.needed}]`);
|
|
53
|
+
for (const line of result.setup) console.log(`setup: ${line}`);
|
|
54
|
+
console.log(`DOCTOR ${result.status}`);
|
|
55
|
+
}
|
|
56
|
+
process.exitCode = args.includes("--strict") && result.status !== "PASS" ? 1 : 0;
|
|
57
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Journal figure pages: an exact column width in millimetres, panels with (a), (b) labels at
|
|
2
|
+
// their top left, a key of stream colours, and the same mm user space as every other kit
|
|
3
|
+
// drawing so the QA gate reads sizes at print scale.
|
|
4
|
+
|
|
5
|
+
import { createCanvas } from "./sheet.mjs";
|
|
6
|
+
import { el, esc, n, pathOf, ptToMm, svgDocument } from "./svg.mjs";
|
|
7
|
+
import { JOURNAL, STREAM, fontFor } from "./theme.mjs";
|
|
8
|
+
|
|
9
|
+
export const WIDTHS = JOURNAL.width;
|
|
10
|
+
|
|
11
|
+
export function createFigure({ width = "double", height, title, desc = "", lang = "en", mode = "journal", labelStyle = "paren", textPt = JOURNAL.textPt } = {}) {
|
|
12
|
+
const w = typeof width === "number" ? width : WIDTHS[width];
|
|
13
|
+
if (!w) throw new Error(`createFigure: width must be a number of mm or one of ${Object.keys(WIDTHS).join(", ")}`);
|
|
14
|
+
if (!height) throw new Error("createFigure: height in mm is required");
|
|
15
|
+
if (height > JOURNAL.maxHeight) throw new Error(`createFigure: ${height} mm is taller than a journal page allows (${JOURNAL.maxHeight} mm)`);
|
|
16
|
+
const panels = [];
|
|
17
|
+
const layers = [];
|
|
18
|
+
const text = ptToMm(textPt);
|
|
19
|
+
const panelText = ptToMm(JOURNAL.panelPt);
|
|
20
|
+
|
|
21
|
+
function panel(label, [x, y, pw, ph]) {
|
|
22
|
+
const box = [x, y, x + pw, y + ph];
|
|
23
|
+
const inner = label === null ? box : [x, y + panelText + 1.6, x + pw, y + ph];
|
|
24
|
+
const entry = { label, box, inner, body: [] };
|
|
25
|
+
panels.push(entry);
|
|
26
|
+
return {
|
|
27
|
+
box,
|
|
28
|
+
inner,
|
|
29
|
+
label,
|
|
30
|
+
canvas: (options = {}) => {
|
|
31
|
+
const c = createCanvas({ box: options.box ?? inner, style: "journal", ...options });
|
|
32
|
+
entry.body.push(() => c.render());
|
|
33
|
+
entry.canvas = c;
|
|
34
|
+
return c;
|
|
35
|
+
},
|
|
36
|
+
iso: (view, { box: target = inner, labelColumn = null, calloutSize = text } = {}) => {
|
|
37
|
+
const fitted = view.fit(target, { labelColumn, size: calloutSize });
|
|
38
|
+
entry.view = view;
|
|
39
|
+
entry.body.push(() => view.render() + view.renderCallouts({ box: target, size: calloutSize }));
|
|
40
|
+
return fitted;
|
|
41
|
+
},
|
|
42
|
+
text: (tx, ty, content, { size = text, anchor = "start", bold = false, color = "#000000", kind = "note" } = {}) => {
|
|
43
|
+
entry.body.push(() => el("text", { x: tx, y: ty, "font-family": fontFor(content), "font-size": size, "text-anchor": anchor, "font-weight": bold ? "bold" : undefined, fill: color, "data-label": kind }, esc(content)));
|
|
44
|
+
},
|
|
45
|
+
raw: (svg) => entry.body.push(() => svg),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// A key of stream roles: a short line swatch and its name, one row each.
|
|
50
|
+
function key(roles, [x, y], { columns = 1, colWidth = 34, rowHeight = text + 1.6 } = {}) {
|
|
51
|
+
const rows = roles.map((role, i) => {
|
|
52
|
+
const spec = STREAM[role];
|
|
53
|
+
if (!spec) throw new Error(`key: unknown stream role ${role}`);
|
|
54
|
+
const cx = x + (i % columns) * colWidth;
|
|
55
|
+
const cy = y + Math.floor(i / columns) * rowHeight;
|
|
56
|
+
return el("g", { "data-legend-item": `role:${role}` },
|
|
57
|
+
el("path", { d: pathOf([[cx, cy - text * 0.3], [cx + 7, cy - text * 0.3]]), stroke: spec.color, "stroke-width": ptToMm(JOURNAL.linePt * 1.4), "stroke-dasharray": spec.dash ?? undefined, fill: "none", "data-legend-line": role }),
|
|
58
|
+
el("text", { x: cx + 9, y: cy, "font-family": fontFor(spec.label), "font-size": text, fill: "#000000", "data-label": "legend" }, esc(spec.label)));
|
|
59
|
+
});
|
|
60
|
+
layers.push(el("g", { "data-key": "streams" }, rows));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function render() {
|
|
64
|
+
const body = [el("rect", { x: 0, y: 0, width: w, height, fill: "#ffffff" })];
|
|
65
|
+
panels.forEach((p) => {
|
|
66
|
+
if (p.label === null) {
|
|
67
|
+
body.push(el("g", { "data-figure-body": "1" }, p.body.map((f) => f())));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const [x0, y0] = p.box;
|
|
71
|
+
const shown = labelStyle === "paren" ? `(${p.label})` : labelStyle === "bold" ? p.label : p.label.toUpperCase();
|
|
72
|
+
body.push(el("g", { "data-panel": p.label, "data-panel-box": p.box.map(n).join(" ") },
|
|
73
|
+
el("text", { x: x0 + 0.2, y: y0 + panelText * 0.8, "font-family": fontFor(shown), "font-size": panelText, "font-weight": "bold", fill: "#000000", "data-label": "panel-label" }, esc(shown)),
|
|
74
|
+
p.body.map((f) => f())));
|
|
75
|
+
});
|
|
76
|
+
body.push(...layers);
|
|
77
|
+
return svgDocument({ width: w, height, title, desc, mode, lang, body: body.join(""), extra: { "data-min-text-pt": JOURNAL.minTextPt } });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { width: w, height, text, panel, key, render, warnings: () => panels.flatMap((p) => [...(p.canvas?.warnings ?? []), ...(p.view?.warnings ?? [])]) };
|
|
81
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Locates fonts for rendering. Pretendard ships with the diagram skill (unmodified official
|
|
2
|
+
// file under the OFL); lit-schematic reads it from there instead of carrying a second copy.
|
|
3
|
+
|
|
4
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { join, resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
export const SKILL_ROOT = resolve(fileURLToPath(new URL("../..", import.meta.url)));
|
|
10
|
+
|
|
11
|
+
export function findPretendard() {
|
|
12
|
+
const inPlugin = [
|
|
13
|
+
join(SKILL_ROOT, "..", "lit-diagram-drawer", "assets", "fonts", "PretendardVariable.woff2"),
|
|
14
|
+
join(SKILL_ROOT, "..", "lit-pptx", "pretendard-font", "public", "static", "Pretendard-Regular.otf"),
|
|
15
|
+
];
|
|
16
|
+
for (const file of inPlugin) if (existsSync(file)) return { file, source: "plugin" };
|
|
17
|
+
const dirs = [join(homedir(), "Library", "Fonts"), "/Library/Fonts", join(homedir(), ".local", "share", "fonts"), "/usr/share/fonts", "C:\\Windows\\Fonts"];
|
|
18
|
+
for (const dir of dirs) {
|
|
19
|
+
try {
|
|
20
|
+
const hit = readdirSync(dir).find((name) => /^Pretendard.*\.(?:otf|ttf|woff2)$/iu.test(name));
|
|
21
|
+
if (hit) return { file: join(dir, hit), source: "system" };
|
|
22
|
+
} catch {
|
|
23
|
+
// directory absent on this platform
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|